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 PoseView()->SetShowHideSelection(false); 240 ResizeTo(fOldFrame.Width(), fOldFrame.Height()); 241 242 InitKeyIndices(); 243 InitAddonsList(false); 244 ApplyShortcutPreferences(false); 245 246 _inherited::Init(); 247 248 entry_ref ref; 249 BPath path; 250 if (!BootedInSafeMode() && FSFindTrackerSettingsDir(&path) == B_OK) { 251 path.Append(kShelfPath); 252 close(open(path.Path(), O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR 253 | S_IRGRP | S_IROTH)); 254 if (get_ref_for_path(path.Path(), &ref) == B_OK) 255 fDeskShelf = new BShelf(&ref, fPoseView); 256 257 if (fDeskShelf != NULL) 258 fDeskShelf->SetDisplaysZombies(true); 259 } 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 fDeskShelf; 397 _inherited::Quit(); 398 } 399 400 401 BPoseView* 402 BDeskWindow::NewPoseView(Model* model, uint32 viewMode) 403 { 404 return new DesktopPoseView(model, viewMode); 405 } 406 407 408 void 409 BDeskWindow::CreatePoseView(Model* model) 410 { 411 fPoseView = NewPoseView(model, 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 fPoseView->SetResizingMode(B_FOLLOW_ALL); 432 fPoseView->ResizeTo(Bounds().Size()); 433 AddChild(fPoseView); 434 435 PoseView()->StartSettingsWatch(); 436 } 437 438 439 void 440 BDeskWindow::AddWindowContextMenus(BMenu* menu) 441 { 442 TemplatesMenu* tempateMenu = new TemplatesMenu(PoseView(), 443 B_TRANSLATE("New")); 444 445 menu->AddItem(tempateMenu); 446 tempateMenu->SetTargetForItems(PoseView()); 447 tempateMenu->SetFont(be_plain_font); 448 449 menu->AddSeparatorItem(); 450 451 BMenu* iconSizeMenu = new BMenu(B_TRANSLATE("Icon view")); 452 453 BMessage* message = new BMessage(kIconMode); 454 message->AddInt32("size", 32); 455 BMenuItem* item = new BMenuItem(B_TRANSLATE("32 x 32"), message); 456 item->SetMarked(PoseView()->IconSizeInt() == 32); 457 item->SetTarget(PoseView()); 458 iconSizeMenu->AddItem(item); 459 460 message = new BMessage(kIconMode); 461 message->AddInt32("size", 40); 462 item = new BMenuItem(B_TRANSLATE("40 x 40"), message); 463 item->SetMarked(PoseView()->IconSizeInt() == 40); 464 item->SetTarget(PoseView()); 465 iconSizeMenu->AddItem(item); 466 467 message = new BMessage(kIconMode); 468 message->AddInt32("size", 48); 469 item = new BMenuItem(B_TRANSLATE("48 x 48"), message); 470 item->SetMarked(PoseView()->IconSizeInt() == 48); 471 item->SetTarget(PoseView()); 472 iconSizeMenu->AddItem(item); 473 474 message = new BMessage(kIconMode); 475 message->AddInt32("size", 64); 476 item = new BMenuItem(B_TRANSLATE("64 x 64"), message); 477 item->SetMarked(PoseView()->IconSizeInt() == 64); 478 item->SetTarget(PoseView()); 479 iconSizeMenu->AddItem(item); 480 481 message = new BMessage(kIconMode); 482 message->AddInt32("size", 96); 483 item = new BMenuItem(B_TRANSLATE("96 x 96"), message); 484 item->SetMarked(PoseView()->IconSizeInt() == 96); 485 item->SetTarget(PoseView()); 486 iconSizeMenu->AddItem(item); 487 488 message = new BMessage(kIconMode); 489 message->AddInt32("size", 128); 490 item = new BMenuItem(B_TRANSLATE("128 x 128"), message); 491 item->SetMarked(PoseView()->IconSizeInt() == 128); 492 item->SetTarget(PoseView()); 493 iconSizeMenu->AddItem(item); 494 495 iconSizeMenu->AddSeparatorItem(); 496 497 message = new BMessage(kIconMode); 498 message->AddInt32("scale", 0); 499 item = new BMenuItem(B_TRANSLATE("Decrease size"), message, '-'); 500 item->SetTarget(PoseView()); 501 iconSizeMenu->AddItem(item); 502 503 message = new BMessage(kIconMode); 504 message->AddInt32("scale", 1); 505 item = new BMenuItem(B_TRANSLATE("Increase size"), message, '+'); 506 item->SetTarget(PoseView()); 507 iconSizeMenu->AddItem(item); 508 509 // A sub menu where the super item can be invoked. 510 menu->AddItem(iconSizeMenu); 511 iconSizeMenu->Superitem()->SetShortcut('1', B_COMMAND_KEY); 512 iconSizeMenu->Superitem()->SetMessage(new BMessage(kIconMode)); 513 iconSizeMenu->Superitem()->SetTarget(PoseView()); 514 iconSizeMenu->Superitem()->SetMarked(PoseView()->ViewMode() == kIconMode); 515 516 item = new BMenuItem(B_TRANSLATE("Mini icon view"), 517 new BMessage(kMiniIconMode), '2'); 518 item->SetMarked(PoseView()->ViewMode() == kMiniIconMode); 519 menu->AddItem(item); 520 521 menu->AddSeparatorItem(); 522 523 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU 524 BMenuItem* pasteItem = new BMenuItem(B_TRANSLATE("Paste"), 525 new BMessage(B_PASTE), 'V'); 526 menu->AddItem(pasteItem); 527 menu->AddSeparatorItem(); 528 #endif 529 menu->AddItem(new BMenuItem(B_TRANSLATE("Clean up"), 530 new BMessage(kCleanup), 'K')); 531 menu->AddItem(new BMenuItem(B_TRANSLATE("Select" B_UTF8_ELLIPSIS), 532 new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY)); 533 menu->AddItem(new BMenuItem(B_TRANSLATE("Select all"), 534 new BMessage(B_SELECT_ALL), 'A')); 535 536 menu->AddSeparatorItem(); 537 menu->AddItem(new MountMenu(B_TRANSLATE("Mount"))); 538 539 menu->AddSeparatorItem(); 540 menu->AddItem(new BMenu(B_TRANSLATE("Add-ons"))); 541 542 // target items as needed 543 menu->SetTargetForItems(PoseView()); 544 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU 545 pasteItem->SetTarget(this); 546 #endif 547 } 548 549 550 void 551 BDeskWindow::WorkspaceActivated(int32 workspace, bool state) 552 { 553 if (fBackgroundImage) 554 fBackgroundImage->WorkspaceActivated(PoseView(), workspace, state); 555 } 556 557 558 void 559 BDeskWindow::SaveDesktopPoseLocations() 560 { 561 PoseView()->SavePoseLocations(&fOldFrame); 562 } 563 564 565 void 566 BDeskWindow::ScreenChanged(BRect frame, color_space space) 567 { 568 bool frameChanged = (frame != fOldFrame); 569 570 SaveDesktopPoseLocations(); 571 fOldFrame = frame; 572 ResizeTo(frame.Width(), frame.Height()); 573 574 if (fBackgroundImage) 575 fBackgroundImage->ScreenChanged(frame, space); 576 577 PoseView()->CheckPoseVisibility(frameChanged ? &frame : 0); 578 // if frame changed, pass new frame so that icons can 579 // get rearranged based on old pose info for the frame 580 } 581 582 583 void 584 BDeskWindow::UpdateDesktopBackgroundImages() 585 { 586 WindowStateNodeOpener opener(this, false); 587 fBackgroundImage = BackgroundImage::Refresh(fBackgroundImage, 588 opener.Node(), true, PoseView()); 589 } 590 591 592 void 593 BDeskWindow::Show() 594 { 595 if (fBackgroundImage) 596 fBackgroundImage->Show(PoseView(), current_workspace()); 597 598 PoseView()->CheckPoseVisibility(); 599 600 _inherited::Show(); 601 } 602 603 604 bool 605 BDeskWindow::ShouldAddScrollBars() const 606 { 607 return false; 608 } 609 610 611 bool 612 BDeskWindow::ShouldAddMenus() const 613 { 614 return false; 615 } 616 617 618 bool 619 BDeskWindow::ShouldAddContainerView() const 620 { 621 return false; 622 } 623 624 625 void 626 BDeskWindow::MessageReceived(BMessage* message) 627 { 628 if (message->WasDropped()) { 629 const rgb_color* color; 630 ssize_t size; 631 // handle "roColour"-style color drops 632 if (message->FindData("RGBColor", 'RGBC', 633 (const void**)&color, &size) == B_OK) { 634 BScreen(this).SetDesktopColor(*color); 635 fPoseView->SetViewColor(*color); 636 fPoseView->SetLowColor(*color); 637 638 // Notify the backgrounds app that the background changed 639 status_t initStatus; 640 BMessenger messenger("application/x-vnd.Haiku-Backgrounds", -1, 641 &initStatus); 642 if (initStatus == B_OK) 643 messenger.SendMessage(message); 644 645 return; 646 } 647 } 648 649 switch (message->what) { 650 case B_PATH_MONITOR: 651 { 652 const char* path = ""; 653 if (!(message->FindString("path", &path) == B_OK 654 && strcmp(path, fShortcutsSettings) == 0)) { 655 656 dev_t device; 657 ino_t node; 658 if (fNodeRef == NULL 659 || message->FindInt32("device", &device) != B_OK 660 || message->FindInt64("node", &node) != B_OK 661 || device != fNodeRef->device 662 || node != fNodeRef->node) 663 break; 664 } 665 ApplyShortcutPreferences(true); 666 break; 667 } 668 case B_NODE_MONITOR: 669 PRINT(("will update addon shortcuts\n")); 670 InitAddonsList(true); 671 ApplyShortcutPreferences(true); 672 break; 673 674 default: 675 _inherited::MessageReceived(message); 676 break; 677 } 678 } 679