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 <Roster.h> 50 #include <Screen.h> 51 #include <String.h> 52 #include <StringList.h> 53 #include <Volume.h> 54 #include <VolumeRoster.h> 55 56 #include <fcntl.h> 57 #include <unistd.h> 58 59 #include "Attributes.h" 60 #include "AutoLock.h" 61 #include "BackgroundImage.h" 62 #include "Commands.h" 63 #include "FSUtils.h" 64 #include "IconMenuItem.h" 65 #include "KeyInfos.h" 66 #include "MountMenu.h" 67 #include "PoseView.h" 68 #include "Tracker.h" 69 #include "TemplatesMenu.h" 70 71 72 const char* kShelfPath = "tracker_shelf"; 73 // replicant support 74 75 const char* kShortcutsSettings = "shortcuts_settings"; 76 const char* kDefaultShortcut = "BEOS:default_shortcut"; 77 const uint32 kDefaultModifiers = B_OPTION_KEY | B_COMMAND_KEY; 78 79 80 static struct AddonShortcut* 81 MatchOne(struct AddonShortcut* item, void* castToName) 82 { 83 if (strcmp(item->model->Name(), (const char*)castToName) == 0) { 84 // found match, bail out 85 return item; 86 } 87 88 return 0; 89 } 90 91 92 static void 93 AddOneShortcut(Model* model, char key, uint32 modifiers, BDeskWindow* window) 94 { 95 if (key == '\0') 96 return; 97 98 BMessage* runAddon = new BMessage(kLoadAddOn); 99 runAddon->AddRef("refs", model->EntryRef()); 100 window->AddShortcut(key, modifiers, runAddon); 101 } 102 103 104 105 static struct AddonShortcut* 106 RevertToDefault(struct AddonShortcut* item, void* castToWindow) 107 { 108 if (item->key != item->defaultKey || item->modifiers != kDefaultModifiers) { 109 BDeskWindow* window = static_cast<BDeskWindow*>(castToWindow); 110 if (window != NULL) { 111 window->RemoveShortcut(item->key, item->modifiers); 112 item->key = item->defaultKey; 113 item->modifiers = kDefaultModifiers; 114 AddOneShortcut(item->model, item->key, item->modifiers, window); 115 } 116 } 117 118 return 0; 119 } 120 121 122 static struct AddonShortcut* 123 FindElement(struct AddonShortcut* item, void* castToOther) 124 { 125 Model* other = static_cast<Model*>(castToOther); 126 if (*item->model->EntryRef() == *other->EntryRef()) 127 return item; 128 129 return 0; 130 } 131 132 133 static void 134 LoadAddOnDir(BDirectory directory, BDeskWindow* window, 135 LockingList<AddonShortcut>* list) 136 { 137 BEntry entry; 138 while (directory.GetNextEntry(&entry) == B_OK) { 139 Model* model = new Model(&entry); 140 if (model->InitCheck() == B_OK && model->IsSymLink()) { 141 // resolve symlinks 142 Model* resolved = new Model(model->EntryRef(), true, true); 143 if (resolved->InitCheck() == B_OK) 144 model->SetLinkTo(resolved); 145 else 146 delete resolved; 147 } 148 if (model->InitCheck() != B_OK 149 || !model->ResolveIfLink()->IsExecutable()) { 150 delete model; 151 continue; 152 } 153 154 char* name = strdup(model->Name()); 155 if (!list->EachElement(MatchOne, name)) { 156 struct AddonShortcut* item = new struct AddonShortcut; 157 item->model = model; 158 159 BResources resources(model->ResolveIfLink()->EntryRef()); 160 size_t size; 161 char* shortcut = (char*)resources.LoadResource(B_STRING_TYPE, 162 kDefaultShortcut, &size); 163 if (shortcut == NULL || strlen(shortcut) > 1) 164 item->key = '\0'; 165 else 166 item->key = shortcut[0]; 167 AddOneShortcut(model, item->key, kDefaultModifiers, window); 168 item->defaultKey = item->key; 169 item->modifiers = kDefaultModifiers; 170 list->AddItem(item); 171 } 172 free(name); 173 } 174 175 node_ref nodeRef; 176 directory.GetNodeRef(&nodeRef); 177 178 TTracker::WatchNode(&nodeRef, B_WATCH_DIRECTORY, window); 179 } 180 181 182 // #pragma mark - BDeskWindow 183 184 185 #undef B_TRANSLATION_CONTEXT 186 #define B_TRANSLATION_CONTEXT "DeskWindow" 187 188 189 BDeskWindow::BDeskWindow(LockingList<BWindow>* windowList) 190 : 191 BContainerWindow(windowList, 0, kPrivateDesktopWindowLook, 192 kPrivateDesktopWindowFeel, B_NOT_MOVABLE | B_WILL_ACCEPT_FIRST_CLICK 193 | B_NOT_ZOOMABLE | B_NOT_CLOSABLE | B_NOT_MINIMIZABLE 194 | B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS, B_ALL_WORKSPACES), 195 fDeskShelf(NULL), 196 fTrashContextMenu(NULL), 197 fNodeRef(NULL), 198 fShortcutsSettings(NULL) 199 { 200 // Add icon view switching shortcuts. These are displayed in the context 201 // menu, although they obviously don't work from those menu items. 202 BMessage* message = new BMessage(kIconMode); 203 AddShortcut('1', B_COMMAND_KEY, message, PoseView()); 204 205 message = new BMessage(kMiniIconMode); 206 AddShortcut('2', B_COMMAND_KEY, message, PoseView()); 207 208 message = new BMessage(kIconMode); 209 message->AddInt32("scale", 1); 210 AddShortcut('+', B_COMMAND_KEY, message, PoseView()); 211 212 message = new BMessage(kIconMode); 213 message->AddInt32("scale", 0); 214 AddShortcut('-', B_COMMAND_KEY, message, PoseView()); 215 } 216 217 218 BDeskWindow::~BDeskWindow() 219 { 220 SaveDesktopPoseLocations(); 221 // explicit call to SavePoseLocations so that extended pose info 222 // gets committed properly 223 PoseView()->DisableSaveLocation(); 224 // prevent double-saving, this would slow down quitting 225 PoseView()->StopSettingsWatch(); 226 stop_watching(this); 227 } 228 229 230 void 231 BDeskWindow::Init(const BMessage*) 232 { 233 // Set the size of the screen before calling the container window's 234 // Init() because it will add volume poses to this window and 235 // they will be clipped otherwise 236 237 BScreen screen(this); 238 fOldFrame = screen.Frame(); 239 240 PoseView()->SetShowHideSelection(false); 241 ResizeTo(fOldFrame.Width(), fOldFrame.Height()); 242 243 entry_ref ref; 244 BPath path; 245 if (!BootedInSafeMode() && FSFindTrackerSettingsDir(&path) == B_OK) { 246 path.Append(kShelfPath); 247 close(open(path.Path(), O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR 248 | S_IRGRP | S_IROTH)); 249 if (get_ref_for_path(path.Path(), &ref) == B_OK) 250 fDeskShelf = new BShelf(&ref, fPoseView); 251 252 if (fDeskShelf != NULL) 253 fDeskShelf->SetDisplaysZombies(true); 254 } 255 InitKeyIndices(); 256 InitAddonsList(false); 257 ApplyShortcutPreferences(false); 258 259 _inherited::Init(); 260 } 261 262 263 void 264 BDeskWindow::InitAddonsList(bool update) 265 { 266 AutoLock<LockingList<AddonShortcut> > lock(fAddonsList); 267 if (lock.IsLocked()) { 268 if (update) { 269 for (int i = fAddonsList->CountItems() - 1; i >= 0; i--) { 270 AddonShortcut* item = fAddonsList->ItemAt(i); 271 RemoveShortcut(item->key, B_OPTION_KEY | B_COMMAND_KEY); 272 } 273 fAddonsList->MakeEmpty(true); 274 } 275 276 BStringList addOnPaths; 277 BPathFinder::FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY, "Tracker", 278 addOnPaths); 279 int32 count = addOnPaths.CountStrings(); 280 for (int32 i = 0; i < count; i++) { 281 LoadAddOnDir(BDirectory(addOnPaths.StringAt(i)), this, 282 fAddonsList); 283 } 284 } 285 } 286 287 288 void 289 BDeskWindow::ApplyShortcutPreferences(bool update) 290 { 291 AutoLock<LockingList<AddonShortcut> > lock(fAddonsList); 292 if (lock.IsLocked()) { 293 if (!update) { 294 BPath path; 295 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { 296 BPathMonitor::StartWatching(path.Path(), 297 B_WATCH_STAT | B_WATCH_FILES_ONLY, this); 298 path.Append(kShortcutsSettings); 299 fShortcutsSettings = new char[strlen(path.Path()) + 1]; 300 strcpy(fShortcutsSettings, path.Path()); 301 } 302 } 303 304 fAddonsList->EachElement(RevertToDefault, this); 305 306 BFile shortcutSettings(fShortcutsSettings, B_READ_ONLY); 307 BMessage fileMsg; 308 if (shortcutSettings.InitCheck() != B_OK 309 || fileMsg.Unflatten(&shortcutSettings) != B_OK) { 310 fNodeRef = NULL; 311 return; 312 } 313 shortcutSettings.GetNodeRef(fNodeRef); 314 315 int32 i = 0; 316 BMessage message; 317 while (fileMsg.FindMessage("spec", i++, &message) == B_OK) { 318 int32 key; 319 if (message.FindInt32("key", &key) == B_OK) { 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, 363 &model); 364 if (item != NULL) { 365 if (item->key != '\0') 366 RemoveShortcut(item->key, item->modifiers); 367 368 item->key = shortcut[0]; 369 item->modifiers = modifiers; 370 AddOneShortcut(&model, item->key, item->modifiers, this); 371 } 372 } 373 } 374 } 375 } 376 377 378 void 379 BDeskWindow::Quit() 380 { 381 if (fNavigationItem != NULL) { 382 // this duplicates BContainerWindow::Quit because 383 // fNavigationItem can be part of fTrashContextMenu 384 // and would get deleted with it 385 BMenu* menu = fNavigationItem->Menu(); 386 if (menu != NULL) 387 menu->RemoveItem(fNavigationItem); 388 389 delete fNavigationItem; 390 fNavigationItem = 0; 391 } 392 393 fAddonsList->MakeEmpty(true); 394 delete fAddonsList; 395 396 delete fTrashContextMenu; 397 fTrashContextMenu = NULL; 398 399 delete fDeskShelf; 400 _inherited::Quit(); 401 } 402 403 404 BPoseView* 405 BDeskWindow::NewPoseView(Model* model, BRect rect, uint32 viewMode) 406 { 407 return new DesktopPoseView(model, rect, viewMode); 408 } 409 410 411 void 412 BDeskWindow::CreatePoseView(Model* model) 413 { 414 fPoseView = NewPoseView(model, Bounds(), kIconMode); 415 fPoseView->SetIconMapping(false); 416 fPoseView->SetEnsurePosesVisible(true); 417 fPoseView->SetAutoScroll(false); 418 419 BScreen screen(this); 420 rgb_color desktopColor = screen.DesktopColor(); 421 if (desktopColor.alpha != 255) { 422 desktopColor.alpha = 255; 423 #if B_BEOS_VERSION > B_BEOS_VERSION_5 424 // This call seems to have the power to cause R5 to freeze! 425 // Please report if commenting this out helped or helped not 426 // on your system 427 screen.SetDesktopColor(desktopColor); 428 #endif 429 } 430 431 fPoseView->SetViewColor(desktopColor); 432 fPoseView->SetLowColor(desktopColor); 433 434 AddChild(fPoseView); 435 436 PoseView()->StartSettingsWatch(); 437 } 438 439 440 void 441 BDeskWindow::AddWindowContextMenus(BMenu* menu) 442 { 443 BRoster roster; 444 if (!roster.IsRunning(kDeskbarSignature)) { 445 menu->AddItem(new BMenuItem(B_TRANSLATE("Restart Deskbar"), 446 new BMessage(kRestartDeskbar))); 447 menu->AddSeparatorItem(); 448 } 449 450 TemplatesMenu* tempateMenu = new TemplatesMenu(PoseView(), 451 B_TRANSLATE("New")); 452 453 menu->AddItem(tempateMenu); 454 tempateMenu->SetTargetForItems(PoseView()); 455 tempateMenu->SetFont(be_plain_font); 456 457 menu->AddSeparatorItem(); 458 459 BMenu* iconSizeMenu = new BMenu(B_TRANSLATE("Icon view")); 460 461 BMessage* message = new BMessage(kIconMode); 462 message->AddInt32("size", 32); 463 BMenuItem* item = new BMenuItem(B_TRANSLATE("32 x 32"), message); 464 item->SetMarked(PoseView()->IconSizeInt() == 32); 465 item->SetTarget(PoseView()); 466 iconSizeMenu->AddItem(item); 467 468 message = new BMessage(kIconMode); 469 message->AddInt32("size", 40); 470 item = new BMenuItem(B_TRANSLATE("40 x 40"), message); 471 item->SetMarked(PoseView()->IconSizeInt() == 40); 472 item->SetTarget(PoseView()); 473 iconSizeMenu->AddItem(item); 474 475 message = new BMessage(kIconMode); 476 message->AddInt32("size", 48); 477 item = new BMenuItem(B_TRANSLATE("48 x 48"), message); 478 item->SetMarked(PoseView()->IconSizeInt() == 48); 479 item->SetTarget(PoseView()); 480 iconSizeMenu->AddItem(item); 481 482 message = new BMessage(kIconMode); 483 message->AddInt32("size", 64); 484 item = new BMenuItem(B_TRANSLATE("64 x 64"), message); 485 item->SetMarked(PoseView()->IconSizeInt() == 64); 486 item->SetTarget(PoseView()); 487 iconSizeMenu->AddItem(item); 488 489 message = new BMessage(kIconMode); 490 message->AddInt32("size", 96); 491 item = new BMenuItem(B_TRANSLATE("96 x 96"), message); 492 item->SetMarked(PoseView()->IconSizeInt() == 96); 493 item->SetTarget(PoseView()); 494 iconSizeMenu->AddItem(item); 495 496 message = new BMessage(kIconMode); 497 message->AddInt32("size", 128); 498 item = new BMenuItem(B_TRANSLATE("128 x 128"), message); 499 item->SetMarked(PoseView()->IconSizeInt() == 128); 500 item->SetTarget(PoseView()); 501 iconSizeMenu->AddItem(item); 502 503 iconSizeMenu->AddSeparatorItem(); 504 505 message = new BMessage(kIconMode); 506 message->AddInt32("scale", 0); 507 item = new BMenuItem(B_TRANSLATE("Decrease size"), message, '-'); 508 item->SetTarget(PoseView()); 509 iconSizeMenu->AddItem(item); 510 511 message = new BMessage(kIconMode); 512 message->AddInt32("scale", 1); 513 item = new BMenuItem(B_TRANSLATE("Increase size"), message, '+'); 514 item->SetTarget(PoseView()); 515 iconSizeMenu->AddItem(item); 516 517 // A sub menu where the super item can be invoked. 518 menu->AddItem(iconSizeMenu); 519 iconSizeMenu->Superitem()->SetShortcut('1', B_COMMAND_KEY); 520 iconSizeMenu->Superitem()->SetMessage(new BMessage(kIconMode)); 521 iconSizeMenu->Superitem()->SetTarget(PoseView()); 522 iconSizeMenu->Superitem()->SetMarked(PoseView()->ViewMode() == kIconMode); 523 524 item = new BMenuItem(B_TRANSLATE("Mini icon view"), 525 new BMessage(kMiniIconMode), '2'); 526 item->SetMarked(PoseView()->ViewMode() == kMiniIconMode); 527 menu->AddItem(item); 528 529 menu->AddSeparatorItem(); 530 531 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU 532 BMenuItem* pasteItem = new BMenuItem(B_TRANSLATE("Paste"), 533 new BMessage(B_PASTE), 'V')); 534 menu->AddItem(pasteItem); 535 menu->AddSeparatorItem(); 536 #endif 537 menu->AddItem(new BMenuItem(B_TRANSLATE("Clean up"), 538 new BMessage(kCleanup), 'K')); 539 menu->AddItem(new BMenuItem(B_TRANSLATE("Select" B_UTF8_ELLIPSIS), 540 new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY)); 541 menu->AddItem(new BMenuItem(B_TRANSLATE("Select all"), 542 new BMessage(B_SELECT_ALL), 'A')); 543 544 menu->AddSeparatorItem(); 545 menu->AddItem(new MountMenu(B_TRANSLATE("Mount"))); 546 547 menu->AddSeparatorItem(); 548 menu->AddItem(new BMenu(B_TRANSLATE("Add-ons"))); 549 550 // target items as needed 551 menu->SetTargetForItems(PoseView()); 552 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU 553 pasteItem->SetTarget(this); 554 #endif 555 } 556 557 558 void 559 BDeskWindow::WorkspaceActivated(int32 workspace, bool state) 560 { 561 if (fBackgroundImage) 562 fBackgroundImage->WorkspaceActivated(PoseView(), workspace, state); 563 } 564 565 566 void 567 BDeskWindow::SaveDesktopPoseLocations() 568 { 569 PoseView()->SavePoseLocations(&fOldFrame); 570 } 571 572 573 void 574 BDeskWindow::ScreenChanged(BRect frame, color_space space) 575 { 576 bool frameChanged = (frame != fOldFrame); 577 578 SaveDesktopPoseLocations(); 579 fOldFrame = frame; 580 ResizeTo(frame.Width(), frame.Height()); 581 582 if (fBackgroundImage) 583 fBackgroundImage->ScreenChanged(frame, space); 584 585 PoseView()->CheckPoseVisibility(frameChanged ? &frame : 0); 586 // if frame changed, pass new frame so that icons can 587 // get rearranged based on old pose info for the frame 588 } 589 590 591 void 592 BDeskWindow::UpdateDesktopBackgroundImages() 593 { 594 WindowStateNodeOpener opener(this, false); 595 fBackgroundImage = BackgroundImage::Refresh(fBackgroundImage, 596 opener.Node(), true, PoseView()); 597 } 598 599 600 void 601 BDeskWindow::Show() 602 { 603 if (fBackgroundImage) 604 fBackgroundImage->Show(PoseView(), current_workspace()); 605 606 PoseView()->CheckPoseVisibility(); 607 608 _inherited::Show(); 609 } 610 611 612 bool 613 BDeskWindow::ShouldAddScrollBars() const 614 { 615 return false; 616 } 617 618 619 bool 620 BDeskWindow::ShouldAddMenus() const 621 { 622 return false; 623 } 624 625 626 bool 627 BDeskWindow::ShouldAddContainerView() const 628 { 629 return false; 630 } 631 632 633 void 634 BDeskWindow::MessageReceived(BMessage* message) 635 { 636 if (message->WasDropped()) { 637 const rgb_color* color; 638 ssize_t size; 639 // handle "roColour"-style color drops 640 if (message->FindData("RGBColor", 'RGBC', 641 (const void**)&color, &size) == B_OK) { 642 BScreen(this).SetDesktopColor(*color); 643 fPoseView->SetViewColor(*color); 644 fPoseView->SetLowColor(*color); 645 646 // Notify the backgrounds app that the background changed 647 status_t initStatus; 648 BMessenger messenger("application/x-vnd.Haiku-Backgrounds", -1, 649 &initStatus); 650 if (initStatus == B_OK) 651 messenger.SendMessage(message); 652 653 return; 654 } 655 } 656 657 switch (message->what) { 658 case B_PATH_MONITOR: 659 { 660 const char* path = ""; 661 if (!(message->FindString("path", &path) == B_OK 662 && strcmp(path, fShortcutsSettings) == 0)) { 663 664 dev_t device; 665 ino_t node; 666 if (fNodeRef == NULL 667 || message->FindInt32("device", &device) != B_OK 668 || message->FindInt64("node", &node) != B_OK 669 || device != fNodeRef->device 670 || node != fNodeRef->node) 671 break; 672 } 673 ApplyShortcutPreferences(true); 674 break; 675 } 676 case B_NODE_MONITOR: 677 PRINT(("will update addon shortcuts\n")); 678 InitAddonsList(true); 679 ApplyShortcutPreferences(true); 680 break; 681 682 default: 683 _inherited::MessageReceived(message); 684 break; 685 } 686 } 687