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