xref: /haiku/src/kits/tracker/DeskWindow.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
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 <Screen.h>
50 #include <String.h>
51 #include <StringList.h>
52 #include <Volume.h>
53 #include <WindowPrivate.h>
54 
55 #include <fcntl.h>
56 #include <unistd.h>
57 
58 #include "Attributes.h"
59 #include "AutoLock.h"
60 #include "BackgroundImage.h"
61 #include "Commands.h"
62 #include "FSUtils.h"
63 #include "IconMenuItem.h"
64 #include "KeyInfos.h"
65 #include "MountMenu.h"
66 #include "PoseView.h"
67 #include "Tracker.h"
68 #include "TemplatesMenu.h"
69 
70 
71 const char* kShelfPath = "tracker_shelf";
72 	// replicant support
73 
74 const char* kShortcutsSettings = "shortcuts_settings";
75 const char* kDefaultShortcut = "BEOS:default_shortcut";
76 const uint32 kDefaultModifiers = B_OPTION_KEY | B_COMMAND_KEY;
77 
78 
79 static struct AddonShortcut*
80 MatchOne(struct AddonShortcut* item, void* castToName)
81 {
82 	if (strcmp(item->model->Name(), (const char*)castToName) == 0) {
83 		// found match, bail out
84 		return item;
85 	}
86 
87 	return 0;
88 }
89 
90 
91 static void
92 AddOneShortcut(Model* model, char key, uint32 modifiers, BDeskWindow* window)
93 {
94 	if (key == '\0')
95 		return;
96 
97 	BMessage* runAddon = new BMessage(kLoadAddOn);
98 	runAddon->AddRef("refs", model->EntryRef());
99 	window->AddShortcut(key, modifiers, runAddon);
100 }
101 
102 
103 
104 static struct AddonShortcut*
105 RevertToDefault(struct AddonShortcut* item, void* castToWindow)
106 {
107 	if (item->key != item->defaultKey || item->modifiers != kDefaultModifiers) {
108 		BDeskWindow* window = static_cast<BDeskWindow*>(castToWindow);
109 		if (window != NULL) {
110 			window->RemoveShortcut(item->key, item->modifiers);
111 			item->key = item->defaultKey;
112 			item->modifiers = kDefaultModifiers;
113 			AddOneShortcut(item->model, item->key, item->modifiers, window);
114 		}
115 	}
116 
117 	return 0;
118 }
119 
120 
121 static struct AddonShortcut*
122 FindElement(struct AddonShortcut* item, void* castToOther)
123 {
124 	Model* other = static_cast<Model*>(castToOther);
125 	if (*item->model->EntryRef() == *other->EntryRef())
126 		return item;
127 
128 	return 0;
129 }
130 
131 
132 static void
133 LoadAddOnDir(BDirectory directory, BDeskWindow* window,
134 	LockingList<AddonShortcut>* list)
135 {
136 	BEntry entry;
137 	while (directory.GetNextEntry(&entry) == B_OK) {
138 		Model* model = new Model(&entry);
139 		if (model->InitCheck() == B_OK && model->IsSymLink()) {
140 			// resolve symlinks
141 			Model* resolved = new Model(model->EntryRef(), true, true);
142 			if (resolved->InitCheck() == B_OK)
143 				model->SetLinkTo(resolved);
144 			else
145 				delete resolved;
146 		}
147 		if (model->InitCheck() != B_OK
148 			|| !model->ResolveIfLink()->IsExecutable()) {
149 			delete model;
150 			continue;
151 		}
152 
153 		char* name = strdup(model->Name());
154 		if (!list->EachElement(MatchOne, name)) {
155 			struct AddonShortcut* item = new struct AddonShortcut;
156 			item->model = model;
157 
158 			BResources resources(model->ResolveIfLink()->EntryRef());
159 			size_t size;
160 			char* shortcut = (char*)resources.LoadResource(B_STRING_TYPE,
161 				kDefaultShortcut, &size);
162 			if (shortcut == NULL || strlen(shortcut) > 1)
163 				item->key = '\0';
164 			else
165 				item->key = shortcut[0];
166 			AddOneShortcut(model, item->key, kDefaultModifiers, window);
167 			item->defaultKey = item->key;
168 			item->modifiers = kDefaultModifiers;
169 			list->AddItem(item);
170 		}
171 		free(name);
172 	}
173 
174 	node_ref nodeRef;
175 	directory.GetNodeRef(&nodeRef);
176 
177 	TTracker::WatchNode(&nodeRef, B_WATCH_DIRECTORY, window);
178 }
179 
180 
181 // #pragma mark - BDeskWindow
182 
183 
184 #undef B_TRANSLATION_CONTEXT
185 #define B_TRANSLATION_CONTEXT "DeskWindow"
186 
187 
188 BDeskWindow::BDeskWindow(LockingList<BWindow>* windowList)
189 	:
190 	BContainerWindow(windowList, 0, kDesktopWindowLook,
191 		kDesktopWindowFeel, B_NOT_MOVABLE | B_WILL_ACCEPT_FIRST_CLICK
192 			| B_NOT_ZOOMABLE | B_NOT_CLOSABLE | B_NOT_MINIMIZABLE
193 			| B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS, B_ALL_WORKSPACES,
194 			false, true),
195 	fDeskShelf(NULL),
196 	fNodeRef(NULL),
197 	fShortcutsSettings(NULL)
198 {
199 	// Add icon view switching shortcuts. These are displayed in the context
200 	// menu, although they obviously don't work from those menu items.
201 	BMessage* message = new BMessage(kIconMode);
202 	AddShortcut('1', B_COMMAND_KEY, message, PoseView());
203 
204 	message = new BMessage(kMiniIconMode);
205 	AddShortcut('2', B_COMMAND_KEY, message, PoseView());
206 
207 	message = new BMessage(kIconMode);
208 	message->AddInt32("scale", 1);
209 	AddShortcut('+', B_COMMAND_KEY, message, PoseView());
210 
211 	message = new BMessage(kIconMode);
212 	message->AddInt32("scale", 0);
213 	AddShortcut('-', B_COMMAND_KEY, message, PoseView());
214 }
215 
216 
217 BDeskWindow::~BDeskWindow()
218 {
219 	SaveDesktopPoseLocations();
220 		// explicit call to SavePoseLocations so that extended pose info
221 		// gets committed properly
222 	PoseView()->DisableSaveLocation();
223 		// prevent double-saving, this would slow down quitting
224 	PoseView()->StopSettingsWatch();
225 	stop_watching(this);
226 }
227 
228 
229 void
230 BDeskWindow::Init(const BMessage*)
231 {
232 	// Set the size of the screen before calling the container window's
233 	// Init() because it will add volume poses to this window and
234 	// they will be clipped otherwise
235 
236 	BScreen screen(this);
237 	fOldFrame = screen.Frame();
238 
239 	PoseView()->SetShowHideSelection(false);
240 	ResizeTo(fOldFrame.Width(), fOldFrame.Height());
241 
242 	InitKeyIndices();
243 	InitAddonsList(false);
244 	ApplyShortcutPreferences(false);
245 
246 	_inherited::Init();
247 
248 	entry_ref ref;
249 	BPath path;
250 	if (!BootedInSafeMode() && FSFindTrackerSettingsDir(&path) == B_OK) {
251 		path.Append(kShelfPath);
252 		close(open(path.Path(), O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR
253 			| S_IRGRP | S_IROTH));
254 		if (get_ref_for_path(path.Path(), &ref) == B_OK)
255 			fDeskShelf = new BShelf(&ref, fPoseView);
256 
257 		if (fDeskShelf != NULL)
258 			fDeskShelf->SetDisplaysZombies(true);
259 	}
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 fDeskShelf;
397 	_inherited::Quit();
398 }
399 
400 
401 BPoseView*
402 BDeskWindow::NewPoseView(Model* model, uint32 viewMode)
403 {
404 	return new DesktopPoseView(model, viewMode);
405 }
406 
407 
408 void
409 BDeskWindow::CreatePoseView(Model* model)
410 {
411 	fPoseView = NewPoseView(model, kIconMode);
412 	fPoseView->SetIconMapping(false);
413 	fPoseView->SetEnsurePosesVisible(true);
414 	fPoseView->SetAutoScroll(false);
415 
416 	BScreen screen(this);
417 	rgb_color desktopColor = screen.DesktopColor();
418 	if (desktopColor.alpha != 255) {
419 		desktopColor.alpha = 255;
420 #if B_BEOS_VERSION > B_BEOS_VERSION_5
421 		// This call seems to have the power to cause R5 to freeze!
422 		// Please report if commenting this out helped or helped not
423 		// on your system
424 		screen.SetDesktopColor(desktopColor);
425 #endif
426 	}
427 
428 	fPoseView->SetViewColor(desktopColor);
429 	fPoseView->SetLowColor(desktopColor);
430 
431 	fPoseView->SetResizingMode(B_FOLLOW_ALL);
432 	fPoseView->ResizeTo(Bounds().Size());
433 	AddChild(fPoseView);
434 
435 	PoseView()->StartSettingsWatch();
436 }
437 
438 
439 void
440 BDeskWindow::AddWindowContextMenus(BMenu* menu)
441 {
442 	TemplatesMenu* tempateMenu = new TemplatesMenu(PoseView(),
443 		B_TRANSLATE("New"));
444 
445 	menu->AddItem(tempateMenu);
446 	tempateMenu->SetTargetForItems(PoseView());
447 	tempateMenu->SetFont(be_plain_font);
448 
449 	menu->AddSeparatorItem();
450 
451 	BMenu* iconSizeMenu = new BMenu(B_TRANSLATE("Icon view"));
452 
453 	BMessage* message = new BMessage(kIconMode);
454 	message->AddInt32("size", 32);
455 	BMenuItem* item = new BMenuItem(B_TRANSLATE("32 x 32"), message);
456 	item->SetMarked(PoseView()->IconSizeInt() == 32);
457 	item->SetTarget(PoseView());
458 	iconSizeMenu->AddItem(item);
459 
460 	message = new BMessage(kIconMode);
461 	message->AddInt32("size", 40);
462 	item = new BMenuItem(B_TRANSLATE("40 x 40"), message);
463 	item->SetMarked(PoseView()->IconSizeInt() == 40);
464 	item->SetTarget(PoseView());
465 	iconSizeMenu->AddItem(item);
466 
467 	message = new BMessage(kIconMode);
468 	message->AddInt32("size", 48);
469 	item = new BMenuItem(B_TRANSLATE("48 x 48"), message);
470 	item->SetMarked(PoseView()->IconSizeInt() == 48);
471 	item->SetTarget(PoseView());
472 	iconSizeMenu->AddItem(item);
473 
474 	message = new BMessage(kIconMode);
475 	message->AddInt32("size", 64);
476 	item = new BMenuItem(B_TRANSLATE("64 x 64"), message);
477 	item->SetMarked(PoseView()->IconSizeInt() == 64);
478 	item->SetTarget(PoseView());
479 	iconSizeMenu->AddItem(item);
480 
481 	message = new BMessage(kIconMode);
482 	message->AddInt32("size", 96);
483 	item = new BMenuItem(B_TRANSLATE("96 x 96"), message);
484 	item->SetMarked(PoseView()->IconSizeInt() == 96);
485 	item->SetTarget(PoseView());
486 	iconSizeMenu->AddItem(item);
487 
488 	message = new BMessage(kIconMode);
489 	message->AddInt32("size", 128);
490 	item = new BMenuItem(B_TRANSLATE("128 x 128"), message);
491 	item->SetMarked(PoseView()->IconSizeInt() == 128);
492 	item->SetTarget(PoseView());
493 	iconSizeMenu->AddItem(item);
494 
495 	iconSizeMenu->AddSeparatorItem();
496 
497 	message = new BMessage(kIconMode);
498 	message->AddInt32("scale", 0);
499 	item = new BMenuItem(B_TRANSLATE("Decrease size"), message, '-');
500 	item->SetTarget(PoseView());
501 	iconSizeMenu->AddItem(item);
502 
503 	message = new BMessage(kIconMode);
504 	message->AddInt32("scale", 1);
505 	item = new BMenuItem(B_TRANSLATE("Increase size"), message, '+');
506 	item->SetTarget(PoseView());
507 	iconSizeMenu->AddItem(item);
508 
509 	// A sub menu where the super item can be invoked.
510 	menu->AddItem(iconSizeMenu);
511 	iconSizeMenu->Superitem()->SetShortcut('1', B_COMMAND_KEY);
512 	iconSizeMenu->Superitem()->SetMessage(new BMessage(kIconMode));
513 	iconSizeMenu->Superitem()->SetTarget(PoseView());
514 	iconSizeMenu->Superitem()->SetMarked(PoseView()->ViewMode() == kIconMode);
515 
516 	item = new BMenuItem(B_TRANSLATE("Mini icon view"),
517 		new BMessage(kMiniIconMode), '2');
518 	item->SetMarked(PoseView()->ViewMode() == kMiniIconMode);
519 	menu->AddItem(item);
520 
521 	menu->AddSeparatorItem();
522 
523 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU
524 	BMenuItem* pasteItem = new BMenuItem(B_TRANSLATE("Paste"),
525 		new BMessage(B_PASTE), 'V');
526 	menu->AddItem(pasteItem);
527 	menu->AddSeparatorItem();
528 #endif
529 	menu->AddItem(new BMenuItem(B_TRANSLATE("Clean up"),
530 		new BMessage(kCleanup), 'K'));
531 	menu->AddItem(new BMenuItem(B_TRANSLATE("Select" B_UTF8_ELLIPSIS),
532 		new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY));
533 	menu->AddItem(new BMenuItem(B_TRANSLATE("Select all"),
534 		new BMessage(B_SELECT_ALL), 'A'));
535 
536 	menu->AddSeparatorItem();
537 	menu->AddItem(new MountMenu(B_TRANSLATE("Mount")));
538 
539 	menu->AddSeparatorItem();
540 	menu->AddItem(new BMenu(B_TRANSLATE("Add-ons")));
541 
542 	// target items as needed
543 	menu->SetTargetForItems(PoseView());
544 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU
545 	pasteItem->SetTarget(this);
546 #endif
547 }
548 
549 
550 void
551 BDeskWindow::WorkspaceActivated(int32 workspace, bool state)
552 {
553 	if (fBackgroundImage)
554 		fBackgroundImage->WorkspaceActivated(PoseView(), workspace, state);
555 }
556 
557 
558 void
559 BDeskWindow::SaveDesktopPoseLocations()
560 {
561 	PoseView()->SavePoseLocations(&fOldFrame);
562 }
563 
564 
565 void
566 BDeskWindow::ScreenChanged(BRect frame, color_space space)
567 {
568 	bool frameChanged = (frame != fOldFrame);
569 
570 	SaveDesktopPoseLocations();
571 	fOldFrame = frame;
572 	ResizeTo(frame.Width(), frame.Height());
573 
574 	if (fBackgroundImage)
575 		fBackgroundImage->ScreenChanged(frame, space);
576 
577 	PoseView()->CheckPoseVisibility(frameChanged ? &frame : 0);
578 		// if frame changed, pass new frame so that icons can
579 		// get rearranged based on old pose info for the frame
580 }
581 
582 
583 void
584 BDeskWindow::UpdateDesktopBackgroundImages()
585 {
586 	WindowStateNodeOpener opener(this, false);
587 	fBackgroundImage = BackgroundImage::Refresh(fBackgroundImage,
588 		opener.Node(), true, PoseView());
589 }
590 
591 
592 void
593 BDeskWindow::Show()
594 {
595 	if (fBackgroundImage)
596 		fBackgroundImage->Show(PoseView(), current_workspace());
597 
598 	PoseView()->CheckPoseVisibility();
599 
600 	_inherited::Show();
601 }
602 
603 
604 bool
605 BDeskWindow::ShouldAddScrollBars() const
606 {
607 	return false;
608 }
609 
610 
611 bool
612 BDeskWindow::ShouldAddMenus() const
613 {
614 	return false;
615 }
616 
617 
618 bool
619 BDeskWindow::ShouldAddContainerView() const
620 {
621 	return false;
622 }
623 
624 
625 void
626 BDeskWindow::MessageReceived(BMessage* message)
627 {
628 	if (message->WasDropped()) {
629 		const rgb_color* color;
630 		ssize_t size;
631 		// handle "roColour"-style color drops
632 		if (message->FindData("RGBColor", 'RGBC',
633 			(const void**)&color, &size) == B_OK) {
634 			BScreen(this).SetDesktopColor(*color);
635 			fPoseView->SetViewColor(*color);
636 			fPoseView->SetLowColor(*color);
637 
638 			// Notify the backgrounds app that the background changed
639 			status_t initStatus;
640 			BMessenger messenger("application/x-vnd.Haiku-Backgrounds", -1,
641 				&initStatus);
642 			if (initStatus == B_OK)
643 				messenger.SendMessage(message);
644 
645 			return;
646 		}
647 	}
648 
649 	switch (message->what) {
650 		case B_PATH_MONITOR:
651 		{
652 			const char* path = "";
653 			if (!(message->FindString("path", &path) == B_OK
654 					&& strcmp(path, fShortcutsSettings) == 0)) {
655 
656 				dev_t device;
657 				ino_t node;
658 				if (fNodeRef == NULL
659 					|| message->FindInt32("device", &device) != B_OK
660 					|| message->FindInt64("node", &node) != B_OK
661 					|| device != fNodeRef->device
662 					|| node != fNodeRef->node)
663 					break;
664 			}
665 			ApplyShortcutPreferences(true);
666 			break;
667 		}
668 		case B_NODE_MONITOR:
669 			PRINT(("will update addon shortcuts\n"));
670 			InitAddonsList(true);
671 			ApplyShortcutPreferences(true);
672 			break;
673 
674 		default:
675 			_inherited::MessageReceived(message);
676 			break;
677 	}
678 }
679