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 449 menu->AddSeparatorItem(); 450 451 BMenu* iconSizeMenu = new BMenu(B_TRANSLATE("Icon view")); 452 BMenuItem* item; 453 454 static const uint32 kIconSizes[] = { 32, 40, 48, 64, 96, 128 }; 455 BMessage* message; 456 457 for (uint32 i = 0; i < sizeof(kIconSizes) / sizeof(uint32); ++i) { 458 uint32 iconSize = kIconSizes[i]; 459 message = new BMessage(kIconMode); 460 message->AddInt32("size", iconSize); 461 BString label; 462 label.SetToFormat(B_TRANSLATE_COMMENT("%" B_PRId32" × %" B_PRId32, 463 "The '×' is the Unicode multiplication sign U+00D7"), 464 iconSize, iconSize); 465 item = new BMenuItem(label, message); 466 item->SetMarked(PoseView()->IconSizeInt() == iconSize); 467 item->SetTarget(PoseView()); 468 iconSizeMenu->AddItem(item); 469 } 470 471 iconSizeMenu->AddSeparatorItem(); 472 473 message = new BMessage(kIconMode); 474 message->AddInt32("scale", 0); 475 item = new BMenuItem(B_TRANSLATE("Decrease size"), message, '-'); 476 item->SetTarget(PoseView()); 477 iconSizeMenu->AddItem(item); 478 479 message = new BMessage(kIconMode); 480 message->AddInt32("scale", 1); 481 item = new BMenuItem(B_TRANSLATE("Increase size"), message, '+'); 482 item->SetTarget(PoseView()); 483 iconSizeMenu->AddItem(item); 484 485 // A sub menu where the super item can be invoked. 486 menu->AddItem(iconSizeMenu); 487 iconSizeMenu->Superitem()->SetShortcut('1', B_COMMAND_KEY); 488 iconSizeMenu->Superitem()->SetMessage(new BMessage(kIconMode)); 489 iconSizeMenu->Superitem()->SetTarget(PoseView()); 490 iconSizeMenu->Superitem()->SetMarked(PoseView()->ViewMode() == kIconMode); 491 492 item = new BMenuItem(B_TRANSLATE("Mini icon view"), 493 new BMessage(kMiniIconMode), '2'); 494 item->SetMarked(PoseView()->ViewMode() == kMiniIconMode); 495 menu->AddItem(item); 496 497 menu->AddSeparatorItem(); 498 499 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU 500 BMenuItem* pasteItem = new BMenuItem(B_TRANSLATE("Paste"), 501 new BMessage(B_PASTE), 'V'); 502 menu->AddItem(pasteItem); 503 menu->AddSeparatorItem(); 504 #endif 505 menu->AddItem(new BMenuItem(B_TRANSLATE("Clean up"), 506 new BMessage(kCleanup), 'K')); 507 menu->AddItem(new BMenuItem(B_TRANSLATE("Select" B_UTF8_ELLIPSIS), 508 new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY)); 509 menu->AddItem(new BMenuItem(B_TRANSLATE("Select all"), 510 new BMessage(B_SELECT_ALL), 'A')); 511 512 menu->AddSeparatorItem(); 513 menu->AddItem(new MountMenu(B_TRANSLATE("Mount"))); 514 515 menu->AddSeparatorItem(); 516 menu->AddItem(new BMenu(B_TRANSLATE("Add-ons"))); 517 518 // target items as needed 519 menu->SetTargetForItems(PoseView()); 520 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU 521 pasteItem->SetTarget(this); 522 #endif 523 } 524 525 526 void 527 BDeskWindow::WorkspaceActivated(int32 workspace, bool state) 528 { 529 if (fBackgroundImage) 530 fBackgroundImage->WorkspaceActivated(PoseView(), workspace, state); 531 } 532 533 534 void 535 BDeskWindow::SaveDesktopPoseLocations() 536 { 537 PoseView()->SavePoseLocations(&fOldFrame); 538 } 539 540 541 void 542 BDeskWindow::ScreenChanged(BRect frame, color_space space) 543 { 544 bool frameChanged = (frame != fOldFrame); 545 546 SaveDesktopPoseLocations(); 547 fOldFrame = frame; 548 ResizeTo(frame.Width(), frame.Height()); 549 550 if (fBackgroundImage) 551 fBackgroundImage->ScreenChanged(frame, space); 552 553 PoseView()->CheckPoseVisibility(frameChanged ? &frame : 0); 554 // if frame changed, pass new frame so that icons can 555 // get rearranged based on old pose info for the frame 556 } 557 558 559 void 560 BDeskWindow::UpdateDesktopBackgroundImages() 561 { 562 WindowStateNodeOpener opener(this, false); 563 fBackgroundImage = BackgroundImage::Refresh(fBackgroundImage, 564 opener.Node(), true, PoseView()); 565 } 566 567 568 void 569 BDeskWindow::Show() 570 { 571 if (fBackgroundImage) 572 fBackgroundImage->Show(PoseView(), current_workspace()); 573 574 PoseView()->CheckPoseVisibility(); 575 576 _inherited::Show(); 577 } 578 579 580 bool 581 BDeskWindow::ShouldAddScrollBars() const 582 { 583 return false; 584 } 585 586 587 bool 588 BDeskWindow::ShouldAddMenus() const 589 { 590 return false; 591 } 592 593 594 bool 595 BDeskWindow::ShouldAddContainerView() const 596 { 597 return false; 598 } 599 600 601 void 602 BDeskWindow::MessageReceived(BMessage* message) 603 { 604 if (message->WasDropped()) { 605 const rgb_color* color; 606 ssize_t size; 607 // handle "roColour"-style color drops 608 if (message->FindData("RGBColor", 'RGBC', 609 (const void**)&color, &size) == B_OK) { 610 BScreen(this).SetDesktopColor(*color); 611 PoseView()->SetViewColor(*color); 612 PoseView()->SetLowColor(*color); 613 614 // Notify the backgrounds app that the background changed 615 status_t initStatus; 616 BMessenger messenger("application/x-vnd.Haiku-Backgrounds", -1, 617 &initStatus); 618 if (initStatus == B_OK) 619 messenger.SendMessage(message); 620 621 return; 622 } 623 } 624 625 switch (message->what) { 626 case B_PATH_MONITOR: 627 { 628 const char* path = ""; 629 if (!(message->FindString("path", &path) == B_OK 630 && strcmp(path, fShortcutsSettings) == 0)) { 631 632 dev_t device; 633 ino_t node; 634 if (fNodeRef == NULL 635 || message->FindInt32("device", &device) != B_OK 636 || message->FindInt64("node", &node) != B_OK 637 || device != fNodeRef->device 638 || node != fNodeRef->node) 639 break; 640 } 641 ApplyShortcutPreferences(true); 642 break; 643 } 644 case B_NODE_MONITOR: 645 PRINT(("will update addon shortcuts\n")); 646 InitAddOnsList(true); 647 ApplyShortcutPreferences(true); 648 break; 649 650 default: 651 _inherited::MessageReceived(message); 652 break; 653 } 654 } 655