xref: /haiku/src/preferences/appearance/CurView.cpp (revision 7ee53ed3bd2222305c93a4959f8c587c373ed97c)
1 //------------------------------------------------------------------------------
2 //	Copyright (c) 2001-2002, OpenBeOS
3 //
4 //	Permission is hereby granted, free of charge, to any person obtaining a
5 //	copy of this software and associated documentation files (the "Software"),
6 //	to deal in the Software without restriction, including without limitation
7 //	the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 //	and/or sell copies of the Software, and to permit persons to whom the
9 //	Software is furnished to do so, subject to the following conditions:
10 //
11 //	The above copyright notice and this permission notice shall be included in
12 //	all copies or substantial portions of the Software.
13 //
14 //	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 //	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 //	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 //	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 //	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 //	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 //	DEALINGS IN THE SOFTWARE.
21 //
22 //	File Name:		CurView.cpp
23 //	Author:			DarkWyrm <bpmagic@columbus.rr.com>
24 //	Description:	cursor handler for the app
25 //
26 //------------------------------------------------------------------------------
27 #include <OS.h>
28 #include <Directory.h>
29 #include <Alert.h>
30 #include <storage/Path.h>
31 #include <Entry.h>
32 #include <File.h>
33 #include <stdio.h>
34 #include "CursorWhichItem.h"
35 #include "CurView.h"
36 #include <PortLink.h>
37 #include "defs.h"
38 #include "ServerConfig.h"
39 #include <ServerProtocol.h>
40 #include <PortMessage.h>
41 #include <InterfaceDefs.h>
42 #include <TranslationUtils.h>
43 
44 //#define DEBUG_CURSORSET
45 
46 #define SAVE_CURSORSET 'svcu'
47 #define DELETE_CURSORSET 'dlcu'
48 #define LOAD_CURSORSET 'ldcu'
49 #define CURSOR_UPDATED 'csru'
50 
51 CurView::CurView(const BRect &frame, const char *name, int32 resize, int32 flags)
52 	:BView(frame,name,resize,flags), settings(B_SIMPLE_DATA)
53 {
54 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
55 
56 
57 	cursorset=new CursorSet("Default");
58 
59 /*
60 	// Code disabled -- cursor set management belongs in another app
61 	BMenuBar *mb=new BMenuBar(BRect(0,0,Bounds().Width(),16),"menubar");
62 
63 	settings_menu=new BMenu("Settings");
64 	settings_menu->AddItem(new BMenuItem("Save Cursor Set",new BMessage(SAVE_CURSORSET),'S'));
65 	settings_menu->AddSeparatorItem();
66 	settings_menu->AddItem(new BMenuItem("Delete Cursor Set",new BMessage(DELETE_CURSORSET)));
67 	mb->AddItem(settings_menu);
68 
69 	cursorset_menu=LoadCursorSets();
70 	if(cursorset_menu)
71 		mb->AddItem(cursorset_menu);
72 	else
73 	{
74 		// We should *never* be here, but just in case....
75 		cursorset_menu=new BMenu("Cursor Sets");
76 		mb->AddItem(cursorset_menu);
77 	}
78 	AddChild(mb);
79 */
80 	BRect wellrect(0,0,20,20);
81 	wellrect.OffsetTo(10,25);
82 	wellrect.right=wellrect.left+50;
83 
84 /*
85 	// Code disabled -- cursor set management belongs in another app
86 	cursorset_label=new BStringView(wellrect,"cursorset_label","Cursor Set: ");
87 	AddChild(cursorset_label);
88 	cursorset_label->ResizeToPreferred();
89 	cursorset_name="<untitled>";
90 */
91 
92 	// Set up list of cursor attributes
93 	BRect cvrect;
94 	BRect rect;
95 
96 	cvrect.Set(0,0,75,75);
97 
98 	bmpview=new BitmapView(BPoint(10,10),new BMessage(CURSOR_UPDATED),this);
99 	bmpview->MoveTo( (Bounds().Width()-bmpview->Bounds().Width())/2,30);
100 	AddChild(bmpview);
101 
102 
103 	rect.left=(Bounds().Width()-200)/2;
104 	rect.right=rect.left+190;
105 	rect.top=bmpview->Frame().bottom+30;
106 	rect.bottom=rect.top+100;
107 
108 	attrlist=new BListView(rect,"AttributeList");
109 
110 	scrollview=new BScrollView("ScrollView",attrlist, B_FOLLOW_LEFT |
111 		B_FOLLOW_TOP, 0, false, true);
112 	AddChild(scrollview);
113 	scrollview->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
114 
115 	attrlist->SetSelectionMessage(new BMessage(ATTRIBUTE_CHOSEN));
116 
117 	attrlist->AddItem(new CursorWhichItem(B_CURSOR_DEFAULT));
118 	attrlist->AddItem(new CursorWhichItem(B_CURSOR_TEXT));
119 	attrlist->AddItem(new CursorWhichItem(B_CURSOR_MOVE));
120 	attrlist->AddItem(new CursorWhichItem(B_CURSOR_DRAG));
121 	attrlist->AddItem(new CursorWhichItem(B_CURSOR_RESIZE));
122 	attrlist->AddItem(new CursorWhichItem(B_CURSOR_RESIZE_NWSE));
123 	attrlist->AddItem(new CursorWhichItem(B_CURSOR_RESIZE_NESW));
124 	attrlist->AddItem(new CursorWhichItem(B_CURSOR_RESIZE_NS));
125 	attrlist->AddItem(new CursorWhichItem(B_CURSOR_RESIZE_EW));
126 
127 
128 	cvrect.Set(0,0,60,30);
129 	cvrect.OffsetTo( (Bounds().Width()-200)/2,scrollview->Frame().bottom+20);
130 
131 	defaults=new BButton(cvrect,"DefaultsButton","Defaults",
132 		new BMessage(DEFAULT_SETTINGS),B_FOLLOW_LEFT |B_FOLLOW_TOP,
133 		B_WILL_DRAW | B_NAVIGABLE);
134 	AddChild(defaults);
135 
136 	cvrect.OffsetBy(70,0);
137 	revert=new BButton(cvrect,"RevertButton","Revert",
138 		new BMessage(REVERT_SETTINGS),B_FOLLOW_LEFT |B_FOLLOW_TOP,
139 		B_WILL_DRAW | B_NAVIGABLE);
140 	AddChild(revert);
141 	revert->SetEnabled(false);
142 
143 	cvrect.OffsetBy(70,0);
144 	apply=new BButton(cvrect,"ApplyButton","Apply",
145 		new BMessage(APPLY_SETTINGS),B_FOLLOW_LEFT |B_FOLLOW_TOP,
146 		B_WILL_DRAW | B_NAVIGABLE);
147 	AddChild(apply);
148 	apply->SetEnabled(false);
149 
150 	BEntry entry(COLOR_SET_DIR);
151 	entry_ref ref;
152 	entry.GetRef(&ref);
153 
154 	attribute=B_PANEL_BACKGROUND_COLOR;
155 	attrstring="Background";
156 	LoadSettings();
157 
158 }
159 
160 CurView::~CurView(void)
161 {
162 	delete cursorset;
163 }
164 
165 void CurView::AllAttached(void)
166 {
167 	attrlist->Select(0);
168 	attrlist->SetTarget(this);
169 	apply->SetTarget(this);
170 	defaults->SetTarget(this);
171 	revert->SetTarget(this);
172 	bmpview->SetTarget(this);
173 	BMessenger msgr(this);
174 }
175 
176 void CurView::MessageReceived(BMessage *msg)
177 {
178 	if(msg->WasDropped())
179 	{
180 	}
181 
182 	switch(msg->what)
183 	{
184 		case CURSOR_UPDATED:
185 		{
186 			CursorWhichItem *cwi=(CursorWhichItem*)attrlist->ItemAt(attrlist->CurrentSelection());
187 			if(cwi)
188 				cwi->SetBitmap(bmpview->GetBitmap());
189 
190 			break;
191 		}
192 		case ATTRIBUTE_CHOSEN:
193 		{
194 			CursorWhichItem *cwi=(CursorWhichItem*)attrlist->ItemAt(attrlist->CurrentSelection());
195 			if(cwi)
196 			{
197 				bmpview->SetBitmap(cwi->GetBitmap());
198 				bmpview->Invalidate();
199 			}
200 
201 			break;
202 		}
203 		default:
204 			BView::MessageReceived(msg);
205 			break;
206 	}
207 }
208 
209 /*
210 // Code disabled -- cursor set management belongs in another app
211 BMenu *CurView::LoadCursorSets(void)
212 {
213 #ifdef DEBUG_CURSORSET
214 printf("Loading cursor sets from disk\n");
215 #endif
216 	// This function populates the member menu *cursorset_menu with the cursor
217 	// set files located in the cursor set directory. To ensure that there are
218 	// no entries pointing to invalid cursor sets, they are validated before
219 	// a menu item is added.
220 	BDirectory dir;
221 	BEntry entry;
222 	BPath path;
223 	BString name;
224 
225 	BMenu *menu=new BMenu("Cursor Sets");
226 
227 	status_t dirstat=dir.SetTo(COLOR_SET_DIR);
228 	if(dirstat!=B_OK)
229 	{
230 		// We couldn't set the directory, so more than likely it just
231 		// doesn't exist. Create it and return an empty menu.
232 		switch(dirstat)
233 		{
234 			case B_NAME_TOO_LONG:
235 			{
236 				BAlert *a=new BAlert("OpenBeOS","Couldn't open the folder for cursor sets. "
237 					"You will be able to change system cursors, but be unable to save them to a cursor set. "
238 					"Please contact OpenBeOS about Appearance Preferences::CurView::"
239 					"LoadCursorSets::B_NAME_TOO_LONG for a bugfix", "OK",
240 					 NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
241 				a->SetFlags(a->Flags() | B_CLOSE_ON_ESCAPE);
242 				a->Go();
243 				break;
244 			}
245 			case B_ENTRY_NOT_FOUND:
246 			{
247 				create_directory(COLOR_SET_DIR,0777);
248 				break;
249 			}
250 			case B_BAD_VALUE:
251 			{
252 				printf("CurView::LoadCursorSets(): Invalid cursorset folder path.\n");
253 				break;
254 			}
255 			case B_NO_MEMORY:
256 			{
257 				printf("CurView::LoadCursorSets(): No memory left. We're probably going to crash now. \n");
258 				break;
259 			}
260 			case B_BUSY:
261 			{
262 				printf("CurView::LoadCursorSets(): Busy node " CURSOR_SET_DIR "\n");
263 				break;
264 			}
265 			case B_FILE_ERROR:
266 			{
267 				BAlert *a=new BAlert("OpenBeOS","Couldn't open the folder for cursor sets "
268 					"because of a file error. Perhaps there is a file (instead of a folder) at " COLOR_SET_DIR
269 					"? You will be able to change system cursors, but be unable to save them to a cursor set. ",
270 					"OK", NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
271 				a->SetFlags(a->Flags() | B_CLOSE_ON_ESCAPE);
272 				a->Go();
273 				break;
274 			}
275 			case B_NO_MORE_FDS:
276 			{
277 				BAlert *a=new BAlert("OpenBeOS","Couldn't open the folder for cursor sets "
278 					"because there are too many open files. Please close some files and restart "
279 					" this application.", "OK",
280 					 NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
281 				a->SetFlags(a->Flags() | B_CLOSE_ON_ESCAPE);
282 				a->Go();
283 				if(Window())
284 					Window()->PostMessage(B_QUIT_REQUESTED);
285 				break;
286 			}
287 		}
288 
289 		return menu;
290 	}
291 
292 	int32 count=dir.CountEntries();
293 
294 
295 	BMessage *msg;
296 	CursorSet cs(NULL);
297 
298 	for(int32 i=0;i<count;i++)
299 	{
300 		dir.GetNextEntry(&entry);
301 		entry.GetPath(&path);
302 
303 		if(cs.Load(path.Path())!=B_OK)
304 			continue;
305 
306 		// Don't include the default set in the menu
307 		name=path.Leaf();
308 		if(name.Compare("Default")==0)
309 			continue;
310 
311 		name=path.Path();
312 
313 		name.Remove(0,name.FindLast('/')+1);
314 
315 		msg=new BMessage(LOAD_CURSORSET);
316 		msg->AddString("name",name);
317 		menu->AddItem(new BMenuItem(name.String(),msg));
318 	}
319 
320 	return menu;
321 }
322 
323 void CurView::SetCursorSetName(const char *name)
324 {
325 	if(!name)
326 		return;
327 	BString namestr("Cursor Set: ");
328 	cursorset_name=name;
329 	namestr+=name;
330 	cursorset_label->SetText(namestr.String());
331 	cursorset_label->ResizeToPreferred();
332 	cursorset_label->Invalidate();
333 }
334 */
335 
336 void CurView::SaveSettings(void)
337 {
338 	// Save the current GUI cursor settings to the GUI cursors file in the
339 	// path specified in defs.h
340 
341 	BString path(SERVER_SETTINGS_DIR);
342 	path+=CURSOR_SETTINGS_NAME;
343 #ifdef DEBUG_CURSORSET
344 printf("SaveSettings: %s\n",path.String());
345 #endif
346 	cursorset->Save(path.String(),B_CREATE_FILE|B_ERASE_FILE);
347 
348 //	prev_set_name=cursorset_name;
349 	revert->SetEnabled(false);
350 }
351 
352 void CurView::LoadSettings(void)
353 {
354 	// Load the current GUI cursor settings from disk. This is done instead of
355 	// getting them from the server at this point for testing purposes. Comment
356 	// out the #define LOAD_SETTINGS_FROM_DISK line to use the server query code
357 #ifdef DEBUG_CURSORSET
358 printf("Loading settings from disk\n");
359 #endif
360 	settings.MakeEmpty();
361 
362 	BDirectory dir,newdir;
363 	if(dir.SetTo(SERVER_SETTINGS_DIR)==B_ENTRY_NOT_FOUND)
364 	{
365 #ifdef DEBUG_CURSORSET
366 printf("Cursor set folder not found. Creating %s\n",SERVER_SETTINGS_DIR);
367 #endif
368 		create_directory(SERVER_SETTINGS_DIR,0777);
369 	}
370 
371 	BString path(SERVER_SETTINGS_DIR);
372 	path+=CURSOR_SETTINGS_NAME;
373 
374 	status_t stat=cursorset->Load(path.String());
375 
376 	if(stat!=B_OK)
377 	{
378 #ifdef DEBUG_CURSORSET
379 printf("Couldn't open file %s for read\n",path.String());
380 #endif
381 		SetDefaults();
382 		SaveSettings();
383 	}
384 	return;
385 }
386 
387 void CurView::SetDefaults(void)
388 {
389 	// The server will perform the necessary work to set defaults, so just ask it to do the
390 	// work for us. It is a synchronous procedure, so we will notify the server and load the cursor
391 	// set 'Default'.
392 	BString string(CURSOR_SET_DIR);
393 	string+="Default";
394 
395 	cursorset->Load(string.String());
396 
397 	port_id port=find_port(SERVER_PORT_NAME);
398 	if(port==B_NAME_NOT_FOUND)
399 		return;
400 
401 	BPrivate::PortLink link(port);
402 	int32 code;
403 
404 	link.StartMessage(AS_SET_SYSCURSOR_DEFAULTS);
405 	link.Flush();
406 	link.GetNextMessage(code);
407 
408 }
409 
410 BitmapView::BitmapView(const BPoint &pt,BMessage *message, const BHandler *handler, const BLooper *looper=NULL)
411  : BBox(BRect(0,0,75,75).OffsetToCopy(pt),"bitmapview",B_FOLLOW_NONE,B_WILL_DRAW, B_PLAIN_BORDER),
412  	BInvoker(message,handler,looper)
413 {
414 	SetFont(be_plain_font);
415 	bitmap=NULL;
416 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
417 	SetDrawingMode(B_OP_ALPHA);
418 	drawrect=Bounds().InsetByCopy(5,5);
419 }
420 
421 BitmapView::~BitmapView(void)
422 {
423 }
424 
425 void BitmapView::SetBitmap(BBitmap *bmp)
426 {
427 	bitmap=bmp;
428 }
429 
430 void BitmapView::Draw(BRect r)
431 {
432 	BBox::Draw(r);
433 
434 	if(bitmap)
435 		DrawBitmap(bitmap, drawrect);
436 }
437 
438 void BitmapView::MessageReceived(BMessage *msg)
439 {
440 	if(msg->WasDropped())
441 	{
442 		entry_ref ref;
443 		if(msg->FindRef("refs",&ref)!=B_OK)
444 			return;
445 
446 		BBitmap *tmp=BTranslationUtils::GetBitmap(&ref);
447 		if(tmp)
448 		{
449 			delete bitmap;
450 			bitmap=tmp;
451 
452 			int32 offset;
453 			BRect r(Bounds());
454 			r.right-=10;
455 			r.bottom-=10;
456 
457 			drawrect=bitmap->Bounds();
458 			if(r.Contains(bitmap->Bounds()))
459 			{
460 				// calculate a centered rect for direct display
461 				offset=((r.IntegerWidth()-bitmap->Bounds().IntegerWidth()) >> 1)+5;
462 				drawrect.OffsetBy(offset,offset);
463 			}
464 			else
465 			{
466 				// calculate a scaled-down rectangle for display
467 				drawrect.left=drawrect.top=0;
468 
469 				if(bitmap->Bounds().Height() > bitmap->Bounds().Width())
470 				{
471 					drawrect.right=(r.Height()*bitmap->Bounds().Width())/bitmap->Bounds().Height();
472 					drawrect.bottom=r.Height();
473 					offset=((r.IntegerWidth()-drawrect.IntegerWidth()) >> 1)+5;
474 					drawrect.OffsetBy(offset,5);
475 				}
476 				else
477 				{
478 					drawrect.bottom=(r.Width()*bitmap->Bounds().Height())/bitmap->Bounds().Width();
479 					drawrect.right=r.Width();
480 					offset=((r.IntegerHeight()-drawrect.IntegerHeight()) >> 1)+5;
481 					drawrect.OffsetBy(5,offset);
482 				}
483 			}
484 			Invoke();
485 			Invalidate();
486 			return;
487 		}
488 	}
489 	else
490 		BBox::MessageReceived(msg);
491 }
492 
493