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