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 "Tracker.h" 68 #include "TemplatesMenu.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 104 static struct AddonShortcut* 105 RevertToDefault(struct AddonShortcut* item, void* castToWindow) 106 { 107 if (item->key != item->defaultKey || item->modifiers != kDefaultModifiers) { 108 BDeskWindow* window = static_cast<BDeskWindow*>(castToWindow); 109 if (window != NULL) { 110 window->RemoveShortcut(item->key, item->modifiers); 111 item->key = item->defaultKey; 112 item->modifiers = kDefaultModifiers; 113 AddOneShortcut(item->model, item->key, item->modifiers, window); 114 } 115 } 116 117 return 0; 118 } 119 120 121 static struct AddonShortcut* 122 FindElement(struct AddonShortcut* item, void* castToOther) 123 { 124 Model* other = static_cast<Model*>(castToOther); 125 if (*item->model->EntryRef() == *other->EntryRef()) 126 return item; 127 128 return 0; 129 } 130 131 132 static void 133 LoadAddOnDir(BDirectory directory, BDeskWindow* window, 134 LockingList<AddonShortcut>* list) 135 { 136 BEntry entry; 137 while (directory.GetNextEntry(&entry) == B_OK) { 138 Model* model = new Model(&entry); 139 if (model->InitCheck() == B_OK && model->IsSymLink()) { 140 // resolve symlinks 141 Model* resolved = new Model(model->EntryRef(), true, true); 142 if (resolved->InitCheck() == B_OK) 143 model->SetLinkTo(resolved); 144 else 145 delete resolved; 146 } 147 if (model->InitCheck() != B_OK 148 || !model->ResolveIfLink()->IsExecutable()) { 149 delete model; 150 continue; 151 } 152 153 char* name = strdup(model->Name()); 154 if (!list->EachElement(MatchOne, name)) { 155 struct AddonShortcut* item = new struct AddonShortcut; 156 item->model = model; 157 158 BResources resources(model->ResolveIfLink()->EntryRef()); 159 size_t size; 160 char* shortcut = (char*)resources.LoadResource(B_STRING_TYPE, 161 kDefaultShortcut, &size); 162 if (shortcut == NULL || strlen(shortcut) > 1) 163 item->key = '\0'; 164 else 165 item->key = shortcut[0]; 166 AddOneShortcut(model, item->key, kDefaultModifiers, window); 167 item->defaultKey = item->key; 168 item->modifiers = kDefaultModifiers; 169 list->AddItem(item); 170 } 171 free(name); 172 } 173 174 node_ref nodeRef; 175 directory.GetNodeRef(&nodeRef); 176 177 TTracker::WatchNode(&nodeRef, B_WATCH_DIRECTORY, window); 178 } 179 180 181 // #pragma mark - BDeskWindow 182 183 184 #undef B_TRANSLATION_CONTEXT 185 #define B_TRANSLATION_CONTEXT "DeskWindow" 186 187 188 BDeskWindow::BDeskWindow(LockingList<BWindow>* windowList) 189 : 190 BContainerWindow(windowList, 0, kDesktopWindowLook, 191 kDesktopWindowFeel, B_NOT_MOVABLE | B_WILL_ACCEPT_FIRST_CLICK 192 | B_NOT_ZOOMABLE | B_NOT_CLOSABLE | B_NOT_MINIMIZABLE 193 | B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS, B_ALL_WORKSPACES, 194 false, true), 195 fDeskShelf(NULL), 196 fNodeRef(NULL), 197 fShortcutsSettings(NULL) 198 { 199 // Add icon view switching shortcuts. These are displayed in the context 200 // menu, although they obviously don't work from those menu items. 201 BMessage* message = new BMessage(kIconMode); 202 AddShortcut('1', B_COMMAND_KEY, message, PoseView()); 203 204 message = new BMessage(kMiniIconMode); 205 AddShortcut('2', B_COMMAND_KEY, message, PoseView()); 206 207 message = new BMessage(kIconMode); 208 message->AddInt32("scale", 1); 209 AddShortcut('+', B_COMMAND_KEY, message, PoseView()); 210 211 message = new BMessage(kIconMode); 212 message->AddInt32("scale", 0); 213 AddShortcut('-', B_COMMAND_KEY, message, PoseView()); 214 } 215 216 217 BDeskWindow::~BDeskWindow() 218 { 219 SaveDesktopPoseLocations(); 220 // explicit call to SavePoseLocations so that extended pose info 221 // gets committed properly 222 PoseView()->DisableSaveLocation(); 223 // prevent double-saving, this would slow down quitting 224 PoseView()->StopSettingsWatch(); 225 stop_watching(this); 226 } 227 228 229 void 230 BDeskWindow::Init(const BMessage*) 231 { 232 // Set the size of the screen before calling the container window's 233 // Init() because it will add volume poses to this window and 234 // they will be clipped otherwise 235 236 BScreen screen(this); 237 fOldFrame = screen.Frame(); 238 239 ResizeTo(fOldFrame.Width(), fOldFrame.Height()); 240 241 InitKeyIndices(); 242 InitAddonsList(false); 243 ApplyShortcutPreferences(false); 244 245 _inherited::Init(); 246 247 entry_ref ref; 248 BPath path; 249 if (!BootedInSafeMode() && FSFindTrackerSettingsDir(&path) == B_OK) { 250 path.Append(kShelfPath); 251 close(open(path.Path(), O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR 252 | S_IRGRP | S_IROTH)); 253 if (get_ref_for_path(path.Path(), &ref) == B_OK) 254 fDeskShelf = new BShelf(&ref, fPoseView); 255 256 if (fDeskShelf != NULL) 257 fDeskShelf->SetDisplaysZombies(true); 258 } 259 } 260 261 262 void 263 BDeskWindow::InitAddonsList(bool update) 264 { 265 AutoLock<LockingList<AddonShortcut> > lock(fAddonsList); 266 if (lock.IsLocked()) { 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, 281 fAddonsList); 282 } 283 } 284 } 285 286 287 void 288 BDeskWindow::ApplyShortcutPreferences(bool update) 289 { 290 AutoLock<LockingList<AddonShortcut> > lock(fAddonsList); 291 if (lock.IsLocked()) { 292 if (!update) { 293 BPath path; 294 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { 295 BPathMonitor::StartWatching(path.Path(), 296 B_WATCH_STAT | B_WATCH_FILES_ONLY, this); 297 path.Append(kShortcutsSettings); 298 fShortcutsSettings = new char[strlen(path.Path()) + 1]; 299 strcpy(fShortcutsSettings, path.Path()); 300 } 301 } 302 303 fAddonsList->EachElement(RevertToDefault, this); 304 305 BFile shortcutSettings(fShortcutsSettings, B_READ_ONLY); 306 BMessage fileMsg; 307 if (shortcutSettings.InitCheck() != B_OK 308 || fileMsg.Unflatten(&shortcutSettings) != B_OK) { 309 fNodeRef = NULL; 310 return; 311 } 312 shortcutSettings.GetNodeRef(fNodeRef); 313 314 int32 i = 0; 315 BMessage message; 316 while (fileMsg.FindMessage("spec", i++, &message) == B_OK) { 317 int32 key; 318 if (message.FindInt32("key", &key) == B_OK) { 319 // only handle shortcuts referring add-ons 320 BString command; 321 if (message.FindString("command", &command) != B_OK) 322 continue; 323 324 bool isInAddons = false; 325 326 BStringList addOnPaths; 327 BPathFinder::FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY, 328 "Tracker/", addOnPaths); 329 for (int32 i = 0; i < addOnPaths.CountStrings(); i++) { 330 if (command.StartsWith(addOnPaths.StringAt(i))) { 331 isInAddons = true; 332 break; 333 } 334 } 335 336 if (!isInAddons) 337 continue; 338 339 BEntry entry(command); 340 if (entry.InitCheck() != B_OK) 341 continue; 342 343 const char* shortcut = GetKeyName(key); 344 if (strlen(shortcut) != 1) 345 continue; 346 347 uint32 modifiers = B_COMMAND_KEY; 348 // it's required by interface kit to at least 349 // have B_COMMAND_KEY 350 int32 value; 351 if (message.FindInt32("mcidx", 0, &value) == B_OK) 352 modifiers |= (value != 0 ? B_SHIFT_KEY : 0); 353 354 if (message.FindInt32("mcidx", 1, &value) == B_OK) 355 modifiers |= (value != 0 ? B_CONTROL_KEY : 0); 356 357 if (message.FindInt32("mcidx", 3, &value) == B_OK) 358 modifiers |= (value != 0 ? B_OPTION_KEY : 0); 359 360 Model model(&entry); 361 AddonShortcut* item = fAddonsList->EachElement(FindElement, 362 &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 376 377 void 378 BDeskWindow::Quit() 379 { 380 if (fNavigationItem != NULL) { 381 // this duplicates BContainerWindow::Quit because 382 // fNavigationItem can be part of fTrashContextMenu 383 // and would get deleted with it 384 BMenu* menu = fNavigationItem->Menu(); 385 if (menu != NULL) 386 menu->RemoveItem(fNavigationItem); 387 388 delete fNavigationItem; 389 fNavigationItem = 0; 390 } 391 392 fAddonsList->MakeEmpty(true); 393 delete fAddonsList; 394 395 delete fDeskShelf; 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 tempateMenu->SetFont(be_plain_font); 447 448 menu->AddSeparatorItem(); 449 450 BMenu* iconSizeMenu = new BMenu(B_TRANSLATE("Icon view")); 451 452 BMessage* message = new BMessage(kIconMode); 453 message->AddInt32("size", 32); 454 BMenuItem* item = new BMenuItem(B_TRANSLATE("32 x 32"), message); 455 item->SetMarked(PoseView()->IconSizeInt() == 32); 456 item->SetTarget(PoseView()); 457 iconSizeMenu->AddItem(item); 458 459 message = new BMessage(kIconMode); 460 message->AddInt32("size", 40); 461 item = new BMenuItem(B_TRANSLATE("40 x 40"), message); 462 item->SetMarked(PoseView()->IconSizeInt() == 40); 463 item->SetTarget(PoseView()); 464 iconSizeMenu->AddItem(item); 465 466 message = new BMessage(kIconMode); 467 message->AddInt32("size", 48); 468 item = new BMenuItem(B_TRANSLATE("48 x 48"), message); 469 item->SetMarked(PoseView()->IconSizeInt() == 48); 470 item->SetTarget(PoseView()); 471 iconSizeMenu->AddItem(item); 472 473 message = new BMessage(kIconMode); 474 message->AddInt32("size", 64); 475 item = new BMenuItem(B_TRANSLATE("64 x 64"), message); 476 item->SetMarked(PoseView()->IconSizeInt() == 64); 477 item->SetTarget(PoseView()); 478 iconSizeMenu->AddItem(item); 479 480 message = new BMessage(kIconMode); 481 message->AddInt32("size", 96); 482 item = new BMenuItem(B_TRANSLATE("96 x 96"), message); 483 item->SetMarked(PoseView()->IconSizeInt() == 96); 484 item->SetTarget(PoseView()); 485 iconSizeMenu->AddItem(item); 486 487 message = new BMessage(kIconMode); 488 message->AddInt32("size", 128); 489 item = new BMenuItem(B_TRANSLATE("128 x 128"), message); 490 item->SetMarked(PoseView()->IconSizeInt() == 128); 491 item->SetTarget(PoseView()); 492 iconSizeMenu->AddItem(item); 493 494 iconSizeMenu->AddSeparatorItem(); 495 496 message = new BMessage(kIconMode); 497 message->AddInt32("scale", 0); 498 item = new BMenuItem(B_TRANSLATE("Decrease size"), message, '-'); 499 item->SetTarget(PoseView()); 500 iconSizeMenu->AddItem(item); 501 502 message = new BMessage(kIconMode); 503 message->AddInt32("scale", 1); 504 item = new BMenuItem(B_TRANSLATE("Increase size"), message, '+'); 505 item->SetTarget(PoseView()); 506 iconSizeMenu->AddItem(item); 507 508 // A sub menu where the super item can be invoked. 509 menu->AddItem(iconSizeMenu); 510 iconSizeMenu->Superitem()->SetShortcut('1', B_COMMAND_KEY); 511 iconSizeMenu->Superitem()->SetMessage(new BMessage(kIconMode)); 512 iconSizeMenu->Superitem()->SetTarget(PoseView()); 513 iconSizeMenu->Superitem()->SetMarked(PoseView()->ViewMode() == kIconMode); 514 515 item = new BMenuItem(B_TRANSLATE("Mini icon view"), 516 new BMessage(kMiniIconMode), '2'); 517 item->SetMarked(PoseView()->ViewMode() == kMiniIconMode); 518 menu->AddItem(item); 519 520 menu->AddSeparatorItem(); 521 522 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU 523 BMenuItem* pasteItem = new BMenuItem(B_TRANSLATE("Paste"), 524 new BMessage(B_PASTE), 'V'); 525 menu->AddItem(pasteItem); 526 menu->AddSeparatorItem(); 527 #endif 528 menu->AddItem(new BMenuItem(B_TRANSLATE("Clean up"), 529 new BMessage(kCleanup), 'K')); 530 menu->AddItem(new BMenuItem(B_TRANSLATE("Select" B_UTF8_ELLIPSIS), 531 new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY)); 532 menu->AddItem(new BMenuItem(B_TRANSLATE("Select all"), 533 new BMessage(B_SELECT_ALL), 'A')); 534 535 menu->AddSeparatorItem(); 536 menu->AddItem(new MountMenu(B_TRANSLATE("Mount"))); 537 538 menu->AddSeparatorItem(); 539 menu->AddItem(new BMenu(B_TRANSLATE("Add-ons"))); 540 541 // target items as needed 542 menu->SetTargetForItems(PoseView()); 543 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU 544 pasteItem->SetTarget(this); 545 #endif 546 } 547 548 549 void 550 BDeskWindow::WorkspaceActivated(int32 workspace, bool state) 551 { 552 if (fBackgroundImage) 553 fBackgroundImage->WorkspaceActivated(PoseView(), workspace, state); 554 } 555 556 557 void 558 BDeskWindow::SaveDesktopPoseLocations() 559 { 560 PoseView()->SavePoseLocations(&fOldFrame); 561 } 562 563 564 void 565 BDeskWindow::ScreenChanged(BRect frame, color_space space) 566 { 567 bool frameChanged = (frame != fOldFrame); 568 569 SaveDesktopPoseLocations(); 570 fOldFrame = frame; 571 ResizeTo(frame.Width(), frame.Height()); 572 573 if (fBackgroundImage) 574 fBackgroundImage->ScreenChanged(frame, space); 575 576 PoseView()->CheckPoseVisibility(frameChanged ? &frame : 0); 577 // if frame changed, pass new frame so that icons can 578 // get rearranged based on old pose info for the frame 579 } 580 581 582 void 583 BDeskWindow::UpdateDesktopBackgroundImages() 584 { 585 WindowStateNodeOpener opener(this, false); 586 fBackgroundImage = BackgroundImage::Refresh(fBackgroundImage, 587 opener.Node(), true, PoseView()); 588 } 589 590 591 void 592 BDeskWindow::Show() 593 { 594 if (fBackgroundImage) 595 fBackgroundImage->Show(PoseView(), current_workspace()); 596 597 PoseView()->CheckPoseVisibility(); 598 599 _inherited::Show(); 600 } 601 602 603 bool 604 BDeskWindow::ShouldAddScrollBars() const 605 { 606 return false; 607 } 608 609 610 bool 611 BDeskWindow::ShouldAddMenus() const 612 { 613 return false; 614 } 615 616 617 bool 618 BDeskWindow::ShouldAddContainerView() const 619 { 620 return false; 621 } 622 623 624 void 625 BDeskWindow::MessageReceived(BMessage* message) 626 { 627 if (message->WasDropped()) { 628 const rgb_color* color; 629 ssize_t size; 630 // handle "roColour"-style color drops 631 if (message->FindData("RGBColor", 'RGBC', 632 (const void**)&color, &size) == B_OK) { 633 BScreen(this).SetDesktopColor(*color); 634 fPoseView->SetViewColor(*color); 635 fPoseView->SetLowColor(*color); 636 637 // Notify the backgrounds app that the background changed 638 status_t initStatus; 639 BMessenger messenger("application/x-vnd.Haiku-Backgrounds", -1, 640 &initStatus); 641 if (initStatus == B_OK) 642 messenger.SendMessage(message); 643 644 return; 645 } 646 } 647 648 switch (message->what) { 649 case B_PATH_MONITOR: 650 { 651 const char* path = ""; 652 if (!(message->FindString("path", &path) == B_OK 653 && strcmp(path, fShortcutsSettings) == 0)) { 654 655 dev_t device; 656 ino_t node; 657 if (fNodeRef == NULL 658 || message->FindInt32("device", &device) != B_OK 659 || message->FindInt64("node", &node) != B_OK 660 || device != fNodeRef->device 661 || node != fNodeRef->node) 662 break; 663 } 664 ApplyShortcutPreferences(true); 665 break; 666 } 667 case B_NODE_MONITOR: 668 PRINT(("will update addon shortcuts\n")); 669 InitAddonsList(true); 670 ApplyShortcutPreferences(true); 671 break; 672 673 default: 674 _inherited::MessageReceived(message); 675 break; 676 } 677 } 678