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 false, true), 196 fDeskShelf(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 InitKeyIndices(); 244 InitAddonsList(false); 245 ApplyShortcutPreferences(false); 246 247 _inherited::Init(); 248 249 entry_ref ref; 250 BPath path; 251 if (!BootedInSafeMode() && FSFindTrackerSettingsDir(&path) == B_OK) { 252 path.Append(kShelfPath); 253 close(open(path.Path(), O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR 254 | S_IRGRP | S_IROTH)); 255 if (get_ref_for_path(path.Path(), &ref) == B_OK) 256 fDeskShelf = new BShelf(&ref, fPoseView); 257 258 if (fDeskShelf != NULL) 259 fDeskShelf->SetDisplaysZombies(true); 260 } 261 } 262 263 264 void 265 BDeskWindow::InitAddonsList(bool update) 266 { 267 AutoLock<LockingList<AddonShortcut> > lock(fAddonsList); 268 if (lock.IsLocked()) { 269 if (update) { 270 for (int i = fAddonsList->CountItems() - 1; i >= 0; i--) { 271 AddonShortcut* item = fAddonsList->ItemAt(i); 272 RemoveShortcut(item->key, B_OPTION_KEY | B_COMMAND_KEY); 273 } 274 fAddonsList->MakeEmpty(true); 275 } 276 277 BStringList addOnPaths; 278 BPathFinder::FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY, "Tracker", 279 addOnPaths); 280 int32 count = addOnPaths.CountStrings(); 281 for (int32 i = 0; i < count; i++) { 282 LoadAddOnDir(BDirectory(addOnPaths.StringAt(i)), this, 283 fAddonsList); 284 } 285 } 286 } 287 288 289 void 290 BDeskWindow::ApplyShortcutPreferences(bool update) 291 { 292 AutoLock<LockingList<AddonShortcut> > lock(fAddonsList); 293 if (lock.IsLocked()) { 294 if (!update) { 295 BPath path; 296 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { 297 BPathMonitor::StartWatching(path.Path(), 298 B_WATCH_STAT | B_WATCH_FILES_ONLY, this); 299 path.Append(kShortcutsSettings); 300 fShortcutsSettings = new char[strlen(path.Path()) + 1]; 301 strcpy(fShortcutsSettings, path.Path()); 302 } 303 } 304 305 fAddonsList->EachElement(RevertToDefault, this); 306 307 BFile shortcutSettings(fShortcutsSettings, B_READ_ONLY); 308 BMessage fileMsg; 309 if (shortcutSettings.InitCheck() != B_OK 310 || fileMsg.Unflatten(&shortcutSettings) != B_OK) { 311 fNodeRef = NULL; 312 return; 313 } 314 shortcutSettings.GetNodeRef(fNodeRef); 315 316 int32 i = 0; 317 BMessage message; 318 while (fileMsg.FindMessage("spec", i++, &message) == B_OK) { 319 int32 key; 320 if (message.FindInt32("key", &key) == B_OK) { 321 // only handle shortcuts referring add-ons 322 BString command; 323 if (message.FindString("command", &command) != B_OK) 324 continue; 325 326 bool isInAddons = false; 327 328 BStringList addOnPaths; 329 BPathFinder::FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY, 330 "Tracker/", addOnPaths); 331 for (int32 i = 0; i < addOnPaths.CountStrings(); i++) { 332 if (command.StartsWith(addOnPaths.StringAt(i))) { 333 isInAddons = true; 334 break; 335 } 336 } 337 338 if (!isInAddons) 339 continue; 340 341 BEntry entry(command); 342 if (entry.InitCheck() != B_OK) 343 continue; 344 345 const char* shortcut = GetKeyName(key); 346 if (strlen(shortcut) != 1) 347 continue; 348 349 uint32 modifiers = B_COMMAND_KEY; 350 // it's required by interface kit to at least 351 // have B_COMMAND_KEY 352 int32 value; 353 if (message.FindInt32("mcidx", 0, &value) == B_OK) 354 modifiers |= (value != 0 ? B_SHIFT_KEY : 0); 355 356 if (message.FindInt32("mcidx", 1, &value) == B_OK) 357 modifiers |= (value != 0 ? B_CONTROL_KEY : 0); 358 359 if (message.FindInt32("mcidx", 3, &value) == B_OK) 360 modifiers |= (value != 0 ? B_OPTION_KEY : 0); 361 362 Model model(&entry); 363 AddonShortcut* item = fAddonsList->EachElement(FindElement, 364 &model); 365 if (item != NULL) { 366 if (item->key != '\0') 367 RemoveShortcut(item->key, item->modifiers); 368 369 item->key = shortcut[0]; 370 item->modifiers = modifiers; 371 AddOneShortcut(&model, item->key, item->modifiers, this); 372 } 373 } 374 } 375 } 376 } 377 378 379 void 380 BDeskWindow::Quit() 381 { 382 if (fNavigationItem != NULL) { 383 // this duplicates BContainerWindow::Quit because 384 // fNavigationItem can be part of fTrashContextMenu 385 // and would get deleted with it 386 BMenu* menu = fNavigationItem->Menu(); 387 if (menu != NULL) 388 menu->RemoveItem(fNavigationItem); 389 390 delete fNavigationItem; 391 fNavigationItem = 0; 392 } 393 394 fAddonsList->MakeEmpty(true); 395 delete fAddonsList; 396 397 delete fDeskShelf; 398 _inherited::Quit(); 399 } 400 401 402 BPoseView* 403 BDeskWindow::NewPoseView(Model* model, uint32 viewMode) 404 { 405 return new DesktopPoseView(model, viewMode); 406 } 407 408 409 void 410 BDeskWindow::CreatePoseView(Model* model) 411 { 412 fPoseView = NewPoseView(model, kIconMode); 413 fPoseView->SetIconMapping(false); 414 fPoseView->SetEnsurePosesVisible(true); 415 fPoseView->SetAutoScroll(false); 416 417 BScreen screen(this); 418 rgb_color desktopColor = screen.DesktopColor(); 419 if (desktopColor.alpha != 255) { 420 desktopColor.alpha = 255; 421 #if B_BEOS_VERSION > B_BEOS_VERSION_5 422 // This call seems to have the power to cause R5 to freeze! 423 // Please report if commenting this out helped or helped not 424 // on your system 425 screen.SetDesktopColor(desktopColor); 426 #endif 427 } 428 429 fPoseView->SetViewColor(desktopColor); 430 fPoseView->SetLowColor(desktopColor); 431 432 fPoseView->SetResizingMode(B_FOLLOW_ALL); 433 fPoseView->ResizeTo(Bounds().Size()); 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