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->Go(); 242 break; 243 } 244 case B_ENTRY_NOT_FOUND: 245 { 246 create_directory(COLOR_SET_DIR,0777); 247 break; 248 } 249 case B_BAD_VALUE: 250 { 251 printf("CurView::LoadCursorSets(): Invalid cursorset folder path.\n"); 252 break; 253 } 254 case B_NO_MEMORY: 255 { 256 printf("CurView::LoadCursorSets(): No memory left. We're probably going to crash now. \n"); 257 break; 258 } 259 case B_BUSY: 260 { 261 printf("CurView::LoadCursorSets(): Busy node " CURSOR_SET_DIR "\n"); 262 break; 263 } 264 case B_FILE_ERROR: 265 { 266 BAlert *a=new BAlert("OpenBeOS","Couldn't open the folder for cursor sets " 267 "because of a file error. Perhaps there is a file (instead of a folder) at " COLOR_SET_DIR 268 "? You will be able to change system cursors, but be unable to save them to a cursor set. ", 269 "OK", NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 270 a->Go(); 271 break; 272 } 273 case B_NO_MORE_FDS: 274 { 275 BAlert *a=new BAlert("OpenBeOS","Couldn't open the folder for cursor sets " 276 "because there are too many open files. Please close some files and restart " 277 " this application.", "OK", 278 NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 279 a->Go(); 280 if(Window()) 281 Window()->PostMessage(B_QUIT_REQUESTED); 282 break; 283 } 284 } 285 286 return menu; 287 } 288 289 int32 count=dir.CountEntries(); 290 291 292 BMessage *msg; 293 CursorSet cs(NULL); 294 295 for(int32 i=0;i<count;i++) 296 { 297 dir.GetNextEntry(&entry); 298 entry.GetPath(&path); 299 300 if(cs.Load(path.Path())!=B_OK) 301 continue; 302 303 // Don't include the default set in the menu 304 name=path.Leaf(); 305 if(name.Compare("Default")==0) 306 continue; 307 308 name=path.Path(); 309 310 name.Remove(0,name.FindLast('/')+1); 311 312 msg=new BMessage(LOAD_CURSORSET); 313 msg->AddString("name",name); 314 menu->AddItem(new BMenuItem(name.String(),msg)); 315 } 316 317 return menu; 318 } 319 320 void CurView::SetCursorSetName(const char *name) 321 { 322 if(!name) 323 return; 324 BString namestr("Cursor Set: "); 325 cursorset_name=name; 326 namestr+=name; 327 cursorset_label->SetText(namestr.String()); 328 cursorset_label->ResizeToPreferred(); 329 cursorset_label->Invalidate(); 330 } 331 */ 332 333 void CurView::SaveSettings(void) 334 { 335 // Save the current GUI cursor settings to the GUI cursors file in the 336 // path specified in defs.h 337 338 BString path(SERVER_SETTINGS_DIR); 339 path+=CURSOR_SETTINGS_NAME; 340 #ifdef DEBUG_CURSORSET 341 printf("SaveSettings: %s\n",path.String()); 342 #endif 343 cursorset->Save(path.String(),B_CREATE_FILE|B_ERASE_FILE); 344 345 // prev_set_name=cursorset_name; 346 revert->SetEnabled(false); 347 } 348 349 void CurView::LoadSettings(void) 350 { 351 // Load the current GUI cursor settings from disk. This is done instead of 352 // getting them from the server at this point for testing purposes. Comment 353 // out the #define LOAD_SETTINGS_FROM_DISK line to use the server query code 354 #ifdef DEBUG_CURSORSET 355 printf("Loading settings from disk\n"); 356 #endif 357 settings.MakeEmpty(); 358 359 BDirectory dir,newdir; 360 if(dir.SetTo(SERVER_SETTINGS_DIR)==B_ENTRY_NOT_FOUND) 361 { 362 #ifdef DEBUG_CURSORSET 363 printf("Cursor set folder not found. Creating %s\n",SERVER_SETTINGS_DIR); 364 #endif 365 create_directory(SERVER_SETTINGS_DIR,0777); 366 } 367 368 BString path(SERVER_SETTINGS_DIR); 369 path+=CURSOR_SETTINGS_NAME; 370 371 status_t stat=cursorset->Load(path.String()); 372 373 if(stat!=B_OK) 374 { 375 #ifdef DEBUG_CURSORSET 376 printf("Couldn't open file %s for read\n",path.String()); 377 #endif 378 SetDefaults(); 379 SaveSettings(); 380 } 381 return; 382 } 383 384 void CurView::SetDefaults(void) 385 { 386 // The server will perform the necessary work to set defaults, so just ask it to do the 387 // work for us. It is a synchronous procedure, so we will notify the server and load the cursor 388 // set 'Default'. 389 BString string(CURSOR_SET_DIR); 390 string+="Default"; 391 392 cursorset->Load(string.String()); 393 394 port_id port=find_port(SERVER_PORT_NAME); 395 if(port==B_NAME_NOT_FOUND) 396 return; 397 398 BPrivate::PortLink link(port); 399 int32 code; 400 401 link.StartMessage(AS_SET_SYSCURSOR_DEFAULTS); 402 link.Flush(); 403 link.GetNextMessage(code); 404 405 } 406 407 BitmapView::BitmapView(const BPoint &pt,BMessage *message, const BHandler *handler, const BLooper *looper=NULL) 408 : BBox(BRect(0,0,75,75).OffsetToCopy(pt),"bitmapview",B_FOLLOW_NONE,B_WILL_DRAW, B_PLAIN_BORDER), 409 BInvoker(message,handler,looper) 410 { 411 SetFont(be_plain_font); 412 bitmap=NULL; 413 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 414 SetDrawingMode(B_OP_ALPHA); 415 drawrect=Bounds().InsetByCopy(5,5); 416 } 417 418 BitmapView::~BitmapView(void) 419 { 420 } 421 422 void BitmapView::SetBitmap(BBitmap *bmp) 423 { 424 bitmap=bmp; 425 } 426 427 void BitmapView::Draw(BRect r) 428 { 429 BBox::Draw(r); 430 431 if(bitmap) 432 DrawBitmap(bitmap, drawrect); 433 } 434 435 void BitmapView::MessageReceived(BMessage *msg) 436 { 437 if(msg->WasDropped()) 438 { 439 entry_ref ref; 440 if(msg->FindRef("refs",&ref)!=B_OK) 441 return; 442 443 BBitmap *tmp=BTranslationUtils::GetBitmap(&ref); 444 if(tmp) 445 { 446 delete bitmap; 447 bitmap=tmp; 448 449 int32 offset; 450 BRect r(Bounds()); 451 r.right-=10; 452 r.bottom-=10; 453 454 drawrect=bitmap->Bounds(); 455 if(r.Contains(bitmap->Bounds())) 456 { 457 // calculate a centered rect for direct display 458 offset=((r.IntegerWidth()-bitmap->Bounds().IntegerWidth()) >> 1)+5; 459 drawrect.OffsetBy(offset,offset); 460 } 461 else 462 { 463 // calculate a scaled-down rectangle for display 464 drawrect.left=drawrect.top=0; 465 466 if(bitmap->Bounds().Height() > bitmap->Bounds().Width()) 467 { 468 drawrect.right=(r.Height()*bitmap->Bounds().Width())/bitmap->Bounds().Height(); 469 drawrect.bottom=r.Height(); 470 offset=((r.IntegerWidth()-drawrect.IntegerWidth()) >> 1)+5; 471 drawrect.OffsetBy(offset,5); 472 } 473 else 474 { 475 drawrect.bottom=(r.Width()*bitmap->Bounds().Height())/bitmap->Bounds().Width(); 476 drawrect.right=r.Width(); 477 offset=((r.IntegerHeight()-drawrect.IntegerHeight()) >> 1)+5; 478 drawrect.OffsetBy(5,offset); 479 } 480 } 481 Invoke(); 482 Invalidate(); 483 return; 484 } 485 } 486 else 487 BBox::MessageReceived(msg); 488 } 489 490