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