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