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 <Debug.h> 36 #include <FindDirectory.h> 37 #include <NodeMonitor.h> 38 #include <Path.h> 39 #include <PopUpMenu.h> 40 #include <Screen.h> 41 #include <Volume.h> 42 #include <VolumeRoster.h> 43 44 #include <fcntl.h> 45 #include <unistd.h> 46 47 #include "Attributes.h" 48 #include "AutoLock.h" 49 #include "BackgroundImage.h" 50 #include "Commands.h" 51 #include "DesktopPoseView.h" 52 #include "DeskWindow.h" 53 #include "FSUtils.h" 54 #include "IconMenuItem.h" 55 #include "MountMenu.h" 56 #include "PoseView.h" 57 #include "Tracker.h" 58 #include "TemplatesMenu.h" 59 60 #if OPEN_TRACKER 61 #include "DeviceMap.h" 62 #else 63 #include <private/storage/DeviceMap.h> 64 #endif 65 66 const char *kShelfPath = "tracker_shelf"; 67 // replicant support 68 69 70 BDeskWindow::BDeskWindow(LockingList<BWindow> *windowList) 71 : BContainerWindow(windowList, 0, 72 kPrivateDesktopWindowLook, kPrivateDesktopWindowFeel, 73 B_NOT_MOVABLE | B_WILL_ACCEPT_FIRST_CLICK | 74 B_NOT_CLOSABLE | B_NOT_MINIMIZABLE | B_ASYNCHRONOUS_CONTROLS, 75 B_ALL_WORKSPACES), 76 fDeskShelf(0), 77 fTrashContextMenu(0), 78 fShouldUpdateAddonShortcuts(true) 79 { 80 // Add icon view switching shortcuts. These are displayed in the context 81 // menu, although they obviously don't work from those menu items. 82 BMessage* message = new BMessage(kIconMode); 83 AddShortcut('1', B_COMMAND_KEY, message, PoseView()); 84 85 message = new BMessage(kMiniIconMode); 86 AddShortcut('2', B_COMMAND_KEY, message, PoseView()); 87 88 message = new BMessage(kIconMode); 89 message->AddInt32("scale", 1); 90 AddShortcut('+', B_COMMAND_KEY, message, PoseView()); 91 92 message = new BMessage(kIconMode); 93 message->AddInt32("scale", 0); 94 AddShortcut('-', B_COMMAND_KEY, message, PoseView()); 95 } 96 97 98 BDeskWindow::~BDeskWindow() 99 { 100 SaveDesktopPoseLocations(); 101 // explicit call to SavePoseLocations so that extended pose info 102 // gets committed properly 103 PoseView()->DisableSaveLocation(); 104 // prevent double-saving, this would slow down quitting 105 PoseView()->StopSettingsWatch(); 106 stop_watching(this); 107 } 108 109 110 static void 111 WatchAddOnDir(directory_which dirName, BDeskWindow *window) 112 { 113 BPath path; 114 if (find_directory(dirName, &path) == B_OK) { 115 path.Append("Tracker"); 116 BNode node(path.Path()); 117 node_ref nodeRef; 118 node.GetNodeRef(&nodeRef); 119 TTracker::WatchNode(&nodeRef, B_WATCH_DIRECTORY, window); 120 } 121 } 122 123 124 void 125 BDeskWindow::Init(const BMessage *) 126 { 127 AddTrashContextMenu(); 128 // 129 // Set the size of the screen before calling the container window's 130 // Init() because it will add volume poses to this window and 131 // they will be clipped otherwise 132 // 133 BScreen screen(this); 134 fOldFrame = screen.Frame(); 135 136 PoseView()->SetShowHideSelection(false); 137 ResizeTo(fOldFrame.Width(), fOldFrame.Height()); 138 139 entry_ref ref; 140 BPath path; 141 if (!BootedInSafeMode() && FSFindTrackerSettingsDir(&path) == B_OK) { 142 path.Append(kShelfPath); 143 close(open(path.Path(), O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); 144 if (get_ref_for_path(path.Path(), &ref) == B_OK) 145 fDeskShelf = new BShelf(&ref, fPoseView); 146 if (fDeskShelf) 147 fDeskShelf->SetDisplaysZombies(true); 148 } 149 150 // watch add-on directories so that we can track the addons with 151 // corresponding shortcuts 152 WatchAddOnDir(B_BEOS_ADDONS_DIRECTORY, this); 153 WatchAddOnDir(B_USER_ADDONS_DIRECTORY, this); 154 WatchAddOnDir(B_COMMON_ADDONS_DIRECTORY, this); 155 156 _inherited::Init(); 157 } 158 159 160 struct AddOneShortcutParams { 161 BDeskWindow *window; 162 std::set<uint32> *currentAddonShortcuts; 163 }; 164 165 static bool 166 AddOneShortcut(const Model *model, const char *, uint32 shortcut, bool /*primary*/, void *context) 167 { 168 if (!shortcut) 169 // no shortcut, bail 170 return false; 171 172 AddOneShortcutParams *params = (AddOneShortcutParams *)context; 173 BMessage *runAddon = new BMessage(kLoadAddOn); 174 runAddon->AddRef("refs", model->EntryRef()); 175 176 params->window->AddShortcut(shortcut, B_OPTION_KEY | B_COMMAND_KEY, 177 runAddon); 178 params->currentAddonShortcuts->insert(shortcut); 179 PRINT(("adding new shortcut %c\n", (char)shortcut)); 180 181 return false; 182 } 183 184 185 void 186 BDeskWindow::MenusBeginning() 187 { 188 _inherited::MenusBeginning(); 189 190 if (fShouldUpdateAddonShortcuts) { 191 PRINT(("updating addon shortcuts\n")); 192 fShouldUpdateAddonShortcuts = false; 193 194 // remove all current addon shortcuts 195 for (std::set<uint32>::iterator it= fCurrentAddonShortcuts.begin(); 196 it != fCurrentAddonShortcuts.end(); it++) { 197 PRINT(("removing shortcut %c\n", *it)); 198 RemoveShortcut(*it, B_OPTION_KEY | B_COMMAND_KEY); 199 } 200 201 fCurrentAddonShortcuts.clear(); 202 203 AddOneShortcutParams params; 204 params.window = this; 205 params.currentAddonShortcuts = &fCurrentAddonShortcuts; 206 EachAddon(&AddOneShortcut, ¶ms); 207 } 208 } 209 210 211 void 212 BDeskWindow::Quit() 213 { 214 if (fNavigationItem) { 215 // this duplicates BContainerWindow::Quit because 216 // fNavigationItem can be part of fTrashContextMenu 217 // and would get deleted with it 218 BMenu *menu = fNavigationItem->Menu(); 219 if (menu) 220 menu->RemoveItem(fNavigationItem); 221 delete fNavigationItem; 222 fNavigationItem = 0; 223 } 224 225 delete fTrashContextMenu; 226 fTrashContextMenu = NULL; 227 228 delete fDeskShelf; 229 _inherited::Quit(); 230 } 231 232 233 BPoseView * 234 BDeskWindow::NewPoseView(Model *model, BRect rect, uint32 viewMode) 235 { 236 return new DesktopPoseView(model, rect, viewMode); 237 } 238 239 240 void 241 BDeskWindow::CreatePoseView(Model *model) 242 { 243 fPoseView = NewPoseView(model, Bounds(), kIconMode); 244 fPoseView->SetIconMapping(false); 245 fPoseView->SetEnsurePosesVisible(true); 246 fPoseView->SetAutoScroll(false); 247 248 BScreen screen(this); 249 rgb_color desktopColor = screen.DesktopColor(); 250 if (desktopColor.alpha != 255) { 251 desktopColor.alpha = 255; 252 #if B_BEOS_VERSION > B_BEOS_VERSION_5 253 // This call seems to have the power to cause R5 to freeze! 254 // Please report if commenting this out helped or helped not 255 // on your system 256 screen.SetDesktopColor(desktopColor); 257 #endif 258 } 259 260 fPoseView->SetViewColor(desktopColor); 261 fPoseView->SetLowColor(desktopColor); 262 263 AddChild(fPoseView); 264 265 PoseView()->StartSettingsWatch(); 266 } 267 268 269 void 270 BDeskWindow::AddWindowContextMenus(BMenu *menu) 271 { 272 TemplatesMenu *tempateMenu = new TemplatesMenu(PoseView()); 273 274 menu->AddItem(tempateMenu); 275 tempateMenu->SetTargetForItems(PoseView()); 276 tempateMenu->SetFont(be_plain_font); 277 278 menu->AddSeparatorItem(); 279 280 BMenu* iconSizeMenu = new BMenu("Icon View"); 281 282 BMessage* message = new BMessage(kIconMode); 283 message->AddInt32("size", 32); 284 BMenuItem* item = new BMenuItem("32 x 32", message); 285 item->SetMarked(PoseView()->IconSizeInt() == 32); 286 item->SetTarget(PoseView()); 287 iconSizeMenu->AddItem(item); 288 289 message = new BMessage(kIconMode); 290 message->AddInt32("size", 40); 291 item = new BMenuItem("40 x 40", message); 292 item->SetMarked(PoseView()->IconSizeInt() == 40); 293 item->SetTarget(PoseView()); 294 iconSizeMenu->AddItem(item); 295 296 message = new BMessage(kIconMode); 297 message->AddInt32("size", 48); 298 item = new BMenuItem("48 x 48", message); 299 item->SetMarked(PoseView()->IconSizeInt() == 48); 300 item->SetTarget(PoseView()); 301 iconSizeMenu->AddItem(item); 302 303 message = new BMessage(kIconMode); 304 message->AddInt32("size", 64); 305 item = new BMenuItem("64 x 64", message); 306 item->SetMarked(PoseView()->IconSizeInt() == 64); 307 item->SetTarget(PoseView()); 308 iconSizeMenu->AddItem(item); 309 310 iconSizeMenu->AddSeparatorItem(); 311 312 message = new BMessage(kIconMode); 313 message->AddInt32("scale", 0); 314 item = new BMenuItem("Decrease Size", message, '-'); 315 item->SetTarget(PoseView()); 316 iconSizeMenu->AddItem(item); 317 318 message = new BMessage(kIconMode); 319 message->AddInt32("scale", 1); 320 item = new BMenuItem("Increase Size", message, '+'); 321 item->SetTarget(PoseView()); 322 iconSizeMenu->AddItem(item); 323 324 // A sub menu where the super item can be invoked. 325 menu->AddItem(iconSizeMenu); 326 iconSizeMenu->Superitem()->SetShortcut('1', B_COMMAND_KEY); 327 iconSizeMenu->Superitem()->SetMessage(new BMessage(kIconMode)); 328 iconSizeMenu->Superitem()->SetTarget(PoseView()); 329 iconSizeMenu->Superitem()->SetMarked(PoseView()->ViewMode() == kIconMode); 330 331 item = new BMenuItem("Mini Icon View", new BMessage(kMiniIconMode), '2'); 332 item->SetMarked(PoseView()->ViewMode() == kMiniIconMode); 333 menu->AddItem(item); 334 335 menu->AddSeparatorItem(); 336 337 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU 338 BMenuItem *pasteItem; 339 menu->AddItem(pasteItem = new BMenuItem("Paste", new BMessage(B_PASTE), 'V')); 340 menu->AddSeparatorItem(); 341 #endif 342 menu->AddItem(new BMenuItem("Clean Up", new BMessage(kCleanup), 'K')); 343 menu->AddItem(new BMenuItem("Select"B_UTF8_ELLIPSIS, 344 new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY)); 345 menu->AddItem(new BMenuItem("Select All", new BMessage(B_SELECT_ALL), 'A')); 346 347 menu->AddSeparatorItem(); 348 menu->AddItem(new MountMenu("Mount")); 349 350 menu->AddSeparatorItem(); 351 menu->AddItem(new BMenu(kAddOnsMenuName)); 352 353 // target items as needed 354 menu->SetTargetForItems(PoseView()); 355 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU 356 pasteItem->SetTarget(this); 357 #endif 358 } 359 360 361 void 362 BDeskWindow::AddTrashContextMenu() 363 { 364 // setup special trash context menu 365 fTrashContextMenu = new BPopUpMenu("TrashContext", false, false); 366 fTrashContextMenu->SetFont(be_plain_font); 367 fTrashContextMenu->AddItem(new BMenuItem("Empty Trash", 368 new BMessage(kEmptyTrash))); 369 fTrashContextMenu->AddItem(new BMenuItem("Open", 370 new BMessage(kOpenSelection), 'O')); 371 fTrashContextMenu->AddItem(new BMenuItem("Get Info", 372 new BMessage(kGetInfo), 'I')); 373 fTrashContextMenu->SetTargetForItems(PoseView()); 374 } 375 376 377 void 378 BDeskWindow::ShowContextMenu(BPoint loc, const entry_ref *ref, BView *view) 379 { 380 BEntry entry; 381 382 // cleanup previous entries 383 DeleteSubmenu(fNavigationItem); 384 385 if (ref && entry.SetTo(ref) == B_OK && FSIsTrashDir(&entry)) { 386 387 if (fTrashContextMenu->Window() || Dragging()) 388 return; 389 390 // selected item was trash, show the trash context menu instead 391 BPoint global(loc); 392 PoseView()->ConvertToScreen(&global); 393 PoseView()->CommitActivePose(); 394 BRect mouse_rect(global.x, global.y, global.x, global.y); 395 mouse_rect.InsetBy(-5, -5); 396 397 EnableNamedMenuItem(fTrashContextMenu, kEmptyTrash, 398 static_cast<TTracker *>(be_app)->TrashFull()); 399 400 SetupNavigationMenu(ref, fTrashContextMenu); 401 fTrashContextMenu->Go(global, true, false, mouse_rect, true); 402 } else 403 _inherited::ShowContextMenu(loc, ref, view); 404 } 405 406 407 void 408 BDeskWindow::WorkspaceActivated(int32 workspace, bool state) 409 { 410 if (fBackgroundImage) 411 fBackgroundImage->WorkspaceActivated(PoseView(), workspace, state); 412 } 413 414 415 void 416 BDeskWindow::SaveDesktopPoseLocations() 417 { 418 PoseView()->SavePoseLocations(&fOldFrame); 419 } 420 421 422 void 423 BDeskWindow::ScreenChanged(BRect frame, color_space space) 424 { 425 bool frameChanged = (frame != fOldFrame); 426 427 SaveDesktopPoseLocations(); 428 fOldFrame = frame; 429 ResizeTo(frame.Width(), frame.Height()); 430 431 if (fBackgroundImage) 432 fBackgroundImage->ScreenChanged(frame, space); 433 434 PoseView()->CheckPoseVisibility(frameChanged ? &frame : 0); 435 // if frame changed, pass new frame so that icons can 436 // get rearranged based on old pose info for the frame 437 } 438 439 440 void 441 BDeskWindow::UpdateDesktopBackgroundImages() 442 { 443 WindowStateNodeOpener opener(this, false); 444 fBackgroundImage = BackgroundImage::Refresh(fBackgroundImage, 445 opener.Node(), true, PoseView()); 446 } 447 448 449 void 450 BDeskWindow::Show() 451 { 452 if (fBackgroundImage) 453 fBackgroundImage->Show(PoseView(), current_workspace()); 454 455 PoseView()->CheckPoseVisibility(); 456 457 _inherited::Show(); 458 } 459 460 461 bool 462 BDeskWindow::ShouldAddScrollBars() const 463 { 464 return false; 465 } 466 467 468 bool 469 BDeskWindow::ShouldAddMenus() const 470 { 471 return false; 472 } 473 474 475 bool 476 BDeskWindow::ShouldAddContainerView() const 477 { 478 return false; 479 } 480 481 482 void 483 BDeskWindow::MessageReceived(BMessage *message) 484 { 485 if (message->WasDropped()) { 486 const rgb_color *color; 487 int32 size; 488 // handle "roColour"-style color drops 489 if (message->FindData("RGBColor", 'RGBC', 490 (const void **)&color, &size) == B_OK) { 491 BScreen(this).SetDesktopColor(*color); 492 fPoseView->SetViewColor(*color); 493 fPoseView->SetLowColor(*color); 494 return; 495 } 496 } 497 498 switch (message->what) { 499 case B_NODE_MONITOR: 500 PRINT(("will update addon shortcuts\n")); 501 fShouldUpdateAddonShortcuts = true; 502 break; 503 504 default: 505 _inherited::MessageReceived(message); 506 break; 507 } 508 } 509 510