xref: /haiku/src/kits/tracker/DeskWindow.cpp (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
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 	ResizeTo(fOldFrame.Width(), fOldFrame.Height());
240 
241 	InitKeyIndices();
242 	InitAddonsList(false);
243 	ApplyShortcutPreferences(false);
244 
245 	_inherited::Init();
246 
247 	entry_ref ref;
248 	BPath path;
249 	if (!BootedInSafeMode() && FSFindTrackerSettingsDir(&path) == B_OK) {
250 		path.Append(kShelfPath);
251 		close(open(path.Path(), O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR
252 			| S_IRGRP | S_IROTH));
253 		if (get_ref_for_path(path.Path(), &ref) == B_OK)
254 			fDeskShelf = new BShelf(&ref, fPoseView);
255 
256 		if (fDeskShelf != NULL)
257 			fDeskShelf->SetDisplaysZombies(true);
258 	}
259 }
260 
261 
262 void
263 BDeskWindow::InitAddonsList(bool update)
264 {
265 	AutoLock<LockingList<AddonShortcut> > lock(fAddonsList);
266 	if (lock.IsLocked()) {
267 		if (update) {
268 			for (int i = fAddonsList->CountItems() - 1; i >= 0; i--) {
269 				AddonShortcut* item = fAddonsList->ItemAt(i);
270 				RemoveShortcut(item->key, B_OPTION_KEY | B_COMMAND_KEY);
271 			}
272 			fAddonsList->MakeEmpty(true);
273 		}
274 
275 		BStringList addOnPaths;
276 		BPathFinder::FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY, "Tracker",
277 			addOnPaths);
278 		int32 count = addOnPaths.CountStrings();
279 		for (int32 i = 0; i < count; i++) {
280 			LoadAddOnDir(BDirectory(addOnPaths.StringAt(i)), this,
281 				fAddonsList);
282 		}
283 	}
284 }
285 
286 
287 void
288 BDeskWindow::ApplyShortcutPreferences(bool update)
289 {
290 	AutoLock<LockingList<AddonShortcut> > lock(fAddonsList);
291 	if (lock.IsLocked()) {
292 		if (!update) {
293 			BPath path;
294 			if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
295 				BPathMonitor::StartWatching(path.Path(),
296 					B_WATCH_STAT | B_WATCH_FILES_ONLY, this);
297 				path.Append(kShortcutsSettings);
298 				fShortcutsSettings = new char[strlen(path.Path()) + 1];
299 				strcpy(fShortcutsSettings, path.Path());
300 			}
301 		}
302 
303 		fAddonsList->EachElement(RevertToDefault, this);
304 
305 		BFile shortcutSettings(fShortcutsSettings, B_READ_ONLY);
306 		BMessage fileMsg;
307 		if (shortcutSettings.InitCheck() != B_OK
308 			|| fileMsg.Unflatten(&shortcutSettings) != B_OK) {
309 			fNodeRef = NULL;
310 			return;
311 		}
312 		shortcutSettings.GetNodeRef(fNodeRef);
313 
314 		int32 i = 0;
315 		BMessage message;
316 		while (fileMsg.FindMessage("spec", i++, &message) == B_OK) {
317 			int32 key;
318 			if (message.FindInt32("key", &key) == B_OK) {
319 				// only handle shortcuts referring add-ons
320 				BString command;
321 				if (message.FindString("command", &command) != B_OK)
322 					continue;
323 
324 				bool isInAddons = false;
325 
326 				BStringList addOnPaths;
327 				BPathFinder::FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY,
328 					"Tracker/", addOnPaths);
329 				for (int32 i = 0; i < addOnPaths.CountStrings(); i++) {
330 					if (command.StartsWith(addOnPaths.StringAt(i))) {
331 						isInAddons = true;
332 						break;
333 					}
334 				}
335 
336 				if (!isInAddons)
337 					continue;
338 
339 				BEntry entry(command);
340 				if (entry.InitCheck() != B_OK)
341 					continue;
342 
343 				const char* shortcut = GetKeyName(key);
344 				if (strlen(shortcut) != 1)
345 					continue;
346 
347 				uint32 modifiers = B_COMMAND_KEY;
348 					// it's required by interface kit to at least
349 					// have B_COMMAND_KEY
350 				int32 value;
351 				if (message.FindInt32("mcidx", 0, &value) == B_OK)
352 					modifiers |= (value != 0 ? B_SHIFT_KEY : 0);
353 
354 				if (message.FindInt32("mcidx", 1, &value) == B_OK)
355 					modifiers |= (value != 0 ? B_CONTROL_KEY : 0);
356 
357 				if (message.FindInt32("mcidx", 3, &value) == B_OK)
358 					modifiers |= (value != 0 ? B_OPTION_KEY : 0);
359 
360 				Model model(&entry);
361 				AddonShortcut* item = fAddonsList->EachElement(FindElement,
362 					&model);
363 				if (item != NULL) {
364 					if (item->key != '\0')
365 						RemoveShortcut(item->key, item->modifiers);
366 
367 					item->key = shortcut[0];
368 					item->modifiers = modifiers;
369 					AddOneShortcut(&model, item->key, item->modifiers, this);
370 				}
371 			}
372 		}
373 	}
374 }
375 
376 
377 void
378 BDeskWindow::Quit()
379 {
380 	if (fNavigationItem != NULL) {
381 		// this duplicates BContainerWindow::Quit because
382 		// fNavigationItem can be part of fTrashContextMenu
383 		// and would get deleted with it
384 		BMenu* menu = fNavigationItem->Menu();
385 		if (menu != NULL)
386 			menu->RemoveItem(fNavigationItem);
387 
388 		delete fNavigationItem;
389 		fNavigationItem = 0;
390 	}
391 
392 	fAddonsList->MakeEmpty(true);
393 	delete fAddonsList;
394 
395 	delete fDeskShelf;
396 	_inherited::Quit();
397 }
398 
399 
400 BPoseView*
401 BDeskWindow::NewPoseView(Model* model, uint32 viewMode)
402 {
403 	return new DesktopPoseView(model, viewMode);
404 }
405 
406 
407 void
408 BDeskWindow::CreatePoseView(Model* model)
409 {
410 	fPoseView = NewPoseView(model, kIconMode);
411 	fPoseView->SetIconMapping(false);
412 	fPoseView->SetEnsurePosesVisible(true);
413 	fPoseView->SetAutoScroll(false);
414 
415 	BScreen screen(this);
416 	rgb_color desktopColor = screen.DesktopColor();
417 	if (desktopColor.alpha != 255) {
418 		desktopColor.alpha = 255;
419 #if B_BEOS_VERSION > B_BEOS_VERSION_5
420 		// This call seems to have the power to cause R5 to freeze!
421 		// Please report if commenting this out helped or helped not
422 		// on your system
423 		screen.SetDesktopColor(desktopColor);
424 #endif
425 	}
426 
427 	fPoseView->SetViewColor(desktopColor);
428 	fPoseView->SetLowColor(desktopColor);
429 
430 	fPoseView->SetResizingMode(B_FOLLOW_ALL);
431 	fPoseView->ResizeTo(Bounds().Size());
432 	AddChild(fPoseView);
433 
434 	PoseView()->StartSettingsWatch();
435 }
436 
437 
438 void
439 BDeskWindow::AddWindowContextMenus(BMenu* menu)
440 {
441 	TemplatesMenu* tempateMenu = new TemplatesMenu(PoseView(),
442 		B_TRANSLATE("New"));
443 
444 	menu->AddItem(tempateMenu);
445 	tempateMenu->SetTargetForItems(PoseView());
446 	tempateMenu->SetFont(be_plain_font);
447 
448 	menu->AddSeparatorItem();
449 
450 	BMenu* iconSizeMenu = new BMenu(B_TRANSLATE("Icon view"));
451 
452 	BMessage* message = new BMessage(kIconMode);
453 	message->AddInt32("size", 32);
454 	BMenuItem* item = new BMenuItem(B_TRANSLATE("32 x 32"), message);
455 	item->SetMarked(PoseView()->IconSizeInt() == 32);
456 	item->SetTarget(PoseView());
457 	iconSizeMenu->AddItem(item);
458 
459 	message = new BMessage(kIconMode);
460 	message->AddInt32("size", 40);
461 	item = new BMenuItem(B_TRANSLATE("40 x 40"), message);
462 	item->SetMarked(PoseView()->IconSizeInt() == 40);
463 	item->SetTarget(PoseView());
464 	iconSizeMenu->AddItem(item);
465 
466 	message = new BMessage(kIconMode);
467 	message->AddInt32("size", 48);
468 	item = new BMenuItem(B_TRANSLATE("48 x 48"), message);
469 	item->SetMarked(PoseView()->IconSizeInt() == 48);
470 	item->SetTarget(PoseView());
471 	iconSizeMenu->AddItem(item);
472 
473 	message = new BMessage(kIconMode);
474 	message->AddInt32("size", 64);
475 	item = new BMenuItem(B_TRANSLATE("64 x 64"), message);
476 	item->SetMarked(PoseView()->IconSizeInt() == 64);
477 	item->SetTarget(PoseView());
478 	iconSizeMenu->AddItem(item);
479 
480 	message = new BMessage(kIconMode);
481 	message->AddInt32("size", 96);
482 	item = new BMenuItem(B_TRANSLATE("96 x 96"), message);
483 	item->SetMarked(PoseView()->IconSizeInt() == 96);
484 	item->SetTarget(PoseView());
485 	iconSizeMenu->AddItem(item);
486 
487 	message = new BMessage(kIconMode);
488 	message->AddInt32("size", 128);
489 	item = new BMenuItem(B_TRANSLATE("128 x 128"), message);
490 	item->SetMarked(PoseView()->IconSizeInt() == 128);
491 	item->SetTarget(PoseView());
492 	iconSizeMenu->AddItem(item);
493 
494 	iconSizeMenu->AddSeparatorItem();
495 
496 	message = new BMessage(kIconMode);
497 	message->AddInt32("scale", 0);
498 	item = new BMenuItem(B_TRANSLATE("Decrease size"), message, '-');
499 	item->SetTarget(PoseView());
500 	iconSizeMenu->AddItem(item);
501 
502 	message = new BMessage(kIconMode);
503 	message->AddInt32("scale", 1);
504 	item = new BMenuItem(B_TRANSLATE("Increase size"), message, '+');
505 	item->SetTarget(PoseView());
506 	iconSizeMenu->AddItem(item);
507 
508 	// A sub menu where the super item can be invoked.
509 	menu->AddItem(iconSizeMenu);
510 	iconSizeMenu->Superitem()->SetShortcut('1', B_COMMAND_KEY);
511 	iconSizeMenu->Superitem()->SetMessage(new BMessage(kIconMode));
512 	iconSizeMenu->Superitem()->SetTarget(PoseView());
513 	iconSizeMenu->Superitem()->SetMarked(PoseView()->ViewMode() == kIconMode);
514 
515 	item = new BMenuItem(B_TRANSLATE("Mini icon view"),
516 		new BMessage(kMiniIconMode), '2');
517 	item->SetMarked(PoseView()->ViewMode() == kMiniIconMode);
518 	menu->AddItem(item);
519 
520 	menu->AddSeparatorItem();
521 
522 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU
523 	BMenuItem* pasteItem = new BMenuItem(B_TRANSLATE("Paste"),
524 		new BMessage(B_PASTE), 'V');
525 	menu->AddItem(pasteItem);
526 	menu->AddSeparatorItem();
527 #endif
528 	menu->AddItem(new BMenuItem(B_TRANSLATE("Clean up"),
529 		new BMessage(kCleanup), 'K'));
530 	menu->AddItem(new BMenuItem(B_TRANSLATE("Select" B_UTF8_ELLIPSIS),
531 		new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY));
532 	menu->AddItem(new BMenuItem(B_TRANSLATE("Select all"),
533 		new BMessage(B_SELECT_ALL), 'A'));
534 
535 	menu->AddSeparatorItem();
536 	menu->AddItem(new MountMenu(B_TRANSLATE("Mount")));
537 
538 	menu->AddSeparatorItem();
539 	menu->AddItem(new BMenu(B_TRANSLATE("Add-ons")));
540 
541 	// target items as needed
542 	menu->SetTargetForItems(PoseView());
543 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU
544 	pasteItem->SetTarget(this);
545 #endif
546 }
547 
548 
549 void
550 BDeskWindow::WorkspaceActivated(int32 workspace, bool state)
551 {
552 	if (fBackgroundImage)
553 		fBackgroundImage->WorkspaceActivated(PoseView(), workspace, state);
554 }
555 
556 
557 void
558 BDeskWindow::SaveDesktopPoseLocations()
559 {
560 	PoseView()->SavePoseLocations(&fOldFrame);
561 }
562 
563 
564 void
565 BDeskWindow::ScreenChanged(BRect frame, color_space space)
566 {
567 	bool frameChanged = (frame != fOldFrame);
568 
569 	SaveDesktopPoseLocations();
570 	fOldFrame = frame;
571 	ResizeTo(frame.Width(), frame.Height());
572 
573 	if (fBackgroundImage)
574 		fBackgroundImage->ScreenChanged(frame, space);
575 
576 	PoseView()->CheckPoseVisibility(frameChanged ? &frame : 0);
577 		// if frame changed, pass new frame so that icons can
578 		// get rearranged based on old pose info for the frame
579 }
580 
581 
582 void
583 BDeskWindow::UpdateDesktopBackgroundImages()
584 {
585 	WindowStateNodeOpener opener(this, false);
586 	fBackgroundImage = BackgroundImage::Refresh(fBackgroundImage,
587 		opener.Node(), true, PoseView());
588 }
589 
590 
591 void
592 BDeskWindow::Show()
593 {
594 	if (fBackgroundImage)
595 		fBackgroundImage->Show(PoseView(), current_workspace());
596 
597 	PoseView()->CheckPoseVisibility();
598 
599 	_inherited::Show();
600 }
601 
602 
603 bool
604 BDeskWindow::ShouldAddScrollBars() const
605 {
606 	return false;
607 }
608 
609 
610 bool
611 BDeskWindow::ShouldAddMenus() const
612 {
613 	return false;
614 }
615 
616 
617 bool
618 BDeskWindow::ShouldAddContainerView() const
619 {
620 	return false;
621 }
622 
623 
624 void
625 BDeskWindow::MessageReceived(BMessage* message)
626 {
627 	if (message->WasDropped()) {
628 		const rgb_color* color;
629 		ssize_t size;
630 		// handle "roColour"-style color drops
631 		if (message->FindData("RGBColor", 'RGBC',
632 			(const void**)&color, &size) == B_OK) {
633 			BScreen(this).SetDesktopColor(*color);
634 			fPoseView->SetViewColor(*color);
635 			fPoseView->SetLowColor(*color);
636 
637 			// Notify the backgrounds app that the background changed
638 			status_t initStatus;
639 			BMessenger messenger("application/x-vnd.Haiku-Backgrounds", -1,
640 				&initStatus);
641 			if (initStatus == B_OK)
642 				messenger.SendMessage(message);
643 
644 			return;
645 		}
646 	}
647 
648 	switch (message->what) {
649 		case B_PATH_MONITOR:
650 		{
651 			const char* path = "";
652 			if (!(message->FindString("path", &path) == B_OK
653 					&& strcmp(path, fShortcutsSettings) == 0)) {
654 
655 				dev_t device;
656 				ino_t node;
657 				if (fNodeRef == NULL
658 					|| message->FindInt32("device", &device) != B_OK
659 					|| message->FindInt64("node", &node) != B_OK
660 					|| device != fNodeRef->device
661 					|| node != fNodeRef->node)
662 					break;
663 			}
664 			ApplyShortcutPreferences(true);
665 			break;
666 		}
667 		case B_NODE_MONITOR:
668 			PRINT(("will update addon shortcuts\n"));
669 			InitAddonsList(true);
670 			ApplyShortcutPreferences(true);
671 			break;
672 
673 		default:
674 			_inherited::MessageReceived(message);
675 			break;
676 	}
677 }
678