1 /* 2 Open Tracker License 3 4 Terms and Conditions 5 6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy of 9 this software and associated documentation files (the "Software"), to deal in 10 the Software without restriction, including without limitation the rights to 11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 of the Software, and to permit persons to whom the Software is furnished to do 13 so, subject to the following conditions: 14 15 The above copyright notice and this permission notice applies to all licensees 16 and shall be included in all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION 23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25 Except as contained in this notice, the name of Be Incorporated shall not be 26 used in advertising or otherwise to promote the sale, use or other dealings in 27 this Software without prior written authorization from Be Incorporated. 28 29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks 30 of Be Incorporated in the United States and other countries. Other brand product 31 names are registered trademarks or trademarks of their respective holders. 32 All rights reserved. 33 */ 34 35 36 #include "DeskWindow.h" 37 38 #include <Catalog.h> 39 #include <Debug.h> 40 #include <FindDirectory.h> 41 #include <Locale.h> 42 #include <Messenger.h> 43 #include <NodeMonitor.h> 44 #include <Path.h> 45 #include <PathFinder.h> 46 #include <PathMonitor.h> 47 #include <PopUpMenu.h> 48 #include <Resources.h> 49 #include <Screen.h> 50 #include <String.h> 51 #include <StringList.h> 52 #include <Volume.h> 53 #include <WindowPrivate.h> 54 55 #include <fcntl.h> 56 #include <unistd.h> 57 58 #include "Attributes.h" 59 #include "AutoLock.h" 60 #include "BackgroundImage.h" 61 #include "Commands.h" 62 #include "FSUtils.h" 63 #include "IconMenuItem.h" 64 #include "KeyInfos.h" 65 #include "MountMenu.h" 66 #include "PoseView.h" 67 #include "TemplatesMenu.h" 68 #include "Tracker.h" 69 70 71 const char* kShelfPath = "tracker_shelf"; 72 // replicant support 73 74 const char* kShortcutsSettings = "shortcuts_settings"; 75 const char* kDefaultShortcut = "BEOS:default_shortcut"; 76 const uint32 kDefaultModifiers = B_OPTION_KEY | B_COMMAND_KEY; 77 78 79 static struct AddOnShortcut* 80 MatchOne(struct AddOnShortcut* item, void* castToName) 81 { 82 if (strcmp(item->model->Name(), (const char*)castToName) == 0) { 83 // found match, bail out 84 return item; 85 } 86 87 return 0; 88 } 89 90 91 static void 92 AddOneShortcut(Model* model, char key, uint32 modifiers, BDeskWindow* window) 93 { 94 if (key == '\0') 95 return; 96 97 BMessage* runAddOn = new BMessage(kLoadAddOn); 98 runAddOn->AddRef("refs", model->EntryRef()); 99 window->AddShortcut(key, modifiers, runAddOn); 100 } 101 102 103 static struct AddOnShortcut* 104 RevertToDefault(struct AddOnShortcut* item, void* castToWindow) 105 { 106 if (item->key != item->defaultKey || item->modifiers != kDefaultModifiers) { 107 BDeskWindow* window = static_cast<BDeskWindow*>(castToWindow); 108 if (window != NULL) { 109 window->RemoveShortcut(item->key, item->modifiers); 110 item->key = item->defaultKey; 111 item->modifiers = kDefaultModifiers; 112 AddOneShortcut(item->model, item->key, item->modifiers, window); 113 } 114 } 115 116 return 0; 117 } 118 119 120 static struct AddOnShortcut* 121 FindElement(struct AddOnShortcut* item, void* castToOther) 122 { 123 Model* other = static_cast<Model*>(castToOther); 124 if (*item->model->EntryRef() == *other->EntryRef()) 125 return item; 126 127 return 0; 128 } 129 130 131 static void 132 LoadAddOnDir(BDirectory directory, BDeskWindow* window, 133 LockingList<AddOnShortcut>* list) 134 { 135 BEntry entry; 136 while (directory.GetNextEntry(&entry) == B_OK) { 137 Model* model = new Model(&entry); 138 if (model->InitCheck() == B_OK && model->IsSymLink()) { 139 // resolve symlinks 140 Model* resolved = new Model(model->EntryRef(), true, true); 141 if (resolved->InitCheck() == B_OK) 142 model->SetLinkTo(resolved); 143 else 144 delete resolved; 145 } 146 if (model->InitCheck() != B_OK 147 || !model->ResolveIfLink()->IsExecutable()) { 148 delete model; 149 continue; 150 } 151 152 char* name = strdup(model->Name()); 153 if (!list->EachElement(MatchOne, name)) { 154 struct AddOnShortcut* item = new struct AddOnShortcut; 155 item->model = model; 156 157 BResources resources(model->ResolveIfLink()->EntryRef()); 158 size_t size; 159 char* shortcut = (char*)resources.LoadResource(B_STRING_TYPE, 160 kDefaultShortcut, &size); 161 if (shortcut == NULL || strlen(shortcut) > 1) 162 item->key = '\0'; 163 else 164 item->key = shortcut[0]; 165 AddOneShortcut(model, item->key, kDefaultModifiers, window); 166 item->defaultKey = item->key; 167 item->modifiers = kDefaultModifiers; 168 list->AddItem(item); 169 } 170 free(name); 171 } 172 173 node_ref nodeRef; 174 directory.GetNodeRef(&nodeRef); 175 176 TTracker::WatchNode(&nodeRef, B_WATCH_DIRECTORY, window); 177 } 178 179 180 // #pragma mark - BDeskWindow 181 182 183 #undef B_TRANSLATION_CONTEXT 184 #define B_TRANSLATION_CONTEXT "DeskWindow" 185 186 187 BDeskWindow::BDeskWindow(LockingList<BWindow>* windowList, uint32 openFlags) 188 : 189 BContainerWindow(windowList, openFlags, kDesktopWindowLook, kDesktopWindowFeel, 190 B_NOT_MOVABLE | B_WILL_ACCEPT_FIRST_CLICK | B_NOT_ZOOMABLE | B_NOT_CLOSABLE 191 | B_NOT_MINIMIZABLE | B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS, 192 B_ALL_WORKSPACES, false), 193 fDeskShelf(NULL), 194 fNodeRef(NULL), 195 fShortcutsSettings(NULL) 196 { 197 // Add icon view switching shortcuts. These are displayed in the context 198 // menu, although they obviously don't work from those menu items. 199 BMessage* message = new BMessage(kIconMode); 200 AddShortcut('1', B_COMMAND_KEY, message, PoseView()); 201 202 message = new BMessage(kMiniIconMode); 203 AddShortcut('2', B_COMMAND_KEY, message, PoseView()); 204 205 message = new BMessage(kIconMode); 206 message->AddInt32("scale", 1); 207 AddShortcut('+', B_COMMAND_KEY, message, PoseView()); 208 209 message = new BMessage(kIconMode); 210 message->AddInt32("scale", 0); 211 AddShortcut('-', B_COMMAND_KEY, message, PoseView()); 212 } 213 214 215 BDeskWindow::~BDeskWindow() 216 { 217 SaveDesktopPoseLocations(); 218 // explicit call to SavePoseLocations so that extended pose info 219 // gets committed properly 220 PoseView()->DisableSaveLocation(); 221 // prevent double-saving, this would slow down quitting 222 PoseView()->StopSettingsWatch(); 223 stop_watching(this); 224 } 225 226 227 void 228 BDeskWindow::Init(const BMessage*) 229 { 230 // Set the size of the screen before calling the container window's 231 // Init() because it will add volume poses to this window and 232 // they will be clipped otherwise 233 234 BScreen screen(this); 235 fOldFrame = screen.Frame(); 236 237 ResizeTo(fOldFrame.Width(), fOldFrame.Height()); 238 239 InitKeyIndices(); 240 InitAddOnsList(false); 241 ApplyShortcutPreferences(false); 242 243 _inherited::Init(); 244 245 entry_ref ref; 246 BPath path; 247 if (!BootedInSafeMode() && FSFindTrackerSettingsDir(&path) == B_OK) { 248 path.Append(kShelfPath); 249 close(open(path.Path(), O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR 250 | S_IRGRP | S_IROTH)); 251 if (get_ref_for_path(path.Path(), &ref) == B_OK) 252 fDeskShelf = new BShelf(&ref, PoseView()); 253 254 if (fDeskShelf != NULL) 255 fDeskShelf->SetDisplaysZombies(true); 256 } 257 } 258 259 260 void 261 BDeskWindow::InitAddOnsList(bool update) 262 { 263 AutoLock<LockingList<AddOnShortcut> > lock(fAddOnsList); 264 if (!lock.IsLocked()) 265 return; 266 267 if (update) { 268 for (int i = fAddOnsList->CountItems() - 1; i >= 0; i--) { 269 AddOnShortcut* item = fAddOnsList->ItemAt(i); 270 RemoveShortcut(item->key, B_OPTION_KEY | B_COMMAND_KEY); 271 } 272 fAddOnsList->MakeEmpty(true); 273 } 274 275 BStringList addOnPaths; 276 BPathFinder::FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY, "Tracker", 277 addOnPaths); 278 int32 count = addOnPaths.CountStrings(); 279 for (int32 i = 0; i < count; i++) 280 LoadAddOnDir(BDirectory(addOnPaths.StringAt(i)), this, fAddOnsList); 281 } 282 283 284 void 285 BDeskWindow::ApplyShortcutPreferences(bool update) 286 { 287 AutoLock<LockingList<AddOnShortcut> > lock(fAddOnsList); 288 if (!lock.IsLocked()) 289 return; 290 291 if (!update) { 292 BPath path; 293 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { 294 BPathMonitor::StartWatching(path.Path(), 295 B_WATCH_STAT | B_WATCH_FILES_ONLY, this); 296 path.Append(kShortcutsSettings); 297 fShortcutsSettings = new char[strlen(path.Path()) + 1]; 298 strcpy(fShortcutsSettings, path.Path()); 299 } 300 } 301 302 fAddOnsList->EachElement(RevertToDefault, this); 303 304 BFile shortcutSettings(fShortcutsSettings, B_READ_ONLY); 305 BMessage fileMsg; 306 if (shortcutSettings.InitCheck() != B_OK 307 || fileMsg.Unflatten(&shortcutSettings) != B_OK) { 308 fNodeRef = NULL; 309 return; 310 } 311 shortcutSettings.GetNodeRef(fNodeRef); 312 313 int32 i = 0; 314 BMessage message; 315 while (fileMsg.FindMessage("spec", i++, &message) == B_OK) { 316 int32 key; 317 if (message.FindInt32("key", &key) != B_OK) 318 continue; 319 320 // only handle shortcuts referring add-ons 321 BString command; 322 if (message.FindString("command", &command) != B_OK) 323 continue; 324 325 bool isInAddOns = false; 326 327 BStringList addOnPaths; 328 BPathFinder::FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY, 329 "Tracker/", addOnPaths); 330 for (int32 i = 0; i < addOnPaths.CountStrings(); i++) { 331 if (command.StartsWith(addOnPaths.StringAt(i))) { 332 isInAddOns = true; 333 break; 334 } 335 } 336 337 if (!isInAddOns) 338 continue; 339 340 BEntry entry(command); 341 if (entry.InitCheck() != B_OK) 342 continue; 343 344 const char* shortcut = GetKeyName(key); 345 if (strlen(shortcut) != 1) 346 continue; 347 348 uint32 modifiers = B_COMMAND_KEY; 349 // it's required by interface kit to at least 350 // have B_COMMAND_KEY 351 int32 value; 352 if (message.FindInt32("mcidx", 0, &value) == B_OK) 353 modifiers |= (value != 0 ? B_SHIFT_KEY : 0); 354 355 if (message.FindInt32("mcidx", 1, &value) == B_OK) 356 modifiers |= (value != 0 ? B_CONTROL_KEY : 0); 357 358 if (message.FindInt32("mcidx", 3, &value) == B_OK) 359 modifiers |= (value != 0 ? B_OPTION_KEY : 0); 360 361 Model model(&entry); 362 AddOnShortcut* item = fAddOnsList->EachElement(FindElement, &model); 363 if (item != NULL) { 364 if (item->key != '\0') 365 RemoveShortcut(item->key, item->modifiers); 366 367 item->key = shortcut[0]; 368 item->modifiers = modifiers; 369 AddOneShortcut(&model, item->key, item->modifiers, this); 370 } 371 } 372 } 373 374 375 void 376 BDeskWindow::Quit() 377 { 378 if (fNavigationItem != NULL) { 379 // this duplicates BContainerWindow::Quit because 380 // fNavigationItem can be part of fTrashContextMenu 381 // and would get deleted with it 382 BMenu* menu = fNavigationItem->Menu(); 383 if (menu != NULL) 384 menu->RemoveItem(fNavigationItem); 385 386 delete fNavigationItem; 387 fNavigationItem = NULL; 388 } 389 390 fAddOnsList->MakeEmpty(true); 391 delete fAddOnsList; 392 393 delete fDeskShelf; 394 395 // inherited will clean up the rest 396 _inherited::Quit(); 397 } 398 399 400 BPoseView* 401 BDeskWindow::NewPoseView(Model* model, uint32 viewMode) 402 { 403 return new DesktopPoseView(model, viewMode); 404 } 405 406 407 void 408 BDeskWindow::CreatePoseView(Model* model) 409 { 410 fPoseView = NewPoseView(model, kIconMode); 411 fPoseView->SetIconMapping(false); 412 fPoseView->SetEnsurePosesVisible(true); 413 fPoseView->SetAutoScroll(false); 414 415 BScreen screen(this); 416 rgb_color desktopColor = screen.DesktopColor(); 417 if (desktopColor.alpha != 255) { 418 desktopColor.alpha = 255; 419 #if B_BEOS_VERSION > B_BEOS_VERSION_5 420 // This call seems to have the power to cause R5 to freeze! 421 // Please report if commenting this out helped or helped not 422 // on your system 423 screen.SetDesktopColor(desktopColor); 424 #endif 425 } 426 427 fPoseView->SetViewColor(desktopColor); 428 fPoseView->SetLowColor(desktopColor); 429 430 fPoseView->SetResizingMode(B_FOLLOW_ALL); 431 fPoseView->ResizeTo(Bounds().Size()); 432 AddChild(fPoseView); 433 434 PoseView()->StartSettingsWatch(); 435 } 436 437 438 void 439 BDeskWindow::AddWindowContextMenus(BMenu* menu) 440 { 441 TemplatesMenu* tempateMenu = new TemplatesMenu(PoseView(), 442 B_TRANSLATE("New")); 443 444 menu->AddItem(tempateMenu); 445 tempateMenu->SetTargetForItems(PoseView()); 446 447 menu->AddSeparatorItem(); 448 449 BMenu* iconSizeMenu = new BMenu(B_TRANSLATE("Icon view")); 450 BMenuItem* item; 451 452 static const uint32 kIconSizes[] = { 32, 40, 48, 64, 96, 128 }; 453 BMessage* message; 454 455 for (uint32 i = 0; i < sizeof(kIconSizes) / sizeof(uint32); ++i) { 456 uint32 iconSize = kIconSizes[i]; 457 message = new BMessage(kIconMode); 458 message->AddInt32("size", iconSize); 459 BString label; 460 label.SetToFormat(B_TRANSLATE_COMMENT("%" B_PRId32" × %" B_PRId32, 461 "The '×' is the Unicode multiplication sign U+00D7"), 462 iconSize, iconSize); 463 item = new BMenuItem(label, message); 464 item->SetMarked(PoseView()->IconSizeInt() == iconSize); 465 item->SetTarget(PoseView()); 466 iconSizeMenu->AddItem(item); 467 } 468 469 iconSizeMenu->AddSeparatorItem(); 470 471 message = new BMessage(kIconMode); 472 message->AddInt32("scale", 0); 473 item = new BMenuItem(B_TRANSLATE("Decrease size"), message, '-'); 474 item->SetTarget(PoseView()); 475 iconSizeMenu->AddItem(item); 476 477 message = new BMessage(kIconMode); 478 message->AddInt32("scale", 1); 479 item = new BMenuItem(B_TRANSLATE("Increase size"), message, '+'); 480 item->SetTarget(PoseView()); 481 iconSizeMenu->AddItem(item); 482 483 // A sub menu where the super item can be invoked. 484 menu->AddItem(iconSizeMenu); 485 iconSizeMenu->Superitem()->SetShortcut('1', B_COMMAND_KEY); 486 iconSizeMenu->Superitem()->SetMessage(new BMessage(kIconMode)); 487 iconSizeMenu->Superitem()->SetTarget(PoseView()); 488 iconSizeMenu->Superitem()->SetMarked(PoseView()->ViewMode() == kIconMode); 489 490 item = new BMenuItem(B_TRANSLATE("Mini icon view"), 491 new BMessage(kMiniIconMode), '2'); 492 item->SetMarked(PoseView()->ViewMode() == kMiniIconMode); 493 menu->AddItem(item); 494 495 menu->AddSeparatorItem(); 496 497 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU 498 BMenuItem* pasteItem = new BMenuItem(B_TRANSLATE("Paste"), 499 new BMessage(B_PASTE), 'V'); 500 menu->AddItem(pasteItem); 501 menu->AddSeparatorItem(); 502 #endif 503 menu->AddItem(new BMenuItem(B_TRANSLATE("Clean up"), 504 new BMessage(kCleanup), 'K')); 505 menu->AddItem(new BMenuItem(B_TRANSLATE("Select" B_UTF8_ELLIPSIS), 506 new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY)); 507 menu->AddItem(new BMenuItem(B_TRANSLATE("Select all"), 508 new BMessage(B_SELECT_ALL), 'A')); 509 510 menu->AddSeparatorItem(); 511 menu->AddItem(new MountMenu(B_TRANSLATE("Mount"))); 512 513 menu->AddSeparatorItem(); 514 menu->AddItem(new BMenu(B_TRANSLATE("Add-ons"))); 515 516 // target items as needed 517 menu->SetTargetForItems(PoseView()); 518 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU 519 pasteItem->SetTarget(this); 520 #endif 521 } 522 523 524 void 525 BDeskWindow::WorkspaceActivated(int32 workspace, bool state) 526 { 527 if (fBackgroundImage) 528 fBackgroundImage->WorkspaceActivated(PoseView(), workspace, state); 529 } 530 531 532 void 533 BDeskWindow::SaveDesktopPoseLocations() 534 { 535 PoseView()->SavePoseLocations(&fOldFrame); 536 } 537 538 539 void 540 BDeskWindow::ScreenChanged(BRect frame, color_space space) 541 { 542 bool frameChanged = (frame != fOldFrame); 543 544 SaveDesktopPoseLocations(); 545 fOldFrame = frame; 546 ResizeTo(frame.Width(), frame.Height()); 547 548 if (fBackgroundImage) 549 fBackgroundImage->ScreenChanged(frame, space); 550 551 PoseView()->CheckPoseVisibility(frameChanged ? &frame : 0); 552 // if frame changed, pass new frame so that icons can 553 // get rearranged based on old pose info for the frame 554 } 555 556 557 void 558 BDeskWindow::UpdateDesktopBackgroundImages() 559 { 560 WindowStateNodeOpener opener(this, false); 561 fBackgroundImage = BackgroundImage::Refresh(fBackgroundImage, 562 opener.Node(), true, PoseView()); 563 } 564 565 566 void 567 BDeskWindow::Show() 568 { 569 if (fBackgroundImage) 570 fBackgroundImage->Show(PoseView(), current_workspace()); 571 572 PoseView()->CheckPoseVisibility(); 573 574 _inherited::Show(); 575 } 576 577 578 bool 579 BDeskWindow::ShouldAddScrollBars() const 580 { 581 return false; 582 } 583 584 585 bool 586 BDeskWindow::ShouldAddMenus() const 587 { 588 return false; 589 } 590 591 592 bool 593 BDeskWindow::ShouldAddContainerView() const 594 { 595 return false; 596 } 597 598 599 void 600 BDeskWindow::MessageReceived(BMessage* message) 601 { 602 if (message->WasDropped()) { 603 const rgb_color* color; 604 ssize_t size; 605 // handle "roColour"-style color drops 606 if (message->FindData("RGBColor", 'RGBC', 607 (const void**)&color, &size) == B_OK) { 608 BScreen(this).SetDesktopColor(*color); 609 PoseView()->SetViewColor(*color); 610 PoseView()->SetLowColor(*color); 611 612 // Notify the backgrounds app that the background changed 613 status_t initStatus; 614 BMessenger messenger("application/x-vnd.Haiku-Backgrounds", -1, 615 &initStatus); 616 if (initStatus == B_OK) 617 messenger.SendMessage(message); 618 619 return; 620 } 621 } 622 623 switch (message->what) { 624 case B_PATH_MONITOR: 625 { 626 const char* path = ""; 627 if (!(message->FindString("path", &path) == B_OK 628 && strcmp(path, fShortcutsSettings) == 0)) { 629 630 dev_t device; 631 ino_t node; 632 if (fNodeRef == NULL 633 || message->FindInt32("device", &device) != B_OK 634 || message->FindInt64("node", &node) != B_OK 635 || device != fNodeRef->device 636 || node != fNodeRef->node) 637 break; 638 } 639 ApplyShortcutPreferences(true); 640 break; 641 } 642 case B_NODE_MONITOR: 643 PRINT(("will update addon shortcuts\n")); 644 InitAddOnsList(true); 645 ApplyShortcutPreferences(true); 646 break; 647 648 default: 649 _inherited::MessageReceived(message); 650 break; 651 } 652 } 653