* Work in progress commit of the Keymap rework. Should not disturb the

existing code.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@29666 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2009-03-23 14:14:49 +00:00
parent 7fd8106e2a
commit 42176b84a4
4 changed files with 1285 additions and 0 deletions

View File

@ -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 <ctype.h>
#include <stdarg.h>
#include <vector>
#include <File.h>
//#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;
}

View File

@ -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 <map>
#include <Point.h>
#include <Rect.h>
#include <String.h>
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<BString, BString> 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

View File

@ -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 <ControlLook.h>
#include <Window.h>
#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;
}

View File

@ -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 <View.h>
#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