1 /* 2 * Copyright 1999-2009 Jeremy Friesner 3 * Copyright 2009-2010 Haiku, Inc. All rights reserved. 4 * Distributed under the terms of the MIT License. 5 * 6 * Authors: 7 * Jeremy Friesner 8 * Fredrik Modéen 9 */ 10 11 12 #include "ShortcutsWindow.h" 13 14 #include <math.h> 15 #include <stdio.h> 16 17 #include <Alert.h> 18 #include <Application.h> 19 #include <Button.h> 20 #include <Catalog.h> 21 #include <Clipboard.h> 22 #include <ColumnListView.h> 23 #include <ColumnTypes.h> 24 #include <ControlLook.h> 25 #include <File.h> 26 #include <FilePanel.h> 27 #include <FindDirectory.h> 28 #include <Input.h> 29 #include <LayoutBuilder.h> 30 #include <Locale.h> 31 #include <Message.h> 32 #include <Menu.h> 33 #include <MenuBar.h> 34 #include <MenuItem.h> 35 #include <MessageFilter.h> 36 #include <Path.h> 37 #include <PopUpMenu.h> 38 #include <Screen.h> 39 #include <ScrollBar.h> 40 #include <ScrollView.h> 41 #include <String.h> 42 #include <SupportDefs.h> 43 44 #include "EditWindow.h" 45 #include "KeyInfos.h" 46 #include "MetaKeyStateMap.h" 47 #include "ParseCommandLine.h" 48 #include "PopUpColumn.h" 49 #include "ShortcutsFilterConstants.h" 50 #include "ShortcutsSpec.h" 51 52 53 // Window sizing constraints 54 #define MAX_WIDTH 10000 55 #define MAX_HEIGHT 10000 56 // SetSizeLimits does not provide a mechanism for specifying an 57 // unrestricted maximum. 10,000 seems to be the most common value used 58 // in other Haiku system applications. 59 60 #define WINDOW_SETTINGS_FILE_NAME "Shortcuts_window_settings" 61 // Because the "shortcuts_settings" file (SHORTCUTS_SETTING_FILE_NAME) is 62 // already used as a communications method between this configurator and 63 // the "shortcut_catcher" input_server filter, it should not be overloaded 64 // with window position information, instead, a separate file is used. 65 66 #undef B_TRANSLATION_CONTEXT 67 #define B_TRANSLATION_CONTEXT "ShortcutsWindow" 68 69 #define ERROR "Shortcuts error" 70 #define WARNING "Shortcuts warning" 71 72 73 // Creates a pop-up-menu that reflects the possible states of the specified 74 // meta-key. 75 static BPopUpMenu* 76 CreateMetaPopUp(int column) 77 { 78 MetaKeyStateMap& map = GetNthKeyMap(column); 79 BPopUpMenu* popup = new BPopUpMenu(NULL, false); 80 int stateCount = map.GetNumStates(); 81 82 for (int i = 0; i < stateCount; i++) 83 popup->AddItem(new BMenuItem(map.GetNthStateDesc(i), NULL)); 84 85 return popup; 86 } 87 88 89 // Creates a pop-up that allows the user to choose a key-cap visually 90 static BPopUpMenu* 91 CreateKeysPopUp() 92 { 93 BPopUpMenu* popup = new BPopUpMenu(NULL, false); 94 int numKeys = GetNumKeyIndices(); 95 for (int i = 0; i < numKeys; i++) { 96 const char* next = GetKeyName(i); 97 if (next != NULL) 98 popup->AddItem(new BMenuItem(next, NULL)); 99 } 100 101 return popup; 102 } 103 104 105 ShortcutsWindow::ShortcutsWindow() 106 : 107 BWindow(BRect(0, 0, 0, 0), B_TRANSLATE_SYSTEM_NAME("Shortcuts"), 108 B_TITLED_WINDOW, B_AUTO_UPDATE_SIZE_LIMITS), 109 fSavePanel(NULL), 110 fOpenPanel(NULL), 111 fSelectPanel(NULL), 112 fKeySetModified(false), 113 fLastOpenWasAppend(false) 114 { 115 ShortcutsSpec::InitializeMetaMaps(); 116 117 BMenuBar* menuBar = new BMenuBar("Menu Bar"); 118 119 BMenu* fileMenu = new BMenu(B_TRANSLATE("File")); 120 fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Open KeySet" B_UTF8_ELLIPSIS), 121 new BMessage(OPEN_KEYSET), 'O')); 122 fileMenu->AddItem(new BMenuItem( 123 B_TRANSLATE("Append KeySet" B_UTF8_ELLIPSIS), 124 new BMessage(APPEND_KEYSET), 'A')); 125 fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Revert to saved"), 126 new BMessage(REVERT_KEYSET), 'R')); 127 fileMenu->AddItem(new BSeparatorItem); 128 fileMenu->AddItem(new BMenuItem( 129 B_TRANSLATE("Save KeySet as" B_UTF8_ELLIPSIS), 130 new BMessage(SAVE_KEYSET_AS), 'S')); 131 fileMenu->AddItem(new BSeparatorItem); 132 fileMenu->AddItem(new BMenuItem(B_TRANSLATE("Quit"), 133 new BMessage(B_QUIT_REQUESTED), 'Q')); 134 menuBar->AddItem(fileMenu); 135 136 fColumnListView = new BColumnListView(NULL, 137 B_WILL_DRAW | B_FRAME_EVENTS, B_FANCY_BORDER, false); 138 139 float cellWidth = be_plain_font->StringWidth("Either") + 20; 140 // ShortcutsSpec does not seem to translate the string "Either". 141 142 for (int i = 0; i < ShortcutsSpec::NUM_META_COLUMNS; i++) { 143 const char* name = ShortcutsSpec::GetColumnName(i); 144 float headerWidth = be_plain_font->StringWidth(name) + 20; 145 float width = max_c(headerWidth, cellWidth); 146 147 fColumnListView->AddColumn(new PopUpColumn(CreateMetaPopUp(i), name, 148 width, width - 1, width * 1.5, B_TRUNCATE_END, false, true, 1), 149 fColumnListView->CountColumns()); 150 } 151 152 float keyCellWidth = be_plain_font->StringWidth("Caps Lock") + 20; 153 fColumnListView->AddColumn(new PopUpColumn(CreateKeysPopUp(), 154 B_TRANSLATE("Key"), keyCellWidth, keyCellWidth - 10, 155 keyCellWidth + 30, B_TRUNCATE_END), 156 fColumnListView->CountColumns()); 157 BPopUpMenu* popup = new BPopUpMenu(NULL, false); 158 popup->AddItem(new BMenuItem( 159 B_TRANSLATE("(Choose application with file requester)"), NULL)); 160 popup->AddItem(new BMenuItem( 161 B_TRANSLATE("*InsertString \"Your Text Here\""), NULL)); 162 popup->AddItem(new BMenuItem( 163 B_TRANSLATE("*MoveMouse +20 +0"), NULL)); 164 popup->AddItem(new BMenuItem(B_TRANSLATE("*MoveMouseTo 50% 50%"), NULL)); 165 popup->AddItem(new BMenuItem(B_TRANSLATE("*MouseButton 1"), NULL)); 166 popup->AddItem(new BMenuItem( 167 B_TRANSLATE("*LaunchHandler text/html"), NULL)); 168 popup->AddItem(new BMenuItem( 169 B_TRANSLATE("*Multi \"*MoveMouseTo 100% 0\" \"*MouseButton 1\""), 170 NULL)); 171 popup->AddItem(new BMenuItem(B_TRANSLATE("*MouseDown"), NULL)); 172 popup->AddItem(new BMenuItem(B_TRANSLATE("*MouseUp"), NULL)); 173 popup->AddItem(new BMenuItem( 174 B_TRANSLATE("*SendMessage application/x-vnd.Be-TRAK 'Tfnd'"), NULL)); 175 popup->AddItem(new BMenuItem(B_TRANSLATE("*Beep"), NULL)); 176 fColumnListView->AddColumn(new PopUpColumn(popup, B_TRANSLATE("Application"), 177 300.0, 223.0, 324.0, B_TRUNCATE_END, true), 178 fColumnListView->CountColumns()); 179 180 fColumnListView->SetSelectionMessage(new BMessage(HOTKEY_ITEM_SELECTED)); 181 fColumnListView->SetSelectionMode(B_SINGLE_SELECTION_LIST); 182 fColumnListView->SetTarget(this); 183 184 fAddButton = new BButton("add", B_TRANSLATE("Add new shortcut"), 185 new BMessage(ADD_HOTKEY_ITEM)); 186 187 fRemoveButton = new BButton("remove", 188 B_TRANSLATE("Remove selected shortcut"), 189 new BMessage(REMOVE_HOTKEY_ITEM)); 190 fRemoveButton->SetEnabled(false); 191 192 fSaveButton = new BButton("save", B_TRANSLATE("Save & apply"), 193 new BMessage(SAVE_KEYSET)); 194 fSaveButton->SetEnabled(false); 195 196 CenterOnScreen(); 197 198 fColumnListView->ResizeAllColumnsToPreferred(); 199 200 entry_ref windowSettingsRef; 201 if (_GetWindowSettingsFile(&windowSettingsRef)) { 202 // The window settings file is not accepted via B_REFS_RECEIVED; this 203 // is a behind-the-scenes file that the user will never see or 204 // interact with. 205 BFile windowSettingsFile(&windowSettingsRef, B_READ_ONLY); 206 BMessage loadMessage; 207 if (loadMessage.Unflatten(&windowSettingsFile) == B_OK) 208 _LoadWindowSettings(loadMessage); 209 } 210 211 entry_ref keySetRef; 212 if (_GetSettingsFile(&keySetRef)) { 213 BMessage message(B_REFS_RECEIVED); 214 message.AddRef("refs", &keySetRef); 215 message.AddString("startupRef", "please"); 216 PostMessage(&message); 217 // tell ourselves to load this file if it exists 218 } 219 220 BLayoutBuilder::Group<>(this, B_VERTICAL, 0) 221 .Add(menuBar) 222 .AddGroup(B_VERTICAL) 223 .SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)) 224 .SetInsets(B_USE_WINDOW_SPACING) 225 .Add(fColumnListView) 226 .AddGroup(B_HORIZONTAL) 227 .AddGroup(B_HORIZONTAL) 228 .SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP)) 229 .Add(fAddButton) 230 .Add(fRemoveButton) 231 .End() 232 .AddGroup(B_HORIZONTAL) 233 .SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT, B_ALIGN_TOP)) 234 .Add(fSaveButton) 235 .End() 236 .End() 237 .End(); 238 239 Show(); 240 } 241 242 243 ShortcutsWindow::~ShortcutsWindow() 244 { 245 delete fSavePanel; 246 delete fOpenPanel; 247 delete fSelectPanel; 248 be_app->PostMessage(B_QUIT_REQUESTED); 249 } 250 251 252 bool 253 ShortcutsWindow::QuitRequested() 254 { 255 bool result = true; 256 257 if (fKeySetModified) { 258 BAlert* alert = new BAlert(WARNING, 259 B_TRANSLATE("Save changes before closing?"), 260 B_TRANSLATE("Cancel"), B_TRANSLATE("Don't save"), 261 B_TRANSLATE("Save")); 262 alert->SetShortcut(0, B_ESCAPE); 263 alert->SetShortcut(1, 'd'); 264 alert->SetShortcut(2, 's'); 265 switch (alert->Go()) { 266 case 0: 267 result = false; 268 break; 269 270 case 1: 271 result = true; 272 break; 273 274 case 2: 275 // Save: automatically if possible, otherwise go back and open 276 // up the file requester 277 if (fLastSaved.InitCheck() == B_OK) { 278 if (_SaveKeySet(fLastSaved) == false) { 279 BAlert* alert = new BAlert(ERROR, 280 B_TRANSLATE("Shortcuts was unable to save your " 281 "KeySet file!"), 282 B_TRANSLATE("Oh no")); 283 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 284 alert->Go(); 285 result = true; // quit anyway 286 } 287 } else { 288 PostMessage(SAVE_KEYSET); 289 result = false; 290 } 291 break; 292 } 293 } 294 295 if (result) { 296 fColumnListView->DeselectAll(); 297 298 // Save the window position. 299 entry_ref ref; 300 if (_GetWindowSettingsFile(&ref)) { 301 BEntry entry(&ref); 302 _SaveWindowSettings(entry); 303 } 304 } 305 306 return result; 307 } 308 309 310 bool 311 ShortcutsWindow::_GetSettingsFile(entry_ref* eref) 312 { 313 BPath path; 314 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK) 315 return false; 316 else 317 path.Append(SHORTCUTS_SETTING_FILE_NAME); 318 319 if (BEntry(path.Path(), true).GetRef(eref) == B_OK) 320 return true; 321 else 322 return false; 323 } 324 325 326 // Saves a settings file to (saveEntry). Returns true iff successful. 327 bool 328 ShortcutsWindow::_SaveKeySet(BEntry& saveEntry) 329 { 330 BFile saveTo(&saveEntry, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 331 if (saveTo.InitCheck() != B_OK) 332 return false; 333 334 BMessage saveMessage; 335 for (int i = 0; i < fColumnListView->CountRows(); i++) { 336 BMessage next; 337 if (((ShortcutsSpec*)fColumnListView->RowAt(i))->Archive(&next) 338 == B_OK) { 339 saveMessage.AddMessage("spec", &next); 340 } else 341 printf("Error archiving ShortcutsSpec #%i!\n", i); 342 } 343 344 bool result = (saveMessage.Flatten(&saveTo) == B_OK); 345 346 if (result) { 347 fKeySetModified = false; 348 fSaveButton->SetEnabled(false); 349 } 350 351 return result; 352 } 353 354 355 // Appends new entries from the file specified in the "spec" entry of 356 // (loadMessage). Returns true iff successful. 357 bool 358 ShortcutsWindow::_LoadKeySet(const BMessage& loadMessage) 359 { 360 int i = 0; 361 BMessage message; 362 while (loadMessage.FindMessage("spec", i++, &message) == B_OK) { 363 ShortcutsSpec* spec 364 = (ShortcutsSpec*)ShortcutsSpec::Instantiate(&message); 365 if (spec != NULL) 366 fColumnListView->AddRow(spec); 367 else 368 printf("_LoadKeySet: Error parsing spec!\n"); 369 } 370 371 return true; 372 } 373 374 375 // Gets the filesystem location of the "Shortcuts_window_settings" file. 376 bool 377 ShortcutsWindow::_GetWindowSettingsFile(entry_ref* eref) 378 { 379 BPath path; 380 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK) 381 return false; 382 else 383 path.Append(WINDOW_SETTINGS_FILE_NAME); 384 385 return BEntry(path.Path(), true).GetRef(eref) == B_OK; 386 } 387 388 389 // Saves the application settings file to (saveEntry). Because this is a 390 // non-essential file, errors are ignored when writing the settings. 391 void 392 ShortcutsWindow::_SaveWindowSettings(BEntry& saveEntry) 393 { 394 BFile saveTo(&saveEntry, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 395 if (saveTo.InitCheck() != B_OK) 396 return; 397 398 BMessage saveMsg; 399 saveMsg.AddRect("window frame", Frame()); 400 401 BMessage columnsState; 402 fColumnListView->SaveState(&columnsState); 403 saveMsg.AddMessage ("columns state", &columnsState); 404 405 saveMsg.Flatten(&saveTo); 406 } 407 408 409 // Loads the application settings file from (loadMessage) and resizes 410 // the interface to match the previously saved settings. Because this 411 // is a non-essential file, errors are ignored when loading the settings. 412 void 413 ShortcutsWindow::_LoadWindowSettings(const BMessage& loadMessage) 414 { 415 BRect frame; 416 if (loadMessage.FindRect("window frame", &frame) == B_OK) { 417 // ensure the frame does not resize below the computed minimum. 418 float width = max_c(Bounds().right, frame.right - frame.left); 419 float height = max_c(Bounds().bottom, frame.bottom - frame.top); 420 ResizeTo(width, height); 421 422 // ensure the frame is not placed outside of the screen. 423 BScreen screen(this); 424 float left = min_c(screen.Frame().right - width, frame.left); 425 float top = min_c(screen.Frame().bottom - height, frame.top); 426 MoveTo(left, top); 427 } 428 429 BMessage columnsStateMessage; 430 if (loadMessage.FindMessage ("columns state", &columnsStateMessage) == B_OK) 431 fColumnListView->LoadState(&columnsStateMessage); 432 } 433 434 435 // Creates a new entry and adds it to the GUI. (defaultCommand) will be the 436 // text in the entry, or NULL if no text is desired. 437 void 438 ShortcutsWindow::_AddNewSpec(const char* defaultCommand) 439 { 440 _MarkKeySetModified(); 441 442 ShortcutsSpec* spec; 443 BRow* curSel = fColumnListView->CurrentSelection(); 444 if (curSel) 445 spec = new ShortcutsSpec(*((ShortcutsSpec*)curSel)); 446 else { 447 spec = new ShortcutsSpec(""); 448 for (int i = 0; i < fColumnListView->CountColumns(); i++) 449 spec->SetField(new BStringField(""), i); 450 } 451 452 fColumnListView->AddRow(spec); 453 fColumnListView->AddToSelection(spec); 454 fColumnListView->ScrollTo(spec); 455 if (defaultCommand) 456 spec->SetCommand(defaultCommand); 457 } 458 459 460 void 461 ShortcutsWindow::MessageReceived(BMessage* message) 462 { 463 switch (message->what) { 464 case OPEN_KEYSET: 465 case APPEND_KEYSET: 466 fLastOpenWasAppend = (message->what == APPEND_KEYSET); 467 if (fOpenPanel) 468 fOpenPanel->Show(); 469 else { 470 BMessenger messenger(this); 471 fOpenPanel = new BFilePanel(B_OPEN_PANEL, &messenger, NULL, 472 0, false); 473 fOpenPanel->Show(); 474 } 475 fOpenPanel->SetButtonLabel(B_DEFAULT_BUTTON, fLastOpenWasAppend ? 476 B_TRANSLATE("Append") : B_TRANSLATE("Open")); 477 break; 478 479 // send a message to myself, to get me to reload the settings file 480 case REVERT_KEYSET: 481 { 482 fLastOpenWasAppend = false; 483 BMessage reload(B_REFS_RECEIVED); 484 entry_ref eref; 485 _GetSettingsFile(&eref); 486 reload.AddRef("refs", &eref); 487 reload.AddString("startupRef", "yeah"); 488 PostMessage(&reload); 489 break; 490 } 491 492 // respond to drag-and-drop messages here 493 case B_SIMPLE_DATA: 494 { 495 int i = 0; 496 497 entry_ref ref; 498 while (message->FindRef("refs", i++, &ref) == B_OK) { 499 BEntry entry(&ref); 500 if (entry.InitCheck() == B_OK) { 501 BPath path(&entry); 502 503 if (path.InitCheck() == B_OK) { 504 // Add a new item with the given path. 505 BString str(path.Path()); 506 DoStandardEscapes(str); 507 _AddNewSpec(str.String()); 508 } 509 } 510 } 511 break; 512 } 513 514 // respond to FileRequester's messages here 515 case B_REFS_RECEIVED: 516 { 517 // Find file ref 518 entry_ref ref; 519 bool isStartMsg = message->HasString("startupRef"); 520 if (message->FindRef("refs", &ref) == B_OK) { 521 // load the file into (fileMsg) 522 BMessage fileMsg; 523 { 524 BFile file(&ref, B_READ_ONLY); 525 if ((file.InitCheck() != B_OK) 526 || (fileMsg.Unflatten(&file) != B_OK)) { 527 if (isStartMsg) { 528 // use this to save to anyway 529 fLastSaved = BEntry(&ref); 530 break; 531 } else { 532 BAlert* alert = new BAlert(ERROR, 533 B_TRANSLATE("Shortcuts was couldn't open your " 534 "KeySet file!"), B_TRANSLATE("OK")); 535 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 536 alert->Go(NULL); 537 break; 538 } 539 } 540 } 541 542 if (fLastOpenWasAppend == false) { 543 // Clear the menu... 544 while (fColumnListView->CountRows()) { 545 ShortcutsSpec* row = 546 static_cast<ShortcutsSpec*>(fColumnListView->RowAt(0)); 547 fColumnListView->RemoveRow(row); 548 delete row; 549 } 550 } 551 552 if (_LoadKeySet(fileMsg)) { 553 if (isStartMsg) fLastSaved = BEntry(&ref); 554 fSaveButton->SetEnabled(isStartMsg == false); 555 556 // If we just loaded in the Shortcuts settings file, then 557 // no need to tell the user to save on exit. 558 entry_ref eref; 559 _GetSettingsFile(&eref); 560 if (ref == eref) fKeySetModified = false; 561 } else { 562 BAlert* alert = new BAlert(ERROR, 563 B_TRANSLATE("Shortcuts was unable to parse your " 564 "KeySet file!"), B_TRANSLATE("OK")); 565 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 566 alert->Go(NULL); 567 break; 568 } 569 } 570 break; 571 } 572 573 // these messages come from the pop-up menu of the Applications column 574 case SELECT_APPLICATION: 575 { 576 ShortcutsSpec* row = 577 static_cast<ShortcutsSpec*>(fColumnListView->CurrentSelection()); 578 if (row != NULL) { 579 entry_ref aref; 580 if (message->FindRef("refs", &aref) == B_OK) { 581 BEntry ent(&aref); 582 if (ent.InitCheck() == B_OK) { 583 BPath path; 584 if ((ent.GetPath(&path) == B_OK) 585 && (row-> 586 ProcessColumnTextString(ShortcutsSpec::STRING_COLUMN_INDEX, 587 path.Path()))) { 588 _MarkKeySetModified(); 589 } 590 } 591 } 592 } 593 break; 594 } 595 596 case SAVE_KEYSET: 597 { 598 bool showSaveError = false; 599 600 const char* name; 601 entry_ref entry; 602 if ((message->FindString("name", &name) == B_OK) 603 && (message->FindRef("directory", &entry) == B_OK)) { 604 BDirectory dir(&entry); 605 BEntry saveTo(&dir, name, true); 606 showSaveError = ((saveTo.InitCheck() != B_OK) 607 || (_SaveKeySet(saveTo) == false)); 608 } else if (fLastSaved.InitCheck() == B_OK) { 609 // We've saved this before, save over previous file. 610 showSaveError = (_SaveKeySet(fLastSaved) == false); 611 } else 612 PostMessage(SAVE_KEYSET_AS); 613 // open the save requester... 614 615 if (showSaveError) { 616 BAlert* alert = new BAlert(ERROR, 617 B_TRANSLATE("Shortcuts wasn't able to save your keyset."), 618 B_TRANSLATE("OK")); 619 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 620 alert->Go(NULL); 621 } 622 break; 623 } 624 625 case SAVE_KEYSET_AS: 626 { 627 if (fSavePanel) 628 fSavePanel->Show(); 629 else { 630 BMessage message(SAVE_KEYSET); 631 BMessenger messenger(this); 632 fSavePanel = new BFilePanel(B_SAVE_PANEL, &messenger, NULL, 0, 633 false, &message); 634 fSavePanel->Show(); 635 } 636 break; 637 } 638 639 case ADD_HOTKEY_ITEM: 640 _AddNewSpec(NULL); 641 break; 642 643 case REMOVE_HOTKEY_ITEM: 644 { 645 BRow* item = fColumnListView->CurrentSelection(); 646 if (item) { 647 int index = fColumnListView->IndexOf(item); 648 fColumnListView->RemoveRow(item); 649 delete item; 650 _MarkKeySetModified(); 651 652 // Rules for new selection: If there is an item at (index), 653 // select it. Otherwise, if there is an item at (index-1), 654 // select it. Otherwise, select nothing. 655 int num = fColumnListView->CountRows(); 656 if (num > 0) { 657 if (index < num) 658 fColumnListView->AddToSelection( 659 fColumnListView->RowAt(index)); 660 else { 661 if (index > 0) 662 index--; 663 if (index < num) 664 fColumnListView->AddToSelection( 665 fColumnListView->RowAt(index)); 666 } 667 } 668 } 669 break; 670 } 671 672 // Received when the user clicks on the ColumnListView 673 case HOTKEY_ITEM_SELECTED: 674 { 675 if (fColumnListView->CountRows() > 0) 676 fRemoveButton->SetEnabled(true); 677 else 678 fRemoveButton->SetEnabled(false); 679 break; 680 } 681 682 // Received when an entry is to be modified in response to GUI activity 683 case HOTKEY_ITEM_MODIFIED: 684 { 685 int32 row, column; 686 687 if ((message->FindInt32("row", &row) == B_OK) 688 && (message->FindInt32("column", &column) == B_OK)) { 689 int32 key; 690 const char* bytes; 691 692 if (row >= 0) { 693 ShortcutsSpec* item = (ShortcutsSpec*) 694 fColumnListView->RowAt(row); 695 bool repaintNeeded = false; // default 696 697 if (message->HasInt32("mouseClick")) { 698 repaintNeeded = item->ProcessColumnMouseClick(column); 699 } else if ((message->FindString("bytes", &bytes) == B_OK) 700 && (message->FindInt32("key", &key) == B_OK)) { 701 repaintNeeded = item->ProcessColumnKeyStroke(column, 702 bytes, key); 703 } else if (message->FindInt32("unmappedkey", &key) == 704 B_OK) { 705 repaintNeeded = ((column == item->KEY_COLUMN_INDEX) 706 && ((key > 0xFF) || (GetKeyName(key) != NULL)) 707 && (item->ProcessColumnKeyStroke(column, NULL, 708 key))); 709 } else if (message->FindString("text", &bytes) == B_OK) { 710 if ((bytes[0] == '(')&&(bytes[1] == 'C')) { 711 if (fSelectPanel) 712 fSelectPanel->Show(); 713 else { 714 BMessage message(SELECT_APPLICATION); 715 BMessenger m(this); 716 fSelectPanel = new BFilePanel(B_OPEN_PANEL, &m, 717 NULL, 0, false, &message); 718 fSelectPanel->Show(); 719 } 720 fSelectPanel->SetButtonLabel(B_DEFAULT_BUTTON, 721 B_TRANSLATE("Select")); 722 } else 723 repaintNeeded = item->ProcessColumnTextString( 724 column, bytes); 725 } 726 727 if (repaintNeeded) { 728 fColumnListView->Invalidate(row); 729 _MarkKeySetModified(); 730 } 731 } 732 } 733 break; 734 } 735 736 default: 737 BWindow::MessageReceived(message); 738 break; 739 } 740 } 741 742 743 void 744 ShortcutsWindow::_MarkKeySetModified() 745 { 746 if (fKeySetModified == false) { 747 fKeySetModified = true; 748 fSaveButton->SetEnabled(true); 749 } 750 } 751 752 753 void 754 ShortcutsWindow::Quit() 755 { 756 BWindow::Quit(); 757 } 758 759 760 void 761 ShortcutsWindow::DispatchMessage(BMessage* message, BHandler* handler) 762 { 763 switch (message->what) { 764 case B_SIMPLE_DATA: 765 MessageReceived(message); 766 break; 767 768 case B_COPY: 769 case B_CUT: 770 if (be_clipboard->Lock()) { 771 ShortcutsSpec* row = 772 static_cast<ShortcutsSpec*>(fColumnListView->CurrentSelection()); 773 if (row) { 774 BMessage* data = be_clipboard->Data(); 775 data->RemoveName("text/plain"); 776 data->AddData("text/plain", B_MIME_TYPE, 777 row->GetCellText(ShortcutsSpec::STRING_COLUMN_INDEX), 778 strlen(row->GetCellText(ShortcutsSpec::STRING_COLUMN_INDEX))); 779 be_clipboard->Commit(); 780 781 if (message->what == B_CUT) { 782 row->ProcessColumnTextString( 783 ShortcutsSpec::STRING_COLUMN_INDEX, ""); 784 _MarkKeySetModified(); 785 } 786 } 787 be_clipboard->Unlock(); 788 } 789 break; 790 791 case B_PASTE: 792 if (be_clipboard->Lock()) { 793 BMessage* data = be_clipboard->Data(); 794 const char* text; 795 ssize_t textLen; 796 if (data->FindData("text/plain", B_MIME_TYPE, (const void**) 797 &text, &textLen) == B_OK) { 798 ShortcutsSpec* row = 799 static_cast<ShortcutsSpec*>(fColumnListView->CurrentSelection()); 800 if (row) { 801 for (ssize_t i = 0; i < textLen; i++) { 802 char buf[2] = {text[i], 0x00}; 803 row->ProcessColumnKeyStroke( 804 ShortcutsSpec::STRING_COLUMN_INDEX, buf, 0); 805 } 806 } 807 _MarkKeySetModified(); 808 } 809 be_clipboard->Unlock(); 810 } 811 break; 812 813 case B_KEY_DOWN: 814 ShortcutsSpec* selected; 815 if (message->GetInt32("modifiers", 0) != 0) 816 BWindow::DispatchMessage(message, handler); 817 else if (handler == fColumnListView 818 && (selected = 819 static_cast<ShortcutsSpec*>(fColumnListView->CurrentSelection()))) { 820 selected->ProcessColumnTextString( 821 ShortcutsSpec::KEY_COLUMN_INDEX, 822 GetKeyName(message->GetInt32("key", 0))); 823 _MarkKeySetModified(); 824 } 825 break; 826 827 default: 828 BWindow::DispatchMessage(message, handler); 829 break; 830 } 831 } 832