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