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 #include <Catalog.h> 36 #include <Debug.h> 37 #include <FindDirectory.h> 38 #include <Locale.h> 39 #include <NodeMonitor.h> 40 #include <Path.h> 41 #include <PathFinder.h> 42 #include <PathMonitor.h> 43 #include <PopUpMenu.h> 44 #include <Resources.h> 45 #include <Roster.h> 46 #include <Screen.h> 47 #include <String.h> 48 #include <StringList.h> 49 #include <Volume.h> 50 #include <VolumeRoster.h> 51 52 #include <fcntl.h> 53 #include <unistd.h> 54 55 #include "Attributes.h" 56 #include "AutoLock.h" 57 #include "BackgroundImage.h" 58 #include "Commands.h" 59 #include "DesktopPoseView.h" 60 #include "DeskWindow.h" 61 #include "FSUtils.h" 62 #include "IconMenuItem.h" 63 #include "KeyInfos.h" 64 #include "MountMenu.h" 65 #include "PoseView.h" 66 #include "Tracker.h" 67 #include "TemplatesMenu.h" 68 69 70 const char* kShelfPath = "tracker_shelf"; 71 // replicant support 72 73 const char* kShortcutsSettings = "shortcuts_settings"; 74 const char* kDefaultShortcut = "BEOS:default_shortcut"; 75 const uint32 kDefaultModifiers = B_OPTION_KEY | B_COMMAND_KEY; 76 77 78 static struct AddonShortcut* 79 MatchOne(struct AddonShortcut* item, void* castToName) 80 { 81 if (strcmp(item->model->Name(), (const char*)castToName) == 0) { 82 // found match, bail out 83 return item; 84 } 85 86 return 0; 87 } 88 89 90 static void 91 AddOneShortcut(Model* model, char key, uint32 modifiers, BDeskWindow* window) 92 { 93 if (key == '\0') 94 return; 95 96 BMessage* runAddon = new BMessage(kLoadAddOn); 97 runAddon->AddRef("refs", model->EntryRef()); 98 window->AddShortcut(key, modifiers, runAddon); 99 } 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) 188 : 189 BContainerWindow(windowList, 0, kPrivateDesktopWindowLook, 190 kPrivateDesktopWindowFeel, B_NOT_MOVABLE | B_WILL_ACCEPT_FIRST_CLICK 191 | B_NOT_ZOOMABLE | B_NOT_CLOSABLE | B_NOT_MINIMIZABLE 192 | B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS, B_ALL_WORKSPACES), 193 fDeskShelf(NULL), 194 fTrashContextMenu(NULL), 195 fNodeRef(NULL), 196 fShortcutsSettings(NULL) 197 { 198 // Add icon view switching shortcuts. These are displayed in the context 199 // menu, although they obviously don't work from those menu items. 200 BMessage* message = new BMessage(kIconMode); 201 AddShortcut('1', B_COMMAND_KEY, message, PoseView()); 202 203 message = new BMessage(kMiniIconMode); 204 AddShortcut('2', B_COMMAND_KEY, message, PoseView()); 205 206 message = new BMessage(kIconMode); 207 message->AddInt32("scale", 1); 208 AddShortcut('+', B_COMMAND_KEY, message, PoseView()); 209 210 message = new BMessage(kIconMode); 211 message->AddInt32("scale", 0); 212 AddShortcut('-', B_COMMAND_KEY, message, PoseView()); 213 } 214 215 216 BDeskWindow::~BDeskWindow() 217 { 218 SaveDesktopPoseLocations(); 219 // explicit call to SavePoseLocations so that extended pose info 220 // gets committed properly 221 PoseView()->DisableSaveLocation(); 222 // prevent double-saving, this would slow down quitting 223 PoseView()->StopSettingsWatch(); 224 stop_watching(this); 225 } 226 227 228 void 229 BDeskWindow::Init(const BMessage*) 230 { 231 // Set the size of the screen before calling the container window's 232 // Init() because it will add volume poses to this window and 233 // they will be clipped otherwise 234 235 BScreen screen(this); 236 fOldFrame = screen.Frame(); 237 238 PoseView()->SetShowHideSelection(false); 239 ResizeTo(fOldFrame.Width(), fOldFrame.Height()); 240 241 entry_ref ref; 242 BPath path; 243 if (!BootedInSafeMode() && FSFindTrackerSettingsDir(&path) == B_OK) { 244 path.Append(kShelfPath); 245 close(open(path.Path(), O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR 246 | S_IRGRP | S_IROTH)); 247 if (get_ref_for_path(path.Path(), &ref) == B_OK) 248 fDeskShelf = new BShelf(&ref, fPoseView); 249 250 if (fDeskShelf != NULL) 251 fDeskShelf->SetDisplaysZombies(true); 252 } 253 InitKeyIndices(); 254 InitAddonsList(false); 255 ApplyShortcutPreferences(false); 256 257 _inherited::Init(); 258 } 259 260 261 void 262 BDeskWindow::InitAddonsList(bool update) 263 { 264 AutoLock<LockingList<AddonShortcut> > lock(fAddonsList); 265 if (lock.IsLocked()) { 266 if (update) { 267 for (int i = fAddonsList->CountItems() - 1; i >= 0; i--) { 268 AddonShortcut* item = fAddonsList->ItemAt(i); 269 RemoveShortcut(item->key, B_OPTION_KEY | B_COMMAND_KEY); 270 } 271 fAddonsList->MakeEmpty(true); 272 } 273 274 BStringList addOnPaths; 275 BPathFinder::FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY, "Tracker/", 276 addOnPaths); 277 for (int32 i = 0; i < addOnPaths.CountStrings(); i++) { 278 LoadAddOnDir(BDirectory(addOnPaths.StringAt(i)), this, 279 fAddonsList); 280 } 281 } 282 } 283 284 285 void 286 BDeskWindow::ApplyShortcutPreferences(bool update) 287 { 288 AutoLock<LockingList<AddonShortcut> > lock(fAddonsList); 289 if (lock.IsLocked()) { 290 if (!update) { 291 BPath path; 292 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { 293 BPathMonitor::StartWatching(path.Path(), 294 B_WATCH_STAT | B_WATCH_FILES_ONLY, this); 295 path.Append(kShortcutsSettings); 296 fShortcutsSettings = new char[strlen(path.Path()) + 1]; 297 strcpy(fShortcutsSettings, path.Path()); 298 } 299 } 300 301 fAddonsList->EachElement(RevertToDefault, this); 302 303 BFile shortcutSettings(fShortcutsSettings, B_READ_ONLY); 304 BMessage fileMsg; 305 if (shortcutSettings.InitCheck() != B_OK 306 || fileMsg.Unflatten(&shortcutSettings) != B_OK) { 307 fNodeRef = NULL; 308 return; 309 } 310 shortcutSettings.GetNodeRef(fNodeRef); 311 312 int32 i = 0; 313 BMessage message; 314 while (fileMsg.FindMessage("spec", i++, &message) == B_OK) { 315 int32 key; 316 if (message.FindInt32("key", &key) == B_OK) { 317 // only handle shortcuts referring add-ons 318 BString command; 319 if (message.FindString("command", &command) != B_OK) 320 continue; 321 322 bool isInAddons = false; 323 324 BStringList addOnPaths; 325 BPathFinder::FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY, 326 "Tracker/", addOnPaths); 327 for (int32 i = 0; i < addOnPaths.CountStrings(); i++) { 328 if (command.FindFirst(addOnPaths.StringAt(i)) == 0) { 329 isInAddons = true; 330 break; 331 } 332 } 333 334 if (!isInAddons) 335 continue; 336 337 BEntry entry(command); 338 if (entry.InitCheck() != B_OK) 339 continue; 340 341 const char* shortcut = GetKeyName(key); 342 if (strlen(shortcut) != 1) 343 continue; 344 345 uint32 modifiers = B_COMMAND_KEY; 346 // it's required by interface kit to at least 347 // have B_COMMAND_KEY 348 int32 value; 349 if (message.FindInt32("mcidx", 0, &value) == B_OK) 350 modifiers |= (value != 0 ? B_SHIFT_KEY : 0); 351 352 if (message.FindInt32("mcidx", 1, &value) == B_OK) 353 modifiers |= (value != 0 ? B_CONTROL_KEY : 0); 354 355 if (message.FindInt32("mcidx", 3, &value) == B_OK) 356 modifiers |= (value != 0 ? B_OPTION_KEY : 0); 357 358 Model model(&entry); 359 AddonShortcut* item = fAddonsList->EachElement(FindElement, 360 &model); 361 if (item != NULL) { 362 if (item->key != '\0') 363 RemoveShortcut(item->key, item->modifiers); 364 365 item->key = shortcut[0]; 366 item->modifiers = modifiers; 367 AddOneShortcut(&model, item->key, item->modifiers, this); 368 } 369 } 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 = 0; 388 } 389 390 fAddonsList->MakeEmpty(true); 391 delete fAddonsList; 392 393 delete fTrashContextMenu; 394 fTrashContextMenu = NULL; 395 396 delete fDeskShelf; 397 _inherited::Quit(); 398 } 399 400 401 BPoseView* 402 BDeskWindow::NewPoseView(Model* model, BRect rect, uint32 viewMode) 403 { 404 return new DesktopPoseView(model, rect, viewMode); 405 } 406 407 408 void 409 BDeskWindow::CreatePoseView(Model* model) 410 { 411 fPoseView = NewPoseView(model, Bounds(), kIconMode); 412 fPoseView->SetIconMapping(false); 413 fPoseView->SetEnsurePosesVisible(true); 414 fPoseView->SetAutoScroll(false); 415 416 BScreen screen(this); 417 rgb_color desktopColor = screen.DesktopColor(); 418 if (desktopColor.alpha != 255) { 419 desktopColor.alpha = 255; 420 #if B_BEOS_VERSION > B_BEOS_VERSION_5 421 // This call seems to have the power to cause R5 to freeze! 422 // Please report if commenting this out helped or helped not 423 // on your system 424 screen.SetDesktopColor(desktopColor); 425 #endif 426 } 427 428 fPoseView->SetViewColor(desktopColor); 429 fPoseView->SetLowColor(desktopColor); 430 431 AddChild(fPoseView); 432 433 PoseView()->StartSettingsWatch(); 434 } 435 436 437 void 438 BDeskWindow::AddWindowContextMenus(BMenu* menu) 439 { 440 BRoster roster; 441 if (!roster.IsRunning(kDeskbarSignature)) { 442 menu->AddItem(new BMenuItem(B_TRANSLATE("Restart Deskbar"), 443 new BMessage(kRestartDeskbar))); 444 menu->AddSeparatorItem(); 445 } 446 447 TemplatesMenu* tempateMenu = new TemplatesMenu(PoseView(), 448 B_TRANSLATE("New")); 449 450 menu->AddItem(tempateMenu); 451 tempateMenu->SetTargetForItems(PoseView()); 452 tempateMenu->SetFont(be_plain_font); 453 454 menu->AddSeparatorItem(); 455 456 BMenu* iconSizeMenu = new BMenu(B_TRANSLATE("Icon view")); 457 458 BMessage* message = new BMessage(kIconMode); 459 message->AddInt32("size", 32); 460 BMenuItem* item = new BMenuItem(B_TRANSLATE("32 x 32"), message); 461 item->SetMarked(PoseView()->IconSizeInt() == 32); 462 item->SetTarget(PoseView()); 463 iconSizeMenu->AddItem(item); 464 465 message = new BMessage(kIconMode); 466 message->AddInt32("size", 40); 467 item = new BMenuItem(B_TRANSLATE("40 x 40"), message); 468 item->SetMarked(PoseView()->IconSizeInt() == 40); 469 item->SetTarget(PoseView()); 470 iconSizeMenu->AddItem(item); 471 472 message = new BMessage(kIconMode); 473 message->AddInt32("size", 48); 474 item = new BMenuItem(B_TRANSLATE("48 x 48"), message); 475 item->SetMarked(PoseView()->IconSizeInt() == 48); 476 item->SetTarget(PoseView()); 477 iconSizeMenu->AddItem(item); 478 479 message = new BMessage(kIconMode); 480 message->AddInt32("size", 64); 481 item = new BMenuItem(B_TRANSLATE("64 x 64"), message); 482 item->SetMarked(PoseView()->IconSizeInt() == 64); 483 item->SetTarget(PoseView()); 484 iconSizeMenu->AddItem(item); 485 486 message = new BMessage(kIconMode); 487 message->AddInt32("size", 96); 488 item = new BMenuItem(B_TRANSLATE("96 x 96"), message); 489 item->SetMarked(PoseView()->IconSizeInt() == 96); 490 item->SetTarget(PoseView()); 491 iconSizeMenu->AddItem(item); 492 493 message = new BMessage(kIconMode); 494 message->AddInt32("size", 128); 495 item = new BMenuItem(B_TRANSLATE("128 x 128"), message); 496 item->SetMarked(PoseView()->IconSizeInt() == 128); 497 item->SetTarget(PoseView()); 498 iconSizeMenu->AddItem(item); 499 500 iconSizeMenu->AddSeparatorItem(); 501 502 message = new BMessage(kIconMode); 503 message->AddInt32("scale", 0); 504 item = new BMenuItem(B_TRANSLATE("Decrease size"), message, '-'); 505 item->SetTarget(PoseView()); 506 iconSizeMenu->AddItem(item); 507 508 message = new BMessage(kIconMode); 509 message->AddInt32("scale", 1); 510 item = new BMenuItem(B_TRANSLATE("Increase size"), message, '+'); 511 item->SetTarget(PoseView()); 512 iconSizeMenu->AddItem(item); 513 514 // A sub menu where the super item can be invoked. 515 menu->AddItem(iconSizeMenu); 516 iconSizeMenu->Superitem()->SetShortcut('1', B_COMMAND_KEY); 517 iconSizeMenu->Superitem()->SetMessage(new BMessage(kIconMode)); 518 iconSizeMenu->Superitem()->SetTarget(PoseView()); 519 iconSizeMenu->Superitem()->SetMarked(PoseView()->ViewMode() == kIconMode); 520 521 item = new BMenuItem(B_TRANSLATE("Mini icon view"), 522 new BMessage(kMiniIconMode), '2'); 523 item->SetMarked(PoseView()->ViewMode() == kMiniIconMode); 524 menu->AddItem(item); 525 526 menu->AddSeparatorItem(); 527 528 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU 529 BMenuItem* pasteItem = new BMenuItem(B_TRANSLATE("Paste"), 530 new BMessage(B_PASTE), 'V')); 531 menu->AddItem(pasteItem); 532 menu->AddSeparatorItem(); 533 #endif 534 menu->AddItem(new BMenuItem(B_TRANSLATE("Clean up"), 535 new BMessage(kCleanup), 'K')); 536 menu->AddItem(new BMenuItem(B_TRANSLATE("Select" B_UTF8_ELLIPSIS), 537 new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY)); 538 menu->AddItem(new BMenuItem(B_TRANSLATE("Select all"), 539 new BMessage(B_SELECT_ALL), 'A')); 540 541 menu->AddSeparatorItem(); 542 menu->AddItem(new MountMenu(B_TRANSLATE("Mount"))); 543 544 menu->AddSeparatorItem(); 545 menu->AddItem(new BMenu(B_TRANSLATE("Add-ons"))); 546 547 // target items as needed 548 menu->SetTargetForItems(PoseView()); 549 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU 550 pasteItem->SetTarget(this); 551 #endif 552 } 553 554 555 void 556 BDeskWindow::WorkspaceActivated(int32 workspace, bool state) 557 { 558 if (fBackgroundImage) 559 fBackgroundImage->WorkspaceActivated(PoseView(), workspace, state); 560 } 561 562 563 void 564 BDeskWindow::SaveDesktopPoseLocations() 565 { 566 PoseView()->SavePoseLocations(&fOldFrame); 567 } 568 569 570 void 571 BDeskWindow::ScreenChanged(BRect frame, color_space space) 572 { 573 bool frameChanged = (frame != fOldFrame); 574 575 SaveDesktopPoseLocations(); 576 fOldFrame = frame; 577 ResizeTo(frame.Width(), frame.Height()); 578 579 if (fBackgroundImage) 580 fBackgroundImage->ScreenChanged(frame, space); 581 582 PoseView()->CheckPoseVisibility(frameChanged ? &frame : 0); 583 // if frame changed, pass new frame so that icons can 584 // get rearranged based on old pose info for the frame 585 } 586 587 588 void 589 BDeskWindow::UpdateDesktopBackgroundImages() 590 { 591 WindowStateNodeOpener opener(this, false); 592 fBackgroundImage = BackgroundImage::Refresh(fBackgroundImage, 593 opener.Node(), true, PoseView()); 594 } 595 596 597 void 598 BDeskWindow::Show() 599 { 600 if (fBackgroundImage) 601 fBackgroundImage->Show(PoseView(), current_workspace()); 602 603 PoseView()->CheckPoseVisibility(); 604 605 _inherited::Show(); 606 } 607 608 609 bool 610 BDeskWindow::ShouldAddScrollBars() const 611 { 612 return false; 613 } 614 615 616 bool 617 BDeskWindow::ShouldAddMenus() const 618 { 619 return false; 620 } 621 622 623 bool 624 BDeskWindow::ShouldAddContainerView() const 625 { 626 return false; 627 } 628 629 630 void 631 BDeskWindow::MessageReceived(BMessage* message) 632 { 633 if (message->WasDropped()) { 634 const rgb_color* color; 635 ssize_t size; 636 // handle "roColour"-style color drops 637 if (message->FindData("RGBColor", 'RGBC', 638 (const void**)&color, &size) == B_OK) { 639 BScreen(this).SetDesktopColor(*color); 640 fPoseView->SetViewColor(*color); 641 fPoseView->SetLowColor(*color); 642 return; 643 } 644 } 645 646 switch (message->what) { 647 case B_PATH_MONITOR: 648 { 649 const char* path = ""; 650 if (!(message->FindString("path", &path) == B_OK 651 && strcmp(path, fShortcutsSettings) == 0)) { 652 653 dev_t device; 654 ino_t node; 655 if (fNodeRef == NULL 656 || message->FindInt32("device", &device) != B_OK 657 || message->FindInt64("node", &node) != B_OK 658 || device != fNodeRef->device 659 || node != fNodeRef->node) 660 break; 661 } 662 ApplyShortcutPreferences(true); 663 break; 664 } 665 case B_NODE_MONITOR: 666 PRINT(("will update addon shortcuts\n")); 667 InitAddonsList(true); 668 ApplyShortcutPreferences(true); 669 break; 670 671 default: 672 _inherited::MessageReceived(message); 673 break; 674 } 675 } 676