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 menu->AddItem(new BMenuItem("Icon View", new BMessage(kIconMode))); 265 menu->AddItem(new BMenuItem("Mini Icon View", new BMessage(kMiniIconMode))); 266 menu->AddSeparatorItem(); 267 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU 268 BMenuItem *pasteItem; 269 menu->AddItem(pasteItem = new BMenuItem("Paste", new BMessage(B_PASTE), 'V')); 270 menu->AddSeparatorItem(); 271 #endif 272 menu->AddItem(new BMenuItem("Clean Up", new BMessage(kCleanup), 'K')); 273 menu->AddItem(new BMenuItem("Select"B_UTF8_ELLIPSIS, 274 new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY)); 275 menu->AddItem(new BMenuItem("Select All", new BMessage(B_SELECT_ALL), 'A')); 276 277 menu->AddSeparatorItem(); 278 menu->AddItem(new MountMenu("Mount")); 279 280 menu->AddSeparatorItem(); 281 menu->AddItem(new BMenu(kAddOnsMenuName)); 282 283 // target items as needed 284 menu->SetTargetForItems(PoseView()); 285 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU 286 pasteItem->SetTarget(this); 287 #endif 288 } 289 290 291 void 292 BDeskWindow::AddTrashContextMenu() 293 { 294 // setup special trash context menu 295 fTrashContextMenu = new BPopUpMenu("TrashContext", false, false); 296 fTrashContextMenu->SetFont(be_plain_font); 297 fTrashContextMenu->AddItem(new BMenuItem("Empty Trash", 298 new BMessage(kEmptyTrash))); 299 fTrashContextMenu->AddItem(new BMenuItem("Open", 300 new BMessage(kOpenSelection), 'O')); 301 fTrashContextMenu->AddItem(new BMenuItem("Get Info", 302 new BMessage(kGetInfo), 'I')); 303 fTrashContextMenu->SetTargetForItems(PoseView()); 304 } 305 306 307 void 308 BDeskWindow::ShowContextMenu(BPoint loc, const entry_ref *ref, BView *view) 309 { 310 BEntry entry; 311 312 // cleanup previous entries 313 DeleteSubmenu(fNavigationItem); 314 315 if (ref && entry.SetTo(ref) == B_OK && FSIsTrashDir(&entry)) { 316 // 317 // don't show any menu if this is the trash 318 if (Dragging() && FSIsTrashDir(&entry)) 319 return; 320 321 // selected item was trash, show the trash context menu instead 322 BPoint global(loc); 323 PoseView()->ConvertToScreen(&global); 324 PoseView()->CommitActivePose(); 325 BRect mouse_rect(global.x, global.y, global.x, global.y); 326 mouse_rect.InsetBy(-5, -5); 327 328 EnableNamedMenuItem(fTrashContextMenu, kEmptyTrash, 329 static_cast<TTracker *>(be_app)->TrashFull()); 330 331 SetupNavigationMenu(ref, fTrashContextMenu); 332 fTrashContextMenu->Go(global, true, false, mouse_rect, true); 333 } else 334 _inherited::ShowContextMenu(loc, ref, view); 335 } 336 337 338 void 339 BDeskWindow::WorkspaceActivated(int32 workspace, bool state) 340 { 341 if (fBackgroundImage) 342 fBackgroundImage->WorkspaceActivated(PoseView(), workspace, state); 343 } 344 345 346 void 347 BDeskWindow::SaveDesktopPoseLocations() 348 { 349 PoseView()->SavePoseLocations(&fOldFrame); 350 } 351 352 353 void 354 BDeskWindow::ScreenChanged(BRect frame, color_space space) 355 { 356 bool frameChanged = (frame != fOldFrame); 357 358 SaveDesktopPoseLocations(); 359 fOldFrame = frame; 360 ResizeTo(frame.Width(), frame.Height()); 361 362 if (fBackgroundImage) 363 fBackgroundImage->ScreenChanged(frame, space); 364 365 PoseView()->CheckPoseVisibility(frameChanged ? &frame : 0); 366 // if frame changed, pass new frame so that icons can 367 // get rearranged based on old pose info for the frame 368 } 369 370 371 void 372 BDeskWindow::UpdateDesktopBackgroundImages() 373 { 374 WindowStateNodeOpener opener(this, false); 375 fBackgroundImage = BackgroundImage::Refresh(fBackgroundImage, 376 opener.Node(), true, PoseView()); 377 } 378 379 380 void 381 BDeskWindow::Show() 382 { 383 if (fBackgroundImage) 384 fBackgroundImage->Show(PoseView(), current_workspace()); 385 386 PoseView()->CheckPoseVisibility(); 387 388 _inherited::Show(); 389 } 390 391 392 bool 393 BDeskWindow::ShouldAddScrollBars() const 394 { 395 return false; 396 } 397 398 399 bool 400 BDeskWindow::ShouldAddMenus() const 401 { 402 return false; 403 } 404 405 406 bool 407 BDeskWindow::ShouldAddContainerView() const 408 { 409 return false; 410 } 411 412 413 void 414 BDeskWindow::MessageReceived(BMessage *message) 415 { 416 if (message->WasDropped()) { 417 const rgb_color *color; 418 int32 size; 419 // handle "roColour"-style color drops 420 if (message->FindData("RGBColor", 'RGBC', 421 (const void **)&color, &size) == B_OK) { 422 BScreen(this).SetDesktopColor(*color); 423 fPoseView->SetViewColor(*color); 424 fPoseView->SetLowColor(*color); 425 return; 426 } 427 } 428 429 switch (message->what) { 430 case B_NODE_MONITOR: 431 PRINT(("will update addon shortcuts\n")); 432 fShouldUpdateAddonShortcuts = true; 433 break; 434 435 default: 436 _inherited::MessageReceived(message); 437 break; 438 } 439 } 440 441