diff --git a/src/preferences/keymap/KeyboardLayout.cpp b/src/preferences/keymap/KeyboardLayout.cpp new file mode 100644 index 0000000000..e0d12e793f --- /dev/null +++ b/src/preferences/keymap/KeyboardLayout.cpp @@ -0,0 +1,694 @@ +/* + * Copyright 2009, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ + + +#include "KeyboardLayout.h" + +#include +#include +#include + +#include + + +//#define TRACE_LAYOUT +#ifdef TRACE_LAYOUT +# define TRACE(x...) printf(x) +#else +# define TRACE(x...) +#endif + + +KeyboardLayout::KeyboardLayout() + : + fKeys(NULL), + fKeyCount(0), + fKeyCapacity(0) +{ + _SetDefault(); +} + + +KeyboardLayout::~KeyboardLayout() +{ + free(fKeys); +} + + +const char* +KeyboardLayout::Name() +{ + return fName.String(); +} + + +int32 +KeyboardLayout::CountKeys() +{ + return fKeyCount; +} + + +Key* +KeyboardLayout::KeyAt(int32 index) +{ + if (index < 0 || index >= fKeyCount) + return NULL; + + return &fKeys[index]; +} + + +Key* +KeyboardLayout::KeyAt(BPoint point) +{ + return NULL; +} + + +BRect +KeyboardLayout::Bounds() +{ + return fBounds; +} + + +status_t +KeyboardLayout::Load(const char* path) +{ + BFile file; + status_t status = file.SetTo(path, B_READ_ONLY); + if (status != B_OK) + return status; + + off_t size; + status = file.GetSize(&size); + if (status != B_OK) + return status; + + if (size > 65536) { + // We don't accept files larger than this + return B_BAD_VALUE; + } + + char* data = (char*)malloc(size + 1); + if (data == NULL) + return B_NO_MEMORY; + + ssize_t bytesRead = file.Read(data, size); + if (bytesRead != size) { + free(data); + + if (bytesRead < 0) + return bytesRead; + + return B_IO_ERROR; + } + + data[size] = '\0'; + + status = _InitFrom(data); + free(data); + + return status; +} + + +void +KeyboardLayout::_SetDefault() +{ + static const char* kDefaultLayout104 = "name = 104 Keys\n" + // Size shortcuts + "default-size = 10,10\n" + "$b = 5,10\n" + "$c = 20,10\n" + "$d = 15,10\n" + "$enter = d20,10\n" + "$f = 10,20\n" + "$g = 13,10\n" + // Key rows + "[ 0,0; d:0x01; :-; :+4; $b:-; d:+4; $b:-; :+4; $b:-; d:+3 ]\n" + "[ 0,20; :+13; d$c:+; $b:-; d:+3; $b:-; d:+4 ]\n" + "[ 0,30; d$d:0x26; :+12; $d:+1; $b:-; d:+3; $b:-; :+3; " + "d$f:+1 ]\n" + "[ 0,40; d$c:0x3b; :+11; $enter:+1; 40,10:-; :+3 ]\n" + "[ 0,50; d24,10:0x4b; :+10; d26,10:+1; 15,10:-; d:+1; " + " 15,10:-; :+3; d$f:+1 ]\n" + "[ 0,60; d$g:0x5c; d$g:0x66; d$g:0x5d; 59,10:+1; d$g:+1; d$g:0x67+1; " + "d$g:0x60; $b:-; d:+3; $b:-; $c:+1; :+1 ]\n"; +#if 0 + static const char* kDefaultLayout105 = "name = 105 Keys International\n" + // Size shortcuts + "default-size = 10,10\n" + "$b = 5,10\n" + "$c = 20,10\n" + "$d = 15,10\n" + "$e = l15,20,13\n" + "$f = 10,20\n" + "$g = 13,10\n" + // Key rows + "[ 0,0; d:0x01; :-; :+4; $b:-; d:+4; $b:-; :+4; $b:-; d:+3 ]\n" + "[ 0,20; :+13; d$c:+; $b:-; d:+3; $b:-; d:+4 ]\n" + "[ 0,30; d$d:0x26; :+12; d$e:0x47; $b:-; d:0x34-0x36; $b:-; :+3; " + "d$f:+1 ]\n" + "[ 0,40; d19,10:0x3b; :+11; :0x33; 51,10:-; :0x48-0x4a ]\n" + "[ 0,50; d$g:0x4b; :0x69; :0x4c+9; d27,10:+1; 15,10:-; d:+1; " + " 15,10:-; :+3; d$f:+1 ]\n" + "[ 0,60; d$g:0x5c; d$g:0x66; d$g:0x5d; 59,10:+1; d$g:+1; d$g:0x67+1; " + "d$g:0x60; $b:-; d:+3; $b:-; $c:+1; :+1 ]\n"; +#endif + + _InitFrom(kDefaultLayout104); +} + + +void +KeyboardLayout::_FreeKeys() +{ + free(fKeys); + fKeys = NULL; + fKeyCount = 0; + fKeyCapacity = 0; + fBounds = BRect(); +} + + +void +KeyboardLayout::_Error(const parse_state& state, const char* reason, ...) +{ + va_list args; + va_start(args, reason); + + fprintf(stderr, "Syntax error in line %ld: ", state.line); + vfprintf(stderr, reason, args); + fprintf(stderr, "\n"); + + va_end(args); +} + + +void +KeyboardLayout::_AddAlternateKeyCode(Key* key, int32 modifier, int32 code) +{ + if (key == NULL) + return; + + // TODO: search for free spot +} + + +bool +KeyboardLayout::_AddKey(const Key& key) +{ + TRACE(" add %ld (%g,%g)\n", key.code, key.frame.left, key.frame.top); + if (fKeyCount + 1 > fKeyCapacity) { + // enlarge array + int32 newCapacity = fKeyCapacity + 32; + Key* newKeys = (Key*)realloc(fKeys, newCapacity * sizeof(Key)); + if (newKeys == NULL) + return false; + + fKeys = newKeys; + fKeyCapacity = newCapacity; + } + + fKeys[fKeyCount] = key; + fKeyCount++; + + fBounds = key.frame | fBounds; + + return true; +} + + +void +KeyboardLayout::_SkipCommentsAndSpace(parse_state& state, const char*& data) +{ + while (data[0] != '\0') { + while (isspace(data[0])) { + if (data[0] == '\n') + state.line++; + data++; + } + + if (data[0] == '#') { + // skip comment + while (data[0] != '\0' && data[0] != '\n') { + data++; + } + } else + break; + } +} + + +void +KeyboardLayout::_Trim(BString& string, bool stripComments) +{ + // Strip leading spaces + int32 i = 0; + while (isspace(string[i])) { + i++; + } + if (i > 0) + string.Remove(0, i); + + // Remove comments + if (stripComments) { + i = string.FindFirst('#'); + if (i >= 0) + string.Truncate(i); + } + + // Strip trailing spaces + i = string.Length() - 1; + while (i > 0 && isspace(string[i])) { + i--; + } + string.Truncate(i + 1); +} + + +bool +KeyboardLayout::_GetPair(const parse_state& state, const char*& data, + BString& name, BString& value) +{ + // Get name + name = ""; + while (data[0] != '\0' && data[0] != '=') { + name += data[0]; + data++; + } + + if (data[0] != '=') { + _Error(state, "no valid pair"); + return false; + } + + // Skip sign + data++; + + // Get value + value = ""; + while (data[0] != '\0' && data[0] != '\n') { + value += data[0]; + data++; + } + + _Trim(name, false); + _Trim(value, true); + + return true; +} + + +bool +KeyboardLayout::_AddKeyCodes(const parse_state& state, BPoint& rowLeftTop, + Key& key, const char* data, int32& lastCount) +{ + if (data[0] == '-') { + // no key, just free space + int32 num = strtoul(data + 1, NULL, 0); + if (num < 1) + num = 1; + else if (num > 32) { + _Error(state, "empty key count too large"); + return false; + } + + key.frame.OffsetTo(rowLeftTop); + rowLeftTop.x = key.frame.left + key.frame.Width() * num; + return true; + } + + int32 modifier = 0; + + if (isalpha(data[0])) { + // TODO: get modifier (ie. "num") + } + + int32 first; + int32 last; + int32 num = 1; + + if (data[0] == '+') { + num = strtoul(data + 1, NULL, 0); + if (num < 1) + num = 1; + else if (num > 32) { + _Error(state, "key count too large"); + return false; + } + + if (fKeyCount > 0) + first = fKeys[fKeyCount - 1].code + 1; + else + first = 1; + + last = first + num - 1; + } else { + char* end; + first = strtoul(data, &end, 0); + last = first; + + if (end[0] == '-') { + last = strtoul(end + 1, NULL, 0); + if (first > last) { + _Error(state, "invalid key code specifier"); + return false; + } + + num = last - first; + } else if (end[0] == '+') { + num = strtoul(end + 1, NULL, 0) + 1; + last = first + num - 1; + } else if (end[0] != '\0') { + _Error(state, "invalid key range"); + return false; + } + } + + if (lastCount != 0) { + // update existing keys + if (lastCount != num) { + _Error(state, "modifier key mismatch"); + return false; + } + + for (int32 i = fKeyCount - num; i < fKeyCount; i++, first++) { + Key* key = KeyAt(i); + + _AddAlternateKeyCode(key, modifier, first); + } + } else { + // add new keys + for (int32 i = first; i <= last; i++) { + key.code = i; + + // "layout" + key.frame.OffsetTo(rowLeftTop); + rowLeftTop.x = key.frame.right; + + _AddKey(key); + } + + lastCount = num; + } + + return true; +} + + +bool +KeyboardLayout::_GetSize(const parse_state& state, const char* data, + float& x, float& y, float* _secondRow) +{ + if (data[0] == '\0') { + // default size + x = fDefaultKeySize.width; + y = fDefaultKeySize.height; + return true; + } + + float secondRow = 0; + int num = sscanf(data, "%g,%g,%g", &x, &y, &secondRow); + if (num < 2) { + _Error(state, "invalid size"); + return false; + } + + if (_secondRow != NULL) + *_secondRow = secondRow; + return true; +} + + +bool +KeyboardLayout::_GetShape(const parse_state& state, const char* data, Key& key) +{ + // the default + key.shape = kRectangleKeyShape; + key.dark = false; + + while (isalpha(data[0])) { + switch (tolower(data[0])) { + case 'r': + key.shape = kRectangleKeyShape; + break; + case 'c': + key.shape = kCircleKeyShape; + break; + case 'l': + key.shape = kEnterKeyShape; + break; + case 'd': + key.dark = true; + break; + + default: + _Error(state, "unknown shape specifier '%c'", data[0]); + return false; + } + + data++; + } + + float width, height; + if (!_GetSize(state, data, width, height, &key.second_row)) + return false; + + // don't accept second row with anything but kEnterKeyShape + if ((key.shape != kEnterKeyShape && key.second_row != 0) + || (key.shape == kEnterKeyShape && key.second_row == 0)) { + _Error(state, "shape size mismatch"); + return false; + } + + key.frame.left = 0; + key.frame.top = 0; + key.frame.right = width; + key.frame.bottom = height; + + return true; +} + + +const char* +KeyboardLayout::_Delimiter(parse_mode mode) +{ + switch (mode) { + default: + case kSize: + return ""; + case kRowStart: + return ";"; + + case kKeyShape: + return ":"; + case kKeyCodes: + return ";:"; + } +} + + +bool +KeyboardLayout::_GetTerm(const char*& data, const char* delimiter, + BString& term, bool closingBracketAllowed) +{ + // Get term + term = ""; + while (data[0] != '\0' && strchr(delimiter, data[0]) == NULL + && data[0] != '\n' && data[0] != '#' + && (!closingBracketAllowed || data[0] != ']')) { + term += data[0]; + data++; + } + + if (data[0] == '\0' && delimiter[0]) + return false; + + _Trim(term, true); + return true; +} + + +bool +KeyboardLayout::_SubstituteVariables(BString& term, VariableMap& variables, + BString& unknown) +{ + while (true) { + int32 index = term.FindFirst('$'); + if (index < 0) + break; + + // find variable name + + VariableMap::iterator iterator = variables.begin(); + for (; iterator != variables.end(); iterator++) { + const BString& name = iterator->first; + if (!name.Compare(&term[index], name.Length())) { + // got one, replace it + term.Remove(index, name.Length()); + term.Insert(iterator->second.String(), index); + break; + } + } + + if (iterator == variables.end()) { + // variable has not been found + unknown = &term[index]; + int32 length = 1; + while (isalpha(unknown[length])) { + length++; + } + unknown.Truncate(length); + return false; + } + } + + return true; +} + + +bool +KeyboardLayout::_ParseTerm(const parse_state& state, const char*& data, + BString& term, VariableMap& variables) +{ + if (!_GetTerm(data, _Delimiter(state.mode), term, + state.mode == kKeyCodes)) { + _Error(state, state.mode == kRowStart + ? "no valid row start" : "invalid term"); + return false; + } + + BString unknown; + if (!_SubstituteVariables(term, variables, unknown)) { + _Error(state, "Unknown variable \"%s\"", unknown.String()); + return false; + } + + return true; +} + + +status_t +KeyboardLayout::_InitFrom(const char* data) +{ + _FreeKeys(); + + VariableMap variables; + BPoint rowLeftTop; + int32 lastKeyCount = 0; + Key key; + + parse_state state = {kPairs, 1}; + + while (data[0] != '\0') { + _SkipCommentsAndSpace(state, data); + + if (data[0] == '[') { + state.mode = kRowStart; + + rowLeftTop = BPoint(0, 0); + data++; + continue; + } else if (data[0] == '\0') + break; + + switch (state.mode) { + case kPairs: + { + BString name; + BString value; + if (!_GetPair(state, data, name, value)) + return B_BAD_VALUE; + + TRACE("<%s> = <%s>\n", name.String(), value.String()); + if (name == "name") + fName = value; + else if (name == "default-size") { + const char* valueString = value.String(); + parse_state tempState = {kSize, state.line}; + BString term; + if (!_ParseTerm(tempState, valueString, term, variables)) + return B_BAD_VALUE; + + TRACE(" size = %s\n", term.String()); + if (!_GetSize(state, term.String(), fDefaultKeySize.width, + fDefaultKeySize.height)) + return B_BAD_VALUE; + } else if (name[0] == '$') + variables[name] = value; + break; + } + + case kRowStart: + case kKeyShape: + case kKeyCodes: + { + if (data[0] == ']') { + if (state.mode == kKeyShape) { + state.mode = kPairs; + data++; + continue; + } + _Error(state, "unexpected row closing bracket"); + return B_BAD_VALUE; + } + + BString term; + if (!_ParseTerm(state, data, term, variables)) + return B_BAD_VALUE; + + switch (state.mode) { + case kRowStart: + if (!_GetSize(state, term.String(), rowLeftTop.x, + rowLeftTop.y)) + return B_BAD_VALUE; + + TRACE("row: %s (%g:%g)\n", term.String(), rowLeftTop.x, + rowLeftTop.y); + + state.mode = kKeyShape; + break; + case kKeyShape: + memset(&key, 0, sizeof(Key)); + if (!_GetShape(state, term.String(), key)) + return B_BAD_VALUE; + + TRACE(" shape: %s (%g:%g:%g)\n", term.String(), + key.frame.Width(), key.frame.Height(), + key.second_row); + + lastKeyCount = 0; + state.mode = kKeyCodes; + break; + case kKeyCodes: + TRACE(" raw key: %s\n", term.String()); + + if (!_AddKeyCodes(state, rowLeftTop, key, term.String(), + lastKeyCount)) + return B_BAD_VALUE; + + if (data[0] != ':') + state.mode = kKeyShape; + break; + + default: + break; + } + if (data[0] != ']' && data[0] != '\0') + data++; + break; + } + + default: + return B_BAD_VALUE; + } + } + + return B_OK; +} + diff --git a/src/preferences/keymap/KeyboardLayout.h b/src/preferences/keymap/KeyboardLayout.h new file mode 100644 index 0000000000..5af0488388 --- /dev/null +++ b/src/preferences/keymap/KeyboardLayout.h @@ -0,0 +1,102 @@ +/* + * Copyright 2009, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ +#ifndef KEYBOARD_LAYOUT_H +#define KEYBOARD_LAYOUT_H + + +#include + +#include +#include +#include + + +enum key_shape { + kRectangleKeyShape, + kCircleKeyShape, + kEnterKeyShape +}; + +struct Key { + int32 code; + int32 alternate_code[3]; + int32 alternate_modifier[3]; + key_shape shape; + BRect frame; + float second_row; + bool dark; +}; + +typedef std::map VariableMap; + +class KeyboardLayout { +public: + KeyboardLayout(); + ~KeyboardLayout(); + + const char* Name(); + + int32 CountKeys(); + Key* KeyAt(int32 index); + Key* KeyAt(BPoint point); + + BRect Bounds(); + BSize DefaultKeySize(); + + status_t Load(const char* path); + +private: + enum parse_mode { + kPairs, + kSize, + kRowStart, + kKeyShape, + kKeyCodes + }; + struct parse_state { + parse_mode mode; + int32 line; + }; + + void _SetDefault(); + void _FreeKeys(); + void _Error(const parse_state& state, + const char* reason, ...); + void _AddAlternateKeyCode(Key* key, int32 modifier, + int32 code); + bool _AddKey(const Key& key); + void _SkipCommentsAndSpace(parse_state& state, + const char*& data); + void _Trim(BString& string, bool stripComments); + bool _GetPair(const parse_state& state, + const char*& data, BString& name, + BString& value); + bool _AddKeyCodes(const parse_state& state, + BPoint& rowLeftTop, Key& key, const char* data, + int32& lastCount); + bool _GetSize(const parse_state& state, const char* data, + float& x, float& y, float* _secondRow = NULL); + bool _GetShape(const parse_state& state, + const char* data, Key& key); + const char* _Delimiter(parse_mode mode); + bool _GetTerm(const char*& data, const char* delimiter, + BString& term, bool closingBracketAllowed); + bool _SubstituteVariables(BString& term, + VariableMap& variables, BString& unknown); + bool _ParseTerm(const parse_state& state, + const char*& data, BString& term, + VariableMap& variables); + + status_t _InitFrom(const char* data); + + BString fName; + Key* fKeys; + int32 fKeyCount; + int32 fKeyCapacity; + BRect fBounds; + BSize fDefaultKeySize; +}; + +#endif // KEYBOARD_LAYOUT_H diff --git a/src/preferences/keymap/KeyboardLayoutView.cpp b/src/preferences/keymap/KeyboardLayoutView.cpp new file mode 100644 index 0000000000..76c243afb6 --- /dev/null +++ b/src/preferences/keymap/KeyboardLayoutView.cpp @@ -0,0 +1,419 @@ +/* + * Copyright 2009, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ + + +#include "KeyboardLayoutView.h" + +#include +#include + +#include "Keymap.h" + + +KeyboardLayoutView::KeyboardLayoutView(const char* name) + : BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS), + fKeymap(NULL), + fModifiers(0), + fDeadKey(0) +{ + fLayout = new KeyboardLayout; + memset(fKeyState, 0, sizeof(fKeyState)); + + SetEventMask(B_KEYBOARD_EVENTS); +} + + +KeyboardLayoutView::~KeyboardLayoutView() +{ +} + + +void +KeyboardLayoutView::SetKeyboardLayout(KeyboardLayout* layout) +{ + fLayout = layout; + Invalidate(); +} + + +void +KeyboardLayoutView::SetKeymap(Keymap* keymap) +{ + fKeymap = keymap; + Invalidate(); +} + + +void +KeyboardLayoutView::AttachedToWindow() +{ + if (Parent()) + SetViewColor(Parent()->ViewColor()); + else + SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + + fFont = *be_plain_font; + fSpecialFont = *be_fixed_font; + + font_height fontHeight; + fFont.GetHeight(&fontHeight); + fFontHeight = fontHeight.ascent + fontHeight.descent; + + FrameResized(0, 0); +} + + +void +KeyboardLayoutView::FrameResized(float width, float height) +{ + float factorX = Bounds().Width() / fLayout->Bounds().Width(); + float factorY = Bounds().Height() / fLayout->Bounds().Height(); + + fFactor = min_c(factorX, factorY); + fOffset = BPoint((Bounds().Width() - fLayout->Bounds().Width() + * fFactor) / 2, + (Bounds().Height() - fLayout->Bounds().Height() * fFactor) / 2); + fMaxFontSize = 14; + fGap = 2; +} + + +#if 0 +BSize +KeyboardLayoutView::MinSize() +{ + // TODO! + BSize size(100, 100); + return size; +} +#endif + +void +KeyboardLayoutView::KeyDown(const char* bytes, int32 numBytes) +{ + _KeyChanged(Window()->CurrentMessage()); +} + + +void +KeyboardLayoutView::KeyUp(const char* bytes, int32 numBytes) +{ + _KeyChanged(Window()->CurrentMessage()); +} + + +void +KeyboardLayoutView::MouseDown(BPoint point) +{ +} + + +void +KeyboardLayoutView::MouseUp(BPoint point) +{ +} + + +void +KeyboardLayoutView::MouseMoved(BPoint point, uint32 transit, + const BMessage* dragMessage) +{ +} + + +void +KeyboardLayoutView::Draw(BRect updateRect) +{ + rgb_color brightColor = {230, 230, 230}; + rgb_color darkColor = {200, 200, 200}; + + for (int32 i = 0; i < fLayout->CountKeys(); i++) { + Key* key = fLayout->KeyAt(i); + + BRect rect = _FrameFor(key); + rgb_color base = key->dark ? darkColor : brightColor; + bool pressed = _IsKeyPressed(key->code); + bool special = false; + + char text[32]; + if (fKeymap != NULL) { + _GetKeyLabel(key, text, sizeof(text), special); + } else { + // Show the key code if there is no keymap + snprintf(text, sizeof(text), "%02lx", key->code); + } + + if (special) { + fSpecialFont.SetSize(_FontSizeFor(rect, text) * 0.8); + SetFont(&fSpecialFont); + } else { + fFont.SetSize(_FontSizeFor(rect, text)); + SetFont(&fFont); + } + + if (key->shape == kRectangleKeyShape) { + be_control_look->DrawButtonFrame(this, rect, updateRect, base, + pressed ? BControlLook::B_ACTIVATED : 0); + be_control_look->DrawButtonBackground(this, rect, updateRect, + base, pressed ? BControlLook::B_ACTIVATED : 0); + + // TODO: make font size depend on key size! + rect.InsetBy(1, 1); + + be_control_look->DrawLabel(this, text, rect, updateRect, + base, 0, BAlignment(B_ALIGN_CENTER, B_ALIGN_MIDDLE)); + } else if (key->shape == kEnterKeyShape) { + // TODO: make better! + rect.bottom -= 20; + be_control_look->DrawButtonBackground(this, rect, updateRect, + key->dark ? darkColor : brightColor, 0, + BControlLook::B_LEFT_BORDER | BControlLook::B_RIGHT_BORDER + | BControlLook::B_TOP_BORDER); + + rect = _FrameFor(key); + rect.top += 20; + rect.left += 10; + be_control_look->DrawButtonBackground(this, rect, updateRect, + key->dark ? darkColor : brightColor, 0, + BControlLook::B_LEFT_BORDER | BControlLook::B_RIGHT_BORDER + | BControlLook::B_BOTTOM_BORDER); + } + } +} + + +void +KeyboardLayoutView::MessageReceived(BMessage* message) +{ + switch (message->what) { + case B_UNMAPPED_KEY_DOWN: + case B_UNMAPPED_KEY_UP: + _KeyChanged(message); + break; + + case B_MODIFIERS_CHANGED: + if (message->FindInt32("modifiers", &fModifiers) == B_OK) + Invalidate(); + break; + + default: + BView::MessageReceived(message); + break; + } +} + + +const char* +KeyboardLayoutView::_ModifierKeyLabel(const key_map& map, uint32 code) +{ + if (code == map.caps_key) + return "CAPS LOCK"; + if (code == map.scroll_key) + return "SCROLL"; + if (code == map.num_key) + return "NUM LOCK"; + if (code == map.left_shift_key || code == map.right_shift_key) + return "SHIFT"; + if (code == map.left_command_key || code == map.right_command_key) + return "COMMAND"; + if (code == map.left_control_key || code == map.right_control_key) + return "CONTROL"; + if (code == map.left_option_key || code == map.right_option_key) + return "OPTION"; + if (code == map.menu_key) + return "MENU"; + + return NULL; +} + + +const char* +KeyboardLayoutView::_SpecialKeyLabel(const char* bytes, size_t numBytes) +{ + if (numBytes != 1) + return NULL; + + if (bytes[0] == B_ESCAPE) + return "ESC"; + if (bytes[0] == B_TAB) + return "\xe2\x86\xb9"; + if (bytes[0] == B_ENTER) + return "\xe2\x86\xb5"; + if (bytes[0] == B_BACKSPACE) + return "back";//\xe2\x86\x92"; + + if (bytes[0] == B_INSERT) + return "INS"; + if (bytes[0] == B_DELETE) + return "DEL"; + if (bytes[0] == B_HOME) + return "HOME"; + if (bytes[0] == B_END) + return "END"; + if (bytes[0] == B_PAGE_UP) + return "PAGE \xe2\x86\x91"; + if (bytes[0] == B_PAGE_DOWN) + return "PAGE \xe2\x86\x93"; + + if (bytes[0] == B_UP_ARROW) + return "\xe2\x86\x91"; + if (bytes[0] == B_LEFT_ARROW) + return "\xe2\x86\x90"; + if (bytes[0] == B_DOWN_ARROW) + return "\xe2\x86\x93"; + if (bytes[0] == B_RIGHT_ARROW) + return "\xe2\x86\x92"; + + return NULL; +} + + +bool +KeyboardLayoutView::_FunctionKeyLabel(const char* bytes, size_t numBytes, + char* text, size_t textSize) +{ + if (bytes[0] != B_FUNCTION_KEY) + return false; + + if (bytes[1] >= B_F1_KEY && bytes[1] <= B_F12_KEY) { + snprintf(text, textSize, "F%d", bytes[1] + 1 - B_F1_KEY); + return true; + } + + return false; +} + + +void +KeyboardLayoutView::_GetKeyLabel(Key* key, char* text, size_t textSize, + bool& specialKey) +{ + const key_map& map = fKeymap->Map(); + specialKey = false; + text[0] = '\0'; + + const char* modifier = _ModifierKeyLabel(map, key->code); + if (modifier != NULL) { + strlcpy(text, modifier, textSize); + specialKey = true; + return; + } + + char* bytes = NULL; + int32 numBytes; + fKeymap->GetChars(key->code, fModifiers, fDeadKey, &bytes, + &numBytes); + if (bytes != NULL) { + const char* special = _SpecialKeyLabel(bytes, numBytes); + if (special != NULL) { + strlcpy(text, special, textSize); + specialKey = true; + return; + } + if (_FunctionKeyLabel(bytes, numBytes, text, textSize)) { + specialKey = true; + return; + } + + bool hasGlyphs; + fFont.GetHasGlyphs(bytes, 1, &hasGlyphs); + if (hasGlyphs) + strlcpy(text, bytes, sizeof(text)); + + delete[] bytes; + } +} + + +bool +KeyboardLayoutView::_IsKeyPressed(int32 code) +{ + if (code >= 16 * 8) + return false; + + return (fKeyState[code / 8] & (1 << (7 - (code & 7)))) != 0; +} + + +Key* +KeyboardLayoutView::_KeyForCode(int32 code) +{ + // TODO: have a lookup array + + for (int32 i = 0; i < fLayout->CountKeys(); i++) { + Key* key = fLayout->KeyAt(i); + if (key->code == code) + return key; + } + + return NULL; +} + + +void +KeyboardLayoutView::_InvalidateKey(int32 code) +{ + Key* key = _KeyForCode(code); + if (key != NULL) + Invalidate(_FrameFor(key)); +} + + +void +KeyboardLayoutView::_KeyChanged(BMessage* message) +{ + const uint8* state; + ssize_t size; + if (message->FindData("states", B_UINT8_TYPE, (const void**)&state, &size) + != B_OK) + return; + + // Update key state, and invalidate change keys + + for (int32 i = 0; i < 16; i++) { + if (fKeyState[i] != state[i]) { + uint8 diff = fKeyState[i] ^ state[i]; + fKeyState[i] = state[i]; + + for (int32 j = 7; diff != 0; j--, diff >>= 1) { + if (diff & 1) { + _InvalidateKey(i * 8 + j); + } + } + } + } +} + + +BRect +KeyboardLayoutView::_FrameFor(Key* key) +{ + BRect rect; + rect.left = key->frame.left * fFactor; + rect.right = (key->frame.right - key->frame.left) * fFactor + rect.left + - fGap - 1; + rect.top = key->frame.top * fFactor; + rect.bottom = (key->frame.bottom - key->frame.top) * fFactor + rect.top + - fGap - 1; + rect.OffsetBy(fOffset); + + return rect; +} + + +float +KeyboardLayoutView::_FontSizeFor(BRect frame, const char* text) +{ +#if 0 + //float stringWidth = fFont.StringWidth(text); + // TODO: take width into account! + + float size = fFont.Size() * (frame.Height() - 6) / fFontHeight; + if (size > fMaxFontSize) + return fMaxFontSize; +#endif + + return 10.0f; +} diff --git a/src/preferences/keymap/KeyboardLayoutView.h b/src/preferences/keymap/KeyboardLayoutView.h new file mode 100644 index 0000000000..3f3133d5b7 --- /dev/null +++ b/src/preferences/keymap/KeyboardLayoutView.h @@ -0,0 +1,70 @@ +/* + * Copyright 2009, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ +#ifndef KEYBOARD_LAYOUT_VIEW_H +#define KEYBOARD_LAYOUT_VIEW_H + + +#include + +#include "KeyboardLayout.h" + +class Keymap; + + +class KeyboardLayoutView : public BView { +public: + KeyboardLayoutView(const char* name); + ~KeyboardLayoutView(); + + void SetKeyboardLayout(KeyboardLayout* layout); + void SetKeymap(Keymap* keymap); + +protected: + virtual void AttachedToWindow(); + virtual void FrameResized(float width, float height); +// virtual BSize MinSize(); + + virtual void KeyDown(const char* bytes, int32 numBytes); + virtual void KeyUp(const char* bytes, int32 numBytes); + virtual void MouseDown(BPoint point); + virtual void MouseUp(BPoint point); + virtual void MouseMoved(BPoint point, uint32 transit, + const BMessage* dragMessage); + + virtual void Draw(BRect updateRect); + virtual void MessageReceived(BMessage* message); + +private: + const char* _ModifierKeyLabel(const key_map& map, uint32 code); + const char* _SpecialKeyLabel(const char* bytes, + size_t numBytes); + bool _FunctionKeyLabel(const char* bytes, + size_t numBytes, char* text, size_t textSize); + void _GetKeyLabel(Key* key, char* text, size_t textSize, + bool& specialKey); + bool _IsKeyPressed(int32 code); + Key* _KeyForCode(int32 code); + void _InvalidateKey(int32 code); + void _KeyChanged(BMessage* message); + BRect _FrameFor(Key* key); + float _FontSizeFor(BRect frame, const char* text); + + KeyboardLayout* fLayout; + Keymap* fKeymap; + + uint8 fKeyState[16]; + int32 fModifiers; + int32 fDeadKey; + + BFont fFont; + BFont fSpecialFont; + float fFontHeight; + float fMaxFontSize; + BPoint fOffset; + float fFactor; + float fGap; +}; + +#endif // KEYBOARD_LAYOUT_VIEW_H