xref: /haiku/src/kits/tracker/DeskWindow.cpp (revision 239222b2369c39dc52df52b0a7cdd6cc0a91bc92)
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, &params);
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