2 * Copyright 2004-2015 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Alexandre Deckner, alex@zappotek.com
7 * Axel Dörfler, axeld@pinc-software.de
9 * John Scipione, jscipione@gmai.com
14 #include "KeymapWindow.h"
22 #include <Directory.h>
24 #include <FindDirectory.h>
25 #include <LayoutBuilder.h>
29 #include <MenuField.h>
32 #include <PopUpMenu.h>
34 #include <ScrollView.h>
35 #include <StringView.h>
36 #include <TextControl.h>
38 #include "KeyboardLayoutView.h"
39 #include "KeymapApplication.h"
40 #include "KeymapListItem.h"
41 #include "KeymapNames.h"
44 #undef B_TRANSLATION_CONTEXT
45 #define B_TRANSLATION_CONTEXT "Keymap window"
48 static const uint32 kMsgMenuFileOpen
= 'mMFO';
49 static const uint32 kMsgMenuFileSaveAs
= 'mMFA';
51 static const uint32 kChangeKeyboardLayout
= 'cKyL';
53 static const uint32 kMsgSwitchShortcuts
= 'swSc';
55 static const uint32 kMsgMenuFontChanged
= 'mMFC';
57 static const uint32 kMsgSystemMapSelected
= 'SmST';
58 static const uint32 kMsgUserMapSelected
= 'UmST';
60 static const uint32 kMsgDefaultKeymap
= 'Dflt';
61 static const uint32 kMsgRevertKeymap
= 'Rvrt';
62 static const uint32 kMsgKeymapUpdated
= 'kMup';
64 static const uint32 kMsgDeadKeyAcuteChanged
= 'dkAc';
65 static const uint32 kMsgDeadKeyCircumflexChanged
= 'dkCc';
66 static const uint32 kMsgDeadKeyDiaeresisChanged
= 'dkDc';
67 static const uint32 kMsgDeadKeyGraveChanged
= 'dkGc';
68 static const uint32 kMsgDeadKeyTildeChanged
= 'dkTc';
70 static const char* kDeadKeyTriggerNone
= "<none>";
72 static const char* kCurrentKeymapName
= "(Current)";
73 static const char* kDefaultKeymapName
= "US-International";
77 compare_key_list_items(const void* a
, const void* b
)
79 KeymapListItem
* item1
= *(KeymapListItem
**)a
;
80 KeymapListItem
* item2
= *(KeymapListItem
**)b
;
81 return BLocale::Default()->StringCompare(item1
->Text(), item2
->Text());
85 KeymapWindow::KeymapWindow()
87 BWindow(BRect(80, 50, 650, 300), B_TRANSLATE_SYSTEM_NAME("Keymap"),
88 B_TITLED_WINDOW
, B_ASYNCHRONOUS_CONTROLS
| B_AUTO_UPDATE_SIZE_LIMITS
)
90 fKeyboardLayoutView
= new KeyboardLayoutView("layout");
91 fKeyboardLayoutView
->SetKeymap(&fCurrentMap
);
92 fKeyboardLayoutView
->SetExplicitMinSize(BSize(B_SIZE_UNSET
, 192));
94 fTextControl
= new BTextControl(B_TRANSLATE("Sample and clipboard:"),
97 fSwitchShortcutsButton
= new BButton("switch", "",
98 new BMessage(kMsgSwitchShortcuts
));
101 BLayoutBuilder::Group
<>(this, B_VERTICAL
, 0)
103 .AddGroup(B_HORIZONTAL
)
104 .SetInsets(B_USE_WINDOW_SPACING
)
105 .Add(_CreateMapLists(), 0.25)
106 .AddGroup(B_VERTICAL
)
107 .Add(fKeyboardLayoutView
)
108 .AddGroup(B_HORIZONTAL
)
109 .Add(_CreateDeadKeyMenuField(), 0.0)
111 .Add(fSwitchShortcutsButton
)
115 .AddGroup(B_HORIZONTAL
)
117 .Add(fDefaultsButton
= new BButton("defaultsButton",
118 B_TRANSLATE("Defaults"),
119 new BMessage(kMsgDefaultKeymap
)))
120 .Add(fRevertButton
= new BButton("revertButton",
121 B_TRANSLATE("Revert"), new BMessage(kMsgRevertKeymap
)))
127 fKeyboardLayoutView
->SetTarget(fTextControl
->TextView());
128 fTextControl
->MakeFocus();
130 // Make sure the user keymap directory exists
132 find_directory(B_USER_SETTINGS_DIRECTORY
, &path
);
133 path
.Append("Keymap");
136 BEntry
entry(path
.Path(), true); // follow symlink
137 BDirectory
userKeymapsDir(&entry
);
138 if (userKeymapsDir
.InitCheck() != B_OK
139 && create_directory(path
.Path(), S_IRWXU
| S_IRWXG
| S_IRWXO
)
141 get_ref_for_path(path
.Path(), &ref
);
142 } else if (entry
.InitCheck() == B_OK
)
145 get_ref_for_path(path
.Path(), &ref
);
147 BMessenger
messenger(this);
148 fOpenPanel
= new BFilePanel(B_OPEN_PANEL
, &messenger
, &ref
,
149 B_FILE_NODE
, false, NULL
);
150 fSavePanel
= new BFilePanel(B_SAVE_PANEL
, &messenger
, &ref
,
151 B_FILE_NODE
, false, NULL
);
154 if (_LoadSettings(windowFrame
) == B_OK
) {
155 ResizeTo(windowFrame
.Width(), windowFrame
.Height());
156 MoveTo(windowFrame
.LeftTop());
161 // TODO: this might be a bug in the interface kit, but scrolling to
162 // selection does not correctly work unless the window is shown.
166 // Try and find the current map name in the two list views (if the name
170 KeymapListItem
* current
171 = static_cast<KeymapListItem
*>(fUserListView
->FirstItem());
173 fCurrentMap
.Load(current
->EntryRef());
174 fPreviousMap
= fCurrentMap
;
175 fAppliedMap
= fCurrentMap
;
176 fCurrentMap
.SetTarget(this, new BMessage(kMsgKeymapUpdated
));
180 _UpdateDeadKeyMenu();
181 _UpdateSwitchShortcutButton();
187 KeymapWindow::~KeymapWindow()
195 KeymapWindow::QuitRequested()
199 be_app
->PostMessage(B_QUIT_REQUESTED
);
205 KeymapWindow::MessageReceived(BMessage
* message
)
207 switch (message
->what
) {
209 case B_REFS_RECEIVED
:
213 while (message
->FindRef("refs", i
++, &ref
) == B_OK
) {
214 fCurrentMap
.Load(ref
);
215 fAppliedMap
= fCurrentMap
;
217 fKeyboardLayoutView
->SetKeymap(&fCurrentMap
);
218 fSystemListView
->DeselectAll();
219 fUserListView
->DeselectAll();
223 case B_SAVE_REQUESTED
:
227 if (message
->FindRef("directory", &ref
) == B_OK
228 && message
->FindString("name", &name
) == B_OK
) {
229 BDirectory
directory(&ref
);
230 BEntry
entry(&directory
, name
);
232 fCurrentMap
.SetName(name
);
233 fCurrentMap
.Save(ref
);
234 fAppliedMap
= fCurrentMap
;
236 fCurrentMapName
= name
;
242 case kMsgMenuFileOpen
:
245 case kMsgMenuFileSaveAs
:
248 case kMsgShowModifierKeysWindow
:
249 be_app
->PostMessage(kMsgShowModifierKeysWindow
);
252 case kChangeKeyboardLayout
:
256 if (message
->FindRef("ref", &ref
) == B_OK
)
259 _SetKeyboardLayout(path
.Path());
263 case kMsgSwitchShortcuts
:
264 _SwitchShortcutKeys();
267 case kMsgMenuFontChanged
:
269 BMenuItem
* item
= fFontMenu
->FindMarked();
272 font
.SetFamilyAndStyle(item
->Label(), NULL
);
273 fKeyboardLayoutView
->SetBaseFont(font
);
274 fTextControl
->TextView()->SetFontAndColor(&font
);
279 case kMsgSystemMapSelected
:
280 case kMsgUserMapSelected
:
283 BListView
* otherListView
;
285 if (message
->what
== kMsgSystemMapSelected
) {
286 listView
= fSystemListView
;
287 otherListView
= fUserListView
;
289 listView
= fUserListView
;
290 otherListView
= fSystemListView
;
293 int32 index
= listView
->CurrentSelection();
297 // Deselect item in other BListView
298 otherListView
->DeselectAll();
300 if (index
== 0 && listView
== fUserListView
) {
301 // we can safely ignore the "(Current)" item
306 = static_cast<KeymapListItem
*>(listView
->ItemAt(index
));
308 fCurrentMap
.Load(item
->EntryRef());
309 fAppliedMap
= fCurrentMap
;
310 fKeyboardLayoutView
->SetKeymap(&fCurrentMap
);
317 case kMsgDefaultKeymap
:
322 case kMsgRevertKeymap
:
327 case kMsgUpdateNormalKeys
:
330 if (message
->FindUInt32("keyCode", &keyCode
) != B_OK
)
334 if (message
->FindBool("unset", &unset
) == B_OK
&& unset
) {
335 fCurrentMap
.SetKey(keyCode
, modifiers(), 0, "", 0);
337 fKeyboardLayoutView
->SetKeymap(&fCurrentMap
);
342 case kMsgUpdateModifierKeys
:
346 if (message
->FindBool("unset", &unset
) != B_OK
)
349 if (message
->FindUInt32("left_shift_key", &keyCode
) == B_OK
) {
350 fCurrentMap
.SetModifier(unset
? 0x00 : keyCode
,
354 if (message
->FindUInt32("right_shift_key", &keyCode
) == B_OK
) {
355 fCurrentMap
.SetModifier(unset
? 0x00 : keyCode
,
359 if (message
->FindUInt32("left_control_key", &keyCode
) == B_OK
) {
360 fCurrentMap
.SetModifier(unset
? 0x00 : keyCode
,
364 if (message
->FindUInt32("right_control_key", &keyCode
) == B_OK
) {
365 fCurrentMap
.SetModifier(unset
? 0x00 : keyCode
,
366 B_RIGHT_CONTROL_KEY
);
369 if (message
->FindUInt32("left_option_key", &keyCode
) == B_OK
) {
370 fCurrentMap
.SetModifier(unset
? 0x00 : keyCode
,
374 if (message
->FindUInt32("right_option_key", &keyCode
) == B_OK
) {
375 fCurrentMap
.SetModifier(unset
? 0x00 : keyCode
,
379 if (message
->FindUInt32("left_command_key", &keyCode
) == B_OK
) {
380 fCurrentMap
.SetModifier(unset
? 0x00 : keyCode
,
384 if (message
->FindUInt32("right_command_key", &keyCode
) == B_OK
) {
385 fCurrentMap
.SetModifier(unset
? 0x00 : keyCode
,
386 B_RIGHT_COMMAND_KEY
);
389 if (message
->FindUInt32("menu_key", &keyCode
) == B_OK
)
390 fCurrentMap
.SetModifier(unset
? 0x00 : keyCode
, B_MENU_KEY
);
392 if (message
->FindUInt32("caps_key", &keyCode
) == B_OK
)
393 fCurrentMap
.SetModifier(unset
? 0x00 : keyCode
, B_CAPS_LOCK
);
395 if (message
->FindUInt32("num_key", &keyCode
) == B_OK
)
396 fCurrentMap
.SetModifier(unset
? 0x00 : keyCode
, B_NUM_LOCK
);
398 if (message
->FindUInt32("scroll_key", &keyCode
) == B_OK
)
399 fCurrentMap
.SetModifier(unset
? 0x00 : keyCode
, B_SCROLL_LOCK
);
402 fKeyboardLayoutView
->SetKeymap(&fCurrentMap
);
406 case kMsgKeymapUpdated
:
408 fSystemListView
->DeselectAll();
409 fUserListView
->Select(0L);
412 case kMsgDeadKeyAcuteChanged
:
414 BMenuItem
* item
= fAcuteMenu
->FindMarked();
416 const char* trigger
= item
->Label();
417 if (strcmp(trigger
, kDeadKeyTriggerNone
) == 0)
419 fCurrentMap
.SetDeadKeyTrigger(kDeadKeyAcute
, trigger
);
420 fKeyboardLayoutView
->Invalidate();
425 case kMsgDeadKeyCircumflexChanged
:
427 BMenuItem
* item
= fCircumflexMenu
->FindMarked();
429 const char* trigger
= item
->Label();
430 if (strcmp(trigger
, kDeadKeyTriggerNone
) == 0)
432 fCurrentMap
.SetDeadKeyTrigger(kDeadKeyCircumflex
, trigger
);
433 fKeyboardLayoutView
->Invalidate();
438 case kMsgDeadKeyDiaeresisChanged
:
440 BMenuItem
* item
= fDiaeresisMenu
->FindMarked();
442 const char* trigger
= item
->Label();
443 if (strcmp(trigger
, kDeadKeyTriggerNone
) == 0)
445 fCurrentMap
.SetDeadKeyTrigger(kDeadKeyDiaeresis
, trigger
);
446 fKeyboardLayoutView
->Invalidate();
451 case kMsgDeadKeyGraveChanged
:
453 BMenuItem
* item
= fGraveMenu
->FindMarked();
455 const char* trigger
= item
->Label();
456 if (strcmp(trigger
, kDeadKeyTriggerNone
) == 0)
458 fCurrentMap
.SetDeadKeyTrigger(kDeadKeyGrave
, trigger
);
459 fKeyboardLayoutView
->Invalidate();
464 case kMsgDeadKeyTildeChanged
:
466 BMenuItem
* item
= fTildeMenu
->FindMarked();
468 const char* trigger
= item
->Label();
469 if (strcmp(trigger
, kDeadKeyTriggerNone
) == 0)
471 fCurrentMap
.SetDeadKeyTrigger(kDeadKeyTilde
, trigger
);
472 fKeyboardLayoutView
->Invalidate();
478 BWindow::MessageReceived(message
);
485 KeymapWindow::_CreateMenu()
487 BMenuBar
* menuBar
= new BMenuBar(Bounds(), "menubar");
489 // Create the File menu
490 BMenu
* menu
= new BMenu(B_TRANSLATE("File"));
491 menu
->AddItem(new BMenuItem(B_TRANSLATE("Open" B_UTF8_ELLIPSIS
),
492 new BMessage(kMsgMenuFileOpen
), 'O'));
493 menu
->AddItem(new BMenuItem(B_TRANSLATE("Save as" B_UTF8_ELLIPSIS
),
494 new BMessage(kMsgMenuFileSaveAs
)));
495 menu
->AddSeparatorItem();
496 menu
->AddItem(new BMenuItem(
497 B_TRANSLATE("Set modifier keys" B_UTF8_ELLIPSIS
),
498 new BMessage(kMsgShowModifierKeysWindow
)));
499 menu
->AddSeparatorItem();
500 menu
->AddItem(new BMenuItem(B_TRANSLATE("Quit"),
501 new BMessage(B_QUIT_REQUESTED
), 'Q'));
502 menuBar
->AddItem(menu
);
504 // Create keyboard layout menu
505 fLayoutMenu
= new BMenu(B_TRANSLATE("Layout"));
506 _AddKeyboardLayouts(fLayoutMenu
);
507 menuBar
->AddItem(fLayoutMenu
);
509 // Create the Font menu
510 fFontMenu
= new BMenu(B_TRANSLATE("Font"));
511 fFontMenu
->SetRadioMode(true);
512 int32 numFamilies
= count_font_families();
513 font_family family
, currentFamily
;
514 font_style currentStyle
;
517 be_plain_font
->GetFamilyAndStyle(¤tFamily
, ¤tStyle
);
519 for (int32 i
= 0; i
< numFamilies
; i
++) {
520 if (get_font_family(i
, &family
, &flags
) == B_OK
) {
522 = new BMenuItem(family
, new BMessage(kMsgMenuFontChanged
));
523 fFontMenu
->AddItem(item
);
525 if (!strcmp(family
, currentFamily
))
526 item
->SetMarked(true);
529 menuBar
->AddItem(fFontMenu
);
536 KeymapWindow::_CreateDeadKeyMenuField()
538 BPopUpMenu
* deadKeyMenu
= new BPopUpMenu(B_TRANSLATE("Select dead keys"),
541 fAcuteMenu
= new BMenu(B_TRANSLATE("Acute trigger"));
542 fAcuteMenu
->SetRadioMode(true);
543 fAcuteMenu
->AddItem(new BMenuItem("\xC2\xB4",
544 new BMessage(kMsgDeadKeyAcuteChanged
)));
545 fAcuteMenu
->AddItem(new BMenuItem("'",
546 new BMessage(kMsgDeadKeyAcuteChanged
)));
547 fAcuteMenu
->AddItem(new BMenuItem(kDeadKeyTriggerNone
,
548 new BMessage(kMsgDeadKeyAcuteChanged
)));
549 deadKeyMenu
->AddItem(fAcuteMenu
);
551 fCircumflexMenu
= new BMenu(B_TRANSLATE("Circumflex trigger"));
552 fCircumflexMenu
->SetRadioMode(true);
553 fCircumflexMenu
->AddItem(new BMenuItem("^",
554 new BMessage(kMsgDeadKeyCircumflexChanged
)));
555 fCircumflexMenu
->AddItem(new BMenuItem(kDeadKeyTriggerNone
,
556 new BMessage(kMsgDeadKeyCircumflexChanged
)));
557 deadKeyMenu
->AddItem(fCircumflexMenu
);
559 fDiaeresisMenu
= new BMenu(B_TRANSLATE("Diaeresis trigger"));
560 fDiaeresisMenu
->SetRadioMode(true);
561 fDiaeresisMenu
->AddItem(new BMenuItem("\xC2\xA8",
562 new BMessage(kMsgDeadKeyDiaeresisChanged
)));
563 fDiaeresisMenu
->AddItem(new BMenuItem("\"",
564 new BMessage(kMsgDeadKeyDiaeresisChanged
)));
565 fDiaeresisMenu
->AddItem(new BMenuItem(kDeadKeyTriggerNone
,
566 new BMessage(kMsgDeadKeyDiaeresisChanged
)));
567 deadKeyMenu
->AddItem(fDiaeresisMenu
);
569 fGraveMenu
= new BMenu(B_TRANSLATE("Grave trigger"));
570 fGraveMenu
->SetRadioMode(true);
571 fGraveMenu
->AddItem(new BMenuItem("`",
572 new BMessage(kMsgDeadKeyGraveChanged
)));
573 fGraveMenu
->AddItem(new BMenuItem(kDeadKeyTriggerNone
,
574 new BMessage(kMsgDeadKeyGraveChanged
)));
575 deadKeyMenu
->AddItem(fGraveMenu
);
577 fTildeMenu
= new BMenu(B_TRANSLATE("Tilde trigger"));
578 fTildeMenu
->SetRadioMode(true);
579 fTildeMenu
->AddItem(new BMenuItem("~",
580 new BMessage(kMsgDeadKeyTildeChanged
)));
581 fTildeMenu
->AddItem(new BMenuItem(kDeadKeyTriggerNone
,
582 new BMessage(kMsgDeadKeyTildeChanged
)));
583 deadKeyMenu
->AddItem(fTildeMenu
);
585 return new BMenuField(NULL
, deadKeyMenu
);
590 KeymapWindow::_CreateMapLists()
593 fSystemListView
= new BListView("systemList");
594 fSystemListView
->SetSelectionMessage(new BMessage(kMsgSystemMapSelected
));
596 BScrollView
* systemScroller
= new BScrollView("systemScrollList",
597 fSystemListView
, 0, false, true);
600 fUserListView
= new BListView("userList");
601 fUserListView
->SetSelectionMessage(new BMessage(kMsgUserMapSelected
));
602 BScrollView
* userScroller
= new BScrollView("userScrollList",
603 fUserListView
, 0, false, true);
610 _SetListViewSize(fSystemListView
);
611 _SetListViewSize(fUserListView
);
613 return BLayoutBuilder::Group
<>(B_VERTICAL
)
614 .Add(new BStringView("system", B_TRANSLATE("System:")))
615 .Add(systemScroller
, 3)
616 .Add(new BStringView("user", B_TRANSLATE("User:")))
623 KeymapWindow::_AddKeyboardLayouts(BMenu
* menu
)
625 directory_which dataDirectories
[] = {
626 B_USER_NONPACKAGED_DATA_DIRECTORY
,
627 B_USER_DATA_DIRECTORY
,
628 B_SYSTEM_NONPACKAGED_DATA_DIRECTORY
,
629 B_SYSTEM_DATA_DIRECTORY
,
633 i
< sizeof(dataDirectories
) / sizeof(dataDirectories
[0]); i
++) {
635 if (find_directory(dataDirectories
[i
], &path
) != B_OK
)
638 if (path
.Append("KeyboardLayouts") != B_OK
)
641 BDirectory directory
;
642 if (directory
.SetTo(path
.Path()) == B_OK
)
643 _AddKeyboardLayoutMenu(menu
, directory
);
648 /*! Adds a menu populated with the keyboard layouts found in the passed
649 in directory to the passed in menu. Each subdirectory in the passed
650 in directory is added as a submenu recursively.
653 KeymapWindow::_AddKeyboardLayoutMenu(BMenu
* menu
, BDirectory directory
)
657 while (directory
.GetNextRef(&ref
) == B_OK
) {
658 if (menu
->FindItem(ref
.name
) != NULL
)
661 BDirectory subdirectory
;
662 subdirectory
.SetTo(&ref
);
663 if (subdirectory
.InitCheck() == B_OK
) {
664 BMenu
* submenu
= new BMenu(B_TRANSLATE_NOCOLLECT(ref
.name
));
666 _AddKeyboardLayoutMenu(submenu
, subdirectory
);
667 menu
->AddItem(submenu
, (int32
)0);
669 BMessage
* message
= new BMessage(kChangeKeyboardLayout
);
671 message
->AddRef("ref", &ref
);
672 menu
->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(ref
.name
),
679 /*! Sets the keyboard layout with the passed in path and marks the
680 corresponding menu item. If the path is not found in the menu this method
681 sets the default keyboard layout and marks the corresponding menu item.
684 KeymapWindow::_SetKeyboardLayout(const char* path
)
686 status_t status
= fKeyboardLayoutView
->GetKeyboardLayout()->Load(path
);
688 // mark a menu item (unmarking all others)
689 _MarkKeyboardLayoutItem(path
, fLayoutMenu
);
691 if (path
== NULL
|| path
[0] == '\0' || status
!= B_OK
) {
692 fKeyboardLayoutView
->GetKeyboardLayout()->SetDefault();
693 BMenuItem
* item
= fLayoutMenu
->FindItem(
694 fKeyboardLayoutView
->GetKeyboardLayout()->Name());
696 item
->SetMarked(true);
699 // Refresh currently set layout
700 fKeyboardLayoutView
->SetKeyboardLayout(
701 fKeyboardLayoutView
->GetKeyboardLayout());
707 /*! Marks a keyboard layout item by iterating through the menus recursively
708 searching for the menu item with the passed in path. This method always
709 iterates through all menu items and unmarks them. If no item with the
710 passed in path is found it is up to the caller to set the default keyboard
711 layout and mark item corresponding to the default keyboard layout path.
714 KeymapWindow::_MarkKeyboardLayoutItem(const char* path
, BMenu
* menu
)
716 BMenuItem
* item
= NULL
;
719 for (int32 i
= 0; i
< menu
->CountItems(); i
++) {
720 item
= menu
->ItemAt(i
);
724 // Unmark each item initially
725 item
->SetMarked(false);
727 BMenu
* submenu
= item
->Submenu();
729 _MarkKeyboardLayoutItem(path
, submenu
);
731 if (item
->Message()->FindRef("ref", &ref
) == B_OK
) {
732 BPath
layoutPath(&ref
);
733 if (path
!= NULL
&& path
[0] != '\0' && layoutPath
== path
) {
734 // Found it, mark the item
735 item
->SetMarked(true);
743 /*! Sets the label of the "Switch Shorcuts" button to make it more
744 descriptive what will happen when you press that button.
747 KeymapWindow::_UpdateSwitchShortcutButton()
749 const char* label
= B_TRANSLATE("Switch shortcut keys");
750 if (fCurrentMap
.KeyForModifier(B_LEFT_COMMAND_KEY
) == 0x5d
751 && fCurrentMap
.KeyForModifier(B_LEFT_CONTROL_KEY
) == 0x5c) {
752 label
= B_TRANSLATE("Switch shortcut keys to Windows/Linux mode");
753 } else if (fCurrentMap
.KeyForModifier(B_LEFT_COMMAND_KEY
) == 0x5c
754 && fCurrentMap
.KeyForModifier(B_LEFT_CONTROL_KEY
) == 0x5d) {
755 label
= B_TRANSLATE("Switch shortcut keys to Haiku mode");
758 fSwitchShortcutsButton
->SetLabel(label
);
762 /*! Marks the menu items corresponding to the dead key state of the current
766 KeymapWindow::_UpdateDeadKeyMenu()
769 fCurrentMap
.GetDeadKeyTrigger(kDeadKeyAcute
, trigger
);
770 if (!trigger
.Length())
771 trigger
= kDeadKeyTriggerNone
;
772 BMenuItem
* menuItem
= fAcuteMenu
->FindItem(trigger
.String());
774 menuItem
->SetMarked(true);
776 fCurrentMap
.GetDeadKeyTrigger(kDeadKeyCircumflex
, trigger
);
777 if (!trigger
.Length())
778 trigger
= kDeadKeyTriggerNone
;
779 menuItem
= fCircumflexMenu
->FindItem(trigger
.String());
781 menuItem
->SetMarked(true);
783 fCurrentMap
.GetDeadKeyTrigger(kDeadKeyDiaeresis
, trigger
);
784 if (!trigger
.Length())
785 trigger
= kDeadKeyTriggerNone
;
786 menuItem
= fDiaeresisMenu
->FindItem(trigger
.String());
788 menuItem
->SetMarked(true);
790 fCurrentMap
.GetDeadKeyTrigger(kDeadKeyGrave
, trigger
);
791 if (!trigger
.Length())
792 trigger
= kDeadKeyTriggerNone
;
793 menuItem
= fGraveMenu
->FindItem(trigger
.String());
795 menuItem
->SetMarked(true);
797 fCurrentMap
.GetDeadKeyTrigger(kDeadKeyTilde
, trigger
);
798 if (!trigger
.Length())
799 trigger
= kDeadKeyTriggerNone
;
800 menuItem
= fTildeMenu
->FindItem(trigger
.String());
802 menuItem
->SetMarked(true);
807 KeymapWindow::_UpdateButtons()
809 if (fCurrentMap
!= fAppliedMap
) {
810 fCurrentMap
.SetName(kCurrentKeymapName
);
814 fDefaultsButton
->SetEnabled(
815 fCurrentMapName
.ICompare(kDefaultKeymapName
) != 0);
816 fRevertButton
->SetEnabled(fCurrentMap
!= fPreviousMap
);
818 _UpdateDeadKeyMenu();
819 _UpdateSwitchShortcutButton();
824 KeymapWindow::_SwitchShortcutKeys()
826 uint32 leftCommand
= fCurrentMap
.Map().left_command_key
;
827 uint32 leftControl
= fCurrentMap
.Map().left_control_key
;
828 uint32 rightCommand
= fCurrentMap
.Map().right_command_key
;
829 uint32 rightControl
= fCurrentMap
.Map().right_control_key
;
832 fCurrentMap
.Map().left_command_key
= leftControl
;
833 fCurrentMap
.Map().left_control_key
= leftCommand
;
836 fCurrentMap
.Map().right_command_key
= rightControl
;
837 fCurrentMap
.Map().right_control_key
= rightCommand
;
839 fKeyboardLayoutView
->SetKeymap(&fCurrentMap
);
844 //! Restores the default keymap.
846 KeymapWindow::_DefaultKeymap()
848 fCurrentMap
.RestoreSystemDefault();
849 fAppliedMap
= fCurrentMap
;
851 fKeyboardLayoutView
->SetKeymap(&fCurrentMap
);
853 fCurrentMapName
= _GetActiveKeymapName();
858 //! Saves previous map to the "Key_map" file.
860 KeymapWindow::_RevertKeymap()
863 _GetCurrentKeymap(ref
);
865 status_t status
= fPreviousMap
.Save(ref
);
866 if (status
!= B_OK
) {
867 printf("error when saving keymap: %s", strerror(status
));
872 fCurrentMap
.Load(ref
);
873 fAppliedMap
= fCurrentMap
;
875 fKeyboardLayoutView
->SetKeymap(&fCurrentMap
);
877 fCurrentMapName
= _GetActiveKeymapName();
882 //! Saves current map to the "Key_map" file.
884 KeymapWindow::_UseKeymap()
887 _GetCurrentKeymap(ref
);
889 status_t status
= fCurrentMap
.Save(ref
);
890 if (status
!= B_OK
) {
891 printf("error when saving : %s", strerror(status
));
896 fAppliedMap
.Load(ref
);
898 fCurrentMapName
= _GetActiveKeymapName();
904 KeymapWindow::_FillSystemMaps()
907 while ((item
= fSystemListView
->RemoveItem(static_cast<int32
>(0))))
910 // TODO: common keymaps!
912 if (find_directory(B_SYSTEM_DATA_DIRECTORY
, &path
) != B_OK
)
915 path
.Append("Keymaps");
917 BDirectory directory
;
920 if (directory
.SetTo(path
.Path()) == B_OK
) {
921 while (directory
.GetNextRef(&ref
) == B_OK
) {
922 fSystemListView
->AddItem(
923 new KeymapListItem(ref
, B_TRANSLATE_NOCOLLECT(ref
.name
)));
927 fSystemListView
->SortItems(&compare_key_list_items
);
932 KeymapWindow::_FillUserMaps()
935 while ((item
= fUserListView
->RemoveItem(static_cast<int32
>(0))))
939 _GetCurrentKeymap(ref
);
941 fUserListView
->AddItem(new KeymapListItem(ref
, B_TRANSLATE("(Current)")));
943 fCurrentMapName
= _GetActiveKeymapName();
946 if (find_directory(B_USER_SETTINGS_DIRECTORY
, &path
) != B_OK
)
949 path
.Append("Keymap");
951 BDirectory directory
;
952 if (directory
.SetTo(path
.Path()) == B_OK
) {
953 while (directory
.GetNextRef(&ref
) == B_OK
) {
954 fUserListView
->AddItem(new KeymapListItem(ref
));
958 fUserListView
->SortItems(&compare_key_list_items
);
963 KeymapWindow::_SetListViewSize(BListView
* listView
)
966 for (int32 i
= 0; i
< listView
->CountItems(); i
++) {
967 BStringItem
* item
= (BStringItem
*)listView
->ItemAt(i
);
968 float width
= listView
->StringWidth(item
->Text());
969 if (width
> minWidth
)
973 listView
->SetExplicitMinSize(BSize(minWidth
+ 8, 32));
978 KeymapWindow::_GetCurrentKeymap(entry_ref
& ref
)
981 if (find_directory(B_USER_SETTINGS_DIRECTORY
, &path
) != B_OK
)
984 path
.Append("Key_map");
986 return get_ref_for_path(path
.Path(), &ref
);
991 KeymapWindow::_GetActiveKeymapName()
993 BString mapName
= kCurrentKeymapName
;
997 _GetCurrentKeymap(ref
);
1001 if (node
.InitCheck() == B_OK
)
1002 node
.ReadAttrString("keymap:name", &mapName
);
1009 KeymapWindow::_SelectCurrentMap(BListView
* view
)
1011 if (fCurrentMapName
.Length() <= 0)
1014 for (int32 i
= 0; i
< view
->CountItems(); i
++) {
1015 BStringItem
* current
= dynamic_cast<BStringItem
*>(view
->ItemAt(i
));
1016 if (current
!= NULL
&& fCurrentMapName
== current
->Text()) {
1018 view
->ScrollToSelection();
1028 KeymapWindow::_SelectCurrentMap()
1030 if (!_SelectCurrentMap(fSystemListView
)
1031 && !_SelectCurrentMap(fUserListView
)) {
1032 // Select the "(Current)" entry if no name matches
1033 fUserListView
->Select(0L);
1039 KeymapWindow::_GetSettings(BFile
& file
, int mode
) const
1042 status_t status
= find_directory(B_USER_SETTINGS_DIRECTORY
, &path
,
1043 (mode
& O_ACCMODE
) != O_RDONLY
);
1047 path
.Append("Keymap settings");
1049 return file
.SetTo(path
.Path(), mode
);
1054 KeymapWindow::_LoadSettings(BRect
& windowFrame
)
1056 BScreen
screen(this);
1058 windowFrame
.Set(-1, -1, 669, 357);
1059 // See if we can use a larger default size
1060 if (screen
.Frame().Width() > 1200) {
1061 windowFrame
.right
= 899;
1062 windowFrame
.bottom
= 349;
1064 float scaling
= be_plain_font
->Size() / 12.0f
;
1065 windowFrame
.right
*= scaling
;
1066 windowFrame
.bottom
*= scaling
;
1069 status_t status
= _GetSettings(file
, B_READ_ONLY
);
1070 if (status
== B_OK
) {
1072 status
= settings
.Unflatten(&file
);
1073 if (status
== B_OK
) {
1075 status
= settings
.FindRect("window frame", &frame
);
1077 windowFrame
= frame
;
1079 const char* layoutPath
;
1080 if (settings
.FindString("keyboard layout", &layoutPath
) == B_OK
)
1081 _SetKeyboardLayout(layoutPath
);
1090 KeymapWindow::_SaveSettings()
1094 = _GetSettings(file
, B_WRITE_ONLY
| B_ERASE_FILE
| B_CREATE_FILE
);
1098 BMessage
settings('keym');
1099 settings
.AddRect("window frame", Frame());
1101 BPath path
= _GetMarkedKeyboardLayoutPath(fLayoutMenu
);
1102 if (path
.InitCheck() == B_OK
)
1103 settings
.AddString("keyboard layout", path
.Path());
1105 return settings
.Flatten(&file
);
1109 /*! Gets the path of the currently marked keyboard layout item
1110 by searching through each of the menus recursively until
1111 a marked item is found.
1114 KeymapWindow::_GetMarkedKeyboardLayoutPath(BMenu
* menu
)
1117 BMenuItem
* item
= NULL
;
1120 for (int32 i
= 0; i
< menu
->CountItems(); i
++) {
1121 item
= menu
->ItemAt(i
);
1125 BMenu
* submenu
= item
->Submenu();
1126 if (submenu
!= NULL
) {
1127 path
= _GetMarkedKeyboardLayoutPath(submenu
);
1128 if (path
.InitCheck() == B_OK
)
1131 if (item
->IsMarked()
1132 && item
->Message()->FindRef("ref", &ref
) == B_OK
) {