xref: /haiku/src/kits/tracker/DeskWindow.cpp (revision 9bd024edbe5d06358e4285100a3240e4d138a712)
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 			false, true),
196 	fDeskShelf(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 	InitKeyIndices();
244 	InitAddonsList(false);
245 	ApplyShortcutPreferences(false);
246 
247 	_inherited::Init();
248 
249 	entry_ref ref;
250 	BPath path;
251 	if (!BootedInSafeMode() && FSFindTrackerSettingsDir(&path) == B_OK) {
252 		path.Append(kShelfPath);
253 		close(open(path.Path(), O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR
254 			| S_IRGRP | S_IROTH));
255 		if (get_ref_for_path(path.Path(), &ref) == B_OK)
256 			fDeskShelf = new BShelf(&ref, fPoseView);
257 
258 		if (fDeskShelf != NULL)
259 			fDeskShelf->SetDisplaysZombies(true);
260 	}
261 }
262 
263 
264 void
265 BDeskWindow::InitAddonsList(bool update)
266 {
267 	AutoLock<LockingList<AddonShortcut> > lock(fAddonsList);
268 	if (lock.IsLocked()) {
269 		if (update) {
270 			for (int i = fAddonsList->CountItems() - 1; i >= 0; i--) {
271 				AddonShortcut* item = fAddonsList->ItemAt(i);
272 				RemoveShortcut(item->key, B_OPTION_KEY | B_COMMAND_KEY);
273 			}
274 			fAddonsList->MakeEmpty(true);
275 		}
276 
277 		BStringList addOnPaths;
278 		BPathFinder::FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY, "Tracker",
279 			addOnPaths);
280 		int32 count = addOnPaths.CountStrings();
281 		for (int32 i = 0; i < count; i++) {
282 			LoadAddOnDir(BDirectory(addOnPaths.StringAt(i)), this,
283 				fAddonsList);
284 		}
285 	}
286 }
287 
288 
289 void
290 BDeskWindow::ApplyShortcutPreferences(bool update)
291 {
292 	AutoLock<LockingList<AddonShortcut> > lock(fAddonsList);
293 	if (lock.IsLocked()) {
294 		if (!update) {
295 			BPath path;
296 			if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
297 				BPathMonitor::StartWatching(path.Path(),
298 					B_WATCH_STAT | B_WATCH_FILES_ONLY, this);
299 				path.Append(kShortcutsSettings);
300 				fShortcutsSettings = new char[strlen(path.Path()) + 1];
301 				strcpy(fShortcutsSettings, path.Path());
302 			}
303 		}
304 
305 		fAddonsList->EachElement(RevertToDefault, this);
306 
307 		BFile shortcutSettings(fShortcutsSettings, B_READ_ONLY);
308 		BMessage fileMsg;
309 		if (shortcutSettings.InitCheck() != B_OK
310 			|| fileMsg.Unflatten(&shortcutSettings) != B_OK) {
311 			fNodeRef = NULL;
312 			return;
313 		}
314 		shortcutSettings.GetNodeRef(fNodeRef);
315 
316 		int32 i = 0;
317 		BMessage message;
318 		while (fileMsg.FindMessage("spec", i++, &message) == B_OK) {
319 			int32 key;
320 			if (message.FindInt32("key", &key) == B_OK) {
321 				// only handle shortcuts referring add-ons
322 				BString command;
323 				if (message.FindString("command", &command) != B_OK)
324 					continue;
325 
326 				bool isInAddons = false;
327 
328 				BStringList addOnPaths;
329 				BPathFinder::FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY,
330 					"Tracker/", addOnPaths);
331 				for (int32 i = 0; i < addOnPaths.CountStrings(); i++) {
332 					if (command.StartsWith(addOnPaths.StringAt(i))) {
333 						isInAddons = true;
334 						break;
335 					}
336 				}
337 
338 				if (!isInAddons)
339 					continue;
340 
341 				BEntry entry(command);
342 				if (entry.InitCheck() != B_OK)
343 					continue;
344 
345 				const char* shortcut = GetKeyName(key);
346 				if (strlen(shortcut) != 1)
347 					continue;
348 
349 				uint32 modifiers = B_COMMAND_KEY;
350 					// it's required by interface kit to at least
351 					// have B_COMMAND_KEY
352 				int32 value;
353 				if (message.FindInt32("mcidx", 0, &value) == B_OK)
354 					modifiers |= (value != 0 ? B_SHIFT_KEY : 0);
355 
356 				if (message.FindInt32("mcidx", 1, &value) == B_OK)
357 					modifiers |= (value != 0 ? B_CONTROL_KEY : 0);
358 
359 				if (message.FindInt32("mcidx", 3, &value) == B_OK)
360 					modifiers |= (value != 0 ? B_OPTION_KEY : 0);
361 
362 				Model model(&entry);
363 				AddonShortcut* item = fAddonsList->EachElement(FindElement,
364 					&model);
365 				if (item != NULL) {
366 					if (item->key != '\0')
367 						RemoveShortcut(item->key, item->modifiers);
368 
369 					item->key = shortcut[0];
370 					item->modifiers = modifiers;
371 					AddOneShortcut(&model, item->key, item->modifiers, this);
372 				}
373 			}
374 		}
375 	}
376 }
377 
378 
379 void
380 BDeskWindow::Quit()
381 {
382 	if (fNavigationItem != NULL) {
383 		// this duplicates BContainerWindow::Quit because
384 		// fNavigationItem can be part of fTrashContextMenu
385 		// and would get deleted with it
386 		BMenu* menu = fNavigationItem->Menu();
387 		if (menu != NULL)
388 			menu->RemoveItem(fNavigationItem);
389 
390 		delete fNavigationItem;
391 		fNavigationItem = 0;
392 	}
393 
394 	fAddonsList->MakeEmpty(true);
395 	delete fAddonsList;
396 
397 	delete fDeskShelf;
398 	_inherited::Quit();
399 }
400 
401 
402 BPoseView*
403 BDeskWindow::NewPoseView(Model* model, uint32 viewMode)
404 {
405 	return new DesktopPoseView(model, viewMode);
406 }
407 
408 
409 void
410 BDeskWindow::CreatePoseView(Model* model)
411 {
412 	fPoseView = NewPoseView(model, kIconMode);
413 	fPoseView->SetIconMapping(false);
414 	fPoseView->SetEnsurePosesVisible(true);
415 	fPoseView->SetAutoScroll(false);
416 
417 	BScreen screen(this);
418 	rgb_color desktopColor = screen.DesktopColor();
419 	if (desktopColor.alpha != 255) {
420 		desktopColor.alpha = 255;
421 #if B_BEOS_VERSION > B_BEOS_VERSION_5
422 		// This call seems to have the power to cause R5 to freeze!
423 		// Please report if commenting this out helped or helped not
424 		// on your system
425 		screen.SetDesktopColor(desktopColor);
426 #endif
427 	}
428 
429 	fPoseView->SetViewColor(desktopColor);
430 	fPoseView->SetLowColor(desktopColor);
431 
432 	fPoseView->SetResizingMode(B_FOLLOW_ALL);
433 	fPoseView->ResizeTo(Bounds().Size());
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