xref: /haiku/src/preferences/keymap/KeyboardLayout.cpp (revision 3ca2e85bfdeee3bcfd3194c208a42e961eea1c96)
1 /*
2  * Copyright 2009, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "KeyboardLayout.h"
8 
9 #include <ctype.h>
10 #include <stdarg.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <vector>
14 
15 #include <File.h>
16 #include <InterfaceDefs.h>
17 
18 
19 #undef TRACE
20 
21 //#define TRACE_LAYOUT
22 #ifdef TRACE_LAYOUT
23 #	define TRACE(x...) printf(x)
24 #else
25 #	define TRACE(x...)
26 #endif
27 
28 
KeyboardLayout()29 KeyboardLayout::KeyboardLayout()
30 	:
31 	fKeys(NULL),
32 	fKeyCount(0),
33 	fKeyCapacity(0),
34 	fIndicators(5, true),
35 	fIsDefault(true)
36 {
37 	SetDefault();
38 }
39 
40 
~KeyboardLayout()41 KeyboardLayout::~KeyboardLayout()
42 {
43 	free(fKeys);
44 }
45 
46 
47 const char*
Name()48 KeyboardLayout::Name()
49 {
50 	return fName.String();
51 }
52 
53 
54 int32
CountKeys()55 KeyboardLayout::CountKeys()
56 {
57 	return fKeyCount;
58 }
59 
60 
61 Key*
KeyAt(int32 index)62 KeyboardLayout::KeyAt(int32 index)
63 {
64 	if (index < 0 || index >= fKeyCount)
65 		return NULL;
66 
67 	return &fKeys[index];
68 }
69 
70 
71 int32
CountIndicators()72 KeyboardLayout::CountIndicators()
73 {
74 	return fIndicators.CountItems();
75 }
76 
77 
78 Indicator*
IndicatorAt(int32 index)79 KeyboardLayout::IndicatorAt(int32 index)
80 {
81 	return fIndicators.ItemAt(index);
82 }
83 
84 
85 BRect
Bounds()86 KeyboardLayout::Bounds()
87 {
88 	return fBounds;
89 }
90 
91 
92 BSize
DefaultKeySize()93 KeyboardLayout::DefaultKeySize()
94 {
95 	return fDefaultKeySize;
96 }
97 
98 
99 int32
IndexForModifier(int32 modifier)100 KeyboardLayout::IndexForModifier(int32 modifier)
101 {
102 	switch (modifier) {
103 		case B_CAPS_LOCK:
104 			return 58;
105 		case B_NUM_LOCK:
106 			return 33;
107 		case B_SCROLL_LOCK:
108 			return 14;
109 		case B_LEFT_SHIFT_KEY:
110 			return 74;
111 		case B_RIGHT_SHIFT_KEY:
112 			return 85;
113 		case B_LEFT_CONTROL_KEY:
114 			return 91;
115 		case B_RIGHT_CONTROL_KEY:
116 			return 98;
117 		case B_LEFT_OPTION_KEY:
118 			return 92;
119 		case B_RIGHT_OPTION_KEY:
120 			return 96;
121 		case B_LEFT_COMMAND_KEY:
122 			return 93;
123 		case B_RIGHT_COMMAND_KEY:
124 			return 95;
125 		case B_MENU_KEY:
126 			return 97;
127 	}
128 
129 	return 0;
130 }
131 
132 
133 status_t
Load(const char * path)134 KeyboardLayout::Load(const char* path)
135 {
136 	entry_ref ref;
137 	get_ref_for_path(path, &ref);
138 
139 	return Load(ref);
140 }
141 
142 
143 status_t
Load(entry_ref & ref)144 KeyboardLayout::Load(entry_ref& ref)
145 {
146 	BFile file;
147 	status_t status = file.SetTo(&ref, B_READ_ONLY);
148 	if (status != B_OK)
149 		return status;
150 
151 	off_t size;
152 	status = file.GetSize(&size);
153 	if (status != B_OK)
154 		return status;
155 
156 	if (size > 65536) {
157 		// We don't accept files larger than this
158 		return B_BAD_VALUE;
159 	}
160 
161 	char* data = (char*)malloc(size + 1);
162 	if (data == NULL)
163 		return B_NO_MEMORY;
164 
165 	ssize_t bytesRead = file.Read(data, size);
166 	if (bytesRead != size) {
167 		free(data);
168 
169 		if (bytesRead < 0)
170 			return bytesRead;
171 
172 		return B_IO_ERROR;
173 	}
174 
175 	data[size] = '\0';
176 
177 	status = _InitFrom(data);
178 	if (status == B_OK)
179 		fIsDefault = false;
180 
181 	free(data);
182 
183 	return status;
184 }
185 
186 
187 void
SetDefault()188 KeyboardLayout::SetDefault()
189 {
190 	// The keyboard layout description language defines the position,
191 	// size, shape, and scancodes of the keys on the keyboard, row by
192 	// row.
193 	// You can define variables that are substituted in the row
194 	// descriptions. Variables are always prefixed with a '$' symbol.
195 
196 	// On top level, only two different terms are accepted: value pairs,
197 	// and row descriptions.
198 	// Value pairs are in the form "<name> = <value>". There are only two
199 	// predefined names at this moment "name" that specifies the name of
200 	// a layout, and "default-size" that specifies the size of keys when
201 	// no specific size is given. Also, variables can be specified this
202 	// way.
203 
204 	// Row descriptions are embedded in the '[' and ']' brackets.
205 	// The first term within a row is the position of the row, written as
206 	// "<x>,<y>;" - the delimiter between terms/keys is usually the
207 	// semi-colon. After the initial position, key descriptions are expected
208 	// until the row is closed with a ']'.
209 
210 	// A key description is of the form "<shape>:<scancodes>", where <shape>
211 	// is combined of the shape of the character, and its size, written as
212 	// "<kind><width>,<height>[,<second-row-width>]".
213 	// The kind can either be 'r' (the default, can also be omitted) for
214 	// rectangular keys, or 'l' for the enter key. Additionally, you can use
215 	// the 'd' character to mark dark keys. The size can be omitted completely,
216 	// in which case the defined "default-size" is used. The default size is
217 	// also used to determine the height of the first row of the enter key;
218 	// the "<second-row-width>" specifier is only valid for enter keys, too.
219 
220 	// The scancodes can be written in different ways:
221 	// 1) "+<count>": adds <count> keys, the scancodes will be used relative
222 	//    to the previous keys.
223 	// 2) "<first>-<last>": keys are added for scancode <first> to scancode
224 	//    <last>.
225 	// 3) "<first>+<count>": one key with the <first> scancode is added, plus
226 	//    <count> ones with relative scancode from that one.
227 
228 	// Finally, you can also define LED indicator. Those can be made instead
229 	// of a key, it accepts a size, but no shape modifiers. Also, instead of
230 	// a scancode, the name of the modifer is given, currently only the
231 	// following are allowed: "led-num", "led-caps", and "led-scroll".
232 
233 	// See for examples in the default layout below.
234 #if 1
235 	static const char* kDefaultLayout105 =
236 		"name = Generic 105-key International\n"
237 		// Size shortcuts
238 		"default-size = 10,10\n"
239 		"$b = 5,10\n"
240 		"$c = 20,10\n"
241 		"$d = 15,10\n"
242 		"$e = l15,20,9\n"
243 		"$f = 10,20\n"
244 		"$g = 13,10\n"
245 		// Key rows
246 		"[ 0,0; d:0x01; :-; :+4; $b:-; d:+4; $b:-; :+4; $b:-; d:+3; $b:-; "
247 			"$g:led-num; $g:led-caps; $g:led-scroll ]\n"
248 		"[ 0,20; :+13; d$c:+; $b:-; d:+3; $b:-; d:+4 ]\n"
249 		"[ 0,30; d$d:0x26; :+12; d$e:0x47; $b:-; d:0x34-0x36; $b:-; :+3; "
250 			"d$f:+1 ]\n"
251 		"[ 0,40; d19,10:0x3b; :+11; :0x33; 51,10:-; :0x48-0x4a ]\n"
252 		"[ 0,50; d$g:0x4b; :0x69; :0x4c+9; d27,10:+1; 15,10:-; d:+1; "
253 		"	15,10:-; :+3; d$f:+1 ]\n"
254 		"[ 0,60; d$g:0x5c; d$g:0x66; d$g:0x5d; 59,10:+1; d$g:+1; d$g:0x67+1; "
255 			"d$g:0x60; $b:-; d:+3; $b:-; $c:+1; :+1 ]\n";
256 
257 	_InitFrom(kDefaultLayout105);
258 #endif
259 #if 0
260 	static const char* kDefaultLayout104 = "name = Generic 104-key\n"
261 		// Size shortcuts
262 		"default-size = 10,10\n"
263 		"$b = 5,10\n"
264 		"$c = 20,10\n"
265 		"$d = 15,10\n"
266 		"$enter = d20,10\n"
267 		"$f = 10,20\n"
268 		"$g = 13,10\n"
269 		// Key rows
270 		"[ 0,0; d:0x01; :-; :+4; $b:-; d:+4; $b:-; :+4; $b:-; d:+3; $b:-; "
271 			"$g:led-num; $g:led-caps; $g:led-scroll ]\n"
272 		"[ 0,20; :+13; d$c:+; $b:-; d:+3; $b:-; d:+4 ]\n"
273 		"[ 0,30; d$d:0x26; :+12; $d:+1; $b:-; d:+3; $b:-; :+3; "
274 			"d$f:+1 ]\n"
275 		"[ 0,40; d$c:0x3b; :+11; $enter:+1; 40,10:-; :+3 ]\n"
276 		"[ 0,50; d24,10:0x4b; :+10; d26,10:+1; 15,10:-; d:+1; "
277 		"	15,10:-; :+3; d$f:+1 ]\n"
278 		"[ 0,60; d$g:0x5c; d$g:0x66; d$g:0x5d; 59,10:+1; d$g:+1; d$g:0x67+1; "
279 			"d$g:0x60; $b:-; d:+3; $b:-; $c:+1; :+1 ]\n";
280 
281 	_InitFrom(kDefaultLayout104);
282 #endif
283 #if 0
284 	static const char* kIBMLaptop = "name = IBM Laptop International\n"
285 		// Size shortcuts
286 		"default-size = 18,18\n"
287 		"$s = 17,10\n"
288 		"$gap = 6,10\n"
289 		"$sgap = 5,10\n"
290 		"$backspace = 38,18\n"
291 		"$tab = 28,18\n"
292 		"$caps = 32,18\n"
293 		"$enter = l28,36,22\n"
294 		"$l-shift-ctrl = 23,18\n"
295 		"$r-shift = 51,18\n"
296 		"$option = 13,18\n"
297 		"$space = 95,18\n"
298 		// Key rows
299 		"[ 0,0; $s:0x01; 148,10:-; $s:0x0e+2; $sgap:-; $s:0x1f+2; ]\n"
300 		"[ 0,10; $s:0x02+3; $gap:-; $s:+4; $gap:-; $s:+4; $sgap:-; "
301 			"$s:0x34+2; ]\n"
302 		"[ 0,20; :0x11+12; $backspace:+1 ]\n"
303 		"[ 0,38; $tab:0x26; :+12; $enter:0x47; ]\n"
304 		"[ 0,56; $caps:0x3b; :+11; :0x33 ]\n"
305 		"[ 0,74; $l-shift-ctrl:0x4b; :0x69; :0x4c+9; $r-shift:+1 ]\n"
306 		"[ 0,92; :0x99; $l-shift-ctrl:0x5c; $option:0x66; :0x5d; $space:+1; "
307 			":+1; :0x68; :0x60; $s:0x9a; $s:0x57; $s:0x9b ]\n"
308 		"[ 221,102; $s:0x61+2; ]\n";
309 
310 	_InitFrom(kIBMLaptop);
311 #endif
312 	fIsDefault = true;
313 }
314 
315 
316 void
_FreeKeys()317 KeyboardLayout::_FreeKeys()
318 {
319 	free(fKeys);
320 	fKeys = NULL;
321 	fKeyCount = 0;
322 	fKeyCapacity = 0;
323 	fBounds = BRect();
324 
325 	fIndicators.MakeEmpty();
326 }
327 
328 
329 void
_Error(const parse_state & state,const char * reason,...)330 KeyboardLayout::_Error(const parse_state& state, const char* reason, ...)
331 {
332 	va_list args;
333 	va_start(args, reason);
334 
335 	fprintf(stderr, "Syntax error in line %" B_PRId32 ": ", state.line);
336 	vfprintf(stderr, reason, args);
337 	fprintf(stderr, "\n");
338 
339 	va_end(args);
340 }
341 
342 
343 void
_AddAlternateKeyCode(Key * key,int32 modifier,int32 code)344 KeyboardLayout::_AddAlternateKeyCode(Key* key, int32 modifier, int32 code)
345 {
346 	if (key == NULL)
347 		return;
348 
349 	// TODO: search for free spot
350 }
351 
352 
353 bool
_AddKey(const Key & key)354 KeyboardLayout::_AddKey(const Key& key)
355 {
356 	TRACE("    add %ld (%g,%g)\n", key.code, key.frame.left, key.frame.top);
357 	if (fKeyCount + 1 > fKeyCapacity) {
358 		// enlarge array
359 		int32 newCapacity = fKeyCapacity + 32;
360 		Key* newKeys = (Key*)realloc((void*)fKeys, newCapacity * sizeof(Key));
361 		if (newKeys == NULL)
362 			return false;
363 
364 		fKeys = newKeys;
365 		fKeyCapacity = newCapacity;
366 	}
367 
368 	fKeys[fKeyCount] = key;
369 	fKeyCount++;
370 
371 	fBounds = key.frame | fBounds;
372 
373 	return true;
374 }
375 
376 
377 void
_SkipCommentsAndSpace(parse_state & state,const char * & data)378 KeyboardLayout::_SkipCommentsAndSpace(parse_state& state, const char*& data)
379 {
380 	while (data[0] != '\0') {
381 		while (isspace(data[0])) {
382 			if (data[0] == '\n')
383 				state.line++;
384 			data++;
385 		}
386 
387 		if (data[0] == '#') {
388 			// skip comment
389 			while (data[0] != '\0' && data[0] != '\n') {
390 				data++;
391 			}
392 		} else
393 			break;
394 	}
395 }
396 
397 
398 void
_Trim(BString & string,bool stripComments)399 KeyboardLayout::_Trim(BString& string, bool stripComments)
400 {
401 	// Strip leading spaces
402 	int32 i = 0;
403 	while (isspace(string[i])) {
404 		i++;
405 	}
406 	if (i > 0)
407 		string.Remove(0, i);
408 
409 	// Remove comments
410 	if (stripComments) {
411 		i = string.FindFirst('#');
412 		if (i >= 0)
413 			string.Truncate(i);
414 	}
415 
416 	// Strip trailing spaces
417 	i = string.Length() - 1;
418 	while (i > 0 && isspace(string[i])) {
419 		i--;
420 	}
421 	string.Truncate(i + 1);
422 }
423 
424 
425 bool
_GetPair(const parse_state & state,const char * & data,BString & name,BString & value)426 KeyboardLayout::_GetPair(const parse_state& state, const char*& data,
427 	BString& name, BString& value)
428 {
429 	// Get name
430 	name = "";
431 	while (data[0] != '\0' && data[0] != '=') {
432 		name += data[0];
433 		data++;
434 	}
435 
436 	if (data[0] != '=') {
437 		_Error(state, "no valid pair");
438 		return false;
439 	}
440 
441 	// Skip sign
442 	data++;
443 
444 	// Get value
445 	value = "";
446 	while (data[0] != '\0' && data[0] != '\n') {
447 		value += data[0];
448 		data++;
449 	}
450 
451 	_Trim(name, false);
452 	_Trim(value, true);
453 
454 	return true;
455 }
456 
457 
458 bool
_AddKeyCodes(const parse_state & state,BPoint & rowLeftTop,Key & key,const char * data,int32 & lastCount)459 KeyboardLayout::_AddKeyCodes(const parse_state& state, BPoint& rowLeftTop,
460 	Key& key, const char* data, int32& lastCount)
461 {
462 	if (data[0] == '-') {
463 		// no key, just free space
464 		int32 num = strtoul(data + 1, NULL, 0);
465 		if (num < 1)
466 			num = 1;
467 		else if (num > 32) {
468 			_Error(state, "empty key count too large");
469 			return false;
470 		}
471 
472 		key.frame.OffsetTo(rowLeftTop);
473 		rowLeftTop.x = key.frame.left + key.frame.Width() * num;
474 		return true;
475 	}
476 
477 	int32 modifier = 0;
478 
479 	if (isalpha(data[0])) {
480 		bool led = false;
481 		if (!strcmp("led-caps", data)) {
482 			modifier = B_CAPS_LOCK;
483 			led = true;
484 		} else if (!strcmp("led-num", data)) {
485 			modifier = B_NUM_LOCK;
486 			led = true;
487 		} else if (!strcmp("led-scroll", data)) {
488 			modifier = B_SCROLL_LOCK;
489 			led = true;
490 		} else {
491 			// TODO: get modifier (ie. "num")
492 		}
493 
494 		if (led) {
495 			key.frame.OffsetTo(rowLeftTop);
496 			rowLeftTop.x = key.frame.right;
497 			fBounds = key.frame | fBounds;
498 
499 			Indicator* indicator = new(std::nothrow) Indicator;
500 			if (indicator != NULL) {
501 				indicator->modifier = modifier;
502 				indicator->frame = key.frame;
503 
504 				fIndicators.AddItem(indicator);
505 			}
506 			return true;
507 		}
508 	}
509 
510 	int32 first;
511 	int32 last;
512 	int32 num = 1;
513 
514 	if (data[0] == '+') {
515 		num = strtoul(data + 1, NULL, 0);
516 		if (num < 1)
517 			num = 1;
518 		else if (num > 32) {
519 			_Error(state, "key count too large");
520 			return false;
521 		}
522 
523 		if (fKeyCount > 0)
524 			first = fKeys[fKeyCount - 1].code + 1;
525 		else
526 			first = 1;
527 
528 		last = first + num - 1;
529 	} else {
530 		char* end;
531 		first = strtoul(data, &end, 0);
532 		last = first;
533 
534 		if (end[0] == '-') {
535 			last = strtoul(end + 1, NULL, 0);
536 			if (first > last) {
537 				_Error(state, "invalid key code specifier");
538 				return false;
539 			}
540 
541 			num = last - first;
542 		} else if (end[0] == '+') {
543 			num = strtoul(end + 1, NULL, 0) + 1;
544 			last = first + num - 1;
545 		} else if (end[0] != '\0') {
546 			_Error(state, "invalid key range");
547 			return false;
548 		}
549 	}
550 
551 	if (lastCount != 0) {
552 		// update existing keys
553 		if (lastCount != num) {
554 			_Error(state, "modifier key mismatch");
555 			return false;
556 		}
557 
558 		for (int32 i = fKeyCount - num; i < fKeyCount; i++, first++) {
559 			Key* key = KeyAt(i);
560 
561 			_AddAlternateKeyCode(key, modifier, first);
562 		}
563 	} else {
564 		// add new keys
565 		for (int32 i = first; i <= last; i++) {
566 			key.code = i;
567 
568 			// "layout"
569 			key.frame.OffsetTo(rowLeftTop);
570 			rowLeftTop.x = key.frame.right;
571 
572 			_AddKey(key);
573 		}
574 
575 		lastCount = num;
576 	}
577 
578 	return true;
579 }
580 
581 
582 bool
_GetSize(const parse_state & state,const char * data,float & x,float & y,float * _secondRow)583 KeyboardLayout::_GetSize(const parse_state& state, const char* data,
584 	float& x, float& y, float* _secondRow)
585 {
586 	if (data[0] == '\0') {
587 		// default size
588 		x = fDefaultKeySize.width;
589 		y = fDefaultKeySize.height;
590 		return true;
591 	}
592 
593 	float secondRow = 0;
594 	int num = sscanf(data, "%g,%g,%g", &x, &y, &secondRow);
595 	if (num < 2) {
596 		_Error(state, "invalid size");
597 		return false;
598 	}
599 
600 	if (_secondRow != NULL)
601 		*_secondRow = secondRow;
602 	return true;
603 }
604 
605 
606 bool
_GetShape(const parse_state & state,const char * data,Key & key)607 KeyboardLayout::_GetShape(const parse_state& state, const char* data, Key& key)
608 {
609 	// the default
610 	key.shape = kRectangleKeyShape;
611 	key.dark = false;
612 
613 	while (isalpha(data[0])) {
614 		switch (tolower(data[0])) {
615 			case 'r':
616 				key.shape = kRectangleKeyShape;
617 				break;
618 			case 'c':
619 				key.shape = kCircleKeyShape;
620 				break;
621 			case 'l':
622 				key.shape = kEnterKeyShape;
623 				break;
624 			case 'd':
625 				key.dark = true;
626 				break;
627 
628 			default:
629 				_Error(state, "unknown shape specifier '%c'", data[0]);
630 				return false;
631 		}
632 
633 		data++;
634 	}
635 
636 	float width, height;
637 	if (!_GetSize(state, data, width, height, &key.second_row))
638 		return false;
639 
640 	// don't accept second row with anything but kEnterKeyShape
641 	if ((key.shape != kEnterKeyShape && key.second_row != 0)
642 		|| (key.shape == kEnterKeyShape && key.second_row == 0)) {
643 		_Error(state, "shape size mismatch");
644 		return false;
645 	}
646 
647 	key.frame.left = 0;
648 	key.frame.top = 0;
649 	key.frame.right = width;
650 	key.frame.bottom = height;
651 
652 	return true;
653 }
654 
655 
656 /*!	Returns the term delimiter expected in a certain parse mode. */
657 const char*
_Delimiter(parse_mode mode)658 KeyboardLayout::_Delimiter(parse_mode mode)
659 {
660 	switch (mode) {
661 		default:
662 		case kSize:
663 			return "";
664 		case kRowStart:
665 			return ";";
666 
667 		case kKeyShape:
668 			return ":";
669 		case kKeyCodes:
670 			return ";:";
671 	}
672 }
673 
674 
675 bool
_GetTerm(const char * & data,const char * delimiter,BString & term,bool closingBracketAllowed)676 KeyboardLayout::_GetTerm(const char*& data, const char* delimiter,
677 	BString& term, bool closingBracketAllowed)
678 {
679 	// Get term
680 	term = "";
681 	while (data[0] != '\0' && strchr(delimiter, data[0]) == NULL
682 		&& data[0] != '\n' && data[0] != '#'
683 		&& (!closingBracketAllowed || data[0] != ']')) {
684 		term += data[0];
685 		data++;
686 	}
687 
688 	if (data[0] == '\0' && delimiter[0])
689 		return false;
690 
691 	_Trim(term, true);
692 	return true;
693 }
694 
695 
696 bool
_SubstituteVariables(BString & term,VariableMap & variables,BString & unknown)697 KeyboardLayout::_SubstituteVariables(BString& term, VariableMap& variables,
698 	BString& unknown)
699 {
700 	while (true) {
701 		int32 index = term.FindFirst('$');
702 		if (index < 0)
703 			break;
704 
705 		// find variable name
706 
707 		VariableMap::iterator iterator = variables.begin();
708 		VariableMap::iterator best = variables.end();
709 		int32 bestLength = 0;
710 
711 		for (; iterator != variables.end(); iterator++) {
712 			const BString& name = iterator->first;
713 			if (!term.CompareAt(index, name, name.Length())
714 				&& name.Length() > bestLength) {
715 				best = iterator;
716 				bestLength = name.Length();
717 			}
718 		}
719 
720 		if (best != variables.end()) {
721 			// got one, replace it
722 			term.Remove(index, bestLength);
723 			term.Insert(best->second.String(), index);
724 		} else {
725 			// variable has not been found
726 			int32 length = 1;
727 			while (isalpha(term[length + index])) {
728 				length++;
729 			}
730 			term.Truncate(length + index);
731 			return false;
732 		}
733 	}
734 
735 	return true;
736 }
737 
738 
739 bool
_ParseTerm(const parse_state & state,const char * & data,BString & term,VariableMap & variables)740 KeyboardLayout::_ParseTerm(const parse_state& state, const char*& data,
741 	BString& term, VariableMap& variables)
742 {
743 	if (!_GetTerm(data, _Delimiter(state.mode), term,
744 			state.mode == kKeyCodes)) {
745 		_Error(state, state.mode == kRowStart
746 			? "no valid row start" : "invalid term");
747 		return false;
748 	}
749 
750 	BString unknown;
751 	if (!_SubstituteVariables(term, variables, unknown)) {
752 		_Error(state, "Unknown variable \"%s\"", unknown.String());
753 		return false;
754 	}
755 
756 	return true;
757 }
758 
759 
760 /*!	Initializes the keyboard layout from the data given.
761 	The string has to be a valid keyboard layout description, otherwise
762 	an error is returned.
763 */
764 status_t
_InitFrom(const char * data)765 KeyboardLayout::_InitFrom(const char* data)
766 {
767 	_FreeKeys();
768 
769 	VariableMap variables;
770 	BPoint rowLeftTop;
771 	int32 lastKeyCount = 0;
772 	Key key;
773 
774 	parse_state state = {kPairs, 1};
775 
776 	while (data[0] != '\0') {
777 		_SkipCommentsAndSpace(state, data);
778 
779 		if (data[0] == '[') {
780 			state.mode = kRowStart;
781 
782 			rowLeftTop = BPoint(0, 0);
783 			data++;
784 			continue;
785 		} else if (data[0] == '\0')
786 			break;
787 
788 		switch (state.mode) {
789 			case kPairs:
790 			{
791 				BString name;
792 				BString value;
793 				if (!_GetPair(state, data, name, value))
794 					return B_BAD_VALUE;
795 
796 				TRACE("<%s> = <%s>\n", name.String(), value.String());
797 				if (name == "name")
798 					fName = value;
799 				else if (name == "default-size") {
800 					const char* valueString = value.String();
801 					parse_state tempState = {kSize, state.line};
802 					BString term;
803 					if (!_ParseTerm(tempState, valueString, term, variables))
804 						return B_BAD_VALUE;
805 
806 					TRACE("  size = %s\n", term.String());
807 					if (!_GetSize(state, term.String(), fDefaultKeySize.width,
808 							fDefaultKeySize.height))
809 						return B_BAD_VALUE;
810 				} else if (name[0] == '$')
811 					variables[name] = value;
812 				break;
813 			}
814 
815 			case kRowStart:
816 			case kKeyShape:
817 			case kKeyCodes:
818 			{
819 				if (data[0] == ']') {
820 					if (state.mode == kKeyShape) {
821 						state.mode = kPairs;
822 						data++;
823 						continue;
824 					}
825 					_Error(state, "unexpected row closing bracket");
826 					return B_BAD_VALUE;
827 				}
828 
829 				BString term;
830 				if (!_ParseTerm(state, data, term, variables))
831 					return B_BAD_VALUE;
832 
833 				switch (state.mode) {
834 					case kRowStart:
835 						if (!_GetSize(state, term.String(), rowLeftTop.x,
836 								rowLeftTop.y))
837 							return B_BAD_VALUE;
838 
839 						TRACE("row: %s (%g:%g)\n", term.String(), rowLeftTop.x,
840 							rowLeftTop.y);
841 
842 						state.mode = kKeyShape;
843 						break;
844 					case kKeyShape:
845 						memset((void*)&key, 0, sizeof(Key));
846 						if (!_GetShape(state, term.String(), key))
847 							return B_BAD_VALUE;
848 
849 						TRACE("  shape: %s (%g:%g:%g)\n", term.String(),
850 							key.frame.Width(), key.frame.Height(),
851 							key.second_row);
852 
853 						lastKeyCount = 0;
854 						state.mode = kKeyCodes;
855 						break;
856 					case kKeyCodes:
857 						TRACE("   raw key: %s\n", term.String());
858 
859 						if (!_AddKeyCodes(state, rowLeftTop, key, term.String(),
860 								lastKeyCount))
861 							return B_BAD_VALUE;
862 
863 						if (data[0] != ':')
864 							state.mode = kKeyShape;
865 						break;
866 
867 					default:
868 						break;
869 				}
870 				if (data[0] != ']' && data[0] != '\0')
871 					data++;
872 				break;
873 			}
874 
875 			default:
876 				return B_BAD_VALUE;
877 		}
878 	}
879 
880 	return B_OK;
881 }
882 
883