* 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:
parent
7fd8106e2a
commit
42176b84a4
694
src/preferences/keymap/KeyboardLayout.cpp
Normal file
694
src/preferences/keymap/KeyboardLayout.cpp
Normal 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;
|
||||
}
|
||||
|
102
src/preferences/keymap/KeyboardLayout.h
Normal file
102
src/preferences/keymap/KeyboardLayout.h
Normal 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
|
419
src/preferences/keymap/KeyboardLayoutView.cpp
Normal file
419
src/preferences/keymap/KeyboardLayoutView.cpp
Normal 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;
|
||||
}
|
70
src/preferences/keymap/KeyboardLayoutView.h
Normal file
70
src/preferences/keymap/KeyboardLayoutView.h
Normal 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
|
Loading…
Reference in New Issue
Block a user