From 83936c1c26990457a3de22f0df6d8fa005e24026 Mon Sep 17 00:00:00 2001 From: Oliver Tappe Date: Tue, 12 May 2009 18:31:17 +0000 Subject: [PATCH] * Added support for selecting the dead key trigger characters from a menubar, offering two choices for acute and diaeresis as well as allowing to switch off each dead key completely. * moved the textview on a line of its own such that the dead key menu and the modifier-switching button live together on one line * added enum dead_key_index and used it at a couple of places instead of having to use the magic numbers 1-5 * refactored the actual updating of the fChars buffer from SetKey() into _SetChars(), which is now being invoked by SetDeadKeyTrigger(), too git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@30718 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/preferences/keymap/Keymap.cpp | 192 +++++++++++++++++------- src/preferences/keymap/Keymap.h | 17 +++ src/preferences/keymap/KeymapWindow.cpp | 178 +++++++++++++++++++++- src/preferences/keymap/KeymapWindow.h | 9 ++ 4 files changed, 343 insertions(+), 53 deletions(-) diff --git a/src/preferences/keymap/Keymap.cpp b/src/preferences/keymap/Keymap.cpp index 7db533589a..9f8aa1f8ed 100644 --- a/src/preferences/keymap/Keymap.cpp +++ b/src/preferences/keymap/Keymap.cpp @@ -446,6 +446,78 @@ Keymap::SetDeadKeyEnabled(uint32 keyCode, uint32 modifiers, bool enabled) } +/*! Returns the trigger character string that is currently set for the dead + key with the given index (which is 1..5). +*/ +void +Keymap::GetDeadKeyTrigger(dead_key_index deadKeyIndex, BString& outTrigger) +{ + outTrigger = ""; + if (deadKeyIndex < 1 || deadKeyIndex > 5) + return; + + int32 deadOffsets[] = { + fKeys.acute_dead_key[1], + fKeys.grave_dead_key[1], + fKeys.circumflex_dead_key[1], + fKeys.dieresis_dead_key[1], + fKeys.tilde_dead_key[1] + }; + + int32 offset = deadOffsets[deadKeyIndex - 1]; + if (offset < 0 || offset >= (int32)fCharsSize) + return; + + uint32 deadNumBytes = fChars[offset]; + if (!deadNumBytes) + return; + + outTrigger.SetTo(&fChars[offset + 1], deadNumBytes); +} + + +/*! Sets the trigger character string that shall be used for the dead key + with the given index (which is 1..5). +*/ +void +Keymap::SetDeadKeyTrigger(dead_key_index deadKeyIndex, const BString& trigger) +{ + if (deadKeyIndex < 1 || deadKeyIndex > 5) + return; + + int32 deadOffsets[] = { + fKeys.acute_dead_key[1], + fKeys.grave_dead_key[1], + fKeys.circumflex_dead_key[1], + fKeys.dieresis_dead_key[1], + fKeys.tilde_dead_key[1] + }; + + int32 offset = deadOffsets[deadKeyIndex - 1]; + if (offset < 0 || offset >= (int32)fCharsSize) + return; + + if (_SetChars(offset, trigger.String(), trigger.Length())) { + // reset modifier table such that new dead key is enabled wherever + // it is available + uint32* deadTables[] = { + &fKeys.acute_tables, + &fKeys.grave_tables, + &fKeys.circumflex_tables, + &fKeys.dieresis_tables, + &fKeys.tilde_tables + }; + *deadTables[deadKeyIndex - 1] + = B_CONTROL_TABLE | B_OPTION_CAPS_SHIFT_TABLE | B_OPTION_CAPS_TABLE + | B_OPTION_SHIFT_TABLE | B_OPTION_TABLE | B_CAPS_SHIFT_TABLE + | B_CAPS_TABLE | B_SHIFT_TABLE | B_NORMAL_TABLE; + + if (fModificationMessage != NULL) + fTarget.SendMessage(fModificationMessage); + } +} + + //! Get the char for a key given modifiers and active dead key void Keymap::GetChars(uint32 keyCode, uint32 modifiers, uint8 activeDeadKey, @@ -487,11 +559,21 @@ Keymap::GetChars(uint32 keyCode, uint32 modifiers, uint8 activeDeadKey, // here we take an potential active dead key int32 *deadKey; switch (activeDeadKey) { - case 1: deadKey = fKeys.acute_dead_key; break; - case 2: deadKey = fKeys.grave_dead_key; break; - case 3: deadKey = fKeys.circumflex_dead_key; break; - case 4: deadKey = fKeys.dieresis_dead_key; break; - case 5: deadKey = fKeys.tilde_dead_key; break; + case kDeadKeyAcute: + deadKey = fKeys.acute_dead_key; + break; + case kDeadKeyGrave: + deadKey = fKeys.grave_dead_key; + break; + case kDeadKeyCircumflex: + deadKey = fKeys.circumflex_dead_key; + break; + case kDeadKeyDiaeresis: + deadKey = fKeys.dieresis_dead_key; + break; + case kDeadKeyTilde: + deadKey = fKeys.tilde_dead_key; + break; default: { // if not dead, we copy and return the char @@ -556,54 +638,10 @@ Keymap::SetKey(uint32 keyCode, uint32 modifiers, int8 deadKey, if (numBytes > 6) return; - int32 oldNumBytes = fChars[offset]; - - if (oldNumBytes == numBytes - && !memcmp(&fChars[offset + 1], bytes, numBytes)) { - // nothing to do - return; + if (_SetChars(offset, bytes, numBytes)) { + if (fModificationMessage != NULL) + fTarget.SendMessage(fModificationMessage); } - - // TODO: handle dead keys! - - int32 diff = numBytes - oldNumBytes; - if (diff != 0) { - fCharsSize += diff; - - if (diff > 0) { - // make space for the new data - char* chars = new(std::nothrow) char[fCharsSize]; - if (chars != NULL) { - memcpy(chars, fChars, offset + oldNumBytes + 1); - memcpy(&chars[offset + 1 + numBytes], - &fChars[offset + 1 + oldNumBytes], - fCharsSize - 2 - offset - diff); - delete[] fChars; - fChars = chars; - } else - return; - } else if (diff < 0) { - // shrink table - memmove(&fChars[offset + numBytes], &fChars[offset + oldNumBytes], - fCharsSize - offset - 2 - diff); - } - - // update offsets - - int32* data = fKeys.control_map; - int32 size = sizeof(fKeys.control_map) / 4 * 9 - + sizeof(fKeys.acute_dead_key) / 4 * 5; - for (int32 i = 0; i < size; i++) { - if (data[i] > offset) - data[i] += diff; - } - } - - memcpy(&fChars[offset + 1], bytes, numBytes); - fChars[offset] = numBytes; - - if (fModificationMessage != NULL) - fTarget.SendMessage(fModificationMessage); } @@ -693,6 +731,56 @@ Keymap::_Offset(uint32 keyCode, uint32 modifiers, uint32* _table) } +bool +Keymap::_SetChars(int32 offset, const char* bytes, int32 numBytes) +{ + int32 oldNumBytes = fChars[offset]; + + if (oldNumBytes == numBytes + && !memcmp(&fChars[offset + 1], bytes, numBytes)) { + // nothing to do + return false; + } + + int32 diff = numBytes - oldNumBytes; + if (diff != 0) { + fCharsSize += diff; + + if (diff > 0) { + // make space for the new data + char* chars = new(std::nothrow) char[fCharsSize]; + if (chars != NULL) { + memcpy(chars, fChars, offset + oldNumBytes + 1); + memcpy(&chars[offset + 1 + numBytes], + &fChars[offset + 1 + oldNumBytes], + fCharsSize - 2 - offset - diff); + delete[] fChars; + fChars = chars; + } else + return false; + } else if (diff < 0) { + // shrink table + memmove(&fChars[offset + numBytes], &fChars[offset + oldNumBytes], + fCharsSize - offset - 2 - diff); + } + + // update offsets + int32* data = fKeys.control_map; + int32 size = sizeof(fKeys.control_map) / 4 * 9 + + sizeof(fKeys.acute_dead_key) / 4 * 5; + for (int32 i = 0; i < size; i++) { + if (data[i] > offset) + data[i] += diff; + } + } + + memcpy(&fChars[offset + 1], bytes, numBytes); + fChars[offset] = numBytes; + + return true; +} + + uint8 Keymap::_GetDeadKeyIndex(int32 offset) { diff --git a/src/preferences/keymap/Keymap.h b/src/preferences/keymap/Keymap.h index 83184a7fe3..5ae3fa3dd6 100644 --- a/src/preferences/keymap/Keymap.h +++ b/src/preferences/keymap/Keymap.h @@ -13,6 +13,16 @@ #include #include #include +#include + + +enum dead_key_index { + kDeadKeyAcute = 1, + kDeadKeyGrave, + kDeadKeyCircumflex, + kDeadKeyDiaeresis, + kDeadKeyTilde +}; class Keymap { @@ -39,6 +49,11 @@ public: uint8 activeDeadKey); void SetDeadKeyEnabled(uint32 keyCode, uint32 modifiers, bool enabled); + void GetDeadKeyTrigger(dead_key_index deadKeyIndex, + BString& outTrigger); + void SetDeadKeyTrigger(dead_key_index deadKeyIndex, + const BString& trigger); + void GetChars(uint32 keyCode, uint32 modifiers, uint8 activeDeadKey, char** chars, int32* numBytes); @@ -57,6 +72,8 @@ public: private: int32 _Offset(uint32 keyCode, uint32 modifiers, uint32* _table = NULL); + bool _SetChars(int32 offset, const char* bytes, + int32 numBytes); uint8 _GetDeadKeyIndex(int32 offset); char* fChars; diff --git a/src/preferences/keymap/KeymapWindow.cpp b/src/preferences/keymap/KeymapWindow.cpp index 847777fbc6..0618edf1d7 100644 --- a/src/preferences/keymap/KeymapWindow.cpp +++ b/src/preferences/keymap/KeymapWindow.cpp @@ -51,6 +51,14 @@ static const uint32 kMsgUseKeymap = 'UkyM'; static const uint32 kMsgRevertKeymap = 'Rvrt'; static const uint32 kMsgKeymapUpdated = 'upkM'; +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 = ""; + KeymapWindow::KeymapWindow() : BWindow(BRect(80, 50, 880, 380), "Keymap", B_TITLED_WINDOW, @@ -80,8 +88,10 @@ KeymapWindow::KeymapWindow() .Add(fKeyboardLayoutView) //.Add(new BStringView("text label", "Sample and Clipboard:")) .Add(BGroupLayoutBuilder(B_HORIZONTAL, 10) - .Add(fTextControl) + .Add(_CreateDeadKeyMenu(), 0.0) + .AddGlue() .Add(fSwitchShortcutsButton)) + .Add(fTextControl) .AddGlue(0.0) .Add(BGroupLayoutBuilder(B_HORIZONTAL, 10) .AddGlue(0.0) @@ -153,6 +163,7 @@ KeymapWindow::KeymapWindow() fAppliedMap = fCurrentMap; fCurrentMap.SetTarget(this, new BMessage(kMsgKeymapUpdated)); + _UpdateDeadKeyMenu(); _UpdateSwitchShortcutButton(); Unlock(); @@ -307,6 +318,71 @@ KeymapWindow::MessageReceived(BMessage* message) 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; @@ -372,6 +448,61 @@ KeymapWindow::_CreateMenu() } +BMenuBar* +KeymapWindow::_CreateDeadKeyMenu() +{ + BMenuBar* menuBar = new BMenuBar("deadkeymenubar"); + fDeadKeyMenu = new BMenu("Select Dead Keys"); + menuBar->AddItem(fDeadKeyMenu); + + fAcuteMenu = new BMenu("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))); + fDeadKeyMenu->AddItem(fAcuteMenu); + + fCircumflexMenu = new BMenu("Circumflex Trigger"); + fCircumflexMenu->SetRadioMode(true); + fCircumflexMenu->AddItem(new BMenuItem("^", + new BMessage(kMsgDeadKeyCircumflexChanged))); + fCircumflexMenu->AddItem(new BMenuItem(kDeadKeyTriggerNone, + new BMessage(kMsgDeadKeyCircumflexChanged))); + fDeadKeyMenu->AddItem(fCircumflexMenu); + + fDiaeresisMenu = new BMenu("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))); + fDeadKeyMenu->AddItem(fDiaeresisMenu); + + fGraveMenu = new BMenu("Grave Trigger"); + fGraveMenu->SetRadioMode(true); + fGraveMenu->AddItem(new BMenuItem("`", + new BMessage(kMsgDeadKeyGraveChanged))); + fGraveMenu->AddItem(new BMenuItem(kDeadKeyTriggerNone, + new BMessage(kMsgDeadKeyGraveChanged))); + fDeadKeyMenu->AddItem(fGraveMenu); + + fTildeMenu = new BMenu("Tilde Trigger"); + fTildeMenu->SetRadioMode(true); + fTildeMenu->AddItem(new BMenuItem("~", + new BMessage(kMsgDeadKeyTildeChanged))); + fTildeMenu->AddItem(new BMenuItem(kDeadKeyTriggerNone, + new BMessage(kMsgDeadKeyTildeChanged))); + fDeadKeyMenu->AddItem(fTildeMenu); + + return menuBar; +} + + BView* KeymapWindow::_CreateMapLists() { @@ -457,12 +588,57 @@ KeymapWindow::_UpdateSwitchShortcutButton() } +/*! Marks the menu items corresponding to the dead key state of the current + key map. +*/ +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() { fUseButton->SetEnabled(!fCurrentMap.Equals(fAppliedMap)); fRevertButton->SetEnabled(!fCurrentMap.Equals(fPreviousMap)); + _UpdateDeadKeyMenu(); _UpdateSwitchShortcutButton(); } diff --git a/src/preferences/keymap/KeymapWindow.h b/src/preferences/keymap/KeymapWindow.h index c7af803fb6..45a406d671 100644 --- a/src/preferences/keymap/KeymapWindow.h +++ b/src/preferences/keymap/KeymapWindow.h @@ -46,6 +46,9 @@ protected: void _UseKeymap(); void _RevertKeymap(); + BMenuBar* _CreateDeadKeyMenu(); + void _UpdateDeadKeyMenu(); + void _FillSystemMaps(); void _FillUserMaps(); void _SetListViewSize(BListView* listView); @@ -64,6 +67,12 @@ protected: KeyboardLayoutView* fKeyboardLayoutView; BTextControl* fTextControl; BButton* fSwitchShortcutsButton; + BMenu* fDeadKeyMenu; + BMenu* fAcuteMenu; + BMenu* fCircumflexMenu; + BMenu* fDiaeresisMenu; + BMenu* fGraveMenu; + BMenu* fTildeMenu; Keymap fCurrentMap; Keymap fPreviousMap;