#include "KeymapWindow.h"
#include <string.h>
#include <stdio.h>
#include <Alert.h>
#include <Button.h>
#include <Catalog.h>
#include <Directory.h>
#include <File.h>
#include <FindDirectory.h>
#include <LayoutBuilder.h>
#include <ListView.h>
#include <Locale.h>
#include <MenuBar.h>
#include <MenuField.h>
#include <MenuItem.h>
#include <Path.h>
#include <PopUpMenu.h>
#include <Screen.h>
#include <ScrollView.h>
#include <StringView.h>
#include <TextControl.h>
#include "KeyboardLayoutNames.h"
#include "KeyboardLayoutView.h"
#include "KeymapApplication.h"
#include "KeymapListItem.h"
#include "KeymapNames.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Keymap window"
static const uint32 kMsgMenuFileOpen = 'mMFO';
static const uint32 kMsgMenuFileSaveAs = 'mMFA';
static const uint32 kChangeKeyboardLayout = 'cKyL';
static const uint32 kMsgSwitchShortcuts = 'swSc';
static const uint32 kMsgMenuFontChanged = 'mMFC';
static const uint32 kMsgSystemMapSelected = 'SmST';
static const uint32 kMsgUserMapSelected = 'UmST';
static const uint32 kMsgDefaultKeymap = 'Dflt';
static const uint32 kMsgRevertKeymap = 'Rvrt';
static const uint32 kMsgKeymapUpdated = 'kMup';
static const uint32 kMsgDeadKeyAcuteChanged = 'dkAc';
static const uint32 kMsgDeadKeyCircumflexChanged = 'dkCc';
static const uint32 kMsgDeadKeyDiaeresisChanged = 'dkDc';
static const uint32 kMsgDeadKeyGraveChanged = 'dkGc';
static const uint32 kMsgDeadKeyTildeChanged = 'dkTc';
static const char* kDeadKeyTriggerNone = "<none>";
static const char* kCurrentKeymapName = "(Current)";
static const char* kDefaultKeymapName = "US-International";
static const float kDefaultHeight = 440;
static const float kDefaultWidth = 1000;
static int
compare_key_list_items(const void* a, const void* b)
{
KeymapListItem* item1 = *(KeymapListItem**)a;
KeymapListItem* item2 = *(KeymapListItem**)b;
return BLocale::Default()->StringCompare(item1->Text(), item2->Text());
}
KeymapWindow::KeymapWindow()
:
BWindow(BRect(80, 50, kDefaultWidth, kDefaultHeight),
B_TRANSLATE_SYSTEM_NAME("Keymap"), B_TITLED_WINDOW,
B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS)
{
BScreen screen(this);
display_mode mode;
status_t status = screen.GetMode(&mode);
if(status == B_OK && (mode.virtual_width <= kDefaultWidth
|| mode.virtual_height <= kDefaultHeight)) {
float width = mode.virtual_width - 64;
ResizeTo(mode.virtual_width - 64,
width * kDefaultHeight / kDefaultWidth);
}
fKeyboardLayoutView = new KeyboardLayoutView("layout");
fKeyboardLayoutView->SetKeymap(&fCurrentMap);
fKeyboardLayoutView->SetExplicitMinSize(BSize(B_SIZE_UNSET, 192));
fTextControl = new BTextControl(B_TRANSLATE("Sample and clipboard:"),
"", NULL);
fSwitchShortcutsButton = new BButton("switch", "",
new BMessage(kMsgSwitchShortcuts));
BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
.Add(_CreateMenu())
.AddGroup(B_HORIZONTAL)
.SetInsets(B_USE_WINDOW_SPACING)
.Add(_CreateMapLists(), 0.25)
.AddGroup(B_VERTICAL)
.Add(fKeyboardLayoutView)
.AddGroup(B_HORIZONTAL)
.Add(_CreateDeadKeyMenuField(), 0.0)
.AddGlue()
.Add(fSwitchShortcutsButton)
.End()
.Add(fTextControl)
.AddGlue(0.0)
.AddGroup(B_HORIZONTAL)
.AddGlue()
.Add(fDefaultsButton = new BButton("defaultsButton",
B_TRANSLATE("Defaults"),
new BMessage(kMsgDefaultKeymap)))
.Add(fRevertButton = new BButton("revertButton",
B_TRANSLATE("Revert"), new BMessage(kMsgRevertKeymap)))
.End()
.End()
.End()
.End();
fKeyboardLayoutView->SetTarget(fTextControl->TextView());
fTextControl->MakeFocus();
BPath path;
find_directory(B_USER_SETTINGS_DIRECTORY, &path);
path.Append("Keymap");
entry_ref ref;
BEntry entry(path.Path(), true);
BDirectory userKeymapsDir(&entry);
if (userKeymapsDir.InitCheck() != B_OK
&& create_directory(path.Path(), S_IRWXU | S_IRWXG | S_IRWXO)
== B_OK) {
get_ref_for_path(path.Path(), &ref);
} else if (entry.InitCheck() == B_OK)
entry.GetRef(&ref);
else
get_ref_for_path(path.Path(), &ref);
BMessenger messenger(this);
fOpenPanel = new BFilePanel(B_OPEN_PANEL, &messenger, &ref,
B_FILE_NODE, false, NULL);
fSavePanel = new BFilePanel(B_SAVE_PANEL, &messenger, &ref,
B_FILE_NODE, false, NULL);
BRect windowFrame;
if (_LoadSettings(windowFrame) == B_OK) {
ResizeTo(windowFrame.Width(), windowFrame.Height());
MoveTo(windowFrame.LeftTop());
MoveOnScreen();
} else
CenterOnScreen();
Show();
Lock();
_SelectCurrentMap();
KeymapListItem* current
= static_cast<KeymapListItem*>(fUserListView->FirstItem());
fCurrentMap.Load(current->EntryRef());
fPreviousMap = fCurrentMap;
fAppliedMap = fCurrentMap;
fCurrentMap.SetTarget(this, new BMessage(kMsgKeymapUpdated));
_UpdateButtons();
_UpdateDeadKeyMenu();
_UpdateSwitchShortcutButton();
Unlock();
}
KeymapWindow::~KeymapWindow()
{
delete fOpenPanel;
delete fSavePanel;
}
bool
KeymapWindow::QuitRequested()
{
_SaveSettings();
be_app->PostMessage(B_QUIT_REQUESTED);
return true;
}
void
KeymapWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case B_SIMPLE_DATA:
case B_REFS_RECEIVED:
{
entry_ref ref;
int32 i = 0;
while (message->FindRef("refs", i++, &ref) == B_OK) {
fCurrentMap.Load(ref);
fAppliedMap = fCurrentMap;
}
fKeyboardLayoutView->SetKeymap(&fCurrentMap);
fSystemListView->DeselectAll();
fUserListView->DeselectAll();
break;
}
case B_SAVE_REQUESTED:
{
entry_ref ref;
const char* name;
if (message->FindRef("directory", &ref) == B_OK
&& message->FindString("name", &name) == B_OK) {
BDirectory directory(&ref);
BEntry entry(&directory, name);
entry.GetRef(&ref);
fCurrentMap.SetName(name);
fCurrentMap.Save(ref);
fAppliedMap = fCurrentMap;
_FillUserMaps();
fCurrentMapName = name;
_SelectCurrentMap();
}
break;
}
case kMsgMenuFileOpen:
fOpenPanel->Show();
break;
case kMsgMenuFileSaveAs:
fSavePanel->Show();
break;
case kMsgShowModifierKeysWindow:
be_app->PostMessage(kMsgShowModifierKeysWindow);
break;
case kChangeKeyboardLayout:
{
entry_ref ref;
BPath path;
if (message->FindRef("ref", &ref) == B_OK)
path.SetTo(&ref);
_SetKeyboardLayout(path.Path());
break;
}
case kMsgSwitchShortcuts:
_SwitchShortcutKeys();
break;
case kMsgMenuFontChanged:
{
BMenuItem* item = fFontMenu->FindMarked();
if (item != NULL) {
BFont font;
font.SetFamilyAndStyle(item->Label(), NULL);
fKeyboardLayoutView->SetBaseFont(font);
fTextControl->TextView()->SetFontAndColor(&font);
}
break;
}
case kMsgSystemMapSelected:
case kMsgUserMapSelected:
{
BListView* listView;
BListView* otherListView;
if (message->what == kMsgSystemMapSelected) {
listView = fSystemListView;
otherListView = fUserListView;
} else {
listView = fUserListView;
otherListView = fSystemListView;
}
int32 index = listView->CurrentSelection();
if (index < 0)
break;
otherListView->DeselectAll();
if (index == 0 && listView == fUserListView) {
break;
}
KeymapListItem* item
= static_cast<KeymapListItem*>(listView->ItemAt(index));
if (item != NULL) {
status_t status = fCurrentMap.Load(item->EntryRef());
if (status != B_OK) {
listView->RemoveItem(item);
break;
}
fAppliedMap = fCurrentMap;
fKeyboardLayoutView->SetKeymap(&fCurrentMap);
_UseKeymap();
_UpdateButtons();
}
break;
}
case kMsgDefaultKeymap:
_DefaultKeymap();
_UpdateButtons();
break;
case kMsgRevertKeymap:
_RevertKeymap();
_UpdateButtons();
break;
case kMsgUpdateNormalKeys:
{
uint32 keyCode;
if (message->FindUInt32("keyCode", &keyCode) != B_OK)
break;
bool unset;
if (message->FindBool("unset", &unset) == B_OK && unset) {
fCurrentMap.SetKey(keyCode, modifiers(), 0, "", 0);
_UpdateButtons();
fKeyboardLayoutView->SetKeymap(&fCurrentMap);
}
break;
}
case kMsgUpdateModifierKeys:
{
uint32 keyCode;
bool unset;
if (message->FindBool("unset", &unset) != B_OK)
unset = false;
if (message->FindUInt32("left_shift_key", &keyCode) == B_OK) {
fCurrentMap.SetModifier(unset ? 0x00 : keyCode,
B_LEFT_SHIFT_KEY);
}
if (message->FindUInt32("right_shift_key", &keyCode) == B_OK) {
fCurrentMap.SetModifier(unset ? 0x00 : keyCode,
B_RIGHT_SHIFT_KEY);
}
if (message->FindUInt32("left_control_key", &keyCode) == B_OK) {
fCurrentMap.SetModifier(unset ? 0x00 : keyCode,
B_LEFT_CONTROL_KEY);
}
if (message->FindUInt32("right_control_key", &keyCode) == B_OK) {
fCurrentMap.SetModifier(unset ? 0x00 : keyCode,
B_RIGHT_CONTROL_KEY);
}
if (message->FindUInt32("left_option_key", &keyCode) == B_OK) {
fCurrentMap.SetModifier(unset ? 0x00 : keyCode,
B_LEFT_OPTION_KEY);
}
if (message->FindUInt32("right_option_key", &keyCode) == B_OK) {
fCurrentMap.SetModifier(unset ? 0x00 : keyCode,
B_RIGHT_OPTION_KEY);
}
if (message->FindUInt32("left_command_key", &keyCode) == B_OK) {
fCurrentMap.SetModifier(unset ? 0x00 : keyCode,
B_LEFT_COMMAND_KEY);
}
if (message->FindUInt32("right_command_key", &keyCode) == B_OK) {
fCurrentMap.SetModifier(unset ? 0x00 : keyCode,
B_RIGHT_COMMAND_KEY);
}
if (message->FindUInt32("menu_key", &keyCode) == B_OK)
fCurrentMap.SetModifier(unset ? 0x00 : keyCode, B_MENU_KEY);
if (message->FindUInt32("caps_key", &keyCode) == B_OK)
fCurrentMap.SetModifier(unset ? 0x00 : keyCode, B_CAPS_LOCK);
if (message->FindUInt32("num_key", &keyCode) == B_OK)
fCurrentMap.SetModifier(unset ? 0x00 : keyCode, B_NUM_LOCK);
if (message->FindUInt32("scroll_key", &keyCode) == B_OK)
fCurrentMap.SetModifier(unset ? 0x00 : keyCode, B_SCROLL_LOCK);
_UpdateButtons();
fKeyboardLayoutView->SetKeymap(&fCurrentMap);
break;
}
case kMsgKeymapUpdated:
_UpdateButtons();
fSystemListView->DeselectAll();
fUserListView->Select(0L);
break;
case kMsgDeadKeyAcuteChanged:
{
BMenuItem* item = fAcuteMenu->FindMarked();
if (item != NULL) {
const char* trigger = item->Label();
if (strcmp(trigger, kDeadKeyTriggerNone) == 0)
trigger = NULL;
fCurrentMap.SetDeadKeyTrigger(kDeadKeyAcute, trigger);
fKeyboardLayoutView->Invalidate();
}
break;
}
case kMsgDeadKeyCircumflexChanged:
{
BMenuItem* item = fCircumflexMenu->FindMarked();
if (item != NULL) {
const char* trigger = item->Label();
if (strcmp(trigger, kDeadKeyTriggerNone) == 0)
trigger = NULL;
fCurrentMap.SetDeadKeyTrigger(kDeadKeyCircumflex, trigger);
fKeyboardLayoutView->Invalidate();
}
break;
}
case kMsgDeadKeyDiaeresisChanged:
{
BMenuItem* item = fDiaeresisMenu->FindMarked();
if (item != NULL) {
const char* trigger = item->Label();
if (strcmp(trigger, kDeadKeyTriggerNone) == 0)
trigger = NULL;
fCurrentMap.SetDeadKeyTrigger(kDeadKeyDiaeresis, trigger);
fKeyboardLayoutView->Invalidate();
}
break;
}
case kMsgDeadKeyGraveChanged:
{
BMenuItem* item = fGraveMenu->FindMarked();
if (item != NULL) {
const char* trigger = item->Label();
if (strcmp(trigger, kDeadKeyTriggerNone) == 0)
trigger = NULL;
fCurrentMap.SetDeadKeyTrigger(kDeadKeyGrave, trigger);
fKeyboardLayoutView->Invalidate();
}
break;
}
case kMsgDeadKeyTildeChanged:
{
BMenuItem* item = fTildeMenu->FindMarked();
if (item != NULL) {
const char* trigger = item->Label();
if (strcmp(trigger, kDeadKeyTriggerNone) == 0)
trigger = NULL;
fCurrentMap.SetDeadKeyTrigger(kDeadKeyTilde, trigger);
fKeyboardLayoutView->Invalidate();
}
break;
}
default:
BWindow::MessageReceived(message);
break;
}
}
BMenuBar*
KeymapWindow::_CreateMenu()
{
BMenuBar* menuBar = new BMenuBar(Bounds(), "menubar");
BMenu* menu = new BMenu(B_TRANSLATE("File"));
menu->AddItem(new BMenuItem(B_TRANSLATE("Open" B_UTF8_ELLIPSIS),
new BMessage(kMsgMenuFileOpen), 'O'));
menu->AddItem(new BMenuItem(B_TRANSLATE("Save as" B_UTF8_ELLIPSIS),
new BMessage(kMsgMenuFileSaveAs)));
menu->AddSeparatorItem();
menu->AddItem(new BMenuItem(
B_TRANSLATE("Set modifier keys" B_UTF8_ELLIPSIS),
new BMessage(kMsgShowModifierKeysWindow)));
menu->AddSeparatorItem();
menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"),
new BMessage(B_QUIT_REQUESTED), 'Q'));
menuBar->AddItem(menu);
fLayoutMenu = new BMenu(B_TRANSLATE("Layout"));
_AddKeyboardLayouts(fLayoutMenu);
menuBar->AddItem(fLayoutMenu);
fFontMenu = new BMenu(B_TRANSLATE("Font"));
fFontMenu->SetRadioMode(true);
int32 numFamilies = count_font_families();
font_family family, currentFamily;
font_style currentStyle;
uint32 flags;
be_plain_font->GetFamilyAndStyle(¤tFamily, ¤tStyle);
for (int32 i = 0; i < numFamilies; i++) {
if (get_font_family(i, &family, &flags) == B_OK) {
BMenuItem* item
= new BMenuItem(family, new BMessage(kMsgMenuFontChanged));
fFontMenu->AddItem(item);
if (!strcmp(family, currentFamily))
item->SetMarked(true);
}
}
menuBar->AddItem(fFontMenu);
return menuBar;
}
BMenuField*
KeymapWindow::_CreateDeadKeyMenuField()
{
BPopUpMenu* deadKeyMenu = new BPopUpMenu(B_TRANSLATE("Select dead keys"),
false, false);
fAcuteMenu = new BMenu(B_TRANSLATE("Acute trigger"));
fAcuteMenu->SetRadioMode(true);
fAcuteMenu->AddItem(new BMenuItem("\xC2\xB4",
new BMessage(kMsgDeadKeyAcuteChanged)));
fAcuteMenu->AddItem(new BMenuItem("'",
new BMessage(kMsgDeadKeyAcuteChanged)));
fAcuteMenu->AddItem(new BMenuItem(kDeadKeyTriggerNone,
new BMessage(kMsgDeadKeyAcuteChanged)));
deadKeyMenu->AddItem(fAcuteMenu);
fCircumflexMenu = new BMenu(B_TRANSLATE("Circumflex trigger"));
fCircumflexMenu->SetRadioMode(true);
fCircumflexMenu->AddItem(new BMenuItem("^",
new BMessage(kMsgDeadKeyCircumflexChanged)));
fCircumflexMenu->AddItem(new BMenuItem(kDeadKeyTriggerNone,
new BMessage(kMsgDeadKeyCircumflexChanged)));
deadKeyMenu->AddItem(fCircumflexMenu);
fDiaeresisMenu = new BMenu(B_TRANSLATE("Diaeresis trigger"));
fDiaeresisMenu->SetRadioMode(true);
fDiaeresisMenu->AddItem(new BMenuItem("\xC2\xA8",
new BMessage(kMsgDeadKeyDiaeresisChanged)));
fDiaeresisMenu->AddItem(new BMenuItem("\"",
new BMessage(kMsgDeadKeyDiaeresisChanged)));
fDiaeresisMenu->AddItem(new BMenuItem(kDeadKeyTriggerNone,
new BMessage(kMsgDeadKeyDiaeresisChanged)));
deadKeyMenu->AddItem(fDiaeresisMenu);
fGraveMenu = new BMenu(B_TRANSLATE("Grave trigger"));
fGraveMenu->SetRadioMode(true);
fGraveMenu->AddItem(new BMenuItem("`",
new BMessage(kMsgDeadKeyGraveChanged)));
fGraveMenu->AddItem(new BMenuItem(kDeadKeyTriggerNone,
new BMessage(kMsgDeadKeyGraveChanged)));
deadKeyMenu->AddItem(fGraveMenu);
fTildeMenu = new BMenu(B_TRANSLATE("Tilde trigger"));
fTildeMenu->SetRadioMode(true);
fTildeMenu->AddItem(new BMenuItem("~",
new BMessage(kMsgDeadKeyTildeChanged)));
fTildeMenu->AddItem(new BMenuItem(kDeadKeyTriggerNone,
new BMessage(kMsgDeadKeyTildeChanged)));
deadKeyMenu->AddItem(fTildeMenu);
return new BMenuField(NULL, deadKeyMenu);
}
BView*
KeymapWindow::_CreateMapLists()
{
fSystemListView = new BListView("systemList");
fSystemListView->SetSelectionMessage(new BMessage(kMsgSystemMapSelected));
BScrollView* systemScroller = new BScrollView("systemScrollList",
fSystemListView, 0, false, true);
fUserListView = new BListView("userList");
fUserListView->SetSelectionMessage(new BMessage(kMsgUserMapSelected));
BScrollView* userScroller = new BScrollView("userScrollList",
fUserListView, 0, false, true);
_FillSystemMaps();
_FillUserMaps();
_SetListViewSize(fSystemListView);
_SetListViewSize(fUserListView);
return BLayoutBuilder::Group<>(B_VERTICAL)
.Add(new BStringView("system", B_TRANSLATE("System:")))
.Add(systemScroller, 3)
.Add(new BStringView("user", B_TRANSLATE("User:")))
.Add(userScroller)
.View();
}
void
KeymapWindow::_AddKeyboardLayouts(BMenu* menu)
{
directory_which dataDirectories[] = {
B_USER_NONPACKAGED_DATA_DIRECTORY,
B_USER_DATA_DIRECTORY,
B_SYSTEM_NONPACKAGED_DATA_DIRECTORY,
B_SYSTEM_DATA_DIRECTORY,
};
for (uint32 i = 0;
i < sizeof(dataDirectories) / sizeof(dataDirectories[0]); i++) {
BPath path;
if (find_directory(dataDirectories[i], &path) != B_OK)
continue;
if (path.Append("KeyboardLayouts") != B_OK)
continue;
BDirectory directory;
if (directory.SetTo(path.Path()) == B_OK)
_AddKeyboardLayoutMenu(menu, directory);
}
}
void
KeymapWindow::_AddKeyboardLayoutMenu(BMenu* menu, BDirectory directory)
{
entry_ref ref;
while (directory.GetNextRef(&ref) == B_OK) {
if (menu->FindItem(ref.name) != NULL)
continue;
BDirectory subdirectory;
subdirectory.SetTo(&ref);
if (subdirectory.InitCheck() == B_OK) {
BMenu* submenu = new BMenu(B_TRANSLATE_NOCOLLECT_ALL((ref.name),
"KeyboardLayoutNames", NULL));
_AddKeyboardLayoutMenu(submenu, subdirectory);
menu->AddItem(submenu, (int32)0);
} else {
BMessage* message = new BMessage(kChangeKeyboardLayout);
message->AddRef("ref", &ref);
menu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT_ALL((ref.name),
"KeyboardLayoutNames", NULL), message), (int32)0);
}
}
}
status_t
KeymapWindow::_SetKeyboardLayout(const char* path)
{
status_t status = fKeyboardLayoutView->GetKeyboardLayout()->Load(path);
_MarkKeyboardLayoutItem(path, fLayoutMenu);
if (path == NULL || path[0] == '\0' || status != B_OK) {
fKeyboardLayoutView->GetKeyboardLayout()->SetDefault();
BMenuItem* item = fLayoutMenu->FindItem(
fKeyboardLayoutView->GetKeyboardLayout()->Name());
if (item != NULL)
item->SetMarked(true);
}
fKeyboardLayoutView->SetKeyboardLayout(
fKeyboardLayoutView->GetKeyboardLayout());
return status;
}
void
KeymapWindow::_MarkKeyboardLayoutItem(const char* path, BMenu* menu)
{
BMenuItem* item = NULL;
entry_ref ref;
for (int32 i = 0; i < menu->CountItems(); i++) {
item = menu->ItemAt(i);
if (item == NULL)
continue;
item->SetMarked(false);
BMenu* submenu = item->Submenu();
if (submenu != NULL)
_MarkKeyboardLayoutItem(path, submenu);
else {
if (item->Message()->FindRef("ref", &ref) == B_OK) {
BPath layoutPath(&ref);
if (path != NULL && path[0] != '\0' && layoutPath == path) {
item->SetMarked(true);
}
}
}
}
}
void
KeymapWindow::_UpdateSwitchShortcutButton()
{
const char* label = B_TRANSLATE("Switch shortcut keys");
if (fCurrentMap.KeyForModifier(B_LEFT_COMMAND_KEY) == 0x5d
&& fCurrentMap.KeyForModifier(B_LEFT_CONTROL_KEY) == 0x5c) {
label = B_TRANSLATE("Switch shortcut keys to Windows/Linux mode");
} else if (fCurrentMap.KeyForModifier(B_LEFT_COMMAND_KEY) == 0x5c
&& fCurrentMap.KeyForModifier(B_LEFT_CONTROL_KEY) == 0x5d) {
label = B_TRANSLATE("Switch shortcut keys to Haiku mode");
}
fSwitchShortcutsButton->SetLabel(label);
}
void
KeymapWindow::_UpdateDeadKeyMenu()
{
BString trigger;
fCurrentMap.GetDeadKeyTrigger(kDeadKeyAcute, trigger);
if (!trigger.Length())
trigger = kDeadKeyTriggerNone;
BMenuItem* menuItem = fAcuteMenu->FindItem(trigger.String());
if (menuItem)
menuItem->SetMarked(true);
fCurrentMap.GetDeadKeyTrigger(kDeadKeyCircumflex, trigger);
if (!trigger.Length())
trigger = kDeadKeyTriggerNone;
menuItem = fCircumflexMenu->FindItem(trigger.String());
if (menuItem)
menuItem->SetMarked(true);
fCurrentMap.GetDeadKeyTrigger(kDeadKeyDiaeresis, trigger);
if (!trigger.Length())
trigger = kDeadKeyTriggerNone;
menuItem = fDiaeresisMenu->FindItem(trigger.String());
if (menuItem)
menuItem->SetMarked(true);
fCurrentMap.GetDeadKeyTrigger(kDeadKeyGrave, trigger);
if (!trigger.Length())
trigger = kDeadKeyTriggerNone;
menuItem = fGraveMenu->FindItem(trigger.String());
if (menuItem)
menuItem->SetMarked(true);
fCurrentMap.GetDeadKeyTrigger(kDeadKeyTilde, trigger);
if (!trigger.Length())
trigger = kDeadKeyTriggerNone;
menuItem = fTildeMenu->FindItem(trigger.String());
if (menuItem)
menuItem->SetMarked(true);
}
void
KeymapWindow::_UpdateButtons()
{
if (fCurrentMap != fAppliedMap) {
fCurrentMap.SetName(kCurrentKeymapName);
_UseKeymap();
}
fDefaultsButton->SetEnabled(
fCurrentMapName.ICompare(kDefaultKeymapName) != 0);
fRevertButton->SetEnabled(fCurrentMap != fPreviousMap);
_UpdateDeadKeyMenu();
_UpdateSwitchShortcutButton();
}
void
KeymapWindow::_SwitchShortcutKeys()
{
uint32 leftCommand = fCurrentMap.Map().left_command_key;
uint32 leftControl = fCurrentMap.Map().left_control_key;
uint32 rightCommand = fCurrentMap.Map().right_command_key;
uint32 rightControl = fCurrentMap.Map().right_control_key;
fCurrentMap.Map().left_command_key = leftControl;
fCurrentMap.Map().left_control_key = leftCommand;
fCurrentMap.Map().right_command_key = rightControl;
fCurrentMap.Map().right_control_key = rightCommand;
fKeyboardLayoutView->SetKeymap(&fCurrentMap);
_UpdateButtons();
}
void
KeymapWindow::_DefaultKeymap()
{
fCurrentMap.RestoreSystemDefault();
fAppliedMap = fCurrentMap;
fKeyboardLayoutView->SetKeymap(&fCurrentMap);
fCurrentMapName = _GetActiveKeymapName();
_SelectCurrentMap();
}
void
KeymapWindow::_RevertKeymap()
{
entry_ref ref;
_GetCurrentKeymap(ref);
status_t status = fPreviousMap.Save(ref);
if (status != B_OK) {
printf("error when saving keymap: %s", strerror(status));
return;
}
fPreviousMap.Use();
fCurrentMap.Load(ref);
fAppliedMap = fCurrentMap;
fKeyboardLayoutView->SetKeymap(&fCurrentMap);
fCurrentMapName = _GetActiveKeymapName();
_SelectCurrentMap();
}
void
KeymapWindow::_UseKeymap()
{
entry_ref ref;
_GetCurrentKeymap(ref);
status_t status = fCurrentMap.Save(ref);
if (status != B_OK) {
printf("error when saving : %s", strerror(status));
return;
}
fCurrentMap.Use();
fAppliedMap.Load(ref);
fCurrentMapName = _GetActiveKeymapName();
_SelectCurrentMap();
}
void
KeymapWindow::_FillSystemMaps()
{
BListItem* item;
while ((item = fSystemListView->RemoveItem(static_cast<int32>(0))))
delete item;
BPath path;
if (find_directory(B_SYSTEM_DATA_DIRECTORY, &path) != B_OK)
return;
path.Append("Keymaps");
BDirectory directory;
entry_ref ref;
if (directory.SetTo(path.Path()) == B_OK) {
while (directory.GetNextRef(&ref) == B_OK) {
fSystemListView->AddItem(
new KeymapListItem(ref,
B_TRANSLATE_NOCOLLECT_ALL((ref.name),
"KeymapNames", NULL)));
}
}
fSystemListView->SortItems(&compare_key_list_items);
}
void
KeymapWindow::_FillUserMaps()
{
BListItem* item;
while ((item = fUserListView->RemoveItem(static_cast<int32>(0))))
delete item;
entry_ref ref;
_GetCurrentKeymap(ref);
fUserListView->AddItem(new KeymapListItem(ref, B_TRANSLATE("(Current)")));
fCurrentMapName = _GetActiveKeymapName();
BPath path;
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
return;
path.Append("Keymap");
BDirectory directory;
if (directory.SetTo(path.Path()) == B_OK) {
while (directory.GetNextRef(&ref) == B_OK) {
fUserListView->AddItem(new KeymapListItem(ref));
}
}
fUserListView->SortItems(&compare_key_list_items);
}
void
KeymapWindow::_SetListViewSize(BListView* listView)
{
float minWidth = 0;
for (int32 i = 0; i < listView->CountItems(); i++) {
BStringItem* item = (BStringItem*)listView->ItemAt(i);
float width = listView->StringWidth(item->Text());
if (width > minWidth)
minWidth = width;
}
listView->SetExplicitMinSize(BSize(minWidth + 8, 32));
}
status_t
KeymapWindow::_GetCurrentKeymap(entry_ref& ref)
{
BPath path;
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
return B_ERROR;
path.Append("Key_map");
return get_ref_for_path(path.Path(), &ref);
}
BString
KeymapWindow::_GetActiveKeymapName()
{
BString mapName = kCurrentKeymapName;
entry_ref ref;
_GetCurrentKeymap(ref);
BNode node(&ref);
if (node.InitCheck() == B_OK)
node.ReadAttrString("keymap:name", &mapName);
return mapName;
}
bool
KeymapWindow::_SelectCurrentMap(BListView* view)
{
if (fCurrentMapName.Length() <= 0)
return false;
for (int32 i = 0; i < view->CountItems(); i++) {
KeymapListItem* current =
static_cast<KeymapListItem *>(view->ItemAt(i));
if (current != NULL && fCurrentMapName == current->EntryRef().name) {
view->Select(i);
view->ScrollToSelection();
return true;
}
}
return false;
}
void
KeymapWindow::_SelectCurrentMap()
{
if (!_SelectCurrentMap(fSystemListView)
&& !_SelectCurrentMap(fUserListView)) {
fUserListView->Select(0L);
}
}
status_t
KeymapWindow::_GetSettings(BFile& file, int mode) const
{
BPath path;
status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path,
(mode & O_ACCMODE) != O_RDONLY);
if (status != B_OK)
return status;
path.Append("Keymap settings");
return file.SetTo(path.Path(), mode);
}
status_t
KeymapWindow::_LoadSettings(BRect& windowFrame)
{
BScreen screen(this);
windowFrame.Set(-1, -1, 669, 357);
if (screen.Frame().Width() > 1200) {
windowFrame.right = 899;
windowFrame.bottom = 349;
}
float scaling = be_plain_font->Size() / 12.0f;
windowFrame.right *= scaling;
windowFrame.bottom *= scaling;
BFile file;
status_t status = _GetSettings(file, B_READ_ONLY);
if (status == B_OK) {
BMessage settings;
status = settings.Unflatten(&file);
if (status == B_OK) {
BRect frame;
status = settings.FindRect("window frame", &frame);
if (status == B_OK)
windowFrame = frame;
const char* layoutPath;
if (settings.FindString("keyboard layout", &layoutPath) == B_OK)
_SetKeyboardLayout(layoutPath);
}
}
return status;
}
status_t
KeymapWindow::_SaveSettings()
{
BFile file;
status_t status
= _GetSettings(file, B_WRITE_ONLY | B_ERASE_FILE | B_CREATE_FILE);
if (status != B_OK)
return status;
BMessage settings('keym');
settings.AddRect("window frame", Frame());
BPath path = _GetMarkedKeyboardLayoutPath(fLayoutMenu);
if (path.InitCheck() == B_OK)
settings.AddString("keyboard layout", path.Path());
return settings.Flatten(&file);
}
BPath
KeymapWindow::_GetMarkedKeyboardLayoutPath(BMenu* menu)
{
BPath path;
BMenuItem* item = NULL;
entry_ref ref;
for (int32 i = 0; i < menu->CountItems(); i++) {
item = menu->ItemAt(i);
if (item == NULL)
continue;
BMenu* submenu = item->Submenu();
if (submenu != NULL) {
path = _GetMarkedKeyboardLayoutPath(submenu);
if (path.InitCheck() == B_OK)
return path;
} else {
if (item->IsMarked()
&& item->Message()->FindRef("ref", &ref) == B_OK) {
path.SetTo(&ref);
return path;
}
}
}
return path;
}