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