1 //------------------------------------------------------------------------------
2 // Copyright (c) 2001-2002, Haiku
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
CurView(const BRect & frame,const char * name,int32 resize,int32 flags)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 SetViewUIColor(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->SetViewUIColor(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
~CurView(void)160 CurView::~CurView(void)
161 {
162 delete cursorset;
163 }
164
AllAttached(void)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
MessageReceived(BMessage * msg)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("Haiku","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 Haiku 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("Haiku","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("Haiku","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
SaveSettings(void)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
LoadSettings(void)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
SetDefaults(void)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
BitmapView(const BPoint & pt,BMessage * message,const BHandler * handler,const BLooper * looper=NULL)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 SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
417 SetDrawingMode(B_OP_ALPHA);
418 drawrect=Bounds().InsetByCopy(5,5);
419 }
420
~BitmapView(void)421 BitmapView::~BitmapView(void)
422 {
423 }
424
SetBitmap(BBitmap * bmp)425 void BitmapView::SetBitmap(BBitmap *bmp)
426 {
427 bitmap=bmp;
428 }
429
Draw(BRect r)430 void BitmapView::Draw(BRect r)
431 {
432 BBox::Draw(r);
433
434 if(bitmap)
435 DrawBitmap(bitmap, drawrect);
436 }
437
MessageReceived(BMessage * msg)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