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