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