xref: /haiku/src/kits/tracker/DeskWindow.cpp (revision 0de25abadc86e260328c6f7c4255acbee8f70d4e)
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 
36 #include "DeskWindow.h"
37 
38 #include <Catalog.h>
39 #include <Debug.h>
40 #include <FindDirectory.h>
41 #include <Locale.h>
42 #include <Messenger.h>
43 #include <NodeMonitor.h>
44 #include <Path.h>
45 #include <PathFinder.h>
46 #include <PathMonitor.h>
47 #include <PopUpMenu.h>
48 #include <Resources.h>
49 #include <Roster.h>
50 #include <Screen.h>
51 #include <String.h>
52 #include <StringList.h>
53 #include <Volume.h>
54 #include <VolumeRoster.h>
55 
56 #include <fcntl.h>
57 #include <unistd.h>
58 
59 #include "Attributes.h"
60 #include "AutoLock.h"
61 #include "BackgroundImage.h"
62 #include "Commands.h"
63 #include "FSUtils.h"
64 #include "IconMenuItem.h"
65 #include "KeyInfos.h"
66 #include "MountMenu.h"
67 #include "PoseView.h"
68 #include "Tracker.h"
69 #include "TemplatesMenu.h"
70 
71 
72 const char* kShelfPath = "tracker_shelf";
73 	// replicant support
74 
75 const char* kShortcutsSettings = "shortcuts_settings";
76 const char* kDefaultShortcut = "BEOS:default_shortcut";
77 const uint32 kDefaultModifiers = B_OPTION_KEY | B_COMMAND_KEY;
78 
79 
80 static struct AddonShortcut*
81 MatchOne(struct AddonShortcut* item, void* castToName)
82 {
83 	if (strcmp(item->model->Name(), (const char*)castToName) == 0) {
84 		// found match, bail out
85 		return item;
86 	}
87 
88 	return 0;
89 }
90 
91 
92 static void
93 AddOneShortcut(Model* model, char key, uint32 modifiers, BDeskWindow* window)
94 {
95 	if (key == '\0')
96 		return;
97 
98 	BMessage* runAddon = new BMessage(kLoadAddOn);
99 	runAddon->AddRef("refs", model->EntryRef());
100 	window->AddShortcut(key, modifiers, runAddon);
101 }
102 
103 
104 
105 static struct AddonShortcut*
106 RevertToDefault(struct AddonShortcut* item, void* castToWindow)
107 {
108 	if (item->key != item->defaultKey || item->modifiers != kDefaultModifiers) {
109 		BDeskWindow* window = static_cast<BDeskWindow*>(castToWindow);
110 		if (window != NULL) {
111 			window->RemoveShortcut(item->key, item->modifiers);
112 			item->key = item->defaultKey;
113 			item->modifiers = kDefaultModifiers;
114 			AddOneShortcut(item->model, item->key, item->modifiers, window);
115 		}
116 	}
117 
118 	return 0;
119 }
120 
121 
122 static struct AddonShortcut*
123 FindElement(struct AddonShortcut* item, void* castToOther)
124 {
125 	Model* other = static_cast<Model*>(castToOther);
126 	if (*item->model->EntryRef() == *other->EntryRef())
127 		return item;
128 
129 	return 0;
130 }
131 
132 
133 static void
134 LoadAddOnDir(BDirectory directory, BDeskWindow* window,
135 	LockingList<AddonShortcut>* list)
136 {
137 	BEntry entry;
138 	while (directory.GetNextEntry(&entry) == B_OK) {
139 		Model* model = new Model(&entry);
140 		if (model->InitCheck() == B_OK && model->IsSymLink()) {
141 			// resolve symlinks
142 			Model* resolved = new Model(model->EntryRef(), true, true);
143 			if (resolved->InitCheck() == B_OK)
144 				model->SetLinkTo(resolved);
145 			else
146 				delete resolved;
147 		}
148 		if (model->InitCheck() != B_OK
149 			|| !model->ResolveIfLink()->IsExecutable()) {
150 			delete model;
151 			continue;
152 		}
153 
154 		char* name = strdup(model->Name());
155 		if (!list->EachElement(MatchOne, name)) {
156 			struct AddonShortcut* item = new struct AddonShortcut;
157 			item->model = model;
158 
159 			BResources resources(model->ResolveIfLink()->EntryRef());
160 			size_t size;
161 			char* shortcut = (char*)resources.LoadResource(B_STRING_TYPE,
162 				kDefaultShortcut, &size);
163 			if (shortcut == NULL || strlen(shortcut) > 1)
164 				item->key = '\0';
165 			else
166 				item->key = shortcut[0];
167 			AddOneShortcut(model, item->key, kDefaultModifiers, window);
168 			item->defaultKey = item->key;
169 			item->modifiers = kDefaultModifiers;
170 			list->AddItem(item);
171 		}
172 		free(name);
173 	}
174 
175 	node_ref nodeRef;
176 	directory.GetNodeRef(&nodeRef);
177 
178 	TTracker::WatchNode(&nodeRef, B_WATCH_DIRECTORY, window);
179 }
180 
181 
182 // #pragma mark - BDeskWindow
183 
184 
185 #undef B_TRANSLATION_CONTEXT
186 #define B_TRANSLATION_CONTEXT "DeskWindow"
187 
188 
189 BDeskWindow::BDeskWindow(LockingList<BWindow>* windowList)
190 	:
191 	BContainerWindow(windowList, 0, kPrivateDesktopWindowLook,
192 		kPrivateDesktopWindowFeel, B_NOT_MOVABLE | B_WILL_ACCEPT_FIRST_CLICK
193 			| B_NOT_ZOOMABLE | B_NOT_CLOSABLE | B_NOT_MINIMIZABLE
194 			| B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS, B_ALL_WORKSPACES),
195 	fDeskShelf(NULL),
196 	fTrashContextMenu(NULL),
197 	fNodeRef(NULL),
198 	fShortcutsSettings(NULL)
199 {
200 	// Add icon view switching shortcuts. These are displayed in the context
201 	// menu, although they obviously don't work from those menu items.
202 	BMessage* message = new BMessage(kIconMode);
203 	AddShortcut('1', B_COMMAND_KEY, message, PoseView());
204 
205 	message = new BMessage(kMiniIconMode);
206 	AddShortcut('2', B_COMMAND_KEY, message, PoseView());
207 
208 	message = new BMessage(kIconMode);
209 	message->AddInt32("scale", 1);
210 	AddShortcut('+', B_COMMAND_KEY, message, PoseView());
211 
212 	message = new BMessage(kIconMode);
213 	message->AddInt32("scale", 0);
214 	AddShortcut('-', B_COMMAND_KEY, message, PoseView());
215 }
216 
217 
218 BDeskWindow::~BDeskWindow()
219 {
220 	SaveDesktopPoseLocations();
221 		// explicit call to SavePoseLocations so that extended pose info
222 		// gets committed properly
223 	PoseView()->DisableSaveLocation();
224 		// prevent double-saving, this would slow down quitting
225 	PoseView()->StopSettingsWatch();
226 	stop_watching(this);
227 }
228 
229 
230 void
231 BDeskWindow::Init(const BMessage*)
232 {
233 	// Set the size of the screen before calling the container window's
234 	// Init() because it will add volume poses to this window and
235 	// they will be clipped otherwise
236 
237 	BScreen screen(this);
238 	fOldFrame = screen.Frame();
239 
240 	PoseView()->SetShowHideSelection(false);
241 	ResizeTo(fOldFrame.Width(), fOldFrame.Height());
242 
243 	entry_ref ref;
244 	BPath path;
245 	if (!BootedInSafeMode() && FSFindTrackerSettingsDir(&path) == B_OK) {
246 		path.Append(kShelfPath);
247 		close(open(path.Path(), O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR
248 			| S_IRGRP | S_IROTH));
249 		if (get_ref_for_path(path.Path(), &ref) == B_OK)
250 			fDeskShelf = new BShelf(&ref, fPoseView);
251 
252 		if (fDeskShelf != NULL)
253 			fDeskShelf->SetDisplaysZombies(true);
254 	}
255 	InitKeyIndices();
256 	InitAddonsList(false);
257 	ApplyShortcutPreferences(false);
258 
259 	_inherited::Init();
260 }
261 
262 
263 void
264 BDeskWindow::InitAddonsList(bool update)
265 {
266 	AutoLock<LockingList<AddonShortcut> > lock(fAddonsList);
267 	if (lock.IsLocked()) {
268 		if (update) {
269 			for (int i = fAddonsList->CountItems() - 1; i >= 0; i--) {
270 				AddonShortcut* item = fAddonsList->ItemAt(i);
271 				RemoveShortcut(item->key, B_OPTION_KEY | B_COMMAND_KEY);
272 			}
273 			fAddonsList->MakeEmpty(true);
274 		}
275 
276 		BStringList addOnPaths;
277 		BPathFinder::FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY, "Tracker",
278 			addOnPaths);
279 		int32 count = addOnPaths.CountStrings();
280 		for (int32 i = 0; i < count; i++) {
281 			LoadAddOnDir(BDirectory(addOnPaths.StringAt(i)), this,
282 				fAddonsList);
283 		}
284 	}
285 }
286 
287 
288 void
289 BDeskWindow::ApplyShortcutPreferences(bool update)
290 {
291 	AutoLock<LockingList<AddonShortcut> > lock(fAddonsList);
292 	if (lock.IsLocked()) {
293 		if (!update) {
294 			BPath path;
295 			if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
296 				BPathMonitor::StartWatching(path.Path(),
297 					B_WATCH_STAT | B_WATCH_FILES_ONLY, this);
298 				path.Append(kShortcutsSettings);
299 				fShortcutsSettings = new char[strlen(path.Path()) + 1];
300 				strcpy(fShortcutsSettings, path.Path());
301 			}
302 		}
303 
304 		fAddonsList->EachElement(RevertToDefault, this);
305 
306 		BFile shortcutSettings(fShortcutsSettings, B_READ_ONLY);
307 		BMessage fileMsg;
308 		if (shortcutSettings.InitCheck() != B_OK
309 			|| fileMsg.Unflatten(&shortcutSettings) != B_OK) {
310 			fNodeRef = NULL;
311 			return;
312 		}
313 		shortcutSettings.GetNodeRef(fNodeRef);
314 
315 		int32 i = 0;
316 		BMessage message;
317 		while (fileMsg.FindMessage("spec", i++, &message) == B_OK) {
318 			int32 key;
319 			if (message.FindInt32("key", &key) == B_OK) {
320 				// only handle shortcuts referring add-ons
321 				BString command;
322 				if (message.FindString("command", &command) != B_OK)
323 					continue;
324 
325 				bool isInAddons = false;
326 
327 				BStringList addOnPaths;
328 				BPathFinder::FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY,
329 					"Tracker/", addOnPaths);
330 				for (int32 i = 0; i < addOnPaths.CountStrings(); i++) {
331 					if (command.StartsWith(addOnPaths.StringAt(i))) {
332 						isInAddons = true;
333 						break;
334 					}
335 				}
336 
337 				if (!isInAddons)
338 					continue;
339 
340 				BEntry entry(command);
341 				if (entry.InitCheck() != B_OK)
342 					continue;
343 
344 				const char* shortcut = GetKeyName(key);
345 				if (strlen(shortcut) != 1)
346 					continue;
347 
348 				uint32 modifiers = B_COMMAND_KEY;
349 					// it's required by interface kit to at least
350 					// have B_COMMAND_KEY
351 				int32 value;
352 				if (message.FindInt32("mcidx", 0, &value) == B_OK)
353 					modifiers |= (value != 0 ? B_SHIFT_KEY : 0);
354 
355 				if (message.FindInt32("mcidx", 1, &value) == B_OK)
356 					modifiers |= (value != 0 ? B_CONTROL_KEY : 0);
357 
358 				if (message.FindInt32("mcidx", 3, &value) == B_OK)
359 					modifiers |= (value != 0 ? B_OPTION_KEY : 0);
360 
361 				Model model(&entry);
362 				AddonShortcut* item = fAddonsList->EachElement(FindElement,
363 					&model);
364 				if (item != NULL) {
365 					if (item->key != '\0')
366 						RemoveShortcut(item->key, item->modifiers);
367 
368 					item->key = shortcut[0];
369 					item->modifiers = modifiers;
370 					AddOneShortcut(&model, item->key, item->modifiers, this);
371 				}
372 			}
373 		}
374 	}
375 }
376 
377 
378 void
379 BDeskWindow::Quit()
380 {
381 	if (fNavigationItem != NULL) {
382 		// this duplicates BContainerWindow::Quit because
383 		// fNavigationItem can be part of fTrashContextMenu
384 		// and would get deleted with it
385 		BMenu* menu = fNavigationItem->Menu();
386 		if (menu != NULL)
387 			menu->RemoveItem(fNavigationItem);
388 
389 		delete fNavigationItem;
390 		fNavigationItem = 0;
391 	}
392 
393 	fAddonsList->MakeEmpty(true);
394 	delete fAddonsList;
395 
396 	delete fTrashContextMenu;
397 	fTrashContextMenu = NULL;
398 
399 	delete fDeskShelf;
400 	_inherited::Quit();
401 }
402 
403 
404 BPoseView*
405 BDeskWindow::NewPoseView(Model* model, BRect rect, uint32 viewMode)
406 {
407 	return new DesktopPoseView(model, rect, viewMode);
408 }
409 
410 
411 void
412 BDeskWindow::CreatePoseView(Model* model)
413 {
414 	fPoseView = NewPoseView(model, Bounds(), kIconMode);
415 	fPoseView->SetIconMapping(false);
416 	fPoseView->SetEnsurePosesVisible(true);
417 	fPoseView->SetAutoScroll(false);
418 
419 	BScreen screen(this);
420 	rgb_color desktopColor = screen.DesktopColor();
421 	if (desktopColor.alpha != 255) {
422 		desktopColor.alpha = 255;
423 #if B_BEOS_VERSION > B_BEOS_VERSION_5
424 		// This call seems to have the power to cause R5 to freeze!
425 		// Please report if commenting this out helped or helped not
426 		// on your system
427 		screen.SetDesktopColor(desktopColor);
428 #endif
429 	}
430 
431 	fPoseView->SetViewColor(desktopColor);
432 	fPoseView->SetLowColor(desktopColor);
433 
434 	AddChild(fPoseView);
435 
436 	PoseView()->StartSettingsWatch();
437 }
438 
439 
440 void
441 BDeskWindow::AddWindowContextMenus(BMenu* menu)
442 {
443 	BRoster roster;
444 	if (!roster.IsRunning(kDeskbarSignature)) {
445 		menu->AddItem(new BMenuItem(B_TRANSLATE("Restart Deskbar"),
446 			new BMessage(kRestartDeskbar)));
447 		menu->AddSeparatorItem();
448 	}
449 
450 	TemplatesMenu* tempateMenu = new TemplatesMenu(PoseView(),
451 		B_TRANSLATE("New"));
452 
453 	menu->AddItem(tempateMenu);
454 	tempateMenu->SetTargetForItems(PoseView());
455 	tempateMenu->SetFont(be_plain_font);
456 
457 	menu->AddSeparatorItem();
458 
459 	BMenu* iconSizeMenu = new BMenu(B_TRANSLATE("Icon view"));
460 
461 	BMessage* message = new BMessage(kIconMode);
462 	message->AddInt32("size", 32);
463 	BMenuItem* item = new BMenuItem(B_TRANSLATE("32 x 32"), message);
464 	item->SetMarked(PoseView()->IconSizeInt() == 32);
465 	item->SetTarget(PoseView());
466 	iconSizeMenu->AddItem(item);
467 
468 	message = new BMessage(kIconMode);
469 	message->AddInt32("size", 40);
470 	item = new BMenuItem(B_TRANSLATE("40 x 40"), message);
471 	item->SetMarked(PoseView()->IconSizeInt() == 40);
472 	item->SetTarget(PoseView());
473 	iconSizeMenu->AddItem(item);
474 
475 	message = new BMessage(kIconMode);
476 	message->AddInt32("size", 48);
477 	item = new BMenuItem(B_TRANSLATE("48 x 48"), message);
478 	item->SetMarked(PoseView()->IconSizeInt() == 48);
479 	item->SetTarget(PoseView());
480 	iconSizeMenu->AddItem(item);
481 
482 	message = new BMessage(kIconMode);
483 	message->AddInt32("size", 64);
484 	item = new BMenuItem(B_TRANSLATE("64 x 64"), message);
485 	item->SetMarked(PoseView()->IconSizeInt() == 64);
486 	item->SetTarget(PoseView());
487 	iconSizeMenu->AddItem(item);
488 
489 	message = new BMessage(kIconMode);
490 	message->AddInt32("size", 96);
491 	item = new BMenuItem(B_TRANSLATE("96 x 96"), message);
492 	item->SetMarked(PoseView()->IconSizeInt() == 96);
493 	item->SetTarget(PoseView());
494 	iconSizeMenu->AddItem(item);
495 
496 	message = new BMessage(kIconMode);
497 	message->AddInt32("size", 128);
498 	item = new BMenuItem(B_TRANSLATE("128 x 128"), message);
499 	item->SetMarked(PoseView()->IconSizeInt() == 128);
500 	item->SetTarget(PoseView());
501 	iconSizeMenu->AddItem(item);
502 
503 	iconSizeMenu->AddSeparatorItem();
504 
505 	message = new BMessage(kIconMode);
506 	message->AddInt32("scale", 0);
507 	item = new BMenuItem(B_TRANSLATE("Decrease size"), message, '-');
508 	item->SetTarget(PoseView());
509 	iconSizeMenu->AddItem(item);
510 
511 	message = new BMessage(kIconMode);
512 	message->AddInt32("scale", 1);
513 	item = new BMenuItem(B_TRANSLATE("Increase size"), message, '+');
514 	item->SetTarget(PoseView());
515 	iconSizeMenu->AddItem(item);
516 
517 	// A sub menu where the super item can be invoked.
518 	menu->AddItem(iconSizeMenu);
519 	iconSizeMenu->Superitem()->SetShortcut('1', B_COMMAND_KEY);
520 	iconSizeMenu->Superitem()->SetMessage(new BMessage(kIconMode));
521 	iconSizeMenu->Superitem()->SetTarget(PoseView());
522 	iconSizeMenu->Superitem()->SetMarked(PoseView()->ViewMode() == kIconMode);
523 
524 	item = new BMenuItem(B_TRANSLATE("Mini icon view"),
525 		new BMessage(kMiniIconMode), '2');
526 	item->SetMarked(PoseView()->ViewMode() == kMiniIconMode);
527 	menu->AddItem(item);
528 
529 	menu->AddSeparatorItem();
530 
531 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU
532 	BMenuItem* pasteItem = new BMenuItem(B_TRANSLATE("Paste"),
533 		new BMessage(B_PASTE), 'V'));
534 	menu->AddItem(pasteItem);
535 	menu->AddSeparatorItem();
536 #endif
537 	menu->AddItem(new BMenuItem(B_TRANSLATE("Clean up"),
538 		new BMessage(kCleanup), 'K'));
539 	menu->AddItem(new BMenuItem(B_TRANSLATE("Select" B_UTF8_ELLIPSIS),
540 		new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY));
541 	menu->AddItem(new BMenuItem(B_TRANSLATE("Select all"),
542 		new BMessage(B_SELECT_ALL), 'A'));
543 
544 	menu->AddSeparatorItem();
545 	menu->AddItem(new MountMenu(B_TRANSLATE("Mount")));
546 
547 	menu->AddSeparatorItem();
548 	menu->AddItem(new BMenu(B_TRANSLATE("Add-ons")));
549 
550 	// target items as needed
551 	menu->SetTargetForItems(PoseView());
552 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU
553 	pasteItem->SetTarget(this);
554 #endif
555 }
556 
557 
558 void
559 BDeskWindow::WorkspaceActivated(int32 workspace, bool state)
560 {
561 	if (fBackgroundImage)
562 		fBackgroundImage->WorkspaceActivated(PoseView(), workspace, state);
563 }
564 
565 
566 void
567 BDeskWindow::SaveDesktopPoseLocations()
568 {
569 	PoseView()->SavePoseLocations(&fOldFrame);
570 }
571 
572 
573 void
574 BDeskWindow::ScreenChanged(BRect frame, color_space space)
575 {
576 	bool frameChanged = (frame != fOldFrame);
577 
578 	SaveDesktopPoseLocations();
579 	fOldFrame = frame;
580 	ResizeTo(frame.Width(), frame.Height());
581 
582 	if (fBackgroundImage)
583 		fBackgroundImage->ScreenChanged(frame, space);
584 
585 	PoseView()->CheckPoseVisibility(frameChanged ? &frame : 0);
586 		// if frame changed, pass new frame so that icons can
587 		// get rearranged based on old pose info for the frame
588 }
589 
590 
591 void
592 BDeskWindow::UpdateDesktopBackgroundImages()
593 {
594 	WindowStateNodeOpener opener(this, false);
595 	fBackgroundImage = BackgroundImage::Refresh(fBackgroundImage,
596 		opener.Node(), true, PoseView());
597 }
598 
599 
600 void
601 BDeskWindow::Show()
602 {
603 	if (fBackgroundImage)
604 		fBackgroundImage->Show(PoseView(), current_workspace());
605 
606 	PoseView()->CheckPoseVisibility();
607 
608 	_inherited::Show();
609 }
610 
611 
612 bool
613 BDeskWindow::ShouldAddScrollBars() const
614 {
615 	return false;
616 }
617 
618 
619 bool
620 BDeskWindow::ShouldAddMenus() const
621 {
622 	return false;
623 }
624 
625 
626 bool
627 BDeskWindow::ShouldAddContainerView() const
628 {
629 	return false;
630 }
631 
632 
633 void
634 BDeskWindow::MessageReceived(BMessage* message)
635 {
636 	if (message->WasDropped()) {
637 		const rgb_color* color;
638 		ssize_t size;
639 		// handle "roColour"-style color drops
640 		if (message->FindData("RGBColor", 'RGBC',
641 			(const void**)&color, &size) == B_OK) {
642 			BScreen(this).SetDesktopColor(*color);
643 			fPoseView->SetViewColor(*color);
644 			fPoseView->SetLowColor(*color);
645 
646 			// Notify the backgrounds app that the background changed
647 			status_t initStatus;
648 			BMessenger messenger("application/x-vnd.Haiku-Backgrounds", -1,
649 				&initStatus);
650 			if (initStatus == B_OK)
651 				messenger.SendMessage(message);
652 
653 			return;
654 		}
655 	}
656 
657 	switch (message->what) {
658 		case B_PATH_MONITOR:
659 		{
660 			const char* path = "";
661 			if (!(message->FindString("path", &path) == B_OK
662 					&& strcmp(path, fShortcutsSettings) == 0)) {
663 
664 				dev_t device;
665 				ino_t node;
666 				if (fNodeRef == NULL
667 					|| message->FindInt32("device", &device) != B_OK
668 					|| message->FindInt64("node", &node) != B_OK
669 					|| device != fNodeRef->device
670 					|| node != fNodeRef->node)
671 					break;
672 			}
673 			ApplyShortcutPreferences(true);
674 			break;
675 		}
676 		case B_NODE_MONITOR:
677 			PRINT(("will update addon shortcuts\n"));
678 			InitAddonsList(true);
679 			ApplyShortcutPreferences(true);
680 			break;
681 
682 		default:
683 			_inherited::MessageReceived(message);
684 			break;
685 	}
686 }
687