1 /* 2 * Copyright 2004-2010 Haiku Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Sandor Vroemisse 7 * Jérôme Duval 8 * Alexandre Deckner, alex@zappotek.com 9 * Axel Dörfler, axeld@pinc-software.de. 10 */ 11 12 13 #include "KeymapWindow.h" 14 15 #include <string.h> 16 #include <stdio.h> 17 18 #include <Alert.h> 19 #include <Button.h> 20 #include <Catalog.h> 21 #include <Directory.h> 22 #include <File.h> 23 #include <FindDirectory.h> 24 #include <GroupLayoutBuilder.h> 25 #include <ListView.h> 26 #include <Locale.h> 27 #include <MenuBar.h> 28 #include <MenuField.h> 29 #include <MenuItem.h> 30 #include <Path.h> 31 #include <PopUpMenu.h> 32 #include <Screen.h> 33 #include <ScrollView.h> 34 #include <StringView.h> 35 #include <TextControl.h> 36 37 #include "KeyboardLayoutView.h" 38 #include "KeymapApplication.h" 39 #include "KeymapListItem.h" 40 41 42 #define B_TRANSLATE_CONTEXT "Keymap window" 43 44 45 static const uint32 kMsgMenuFileOpen = 'mMFO'; 46 static const uint32 kMsgMenuFileSaveAs = 'mMFA'; 47 48 static const uint32 kChangeKeyboardLayout = 'cKyL'; 49 50 static const uint32 kMsgSwitchShortcuts = 'swSc'; 51 52 static const uint32 kMsgMenuFontChanged = 'mMFC'; 53 54 static const uint32 kMsgSystemMapSelected = 'SmST'; 55 static const uint32 kMsgUserMapSelected = 'UmST'; 56 57 static const uint32 kMsgRevertKeymap = 'Rvrt'; 58 static const uint32 kMsgKeymapUpdated = 'upkM'; 59 60 static const uint32 kMsgDeadKeyAcuteChanged = 'dkAc'; 61 static const uint32 kMsgDeadKeyCircumflexChanged = 'dkCc'; 62 static const uint32 kMsgDeadKeyDiaeresisChanged = 'dkDc'; 63 static const uint32 kMsgDeadKeyGraveChanged = 'dkGc'; 64 static const uint32 kMsgDeadKeyTildeChanged = 'dkTc'; 65 66 static const char* kDeadKeyTriggerNone = "<none>"; 67 68 static const char* kCurrentKeymapName = "(Current)"; 69 70 71 KeymapWindow::KeymapWindow() 72 : 73 BWindow(BRect(80, 50, 880, 380), B_TRANSLATE_APP_NAME("Keymap"), 74 B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS) 75 { 76 SetLayout(new BGroupLayout(B_VERTICAL)); 77 78 fKeyboardLayoutView = new KeyboardLayoutView("layout"); 79 fKeyboardLayoutView->SetKeymap(&fCurrentMap); 80 81 fTextControl = new BTextControl(B_TRANSLATE("Sample and clipboard:"), 82 "", NULL); 83 84 fSwitchShortcutsButton = new BButton("switch", "", 85 new BMessage(kMsgSwitchShortcuts)); 86 87 fRevertButton = new BButton("revertButton", B_TRANSLATE("Revert"), 88 new BMessage(kMsgRevertKeymap)); 89 90 // controls pane 91 AddChild(BGroupLayoutBuilder(B_VERTICAL) 92 .Add(_CreateMenu()) 93 .Add(BGroupLayoutBuilder(B_HORIZONTAL, 10) 94 .Add(_CreateMapLists(), 0.25) 95 .Add(BGroupLayoutBuilder(B_VERTICAL, 10) 96 .Add(fKeyboardLayoutView) 97 //.Add(new BStringView("text label", "Sample and clipboard:")) 98 .Add(BGroupLayoutBuilder(B_HORIZONTAL, 10) 99 .Add(_CreateDeadKeyMenuField(), 0.0) 100 .AddGlue() 101 .Add(fSwitchShortcutsButton)) 102 .Add(fTextControl) 103 .AddGlue(0.0) 104 .Add(BGroupLayoutBuilder(B_HORIZONTAL, 10) 105 .AddGlue(0.0) 106 .Add(fRevertButton))) 107 .SetInsets(10, 10, 10, 10))); 108 109 fKeyboardLayoutView->SetTarget(fTextControl->TextView()); 110 fTextControl->MakeFocus(); 111 112 // Make sure the user keymap directory exists 113 BPath path; 114 find_directory(B_USER_SETTINGS_DIRECTORY, &path); 115 path.Append("Keymap"); 116 117 entry_ref ref; 118 get_ref_for_path(path.Path(), &ref); 119 120 BDirectory userKeymapsDir(&ref); 121 if (userKeymapsDir.InitCheck() != B_OK) 122 create_directory(path.Path(), S_IRWXU | S_IRWXG | S_IRWXO); 123 124 BMessenger messenger(this); 125 fOpenPanel = new BFilePanel(B_OPEN_PANEL, &messenger, &ref, 126 B_FILE_NODE, false, NULL); 127 fSavePanel = new BFilePanel(B_SAVE_PANEL, &messenger, &ref, 128 B_FILE_NODE, false, NULL); 129 130 BRect windowFrame; 131 BString keyboardLayout; 132 _LoadSettings(windowFrame, keyboardLayout); 133 _SetKeyboardLayout(keyboardLayout.String()); 134 135 ResizeTo(windowFrame.Width(), windowFrame.Height()); 136 MoveTo(windowFrame.LeftTop()); 137 138 // TODO: this might be a bug in the interface kit, but scrolling to 139 // selection does not correctly work unless the window is shown. 140 Show(); 141 Lock(); 142 143 // Try and find the current map name in the two list views (if the name 144 // was read at all) 145 _SelectCurrentMap(); 146 147 KeymapListItem* current 148 = static_cast<KeymapListItem*>(fUserListView->FirstItem()); 149 150 fCurrentMap.Load(current->EntryRef()); 151 fPreviousMap = fCurrentMap; 152 fAppliedMap = fCurrentMap; 153 fCurrentMap.SetTarget(this, new BMessage(kMsgKeymapUpdated)); 154 155 _UpdateButtons(); 156 157 _UpdateDeadKeyMenu(); 158 _UpdateSwitchShortcutButton(); 159 160 Unlock(); 161 } 162 163 164 KeymapWindow::~KeymapWindow() 165 { 166 delete fOpenPanel; 167 delete fSavePanel; 168 } 169 170 171 bool 172 KeymapWindow::QuitRequested() 173 { 174 _SaveSettings(); 175 176 be_app->PostMessage(B_QUIT_REQUESTED); 177 return true; 178 } 179 180 181 void 182 KeymapWindow::MessageReceived(BMessage* message) 183 { 184 switch (message->what) { 185 case B_SIMPLE_DATA: 186 case B_REFS_RECEIVED: 187 { 188 entry_ref ref; 189 int32 i = 0; 190 while (message->FindRef("refs", i++, &ref) == B_OK) { 191 fCurrentMap.Load(ref); 192 fAppliedMap = fCurrentMap; 193 } 194 fKeyboardLayoutView->SetKeymap(&fCurrentMap); 195 fSystemListView->DeselectAll(); 196 fUserListView->DeselectAll(); 197 break; 198 } 199 200 case B_SAVE_REQUESTED: 201 { 202 entry_ref ref; 203 const char *name; 204 if (message->FindRef("directory", &ref) == B_OK 205 && message->FindString("name", &name) == B_OK) { 206 BDirectory directory(&ref); 207 BEntry entry(&directory, name); 208 entry.GetRef(&ref); 209 fCurrentMap.SetName(name); 210 fCurrentMap.Save(ref); 211 fAppliedMap = fCurrentMap; 212 _FillUserMaps(); 213 fCurrentMapName = name; 214 _SelectCurrentMap(); 215 } 216 break; 217 } 218 219 case kMsgMenuFileOpen: 220 fOpenPanel->Show(); 221 break; 222 case kMsgMenuFileSaveAs: 223 fSavePanel->Show(); 224 break; 225 226 case kChangeKeyboardLayout: 227 { 228 entry_ref ref; 229 BPath path; 230 if (message->FindRef("ref", &ref) == B_OK) 231 path.SetTo(&ref); 232 233 _SetKeyboardLayout(path.Path()); 234 break; 235 } 236 237 case kMsgSwitchShortcuts: 238 _SwitchShortcutKeys(); 239 break; 240 241 case kMsgMenuFontChanged: 242 { 243 BMenuItem *item = fFontMenu->FindMarked(); 244 if (item != NULL) { 245 BFont font; 246 font.SetFamilyAndStyle(item->Label(), NULL); 247 fKeyboardLayoutView->SetBaseFont(font); 248 fTextControl->TextView()->SetFontAndColor(&font); 249 } 250 break; 251 } 252 253 case kMsgSystemMapSelected: 254 case kMsgUserMapSelected: 255 { 256 BListView* listView; 257 BListView* otherListView; 258 259 if (message->what == kMsgSystemMapSelected) { 260 listView = fSystemListView; 261 otherListView = fUserListView; 262 } else { 263 listView = fUserListView; 264 otherListView = fSystemListView; 265 } 266 267 int32 index = listView->CurrentSelection(); 268 if (index < 0) 269 break; 270 271 // Deselect item in other BListView 272 otherListView->DeselectAll(); 273 274 if (index == 0 && listView == fUserListView) { 275 // we can safely ignore the "(Current)" item 276 break; 277 } 278 279 KeymapListItem* item 280 = static_cast<KeymapListItem*>(listView->ItemAt(index)); 281 if (item != NULL) { 282 fCurrentMap.Load(item->EntryRef()); 283 fAppliedMap = fCurrentMap; 284 fKeyboardLayoutView->SetKeymap(&fCurrentMap); 285 _UseKeymap(); 286 _UpdateButtons(); 287 } 288 break; 289 } 290 291 case kMsgRevertKeymap: 292 _RevertKeymap(); 293 _UpdateButtons(); 294 break; 295 296 case kMsgKeymapUpdated: 297 _UpdateButtons(); 298 fSystemListView->DeselectAll(); 299 fUserListView->Select(0L); 300 break; 301 302 case kMsgDeadKeyAcuteChanged: 303 { 304 BMenuItem* item = fAcuteMenu->FindMarked(); 305 if (item != NULL) { 306 const char* trigger = item->Label(); 307 if (strcmp(trigger, kDeadKeyTriggerNone) == 0) 308 trigger = NULL; 309 fCurrentMap.SetDeadKeyTrigger(kDeadKeyAcute, trigger); 310 fKeyboardLayoutView->Invalidate(); 311 } 312 break; 313 } 314 315 case kMsgDeadKeyCircumflexChanged: 316 { 317 BMenuItem* item = fCircumflexMenu->FindMarked(); 318 if (item != NULL) { 319 const char* trigger = item->Label(); 320 if (strcmp(trigger, kDeadKeyTriggerNone) == 0) 321 trigger = NULL; 322 fCurrentMap.SetDeadKeyTrigger(kDeadKeyCircumflex, trigger); 323 fKeyboardLayoutView->Invalidate(); 324 } 325 break; 326 } 327 328 case kMsgDeadKeyDiaeresisChanged: 329 { 330 BMenuItem* item = fDiaeresisMenu->FindMarked(); 331 if (item != NULL) { 332 const char* trigger = item->Label(); 333 if (strcmp(trigger, kDeadKeyTriggerNone) == 0) 334 trigger = NULL; 335 fCurrentMap.SetDeadKeyTrigger(kDeadKeyDiaeresis, trigger); 336 fKeyboardLayoutView->Invalidate(); 337 } 338 break; 339 } 340 341 case kMsgDeadKeyGraveChanged: 342 { 343 BMenuItem* item = fGraveMenu->FindMarked(); 344 if (item != NULL) { 345 const char* trigger = item->Label(); 346 if (strcmp(trigger, kDeadKeyTriggerNone) == 0) 347 trigger = NULL; 348 fCurrentMap.SetDeadKeyTrigger(kDeadKeyGrave, trigger); 349 fKeyboardLayoutView->Invalidate(); 350 } 351 break; 352 } 353 354 case kMsgDeadKeyTildeChanged: 355 { 356 BMenuItem* item = fTildeMenu->FindMarked(); 357 if (item != NULL) { 358 const char* trigger = item->Label(); 359 if (strcmp(trigger, kDeadKeyTriggerNone) == 0) 360 trigger = NULL; 361 fCurrentMap.SetDeadKeyTrigger(kDeadKeyTilde, trigger); 362 fKeyboardLayoutView->Invalidate(); 363 } 364 break; 365 } 366 367 default: 368 BWindow::MessageReceived(message); 369 break; 370 } 371 } 372 373 374 BMenuBar* 375 KeymapWindow::_CreateMenu() 376 { 377 BMenuBar* menuBar = new BMenuBar(Bounds(), "menubar"); 378 BMenuItem* item; 379 380 // Create the File menu 381 BMenu* menu = new BMenu(B_TRANSLATE("File")); 382 menu->AddItem(new BMenuItem(B_TRANSLATE("Open" B_UTF8_ELLIPSIS), 383 new BMessage(kMsgMenuFileOpen), 'O')); 384 menu->AddSeparatorItem(); 385 menu->AddItem(new BMenuItem(B_TRANSLATE("Save as" B_UTF8_ELLIPSIS), 386 new BMessage(kMsgMenuFileSaveAs))); 387 menu->AddSeparatorItem(); 388 menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"), 389 new BMessage(B_QUIT_REQUESTED), 'Q')); 390 menuBar->AddItem(menu); 391 392 // Create keyboard layout menu 393 fLayoutMenu = new BMenu(B_TRANSLATE("Layout")); 394 fLayoutMenu->SetRadioMode(true); 395 fLayoutMenu->AddItem(item = new BMenuItem( 396 fKeyboardLayoutView->GetKeyboardLayout()->Name(), 397 new BMessage(kChangeKeyboardLayout))); 398 item->SetMarked(true); 399 400 _AddKeyboardLayouts(fLayoutMenu); 401 menuBar->AddItem(fLayoutMenu); 402 403 // Create the Font menu 404 fFontMenu = new BMenu(B_TRANSLATE("Font")); 405 fFontMenu->SetRadioMode(true); 406 int32 numFamilies = count_font_families(); 407 font_family family, currentFamily; 408 font_style currentStyle; 409 uint32 flags; 410 411 be_plain_font->GetFamilyAndStyle(¤tFamily, ¤tStyle); 412 413 for (int32 i = 0; i < numFamilies; i++) { 414 if (get_font_family(i, &family, &flags) == B_OK) { 415 BMenuItem *item = 416 new BMenuItem(family, new BMessage(kMsgMenuFontChanged)); 417 fFontMenu->AddItem(item); 418 419 if (!strcmp(family, currentFamily)) 420 item->SetMarked(true); 421 } 422 } 423 menuBar->AddItem(fFontMenu); 424 425 return menuBar; 426 } 427 428 429 BMenuField* 430 KeymapWindow::_CreateDeadKeyMenuField() 431 { 432 BPopUpMenu* deadKeyMenu = new BPopUpMenu(B_TRANSLATE("Select dead keys"), 433 false, false); 434 435 fAcuteMenu = new BMenu(B_TRANSLATE("Acute trigger")); 436 fAcuteMenu->SetRadioMode(true); 437 fAcuteMenu->AddItem(new BMenuItem("\xC2\xB4", 438 new BMessage(kMsgDeadKeyAcuteChanged))); 439 fAcuteMenu->AddItem(new BMenuItem("'", 440 new BMessage(kMsgDeadKeyAcuteChanged))); 441 fAcuteMenu->AddItem(new BMenuItem(kDeadKeyTriggerNone, 442 new BMessage(kMsgDeadKeyAcuteChanged))); 443 deadKeyMenu->AddItem(fAcuteMenu); 444 445 fCircumflexMenu = new BMenu(B_TRANSLATE("Circumflex trigger")); 446 fCircumflexMenu->SetRadioMode(true); 447 fCircumflexMenu->AddItem(new BMenuItem("^", 448 new BMessage(kMsgDeadKeyCircumflexChanged))); 449 fCircumflexMenu->AddItem(new BMenuItem(kDeadKeyTriggerNone, 450 new BMessage(kMsgDeadKeyCircumflexChanged))); 451 deadKeyMenu->AddItem(fCircumflexMenu); 452 453 fDiaeresisMenu = new BMenu(B_TRANSLATE("Diaeresis trigger")); 454 fDiaeresisMenu->SetRadioMode(true); 455 fDiaeresisMenu->AddItem(new BMenuItem("\xC2\xA8", 456 new BMessage(kMsgDeadKeyDiaeresisChanged))); 457 fDiaeresisMenu->AddItem(new BMenuItem("\"", 458 new BMessage(kMsgDeadKeyDiaeresisChanged))); 459 fDiaeresisMenu->AddItem(new BMenuItem(kDeadKeyTriggerNone, 460 new BMessage(kMsgDeadKeyDiaeresisChanged))); 461 deadKeyMenu->AddItem(fDiaeresisMenu); 462 463 fGraveMenu = new BMenu(B_TRANSLATE("Grave trigger")); 464 fGraveMenu->SetRadioMode(true); 465 fGraveMenu->AddItem(new BMenuItem("`", 466 new BMessage(kMsgDeadKeyGraveChanged))); 467 fGraveMenu->AddItem(new BMenuItem(kDeadKeyTriggerNone, 468 new BMessage(kMsgDeadKeyGraveChanged))); 469 deadKeyMenu->AddItem(fGraveMenu); 470 471 fTildeMenu = new BMenu(B_TRANSLATE("Tilde trigger")); 472 fTildeMenu->SetRadioMode(true); 473 fTildeMenu->AddItem(new BMenuItem("~", 474 new BMessage(kMsgDeadKeyTildeChanged))); 475 fTildeMenu->AddItem(new BMenuItem(kDeadKeyTriggerNone, 476 new BMessage(kMsgDeadKeyTildeChanged))); 477 deadKeyMenu->AddItem(fTildeMenu); 478 479 return new BMenuField(NULL, deadKeyMenu); 480 } 481 482 483 BView* 484 KeymapWindow::_CreateMapLists() 485 { 486 // The System list 487 fSystemListView = new BListView("systemList"); 488 fSystemListView->SetSelectionMessage(new BMessage(kMsgSystemMapSelected)); 489 490 BScrollView* systemScroller = new BScrollView("systemScrollList", 491 fSystemListView, 0, false, true); 492 493 // The User list 494 fUserListView = new BListView("userList"); 495 fUserListView->SetSelectionMessage(new BMessage(kMsgUserMapSelected)); 496 BScrollView* userScroller = new BScrollView("userScrollList", 497 fUserListView, 0, false, true); 498 499 // Saved keymaps 500 501 _FillSystemMaps(); 502 _FillUserMaps(); 503 504 _SetListViewSize(fSystemListView); 505 _SetListViewSize(fUserListView); 506 507 return BGroupLayoutBuilder(B_VERTICAL) 508 .Add(new BStringView("system", B_TRANSLATE("System:"))) 509 .Add(systemScroller, 3) 510 .Add(new BStringView("user", B_TRANSLATE("User:"))) 511 .Add(userScroller) 512 .TopView(); 513 } 514 515 516 void 517 KeymapWindow::_AddKeyboardLayouts(BMenu* menu) 518 { 519 directory_which dataDirectories[] = { 520 B_USER_DATA_DIRECTORY, 521 B_COMMON_DATA_DIRECTORY, 522 B_BEOS_DATA_DIRECTORY 523 }; 524 525 for (uint32 i = 0; 526 i < sizeof(dataDirectories) / sizeof(dataDirectories[0]); i++) { 527 BPath path; 528 if (find_directory(dataDirectories[i], &path) != B_OK) 529 continue; 530 531 path.Append("KeyboardLayouts"); 532 533 BDirectory directory; 534 if (directory.SetTo(path.Path()) == B_OK) { 535 entry_ref ref; 536 while (directory.GetNextRef(&ref) == B_OK) { 537 if (menu->FindItem(ref.name) != NULL) 538 continue; 539 540 BMessage* message = new BMessage(kChangeKeyboardLayout); 541 message->AddRef("ref", &ref); 542 543 menu->AddItem(new BMenuItem(ref.name, message)); 544 } 545 } 546 } 547 } 548 549 550 status_t 551 KeymapWindow::_SetKeyboardLayout(const char* path) 552 { 553 status_t status = B_OK; 554 555 if (path != NULL && path[0] != '\0') { 556 status = fKeyboardLayoutView->GetKeyboardLayout()->Load(path); 557 if (status == B_OK) { 558 // select item 559 for (int32 i = fLayoutMenu->CountItems(); i-- > 0;) { 560 BMenuItem* item = fLayoutMenu->ItemAt(i); 561 BMessage* message = item->Message(); 562 entry_ref ref; 563 if (message->FindRef("ref", &ref) == B_OK) { 564 BPath layoutPath(&ref); 565 if (layoutPath == path) { 566 item->SetMarked(true); 567 break; 568 } 569 } 570 } 571 } 572 } 573 574 if (path == NULL || status != B_OK) { 575 fKeyboardLayoutView->GetKeyboardLayout()->SetDefault(); 576 fLayoutMenu->ItemAt(0)->SetMarked(true); 577 } 578 579 // Refresh currently set layout 580 fKeyboardLayoutView->SetKeyboardLayout( 581 fKeyboardLayoutView->GetKeyboardLayout()); 582 583 return status; 584 } 585 586 587 /*! Sets the label of the "Switch Shorcuts" button to make it more 588 descriptive what will happen when you press that button. 589 */ 590 void 591 KeymapWindow::_UpdateSwitchShortcutButton() 592 { 593 const char* label = B_TRANSLATE("Switch shortcut keys"); 594 if (fCurrentMap.KeyForModifier(B_LEFT_COMMAND_KEY) == 0x5d 595 && fCurrentMap.KeyForModifier(B_LEFT_CONTROL_KEY) == 0x5c) { 596 label = B_TRANSLATE("Switch shortcut keys to Windows/Linux mode"); 597 } else if (fCurrentMap.KeyForModifier(B_LEFT_COMMAND_KEY) == 0x5c 598 && fCurrentMap.KeyForModifier(B_LEFT_CONTROL_KEY) == 0x5d) { 599 label = B_TRANSLATE("Switch shortcut keys to Haiku mode"); 600 } 601 602 fSwitchShortcutsButton->SetLabel(label); 603 } 604 605 606 /*! Marks the menu items corresponding to the dead key state of the current 607 key map. 608 */ 609 void 610 KeymapWindow::_UpdateDeadKeyMenu() 611 { 612 BString trigger; 613 fCurrentMap.GetDeadKeyTrigger(kDeadKeyAcute, trigger); 614 if (!trigger.Length()) 615 trigger = kDeadKeyTriggerNone; 616 BMenuItem* menuItem = fAcuteMenu->FindItem(trigger.String()); 617 if (menuItem) 618 menuItem->SetMarked(true); 619 620 fCurrentMap.GetDeadKeyTrigger(kDeadKeyCircumflex, trigger); 621 if (!trigger.Length()) 622 trigger = kDeadKeyTriggerNone; 623 menuItem = fCircumflexMenu->FindItem(trigger.String()); 624 if (menuItem) 625 menuItem->SetMarked(true); 626 627 fCurrentMap.GetDeadKeyTrigger(kDeadKeyDiaeresis, trigger); 628 if (!trigger.Length()) 629 trigger = kDeadKeyTriggerNone; 630 menuItem = fDiaeresisMenu->FindItem(trigger.String()); 631 if (menuItem) 632 menuItem->SetMarked(true); 633 634 fCurrentMap.GetDeadKeyTrigger(kDeadKeyGrave, trigger); 635 if (!trigger.Length()) 636 trigger = kDeadKeyTriggerNone; 637 menuItem = fGraveMenu->FindItem(trigger.String()); 638 if (menuItem) 639 menuItem->SetMarked(true); 640 641 fCurrentMap.GetDeadKeyTrigger(kDeadKeyTilde, trigger); 642 if (!trigger.Length()) 643 trigger = kDeadKeyTriggerNone; 644 menuItem = fTildeMenu->FindItem(trigger.String()); 645 if (menuItem) 646 menuItem->SetMarked(true); 647 } 648 649 650 void 651 KeymapWindow::_UpdateButtons() 652 { 653 if (fCurrentMap != fAppliedMap) { 654 fCurrentMap.SetName(kCurrentKeymapName); 655 _UseKeymap(); 656 } 657 658 fRevertButton->SetEnabled(fCurrentMap != fPreviousMap); 659 660 _UpdateDeadKeyMenu(); 661 _UpdateSwitchShortcutButton(); 662 } 663 664 665 void 666 KeymapWindow::_SwitchShortcutKeys() 667 { 668 uint32 leftCommand = fCurrentMap.Map().left_command_key; 669 uint32 leftControl = fCurrentMap.Map().left_control_key; 670 uint32 rightCommand = fCurrentMap.Map().right_command_key; 671 uint32 rightControl = fCurrentMap.Map().right_control_key; 672 673 // switch left side 674 fCurrentMap.Map().left_command_key = leftControl; 675 fCurrentMap.Map().left_control_key = leftCommand; 676 677 // switch right side 678 fCurrentMap.Map().right_command_key = rightControl; 679 fCurrentMap.Map().right_control_key = rightCommand; 680 681 fKeyboardLayoutView->SetKeymap(&fCurrentMap); 682 _UpdateButtons(); 683 } 684 685 686 //! Saves previous map to the "Key_map" file. 687 void 688 KeymapWindow::_RevertKeymap() 689 { 690 entry_ref ref; 691 _GetCurrentKeymap(ref); 692 693 status_t status = fPreviousMap.Save(ref); 694 if (status != B_OK) { 695 printf("error when saving keymap: %s", strerror(status)); 696 return; 697 } 698 699 fPreviousMap.Use(); 700 fCurrentMap.Load(ref); 701 fAppliedMap = fCurrentMap; 702 703 fKeyboardLayoutView->SetKeymap(&fCurrentMap); 704 705 fCurrentMapName = _GetActiveKeymapName(); 706 _SelectCurrentMap(); 707 } 708 709 710 //! Saves current map to the "Key_map" file. 711 void 712 KeymapWindow::_UseKeymap() 713 { 714 entry_ref ref; 715 _GetCurrentKeymap(ref); 716 717 status_t status = fCurrentMap.Save(ref); 718 if (status != B_OK) { 719 printf("error when saving : %s", strerror(status)); 720 return; 721 } 722 723 fCurrentMap.Use(); 724 fAppliedMap.Load(ref); 725 726 fCurrentMapName = _GetActiveKeymapName(); 727 _SelectCurrentMap(); 728 } 729 730 731 void 732 KeymapWindow::_FillSystemMaps() 733 { 734 BListItem *item; 735 while ((item = fSystemListView->RemoveItem(static_cast<int32>(0)))) 736 delete item; 737 738 // TODO: common keymaps! 739 BPath path; 740 if (find_directory(B_SYSTEM_DATA_DIRECTORY, &path) != B_OK) 741 return; 742 743 path.Append("Keymaps"); 744 745 BDirectory directory; 746 entry_ref ref; 747 748 if (directory.SetTo(path.Path()) == B_OK) { 749 while (directory.GetNextRef(&ref) == B_OK) { 750 fSystemListView->AddItem(new KeymapListItem(ref)); 751 } 752 } 753 } 754 755 756 void 757 KeymapWindow::_FillUserMaps() 758 { 759 BListItem* item; 760 while ((item = fUserListView->RemoveItem(static_cast<int32>(0)))) 761 delete item; 762 763 entry_ref ref; 764 _GetCurrentKeymap(ref); 765 766 fUserListView->AddItem(new KeymapListItem(ref, B_TRANSLATE("(Current)"))); 767 768 fCurrentMapName = _GetActiveKeymapName(); 769 770 BPath path; 771 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK) 772 return; 773 774 path.Append("Keymap"); 775 776 BDirectory directory; 777 if (directory.SetTo(path.Path()) == B_OK) { 778 while (directory.GetNextRef(&ref) == B_OK) { 779 fUserListView->AddItem(new KeymapListItem(ref)); 780 } 781 } 782 } 783 784 785 void 786 KeymapWindow::_SetListViewSize(BListView* listView) 787 { 788 float minWidth = 0; 789 for (int32 i = 0; i < listView->CountItems(); i++) { 790 BStringItem* item = (BStringItem*)listView->ItemAt(i); 791 float width = listView->StringWidth(item->Text()); 792 if (width > minWidth) 793 minWidth = width; 794 } 795 796 listView->SetExplicitMinSize(BSize(minWidth + 8, 32)); 797 } 798 799 800 status_t 801 KeymapWindow::_GetCurrentKeymap(entry_ref& ref) 802 { 803 BPath path; 804 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK) 805 return B_ERROR; 806 807 path.Append("Key_map"); 808 809 return get_ref_for_path(path.Path(), &ref); 810 } 811 812 813 BString 814 KeymapWindow::_GetActiveKeymapName() 815 { 816 BString mapName = kCurrentKeymapName; // safe default 817 818 entry_ref ref; 819 _GetCurrentKeymap(ref); 820 821 BNode node(&ref); 822 823 if (node.InitCheck() == B_OK) 824 node.ReadAttrString("keymap:name", &mapName); 825 826 return mapName; 827 } 828 829 830 bool 831 KeymapWindow::_SelectCurrentMap(BListView* view) 832 { 833 if (fCurrentMapName.Length() <= 0) 834 return false; 835 836 for (int32 i = 0; i < view->CountItems(); i++) { 837 BStringItem* current = dynamic_cast<BStringItem *>(view->ItemAt(i)); 838 if (current != NULL && fCurrentMapName == current->Text()) { 839 view->Select(i); 840 view->ScrollToSelection(); 841 return true; 842 } 843 } 844 845 return false; 846 } 847 848 849 void 850 KeymapWindow::_SelectCurrentMap() 851 { 852 if (!_SelectCurrentMap(fSystemListView) 853 && !_SelectCurrentMap(fUserListView)) { 854 // Select the "(Current)" entry if no name matches 855 fUserListView->Select(0L); 856 } 857 } 858 859 860 status_t 861 KeymapWindow::_GetSettings(BFile& file, int mode) const 862 { 863 BPath path; 864 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path, 865 (mode & O_ACCMODE) != O_RDONLY); 866 if (status != B_OK) 867 return status; 868 869 path.Append("Keymap settings"); 870 871 return file.SetTo(path.Path(), mode); 872 } 873 874 875 status_t 876 KeymapWindow::_LoadSettings(BRect& windowFrame, BString& keyboardLayout) 877 { 878 BScreen screen(this); 879 880 windowFrame.Set(-1, -1, 799, 329); 881 // See if we can use a larger default size 882 if (screen.Frame().Width() > 1200) { 883 windowFrame.right = 899; 884 windowFrame.bottom = 349; 885 } 886 887 keyboardLayout = ""; 888 889 BFile file; 890 status_t status = _GetSettings(file, B_READ_ONLY); 891 if (status == B_OK) { 892 BMessage settings; 893 status = settings.Unflatten(&file); 894 if (status == B_OK) { 895 BRect frame; 896 if (settings.FindRect("window frame", &frame) == B_OK) 897 windowFrame = frame; 898 899 settings.FindString("keyboard layout", &keyboardLayout); 900 } 901 } 902 903 if (!screen.Frame().Contains(windowFrame)) { 904 // Make sure the window is not larger than the screen 905 if (windowFrame.Width() > screen.Frame().Width()) 906 windowFrame.right = windowFrame.left + screen.Frame().Width(); 907 if (windowFrame.Height() > screen.Frame().Height()) 908 windowFrame.bottom = windowFrame.top + screen.Frame().Height(); 909 910 // Make sure the window is on screen (and center if it isn't) 911 if (windowFrame.left < screen.Frame().left 912 || windowFrame.right > screen.Frame().right 913 || windowFrame.top < screen.Frame().top 914 || windowFrame.bottom > screen.Frame().bottom) { 915 windowFrame.OffsetTo(BAlert::AlertPosition(windowFrame.Width(), 916 windowFrame.Height())); 917 } 918 } 919 920 return status; 921 } 922 923 924 status_t 925 KeymapWindow::_SaveSettings() const 926 { 927 BFile file; 928 status_t status 929 = _GetSettings(file, B_WRITE_ONLY | B_ERASE_FILE | B_CREATE_FILE); 930 if (status != B_OK) 931 return status; 932 933 BMessage settings('keym'); 934 settings.AddRect("window frame", Frame()); 935 936 BMenuItem* item = fLayoutMenu->FindMarked(); 937 entry_ref ref; 938 if (item != NULL && item->Message()->FindRef("ref", &ref) == B_OK) { 939 BPath path(&ref); 940 if (path.InitCheck() == B_OK) 941 settings.AddString("keyboard layout", path.Path()); 942 } 943 944 return settings.Flatten(&file); 945 } 946