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