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 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, uint32 openFlags) 188 : 189 BContainerWindow(windowList, openFlags, 190 kDesktopWindowLook, kDesktopWindowFeel, 191 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, 194 B_ALL_WORKSPACES, 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, PoseView()); 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 return; 268 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, fAddOnsList); 283 } 284 285 286 void 287 BDeskWindow::ApplyShortcutPreferences(bool update) 288 { 289 AutoLock<LockingList<AddOnShortcut> > lock(fAddOnsList); 290 if (!lock.IsLocked()) 291 return; 292 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 continue; 321 322 // only handle shortcuts referring add-ons 323 BString command; 324 if (message.FindString("command", &command) != B_OK) 325 continue; 326 327 bool isInAddOns = false; 328 329 BStringList addOnPaths; 330 BPathFinder::FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY, 331 "Tracker/", addOnPaths); 332 for (int32 i = 0; i < addOnPaths.CountStrings(); i++) { 333 if (command.StartsWith(addOnPaths.StringAt(i))) { 334 isInAddOns = true; 335 break; 336 } 337 } 338 339 if (!isInAddOns) 340 continue; 341 342 BEntry entry(command); 343 if (entry.InitCheck() != B_OK) 344 continue; 345 346 const char* shortcut = GetKeyName(key); 347 if (strlen(shortcut) != 1) 348 continue; 349 350 uint32 modifiers = B_COMMAND_KEY; 351 // it's required by interface kit to at least 352 // have B_COMMAND_KEY 353 int32 value; 354 if (message.FindInt32("mcidx", 0, &value) == B_OK) 355 modifiers |= (value != 0 ? B_SHIFT_KEY : 0); 356 357 if (message.FindInt32("mcidx", 1, &value) == B_OK) 358 modifiers |= (value != 0 ? B_CONTROL_KEY : 0); 359 360 if (message.FindInt32("mcidx", 3, &value) == B_OK) 361 modifiers |= (value != 0 ? B_OPTION_KEY : 0); 362 363 Model model(&entry); 364 AddOnShortcut* item = fAddOnsList->EachElement(FindElement, &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 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 = NULL; 390 } 391 392 fAddOnsList->MakeEmpty(true); 393 delete fAddOnsList; 394 395 delete fDeskShelf; 396 397 // inherited will clean up the rest 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 TemplatesMenu* tempateMenu = new TemplatesMenu(PoseView(), 444 B_TRANSLATE("New")); 445 446 menu->AddItem(tempateMenu); 447 tempateMenu->SetTargetForItems(PoseView()); 448 tempateMenu->SetFont(be_plain_font); 449 450 menu->AddSeparatorItem(); 451 452 BMenu* iconSizeMenu = new BMenu(B_TRANSLATE("Icon view")); 453 BMenuItem* item; 454 455 static const uint32 kIconSizes[] = { 32, 40, 48, 64, 96, 128 }; 456 BMessage* message; 457 458 for (uint32 i = 0; i < sizeof(kIconSizes) / sizeof(uint32); ++i) { 459 uint32 iconSize = kIconSizes[i]; 460 message = new BMessage(kIconMode); 461 message->AddInt32("size", iconSize); 462 BString label; 463 label.SetToFormat(B_TRANSLATE_COMMENT("%" B_PRId32" × %" B_PRId32, 464 "The '×' is the Unicode multiplication sign U+00D7"), 465 iconSize, iconSize); 466 item = new BMenuItem(label, message); 467 item->SetMarked(PoseView()->IconSizeInt() == iconSize); 468 item->SetTarget(PoseView()); 469 iconSizeMenu->AddItem(item); 470 } 471 472 iconSizeMenu->AddSeparatorItem(); 473 474 message = new BMessage(kIconMode); 475 message->AddInt32("scale", 0); 476 item = new BMenuItem(B_TRANSLATE("Decrease size"), message, '-'); 477 item->SetTarget(PoseView()); 478 iconSizeMenu->AddItem(item); 479 480 message = new BMessage(kIconMode); 481 message->AddInt32("scale", 1); 482 item = new BMenuItem(B_TRANSLATE("Increase size"), message, '+'); 483 item->SetTarget(PoseView()); 484 iconSizeMenu->AddItem(item); 485 486 // A sub menu where the super item can be invoked. 487 menu->AddItem(iconSizeMenu); 488 iconSizeMenu->Superitem()->SetShortcut('1', B_COMMAND_KEY); 489 iconSizeMenu->Superitem()->SetMessage(new BMessage(kIconMode)); 490 iconSizeMenu->Superitem()->SetTarget(PoseView()); 491 iconSizeMenu->Superitem()->SetMarked(PoseView()->ViewMode() == kIconMode); 492 493 item = new BMenuItem(B_TRANSLATE("Mini icon view"), 494 new BMessage(kMiniIconMode), '2'); 495 item->SetMarked(PoseView()->ViewMode() == kMiniIconMode); 496 menu->AddItem(item); 497 498 menu->AddSeparatorItem(); 499 500 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU 501 BMenuItem* pasteItem = new BMenuItem(B_TRANSLATE("Paste"), 502 new BMessage(B_PASTE), 'V'); 503 menu->AddItem(pasteItem); 504 menu->AddSeparatorItem(); 505 #endif 506 menu->AddItem(new BMenuItem(B_TRANSLATE("Clean up"), 507 new BMessage(kCleanup), 'K')); 508 menu->AddItem(new BMenuItem(B_TRANSLATE("Select" B_UTF8_ELLIPSIS), 509 new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY)); 510 menu->AddItem(new BMenuItem(B_TRANSLATE("Select all"), 511 new BMessage(B_SELECT_ALL), 'A')); 512 513 menu->AddSeparatorItem(); 514 menu->AddItem(new MountMenu(B_TRANSLATE("Mount"))); 515 516 menu->AddSeparatorItem(); 517 menu->AddItem(new BMenu(B_TRANSLATE("Add-ons"))); 518 519 // target items as needed 520 menu->SetTargetForItems(PoseView()); 521 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU 522 pasteItem->SetTarget(this); 523 #endif 524 } 525 526 527 void 528 BDeskWindow::WorkspaceActivated(int32 workspace, bool state) 529 { 530 if (fBackgroundImage) 531 fBackgroundImage->WorkspaceActivated(PoseView(), workspace, state); 532 } 533 534 535 void 536 BDeskWindow::SaveDesktopPoseLocations() 537 { 538 PoseView()->SavePoseLocations(&fOldFrame); 539 } 540 541 542 void 543 BDeskWindow::ScreenChanged(BRect frame, color_space space) 544 { 545 bool frameChanged = (frame != fOldFrame); 546 547 SaveDesktopPoseLocations(); 548 fOldFrame = frame; 549 ResizeTo(frame.Width(), frame.Height()); 550 551 if (fBackgroundImage) 552 fBackgroundImage->ScreenChanged(frame, space); 553 554 PoseView()->CheckPoseVisibility(frameChanged ? &frame : 0); 555 // if frame changed, pass new frame so that icons can 556 // get rearranged based on old pose info for the frame 557 } 558 559 560 void 561 BDeskWindow::UpdateDesktopBackgroundImages() 562 { 563 WindowStateNodeOpener opener(this, false); 564 fBackgroundImage = BackgroundImage::Refresh(fBackgroundImage, 565 opener.Node(), true, PoseView()); 566 } 567 568 569 void 570 BDeskWindow::Show() 571 { 572 if (fBackgroundImage) 573 fBackgroundImage->Show(PoseView(), current_workspace()); 574 575 PoseView()->CheckPoseVisibility(); 576 577 _inherited::Show(); 578 } 579 580 581 bool 582 BDeskWindow::ShouldAddScrollBars() const 583 { 584 return false; 585 } 586 587 588 bool 589 BDeskWindow::ShouldAddMenus() const 590 { 591 return false; 592 } 593 594 595 bool 596 BDeskWindow::ShouldAddContainerView() const 597 { 598 return false; 599 } 600 601 602 void 603 BDeskWindow::MessageReceived(BMessage* message) 604 { 605 if (message->WasDropped()) { 606 const rgb_color* color; 607 ssize_t size; 608 // handle "roColour"-style color drops 609 if (message->FindData("RGBColor", 'RGBC', 610 (const void**)&color, &size) == B_OK) { 611 BScreen(this).SetDesktopColor(*color); 612 PoseView()->SetViewColor(*color); 613 PoseView()->SetLowColor(*color); 614 615 // Notify the backgrounds app that the background changed 616 status_t initStatus; 617 BMessenger messenger("application/x-vnd.Haiku-Backgrounds", -1, 618 &initStatus); 619 if (initStatus == B_OK) 620 messenger.SendMessage(message); 621 622 return; 623 } 624 } 625 626 switch (message->what) { 627 case B_PATH_MONITOR: 628 { 629 const char* path = ""; 630 if (!(message->FindString("path", &path) == B_OK 631 && strcmp(path, fShortcutsSettings) == 0)) { 632 633 dev_t device; 634 ino_t node; 635 if (fNodeRef == NULL 636 || message->FindInt32("device", &device) != B_OK 637 || message->FindInt64("node", &node) != B_OK 638 || device != fNodeRef->device 639 || node != fNodeRef->node) 640 break; 641 } 642 ApplyShortcutPreferences(true); 643 break; 644 } 645 case B_NODE_MONITOR: 646 PRINT(("will update addon shortcuts\n")); 647 InitAddOnsList(true); 648 ApplyShortcutPreferences(true); 649 break; 650 651 default: 652 _inherited::MessageReceived(message); 653 break; 654 } 655 } 656