1 /* 2 * Copyright 1999-2009 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 14 #include <math.h> 15 #include <stdio.h> 16 17 18 #include <Alert.h> 19 #include <Application.h> 20 #include <Clipboard.h> 21 #include <MessageFilter.h> 22 #include <Menu.h> 23 #include <MenuItem.h> 24 #include <MenuBar.h> 25 #include <ScrollBar.h> 26 #include <ScrollView.h> 27 #include <String.h> 28 #include <Input.h> 29 #include <PopUpMenu.h> 30 #include <File.h> 31 #include <Path.h> 32 #include <FindDirectory.h> 33 34 #include "ColumnListView.h" 35 36 37 #include "KeyInfos.h" 38 #include "ShortcutsSpec.h" 39 #include "ParseCommandLine.h" 40 #include "MetaKeyStateMap.h" 41 #include "ShortcutsFilterConstants.h" 42 43 // Window sizing constraints 44 #define MIN_WIDTH 600 45 #define MIN_HEIGHT 130 46 #define MAX_WIDTH 65535 47 #define MAX_HEIGHT 65535 48 49 // Default window position 50 #define WINDOW_START_X 30 51 #define WINDOW_START_Y 100 52 53 #define ERROR "Shortcuts Error" 54 #define WARNING "Shortcuts warning" 55 56 // Global constants for Shortcuts 57 #define V_SPACING 5 // vertical spacing between GUI components 58 59 60 // Creates a pop-up-menu that reflects the possible states of the specified 61 // meta-key. 62 static BPopUpMenu* CreateMetaPopUp(int col); 63 static BPopUpMenu* CreateMetaPopUp(int col) 64 { 65 MetaKeyStateMap& map = GetNthKeyMap(col); 66 BPopUpMenu * popup = new BPopUpMenu(NULL, false); 67 int numStates = map.GetNumStates(); 68 69 for (int i = 0; i < numStates; i++) 70 popup->AddItem(new BMenuItem(map.GetNthStateDesc(i), NULL)); 71 72 return popup; 73 } 74 75 // Creates a pop-up that allows the user to choose a key-cap visually 76 static BPopUpMenu* CreateKeysPopUp(); 77 static BPopUpMenu* CreateKeysPopUp() 78 { 79 BPopUpMenu* popup = new BPopUpMenu(NULL, false); 80 int numKeys = GetNumKeyIndices(); 81 for (int i = 0; i < numKeys; i++) { 82 const char* next = GetKeyName(i); 83 84 if (next) 85 popup->AddItem(new BMenuItem(next, NULL)); 86 } 87 return popup; 88 } 89 90 91 ShortcutsWindow::ShortcutsWindow() 92 : 93 BWindow(BRect(WINDOW_START_X, WINDOW_START_Y, WINDOW_START_X + MIN_WIDTH, 94 WINDOW_START_Y + MIN_HEIGHT * 2), "Shortcuts", B_DOCUMENT_WINDOW, 0L), 95 fSavePanel(NULL), 96 fOpenPanel(NULL), 97 fSelectPanel(NULL), 98 fKeySetModified(false), 99 fLastOpenWasAppend(false) 100 { 101 InitializeMetaMaps(); 102 SetSizeLimits(MIN_WIDTH, MAX_WIDTH, MIN_HEIGHT, MAX_HEIGHT); 103 BMenuBar* menuBar = new BMenuBar(BRect(0, 0, 0, 0), "Menu Bar"); 104 105 BMenu* fileMenu = new BMenu("File"); 106 fileMenu->AddItem(new BMenuItem("Open KeySet...", 107 new BMessage(OPEN_KEYSET), 'O')); 108 fileMenu->AddItem(new BMenuItem("Append KeySet...", 109 new BMessage(APPEND_KEYSET), 'A')); 110 fileMenu->AddItem(new BMenuItem("Revert to Saved", 111 new BMessage(REVERT_KEYSET), 'A')); 112 fileMenu->AddItem(new BSeparatorItem); 113 fileMenu->AddItem(new BMenuItem("Save KeySet As...", 114 new BMessage(SAVE_KEYSET_AS), 'S')); 115 fileMenu->AddItem(new BSeparatorItem); 116 fileMenu->AddItem(new BMenuItem("About Shortcuts", new BMessage(B_ABOUT_REQUESTED))); 117 fileMenu->AddItem(new BSeparatorItem); 118 fileMenu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED), 119 'Q')); 120 menuBar->AddItem(fileMenu); 121 122 AddChild(menuBar); 123 124 font_height fh; 125 be_plain_font->GetHeight(&fh); 126 float vButtonHeight = ceil(fh.ascent) + ceil(fh.descent) + 5.0f; 127 128 BRect tableBounds = Bounds(); 129 tableBounds.top = menuBar->Bounds().bottom + 1; 130 tableBounds.right -= B_V_SCROLL_BAR_WIDTH; 131 tableBounds.bottom -= (B_H_SCROLL_BAR_HEIGHT + V_SPACING + vButtonHeight + 132 V_SPACING * 2); 133 134 BScrollView* containerView; 135 fColumnListView = new ColumnListView(tableBounds, &containerView, NULL, 136 B_FOLLOW_ALL_SIDES, B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE, 137 B_SINGLE_SELECTION_LIST, true, true, true, B_NO_BORDER); 138 139 fColumnListView->SetEditMessage(new BMessage(HOTKEY_ITEM_MODIFIED), 140 BMessenger(this)); 141 142 const float metaWidth = 50.0f; 143 144 for (int i = 0; i < ShortcutsSpec::NUM_META_COLUMNS; i++) 145 fColumnListView->AddColumn( 146 new CLVColumn(ShortcutsSpec::GetColumnName(i), CreateMetaPopUp(i), 147 metaWidth, CLV_SORT_KEYABLE)); 148 149 fColumnListView->AddColumn(new CLVColumn("Key", CreateKeysPopUp(), 60, 150 CLV_SORT_KEYABLE)); 151 152 BPopUpMenu* popup = new BPopUpMenu(NULL, false); 153 popup->AddItem(new BMenuItem("(Choose App with File Requester)", NULL)); 154 popup->AddItem(new BMenuItem("*InsertString \"Your Text Here\"", NULL)); 155 popup->AddItem(new BMenuItem("*MoveMouse +20 +0", NULL)); 156 popup->AddItem(new BMenuItem("*MoveMouseTo 50% 50%", NULL)); 157 popup->AddItem(new BMenuItem("*MouseButton 1", NULL)); 158 popup->AddItem(new BMenuItem("*LaunchHandler text/html", NULL)); 159 popup->AddItem(new BMenuItem( 160 "*Multi \"*MoveMouseTo 100% 0\" \"*MouseButton 1\"", NULL)); 161 popup->AddItem(new BMenuItem("*MouseDown", NULL)); 162 popup->AddItem(new BMenuItem("*MouseUp", NULL)); 163 popup->AddItem(new BMenuItem( 164 "*SendMessage application/x-vnd.Be-TRAK 'Tfnd'", NULL)); 165 popup->AddItem(new BMenuItem("*Beep", NULL)); 166 fColumnListView->AddColumn(new CLVColumn("Application", popup, 323.0, 167 CLV_SORT_KEYABLE)); 168 169 fColumnListView->SetSortFunction(ShortcutsSpec::MyCompare); 170 AddChild(containerView); 171 172 fColumnListView->SetSelectionMessage(new BMessage(HOTKEY_ITEM_SELECTED)); 173 fColumnListView->SetTarget(this); 174 175 BRect buttonBounds = Bounds(); 176 buttonBounds.left += V_SPACING; 177 buttonBounds.right = ((buttonBounds.right - buttonBounds.left) / 2.0f) + 178 buttonBounds.left; 179 buttonBounds.bottom -= V_SPACING * 2; 180 buttonBounds.top = buttonBounds.bottom - vButtonHeight; 181 buttonBounds.right -= B_V_SCROLL_BAR_WIDTH; 182 float origRight = buttonBounds.right; 183 buttonBounds.right = (buttonBounds.left + origRight) * 0.40f - 184 (V_SPACING / 2); 185 AddChild(fAddButton = new ResizableButton(Bounds(), buttonBounds, "add", 186 "Add New Shortcut", new BMessage(ADD_HOTKEY_ITEM))); 187 buttonBounds.left = buttonBounds.right + V_SPACING; 188 buttonBounds.right = origRight; 189 AddChild(fRemoveButton = new ResizableButton(Bounds(), buttonBounds, 190 "remove", "Remove Selected Shortcut", 191 new BMessage(REMOVE_HOTKEY_ITEM))); 192 193 fRemoveButton->SetEnabled(false); 194 195 float offset = (buttonBounds.right - buttonBounds.left) / 2.0f; 196 BRect saveButtonBounds = buttonBounds; 197 saveButtonBounds.right = Bounds().right - B_V_SCROLL_BAR_WIDTH - offset; 198 saveButtonBounds.left = buttonBounds.right + V_SPACING + offset; 199 AddChild(fSaveButton = new ResizableButton(Bounds(), saveButtonBounds, 200 "save", "Save & Apply", new BMessage(SAVE_KEYSET))); 201 202 fSaveButton->SetEnabled(false); 203 204 entry_ref ref; 205 if (_GetSettingsFile(&ref)) { 206 BMessage msg(B_REFS_RECEIVED); 207 msg.AddRef("refs", &ref); 208 msg.AddString("startupRef", "please"); 209 PostMessage(&msg); // Tell ourself to load this file if it exists. 210 } 211 Show(); 212 } 213 214 215 ShortcutsWindow::~ShortcutsWindow() 216 { 217 delete fSavePanel; 218 delete fOpenPanel; 219 delete fSelectPanel; 220 be_app->PostMessage(B_QUIT_REQUESTED); 221 } 222 223 224 bool 225 ShortcutsWindow::QuitRequested() 226 { 227 bool ret = true; 228 229 if (fKeySetModified) { 230 BAlert* alert = new BAlert(WARNING, 231 "Really quit without saving your changes?", "Don't Save", "Cancel", 232 "Save"); 233 switch(alert->Go()) { 234 case 1: 235 ret = false; 236 break; 237 238 case 2: 239 // Save: automatically if possible, otherwise go back and open 240 // up the file requester 241 if (fLastSaved.InitCheck() == B_NO_ERROR) { 242 if (_SaveKeySet(fLastSaved) == false) { 243 (new BAlert(ERROR, 244 "Shortcuts was unable to save your KeySet file!", 245 "Oh no"))->Go(); 246 ret = true; //quit anyway 247 } 248 } else { 249 PostMessage(SAVE_KEYSET); 250 ret = false; 251 } 252 break; 253 default: 254 ret = true; 255 break; 256 } 257 } 258 259 if (ret) 260 fColumnListView->DeselectAll(); // avoid mysterious crash on PPC!? 261 return ret; 262 } 263 264 265 bool 266 ShortcutsWindow::_GetSettingsFile(entry_ref* eref) 267 { 268 BPath path; 269 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK) 270 return false; 271 else 272 path.Append(SHORTCUTS_SETTING_FILE_NAME); 273 274 if (BEntry(path.Path(), true).GetRef(eref) == B_NO_ERROR) 275 return true; 276 else 277 return false; 278 } 279 280 281 // Saves a settings file to (saveEntry). Returns true iff successful. 282 bool 283 ShortcutsWindow::_SaveKeySet(BEntry& saveEntry) 284 { 285 BFile saveTo(&saveEntry, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 286 if (saveTo.InitCheck() != B_NO_ERROR) 287 return false; 288 289 BMessage saveMsg; 290 for (int i = 0; i < fColumnListView->CountItems(); i++) { 291 BMessage next; 292 if (((ShortcutsSpec*)fColumnListView->ItemAt(i))->Archive(&next) 293 == B_NO_ERROR) 294 saveMsg.AddMessage("spec", &next); 295 else 296 printf("Error archiving ShortcutsSpec #%i!\n",i); 297 } 298 299 bool ret = (saveMsg.Flatten(&saveTo) == B_NO_ERROR); 300 301 if (ret) { 302 fKeySetModified = false; 303 fSaveButton->SetEnabled(false); 304 } 305 306 return ret; 307 } 308 309 310 // Appends new entries from the file specified in the "spec" entry of 311 // (loadMsg). Returns true iff successful. 312 bool 313 ShortcutsWindow::_LoadKeySet(const BMessage& loadMsg) 314 { 315 int i = 0; 316 BMessage msg; 317 while (loadMsg.FindMessage("spec", i++, &msg) == B_NO_ERROR) { 318 ShortcutsSpec* spec = (ShortcutsSpec*)ShortcutsSpec::Instantiate(&msg); 319 if (spec != NULL) 320 fColumnListView->AddItem(spec); 321 else 322 printf("_LoadKeySet: Error parsing spec!\n"); 323 } 324 return true; 325 } 326 327 328 // Creates a new entry and adds it to the GUI. (defaultCommand) will be the 329 // text in the entry, or NULL if no text is desired. 330 void 331 ShortcutsWindow::_AddNewSpec(const char* defaultCommand) 332 { 333 _MarkKeySetModified(); 334 335 ShortcutsSpec* spec; 336 int curSel = fColumnListView->CurrentSelection(); 337 if (curSel >= 0) { 338 spec = new ShortcutsSpec(*((ShortcutsSpec*) 339 fColumnListView->ItemAt(curSel))); 340 341 if (defaultCommand) 342 spec->SetCommand(defaultCommand); 343 } else 344 spec = new ShortcutsSpec(defaultCommand ? defaultCommand : ""); 345 346 fColumnListView->AddItem(spec); 347 fColumnListView->Select(fColumnListView->CountItems() - 1); 348 fColumnListView->ScrollToSelection(); 349 } 350 351 352 void 353 ShortcutsWindow::MessageReceived(BMessage* msg) 354 { 355 switch(msg->what) { 356 case OPEN_KEYSET: 357 case APPEND_KEYSET: 358 fLastOpenWasAppend = (msg->what == APPEND_KEYSET); 359 if (fOpenPanel) 360 fOpenPanel->Show(); 361 else { 362 BMessenger m(this); 363 fOpenPanel = new BFilePanel(B_OPEN_PANEL, &m, NULL, 0, false); 364 fOpenPanel->Show(); 365 } 366 fOpenPanel->SetButtonLabel(B_DEFAULT_BUTTON, fLastOpenWasAppend ? 367 "Append" : "Open"); 368 break; 369 370 case REVERT_KEYSET: 371 { 372 // Send a message to myself, to get me to reload the settings file 373 fLastOpenWasAppend = false; 374 BMessage reload(B_REFS_RECEIVED); 375 entry_ref eref; 376 _GetSettingsFile(&eref); 377 reload.AddRef("refs", &eref); 378 reload.AddString("startupRef", "yeah"); 379 PostMessage(&reload); 380 } 381 break; 382 383 // Respond to drag-and-drop messages here 384 case B_SIMPLE_DATA: 385 { 386 int i = 0; 387 388 entry_ref ref; 389 while (msg->FindRef("refs", i++, &ref) == B_NO_ERROR) { 390 BEntry entry(&ref); 391 if (entry.InitCheck() == B_NO_ERROR) { 392 BPath path(&entry); 393 394 if (path.InitCheck() == B_NO_ERROR) { 395 // Add a new item with the given path. 396 BString str(path.Path()); 397 DoStandardEscapes(str); 398 _AddNewSpec(str.String()); 399 } 400 } 401 } 402 } 403 break; 404 405 // Respond to FileRequester's messages here 406 case B_REFS_RECEIVED: 407 { 408 // Find file ref 409 entry_ref ref; 410 bool isStartMsg = msg->HasString("startupRef"); 411 if (msg->FindRef("refs", &ref) == B_NO_ERROR) { 412 // load the file into (fileMsg) 413 BMessage fileMsg; 414 { 415 BFile file(&ref, B_READ_ONLY); 416 if ((file.InitCheck() != B_NO_ERROR) 417 || (fileMsg.Unflatten(&file) != B_NO_ERROR)) { 418 if (isStartMsg) { 419 // use this to save to anyway 420 fLastSaved = BEntry(&ref); 421 break; 422 } else { 423 (new BAlert(ERROR, 424 "Shortcuts was couldn't open your KeySet file!" 425 , "Okay"))->Go(NULL); 426 break; 427 } 428 } 429 } 430 431 if (fLastOpenWasAppend == false) { 432 // Clear the menu... 433 ShortcutsSpec * item; 434 do { 435 delete (item = ((ShortcutsSpec*) 436 fColumnListView->RemoveItem(int32(0)))); 437 } while (item); 438 } 439 440 if (_LoadKeySet(fileMsg)) { 441 if (isStartMsg) fLastSaved = BEntry(&ref); 442 fSaveButton->SetEnabled(isStartMsg == false); 443 444 // If we just loaded in the Shortcuts settings file, then 445 // no need to tell the user to save on exit. 446 entry_ref eref; 447 _GetSettingsFile(&eref); 448 if (ref == eref) fKeySetModified = false; 449 } else { 450 (new BAlert(ERROR, 451 "Shortcuts was unable to parse your KeySet file!", 452 "Okay"))->Go(NULL); 453 break; 454 } 455 } 456 } 457 break; 458 459 // These messages come from the pop-up menu of the Applications column 460 case SELECT_APPLICATION: 461 { 462 int csel = fColumnListView->CurrentSelection(); 463 if (csel >= 0) { 464 entry_ref aref; 465 if (msg->FindRef("refs", &aref) == B_NO_ERROR) { 466 BEntry ent(&aref); 467 if (ent.InitCheck() == B_NO_ERROR) { 468 BPath path; 469 if ((ent.GetPath(&path) == B_NO_ERROR) 470 && (((ShortcutsSpec *) 471 fColumnListView->ItemAt(csel))-> 472 ProcessColumnTextString(ShortcutsSpec:: 473 STRING_COLUMN_INDEX, path.Path()))) { 474 475 fColumnListView->InvalidateItem(csel); 476 _MarkKeySetModified(); 477 } 478 } 479 } 480 } 481 } 482 break; 483 484 case SAVE_KEYSET: 485 { 486 bool showSaveError = false; 487 488 const char * name; 489 entry_ref entry; 490 if ((msg->FindString("name", &name) == B_NO_ERROR) 491 && (msg->FindRef("directory", &entry) == B_NO_ERROR)) { 492 BDirectory dir(&entry); 493 BEntry saveTo(&dir, name, true); 494 showSaveError = ((saveTo.InitCheck() != B_NO_ERROR) 495 || (_SaveKeySet(saveTo) == false)); 496 } else if (fLastSaved.InitCheck() == B_NO_ERROR) { 497 // We've saved this before, save over previous file. 498 showSaveError = (_SaveKeySet(fLastSaved) == false); 499 } else PostMessage(SAVE_KEYSET_AS); // open the save requester... 500 501 if (showSaveError) { 502 (new BAlert(ERROR, "Shortcuts wasn't able to save your keyset." 503 , "Okay"))->Go(NULL); 504 } 505 } 506 break; 507 508 case SAVE_KEYSET_AS: 509 { 510 if (fSavePanel) 511 fSavePanel->Show(); 512 else { 513 BMessage msg(SAVE_KEYSET); 514 BMessenger messenger(this); 515 fSavePanel = new BFilePanel(B_SAVE_PANEL, &messenger, NULL, 0, 516 false, &msg); 517 fSavePanel->Show(); 518 } 519 } 520 break; 521 522 case B_ABOUT_REQUESTED: 523 be_app_messenger.SendMessage(B_ABOUT_REQUESTED); 524 break; 525 526 case ADD_HOTKEY_ITEM: 527 _AddNewSpec(NULL); 528 break; 529 530 case REMOVE_HOTKEY_ITEM: 531 { 532 int index = fColumnListView->CurrentSelection(); 533 if (index >= 0) { 534 CLVListItem* item = (CLVListItem*) 535 fColumnListView->ItemAt(index); 536 fColumnListView->RemoveItem(index); 537 delete item; 538 _MarkKeySetModified(); 539 540 // Rules for new selection: If there is an item at (index), 541 // select it. Otherwise, if there is an item at (index-1), 542 // select it. Otherwise, select nothing. 543 int num = fColumnListView->CountItems(); 544 if (num > 0) { 545 if (index < num) 546 fColumnListView->Select(index); 547 else { 548 if (index > 0) 549 index--; 550 if (index < num) 551 fColumnListView->Select(index); 552 } 553 } 554 } 555 } 556 break; 557 558 // Received when the user clicks on the ColumnListView 559 case HOTKEY_ITEM_SELECTED: 560 { 561 int32 index = -1; 562 msg->FindInt32("index", &index); 563 bool validItem = (index >= 0); 564 fRemoveButton->SetEnabled(validItem); 565 } 566 break; 567 568 // Received when an entry is to be modified in response to GUI activity 569 case HOTKEY_ITEM_MODIFIED: 570 { 571 int32 row, column; 572 573 if ((msg->FindInt32("row", &row) == B_NO_ERROR) 574 && (msg->FindInt32("column", &column) == B_NO_ERROR)) { 575 int32 key; 576 const char* bytes; 577 578 if (row >= 0) { 579 ShortcutsSpec* item = (ShortcutsSpec*) 580 fColumnListView->ItemAt(row); 581 bool repaintNeeded = false; // default 582 583 if (msg->HasInt32("mouseClick")) { 584 repaintNeeded = item->ProcessColumnMouseClick(column); 585 } else if ((msg->FindString("bytes", &bytes) == B_NO_ERROR) 586 && (msg->FindInt32("key", &key) == B_NO_ERROR)) { 587 repaintNeeded = item->ProcessColumnKeyStroke(column, 588 bytes, key); 589 } else if (msg->FindInt32("unmappedkey", &key) == 590 B_NO_ERROR) { 591 repaintNeeded = ((column == item->KEY_COLUMN_INDEX) 592 && ((key > 0xFF) || (GetKeyName(key) != NULL)) 593 && (item->ProcessColumnKeyStroke(column, NULL, 594 key))); 595 } else if (msg->FindString("text", &bytes) == B_NO_ERROR) { 596 if ((bytes[0] == '(')&&(bytes[1] == 'C')) { 597 if (fSelectPanel) 598 fSelectPanel->Show(); 599 else { 600 BMessage msg(SELECT_APPLICATION); 601 BMessenger m(this); 602 fSelectPanel = new BFilePanel(B_OPEN_PANEL, &m, 603 NULL, 0, false, &msg); 604 fSelectPanel->Show(); 605 } 606 fSelectPanel->SetButtonLabel(B_DEFAULT_BUTTON, 607 "Select"); 608 } else 609 repaintNeeded = item->ProcessColumnTextString( 610 column, bytes); 611 } 612 613 if (repaintNeeded) { 614 fColumnListView->InvalidateItem(row); 615 _MarkKeySetModified(); 616 } 617 } 618 } 619 } 620 break; 621 622 default: 623 BWindow::MessageReceived(msg); 624 break; 625 } 626 } 627 628 629 void 630 ShortcutsWindow::_MarkKeySetModified() 631 { 632 if (fKeySetModified == false) { 633 fKeySetModified = true; 634 fSaveButton->SetEnabled(true); 635 } 636 } 637 638 639 void 640 ShortcutsWindow::Quit() 641 { 642 for (int i = fColumnListView->CountItems() - 1; i >= 0; i--) 643 delete (ShortcutsSpec*)fColumnListView->ItemAt(i); 644 645 fColumnListView->MakeEmpty(); 646 BWindow::Quit(); 647 } 648 649 650 void 651 ShortcutsWindow::FrameResized(float w, float h) 652 { 653 fAddButton->ChangeToNewSize(w, h); 654 fRemoveButton->ChangeToNewSize(w, h); 655 fSaveButton->ChangeToNewSize(w, h); 656 } 657 658 659 void 660 ShortcutsWindow::DispatchMessage(BMessage* msg, BHandler* handler) 661 { 662 switch(msg->what) { 663 case B_COPY: 664 case B_CUT: 665 if (be_clipboard->Lock()) { 666 int32 row = fColumnListView->CurrentSelection(); 667 int32 column = fColumnListView->GetSelectedColumn(); 668 if ((row >= 0) 669 && (column == ShortcutsSpec::STRING_COLUMN_INDEX)) { 670 ShortcutsSpec* spec = (ShortcutsSpec*) 671 fColumnListView->ItemAt(row); 672 if (spec) { 673 BMessage* data = be_clipboard->Data(); 674 data->RemoveName("text/plain"); 675 data->AddData("text/plain", B_MIME_TYPE, 676 spec->GetCellText(column), 677 strlen(spec->GetCellText(column))); 678 be_clipboard->Commit(); 679 680 if (msg->what == B_CUT) { 681 spec->ProcessColumnTextString(column, ""); 682 _MarkKeySetModified(); 683 fColumnListView->InvalidateItem(row); 684 } 685 } 686 } 687 be_clipboard->Unlock(); 688 } 689 break; 690 691 case B_PASTE: 692 if (be_clipboard->Lock()) { 693 BMessage* data = be_clipboard->Data(); 694 const char* text; 695 ssize_t textLen; 696 if (data->FindData("text/plain", B_MIME_TYPE, (const void**) 697 &text, &textLen) == B_NO_ERROR) { 698 int32 row = fColumnListView->CurrentSelection(); 699 int32 column = fColumnListView->GetSelectedColumn(); 700 if ((row >= 0) 701 && (column == ShortcutsSpec::STRING_COLUMN_INDEX)) { 702 ShortcutsSpec* spec = (ShortcutsSpec*) 703 fColumnListView->ItemAt(row); 704 if (spec) { 705 for (ssize_t i = 0; i < textLen; i++) { 706 char buf[2] = {text[i], 0x00}; 707 spec->ProcessColumnKeyStroke(column, buf, 0); 708 } 709 } 710 fColumnListView->InvalidateItem(row); 711 _MarkKeySetModified(); 712 } 713 } 714 be_clipboard->Unlock(); 715 } 716 break; 717 718 default: 719 BWindow::DispatchMessage(msg, handler); 720 break; 721 } 722 } 723