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