xref: /haiku/src/preferences/keymap/Keymap.cpp (revision b289aaf66bbf6e173aa90fa194fc256965f1b34d)
1 /*
2  * Copyright 2004-2009 Haiku Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Sandor Vroemisse
7  *		Jérôme Duval
8  *		Axel Dörfler, axeld@pinc-software.de.
9  */
10 
11 #include "Keymap.h"
12 
13 #include <new>
14 #include <stdio.h>
15 #include <string.h>
16 
17 #include <ByteOrder.h>
18 #include <File.h>
19 
20 #include <input_globals.h>
21 
22 
23 static const uint32 kModifierKeys = B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY
24 	| B_CAPS_LOCK | B_OPTION_KEY | B_MENU_KEY;
25 
26 
27 static void
28 print_key(char *chars, int32 offset)
29 {
30 	int size = chars[offset++];
31 
32 	switch (size) {
33 		case 0:
34 			// Not mapped
35 			printf("N/A");
36 			break;
37 
38 		case 1:
39 			// 1-byte UTF-8/ASCII character
40 			printf("%c", chars[offset]);
41 			break;
42 
43 		default:
44 		{
45 			// 2-, 3-, or 4-byte UTF-8 character
46 			char *str = new char[size + 1];
47 			strncpy(str, &chars[offset], size);
48 			str[size] = 0;
49 			printf("%s", str);
50 			delete[] str;
51 			break;
52 		}
53 	}
54 
55 	printf("\t");
56 }
57 
58 
59 //	#pragma mark -
60 
61 
62 Keymap::Keymap()
63 	:
64 	fChars(NULL),
65 	fCharsSize(0),
66 	fModificationMessage(NULL)
67 {
68 }
69 
70 
71 Keymap::~Keymap()
72 {
73 	delete fModificationMessage;
74 }
75 
76 
77 void
78 Keymap::SetTarget(BMessenger target, BMessage* modificationMessage)
79 {
80 	delete fModificationMessage;
81 
82 	fTarget = target;
83 	fModificationMessage = modificationMessage;
84 }
85 
86 
87 void
88 Keymap::SetName(const char* name)
89 {
90 	strlcpy(fName, name, sizeof(fName));
91 }
92 
93 
94 void
95 Keymap::DumpKeymap()
96 {
97 	// Print a chart of the normal, shift, option, and option+shift
98 	// keys.
99 	printf("Key #\tNormal\tShift\tCaps\tC+S\tOption\tO+S\tO+C\tO+C+S\tControl\n");
100 	for (int i = 0; i < 128; i++) {
101 		printf(" 0x%x\t", i);
102 		print_key(fChars, fKeys.normal_map[i]);
103 		print_key(fChars, fKeys.shift_map[i]);
104 		print_key(fChars, fKeys.caps_map[i]);
105 		print_key(fChars, fKeys.caps_shift_map[i]);
106 		print_key(fChars, fKeys.option_map[i]);
107 		print_key(fChars, fKeys.option_shift_map[i]);
108 		print_key(fChars, fKeys.option_caps_map[i]);
109 		print_key(fChars, fKeys.option_caps_shift_map[i]);
110 		print_key(fChars, fKeys.control_map[i]);
111 		printf("\n");
112 	}
113 }
114 
115 
116 /*
117 	file format in big endian :
118 	struct key_map
119 	uint32 size of following charset
120 	charset (offsets go into this with size of character followed by character)
121 */
122 // we load a map from a file
123 status_t
124 Keymap::Load(entry_ref &ref)
125 {
126 	status_t err;
127 	BEntry entry(&ref, true);
128 	if ((err = entry.InitCheck()) != B_OK) {
129 		fprintf(stderr, "error loading keymap: %s\n", strerror(err));
130 		return err;
131 	}
132 
133 	BFile file(&entry, B_READ_ONLY);
134 	if ((err = file.InitCheck()) != B_OK) {
135 		fprintf(stderr, "error loading keymap: %s\n", strerror(err));
136 		return err;
137 	}
138 
139 	if ((err = file.Read(&fKeys, sizeof(fKeys))) < (ssize_t)sizeof(fKeys)) {
140 		fprintf(stderr, "error reading keymap keys: %s\n", strerror(err));
141 		return B_BAD_VALUE;
142 	}
143 
144 	for (uint32 i = 0; i < sizeof(fKeys) / 4; i++)
145 		((uint32*)&fKeys)[i] = B_BENDIAN_TO_HOST_INT32(((uint32*)&fKeys)[i]);
146 
147 	if ((err = file.Read(&fCharsSize, sizeof(uint32))) < (ssize_t)sizeof(uint32)) {
148 		fprintf(stderr, "error reading keymap size: %s\n", strerror(err));
149 		return B_BAD_VALUE;
150 	}
151 
152 	fCharsSize = B_BENDIAN_TO_HOST_INT32(fCharsSize);
153 	delete[] fChars;
154 
155 	fChars = new char[fCharsSize];
156 
157 	ssize_t bytesRead = file.Read(fChars, fCharsSize);
158 	if (bytesRead < 0) {
159 		fprintf(stderr, "error reading keymap chars: %s\n", strerror(err));
160 		return (status_t)bytesRead;
161 	}
162 
163 	// fetch name from attribute and fall back to filename
164 
165 	bytesRead = file.ReadAttr("keymap:name", B_STRING_TYPE, 0, fName,
166 		sizeof(fName));
167 	if (bytesRead > 0)
168 		fName[bytesRead] = '\0';
169 	else
170 		strlcpy(fName, ref.name, sizeof(fName));
171 	return B_OK;
172 }
173 
174 
175 //!	We save a map to a file
176 status_t
177 Keymap::Save(entry_ref& ref)
178 {
179 	BFile file;
180 	status_t status = file.SetTo(&ref,
181 		B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
182 	if (status != B_OK) {
183 		printf("error %s\n", strerror(status));
184 		return status;
185 	}
186 
187 	for (uint32 i = 0; i < sizeof(fKeys) / 4; i++)
188 		((uint32*)&fKeys)[i] = B_HOST_TO_BENDIAN_INT32(((uint32*)&fKeys)[i]);
189 
190 	ssize_t bytesWritten = file.Write(&fKeys, sizeof(fKeys));
191 	if (bytesWritten < (ssize_t)sizeof(fKeys))
192 		status = bytesWritten < 0 ? bytesWritten : B_IO_ERROR;
193 
194 	for (uint32 i = 0; i < sizeof(fKeys) / 4; i++)
195 		((uint32*)&fKeys)[i] = B_BENDIAN_TO_HOST_INT32(((uint32*)&fKeys)[i]);
196 
197 	if (status == B_OK) {
198 		fCharsSize = B_HOST_TO_BENDIAN_INT32(fCharsSize);
199 
200 		bytesWritten = file.Write(&fCharsSize, sizeof(uint32));
201 		if (bytesWritten < (ssize_t)sizeof(uint32))
202 			status = bytesWritten < 0 ? bytesWritten : B_IO_ERROR;
203 
204 		fCharsSize = B_BENDIAN_TO_HOST_INT32(fCharsSize);
205 	}
206 
207 	if (status == B_OK) {
208 		bytesWritten = file.Write(fChars, fCharsSize);
209 		if (bytesWritten < (ssize_t)fCharsSize)
210 			status = bytesWritten < 0 ? bytesWritten : B_IO_ERROR;
211 	}
212 
213 	if (status == B_OK) {
214 		file.WriteAttr("keymap:name", B_STRING_TYPE, 0, fName, strlen(fName));
215 			// Failing would be non-fatal
216 	}
217 
218 	return status;
219 }
220 
221 
222 bool
223 Keymap::Equals(const Keymap& other) const
224 {
225 	// not really efficient but this is the only way i found
226 	// to reliably compare keymaps (used only for apply and revert)
227 	return fCharsSize == other.fCharsSize
228 		&& !memcmp(&other.fKeys, &fKeys, sizeof(key_map))
229 		&& !memcmp(other.fChars, fChars, fCharsSize);
230 }
231 
232 
233 /*!	We need to know if a key is a modifier key to choose
234 	a valid key when several are pressed together.
235 */
236 bool
237 Keymap::IsModifierKey(uint32 keyCode)
238 {
239 	return keyCode == fKeys.caps_key
240 		|| keyCode == fKeys.num_key
241 		|| keyCode == fKeys.scroll_key
242 		|| keyCode == fKeys.left_shift_key
243 		|| keyCode == fKeys.right_shift_key
244 		|| keyCode == fKeys.left_command_key
245 		|| keyCode == fKeys.right_command_key
246 		|| keyCode == fKeys.left_control_key
247 		|| keyCode == fKeys.right_control_key
248 		|| keyCode == fKeys.left_option_key
249 		|| keyCode == fKeys.right_option_key
250 		|| keyCode == fKeys.menu_key;
251 }
252 
253 
254 //! We need to know a modifier for a key
255 uint32
256 Keymap::Modifier(uint32 keyCode)
257 {
258 	if (keyCode == fKeys.caps_key)
259 		return B_CAPS_LOCK;
260 	if (keyCode == fKeys.num_key)
261 		return B_NUM_LOCK;
262 	if (keyCode == fKeys.scroll_key)
263 		return B_SCROLL_LOCK;
264 	if (keyCode == fKeys.left_shift_key)
265 		return B_LEFT_SHIFT_KEY | B_SHIFT_KEY;
266 	if (keyCode == fKeys.right_shift_key)
267 		return B_RIGHT_SHIFT_KEY | B_SHIFT_KEY;
268 	if (keyCode == fKeys.left_command_key)
269 		return B_LEFT_COMMAND_KEY | B_COMMAND_KEY;
270 	if (keyCode == fKeys.right_command_key)
271 		return B_RIGHT_COMMAND_KEY | B_COMMAND_KEY;
272 	if (keyCode == fKeys.left_control_key)
273 		return B_LEFT_CONTROL_KEY | B_CONTROL_KEY;
274 	if (keyCode == fKeys.right_control_key)
275 		return B_RIGHT_CONTROL_KEY | B_CONTROL_KEY;
276 	if (keyCode == fKeys.left_option_key)
277 		return B_LEFT_OPTION_KEY | B_OPTION_KEY;
278 	if (keyCode == fKeys.right_option_key)
279 		return B_RIGHT_OPTION_KEY | B_OPTION_KEY;
280 	if (keyCode == fKeys.menu_key)
281 		return B_MENU_KEY;
282 
283 	return 0;
284 }
285 
286 
287 uint32
288 Keymap::KeyForModifier(uint32 modifier)
289 {
290 	if (modifier == B_CAPS_LOCK)
291 		return fKeys.caps_key;
292 	if (modifier == B_NUM_LOCK)
293 		return fKeys.num_key;
294 	if (modifier == B_SCROLL_LOCK)
295 		return fKeys.scroll_key;
296 	if (modifier == B_LEFT_SHIFT_KEY || modifier == B_SHIFT_KEY)
297 		return fKeys.left_shift_key;
298 	if (modifier == B_RIGHT_SHIFT_KEY)
299 		return fKeys.right_shift_key;
300 	if (modifier == B_LEFT_COMMAND_KEY || modifier == B_COMMAND_KEY)
301 		return fKeys.left_command_key;
302 	if (modifier == B_RIGHT_COMMAND_KEY)
303 		return fKeys.right_command_key;
304 	if (modifier == B_LEFT_CONTROL_KEY || modifier == B_CONTROL_KEY)
305 		return fKeys.left_control_key;
306 	if (modifier == B_RIGHT_CONTROL_KEY)
307 		return fKeys.right_control_key;
308 	if (modifier == B_LEFT_OPTION_KEY || modifier == B_OPTION_KEY)
309 		return fKeys.left_option_key;
310 	if (modifier == B_RIGHT_OPTION_KEY)
311 		return fKeys.right_option_key;
312 	if (modifier == B_MENU_KEY)
313 		return fKeys.menu_key;
314 
315 	return 0;
316 }
317 
318 
319 status_t
320 Keymap::SetModifier(uint32 keyCode, uint32 modifier)
321 {
322 	const uint32 kSingleKeys = B_LEFT_SHIFT_KEY | B_RIGHT_SHIFT_KEY
323 		| B_LEFT_COMMAND_KEY | B_RIGHT_COMMAND_KEY | B_LEFT_CONTROL_KEY
324 		| B_RIGHT_CONTROL_KEY | B_LEFT_OPTION_KEY | B_RIGHT_OPTION_KEY;
325 
326 	if ((modifier & kSingleKeys) != 0)
327 		modifier &= kSingleKeys;
328 	else if ((modifier & kModifierKeys) != 0)
329 		modifier &= kModifierKeys;
330 
331 	if (modifier == B_CAPS_LOCK)
332 		fKeys.caps_key = keyCode;
333 	else if (modifier == B_NUM_LOCK)
334 		fKeys.num_key = keyCode;
335 	else if (modifier == B_SCROLL_LOCK)
336 		fKeys.scroll_key = keyCode;
337 	else if (modifier == B_LEFT_SHIFT_KEY)
338 		fKeys.left_shift_key = keyCode;
339 	else if (modifier == B_RIGHT_SHIFT_KEY)
340 		fKeys.right_shift_key = keyCode;
341 	else if (modifier == B_LEFT_COMMAND_KEY)
342 		fKeys.left_command_key = keyCode;
343 	else if (modifier == B_RIGHT_COMMAND_KEY)
344 		fKeys.right_command_key = keyCode;
345 	else if (modifier == B_LEFT_CONTROL_KEY)
346 		fKeys.left_control_key = keyCode;
347 	else if (modifier == B_RIGHT_CONTROL_KEY)
348 		fKeys.right_control_key = keyCode;
349 	else if (modifier == B_LEFT_OPTION_KEY)
350 		fKeys.left_option_key = keyCode;
351 	else if (modifier == B_RIGHT_OPTION_KEY)
352 		fKeys.right_option_key = keyCode;
353 	else if (modifier == B_MENU_KEY)
354 		fKeys.menu_key = keyCode;
355 	else
356 		return B_BAD_VALUE;
357 
358 	if (fModificationMessage != NULL)
359 		fTarget.SendMessage(fModificationMessage);
360 
361 	return B_OK;
362 }
363 
364 
365 /*! Checks whether a key is a dead key.
366 	If it is, the enabled/disabled state of that dead key will be passed
367 	out via isEnabled (isEnabled is not touched for non-dead keys).
368 */
369 uint8
370 Keymap::IsDeadKey(uint32 keyCode, uint32 modifiers, bool* isEnabled)
371 {
372 	uint32 tableMask = 0;
373 	int32 offset = _Offset(keyCode, modifiers, &tableMask);
374 	uint8 deadKeyIndex = _GetDeadKeyIndex(offset);
375 	if (deadKeyIndex > 0 && isEnabled != NULL) {
376 		uint32 deadTables[] = {
377 			fKeys.acute_tables,
378 			fKeys.grave_tables,
379 			fKeys.circumflex_tables,
380 			fKeys.dieresis_tables,
381 			fKeys.tilde_tables
382 		};
383 		*isEnabled = (deadTables[deadKeyIndex - 1] & tableMask) != 0;
384 	}
385 
386 	return deadKeyIndex;
387 }
388 
389 
390 //! Tell if a key is a dead second key, needed for draw a dead second key.
391 bool
392 Keymap::IsDeadSecondKey(uint32 keyCode, uint32 modifiers, uint8 activeDeadKey)
393 {
394 	if (!activeDeadKey)
395 		return false;
396 
397 	int32 offset = _Offset(keyCode, modifiers);
398 	if (offset < 0)
399 		return false;
400 
401 	uint32 numBytes = fChars[offset];
402 	if (!numBytes)
403 		return false;
404 
405 	int32* deadOffsets[] = {
406 		fKeys.acute_dead_key,
407 		fKeys.grave_dead_key,
408 		fKeys.circumflex_dead_key,
409 		fKeys.dieresis_dead_key,
410 		fKeys.tilde_dead_key
411 	};
412 
413 	int32 *deadOffset = deadOffsets[activeDeadKey - 1];
414 
415 	for (int32 i=0; i<32; i++) {
416 		if (offset == deadOffset[i])
417 			return true;
418 
419 		uint32 deadNumBytes = fChars[deadOffset[i]];
420 
421 		if (!deadNumBytes)
422 			continue;
423 
424 		if (strncmp(&fChars[offset + 1], &fChars[deadOffset[i] + 1],
425 				deadNumBytes) == 0)
426 			return true;
427 		i++;
428 	}
429 	return false;
430 }
431 
432 
433 //! Enables/disables the "deadness" of the given keycode/modifier combo.
434 void
435 Keymap::SetDeadKeyEnabled(uint32 keyCode, uint32 modifiers, bool enabled)
436 {
437 	uint32 tableMask = 0;
438 	int32 offset = _Offset(keyCode, modifiers, &tableMask);
439 	uint8 deadKeyIndex = _GetDeadKeyIndex(offset);
440 	if (deadKeyIndex > 0) {
441 		uint32* deadTables[] = {
442 			&fKeys.acute_tables,
443 			&fKeys.grave_tables,
444 			&fKeys.circumflex_tables,
445 			&fKeys.dieresis_tables,
446 			&fKeys.tilde_tables
447 		};
448 
449 		if (enabled)
450 			(*deadTables[deadKeyIndex - 1]) |= tableMask;
451 		else
452 			(*deadTables[deadKeyIndex - 1]) &= ~tableMask;
453 
454 		if (fModificationMessage != NULL)
455 			fTarget.SendMessage(fModificationMessage);
456 	}
457 }
458 
459 
460 /*! Returns the trigger character string that is currently set for the dead
461 	key with the given index (which is 1..5).
462 */
463 void
464 Keymap::GetDeadKeyTrigger(dead_key_index deadKeyIndex, BString& outTrigger)
465 {
466 	outTrigger = "";
467 	if (deadKeyIndex < 1 || deadKeyIndex > 5)
468 		return;
469 
470 	int32 deadOffsets[] = {
471 		fKeys.acute_dead_key[1],
472 		fKeys.grave_dead_key[1],
473 		fKeys.circumflex_dead_key[1],
474 		fKeys.dieresis_dead_key[1],
475 		fKeys.tilde_dead_key[1]
476 	};
477 
478 	int32 offset = deadOffsets[deadKeyIndex - 1];
479 	if (offset < 0 || offset >= (int32)fCharsSize)
480 		return;
481 
482 	uint32 deadNumBytes = fChars[offset];
483 	if (!deadNumBytes)
484 		return;
485 
486 	outTrigger.SetTo(&fChars[offset + 1], deadNumBytes);
487 }
488 
489 
490 /*! Sets the trigger character string that shall be used for the dead key
491 	with the given index (which is 1..5).
492 */
493 void
494 Keymap::SetDeadKeyTrigger(dead_key_index deadKeyIndex, const BString& trigger)
495 {
496 	if (deadKeyIndex < 1 || deadKeyIndex > 5)
497 		return;
498 
499 	int32 deadOffsets[] = {
500 		fKeys.acute_dead_key[1],
501 		fKeys.grave_dead_key[1],
502 		fKeys.circumflex_dead_key[1],
503 		fKeys.dieresis_dead_key[1],
504 		fKeys.tilde_dead_key[1]
505 	};
506 
507 	int32 offset = deadOffsets[deadKeyIndex - 1];
508 	if (offset < 0 || offset >= (int32)fCharsSize)
509 		return;
510 
511 	if (_SetChars(offset, trigger.String(), trigger.Length())) {
512 		// reset modifier table such that new dead key is enabled wherever
513 		// it is available
514 		uint32* deadTables[] = {
515 			&fKeys.acute_tables,
516 			&fKeys.grave_tables,
517 			&fKeys.circumflex_tables,
518 			&fKeys.dieresis_tables,
519 			&fKeys.tilde_tables
520 		};
521 		*deadTables[deadKeyIndex - 1]
522 			= B_CONTROL_TABLE | B_OPTION_CAPS_SHIFT_TABLE | B_OPTION_CAPS_TABLE
523 				| B_OPTION_SHIFT_TABLE | B_OPTION_TABLE | B_CAPS_SHIFT_TABLE
524 				| B_CAPS_TABLE | B_SHIFT_TABLE | B_NORMAL_TABLE;
525 
526 		if (fModificationMessage != NULL)
527 			fTarget.SendMessage(fModificationMessage);
528 	}
529 }
530 
531 
532 //! Get the char for a key given modifiers and active dead key
533 void
534 Keymap::GetChars(uint32 keyCode, uint32 modifiers, uint8 activeDeadKey,
535 	char** chars, int32* numBytes)
536 {
537 	*numBytes = 0;
538 	*chars = NULL;
539 
540 	if (keyCode > 128 || fChars == NULL)
541 		return;
542 
543 	// here we take NUMLOCK into account
544 	if ((modifiers & B_NUM_LOCK) != 0) {
545 		switch (keyCode) {
546 			case 0x37:
547 			case 0x38:
548 			case 0x39:
549 			case 0x48:
550 			case 0x49:
551 			case 0x4a:
552 			case 0x58:
553 			case 0x59:
554 			case 0x5a:
555 			case 0x64:
556 			case 0x65:
557 				modifiers ^= B_SHIFT_KEY;
558 		}
559 	}
560 
561 	int32 offset = _Offset(keyCode, modifiers);
562 	if (offset < 0)
563 		return;
564 
565 	// here we get the char size
566 	*numBytes = fChars[offset];
567 	if (!*numBytes)
568 		return;
569 
570 	// here we take an potential active dead key
571 	int32 *deadKey;
572 	switch (activeDeadKey) {
573 		case kDeadKeyAcute:
574 			deadKey = fKeys.acute_dead_key;
575 			break;
576 		case kDeadKeyGrave:
577 			deadKey = fKeys.grave_dead_key;
578 			break;
579 		case kDeadKeyCircumflex:
580 			deadKey = fKeys.circumflex_dead_key;
581 			break;
582 		case kDeadKeyDiaeresis:
583 			deadKey = fKeys.dieresis_dead_key;
584 			break;
585 		case kDeadKeyTilde:
586 			deadKey = fKeys.tilde_dead_key;
587 			break;
588 		default:
589 		{
590 			// if not dead, we copy and return the char
591 			char *str = *chars = new char[*numBytes + 1];
592 			strncpy(str, &fChars[offset + 1], *numBytes);
593 			str[*numBytes] = 0;
594 			return;
595 		}
596 	}
597 
598 	// if dead key, we search for our current offset char in the dead key
599 	// offset table string comparison is needed
600 	for (int32 i = 0; i < 32; i++) {
601 		if (strncmp(&fChars[offset + 1], &fChars[deadKey[i] + 1], *numBytes)
602 				== 0) {
603 			*numBytes = fChars[deadKey[i + 1]];
604 
605 			switch (*numBytes) {
606 				case 0:
607 					// Not mapped
608 					*chars = NULL;
609 					break;
610 				default:
611 				{
612 					// 1-, 2-, 3-, or 4-byte UTF-8 character
613 					char *str = *chars = new char[*numBytes + 1];
614 					strncpy(str, &fChars[deadKey[i + 1] + 1], *numBytes);
615 					str[*numBytes] = 0;
616 					break;
617 				}
618 			}
619 			return;
620 		}
621 		i++;
622 	}
623 
624 	// if not found we return the current char mapped
625 	*chars = new char[*numBytes + 1];
626 	strncpy(*chars, &fChars[offset + 1], *numBytes);
627 	(*chars)[*numBytes] = 0;
628 }
629 
630 
631 //! We make our input server use the map in /boot/home/config/settings/Keymap
632 status_t
633 Keymap::Use()
634 {
635 	return _restore_key_map_();
636 }
637 
638 
639 void
640 Keymap::SetKey(uint32 keyCode, uint32 modifiers, int8 deadKey,
641 	const char* bytes, int32 numBytes)
642 {
643 	int32 offset = _Offset(keyCode, modifiers);
644 	if (offset < 0)
645 		return;
646 
647 	if (numBytes == -1)
648 		numBytes = strlen(bytes);
649 	if (numBytes > 6)
650 		return;
651 
652 	if (_SetChars(offset, bytes, numBytes)) {
653 		if (fModificationMessage != NULL)
654 			fTarget.SendMessage(fModificationMessage);
655 	}
656 }
657 
658 
659 Keymap&
660 Keymap::operator=(const Keymap& other)
661 {
662 	if (this == &other)
663 		return *this;
664 
665 	delete[] fChars;
666 	delete fModificationMessage;
667 
668 	fChars = new(std::nothrow) char[other.fCharsSize];
669 	if (fChars != NULL) {
670 		memcpy(fChars, other.fChars, other.fCharsSize);
671 		fCharsSize = other.fCharsSize;
672 	} else
673 		fCharsSize = 0;
674 
675 	memcpy(&fKeys, &other.fKeys, sizeof(key_map));
676 	strlcpy(fName, other.fName, sizeof(fName));
677 
678 	fTarget = other.fTarget;
679 
680 	if (other.fModificationMessage != NULL)
681 		fModificationMessage = new BMessage(*other.fModificationMessage);
682 
683 	return *this;
684 }
685 
686 
687 int32
688 Keymap::_Offset(uint32 keyCode, uint32 modifiers, uint32* _table)
689 {
690 	int32 offset;
691 	uint32 table;
692 
693 	if (keyCode >= 128)
694 		return -1;
695 
696 	switch (modifiers & kModifierKeys) {
697 		case B_SHIFT_KEY:
698 			offset = fKeys.shift_map[keyCode];
699 			table = B_SHIFT_TABLE;
700 			break;
701 		case B_CAPS_LOCK:
702 			offset = fKeys.caps_map[keyCode];
703 			table = B_CAPS_TABLE;
704 			break;
705 		case B_CAPS_LOCK | B_SHIFT_KEY:
706 			offset = fKeys.caps_shift_map[keyCode];
707 			table = B_CAPS_SHIFT_TABLE;
708 			break;
709 		case B_OPTION_KEY:
710 			offset = fKeys.option_map[keyCode];
711 			table = B_OPTION_TABLE;
712 			break;
713 		case B_OPTION_KEY | B_SHIFT_KEY:
714 			offset = fKeys.option_shift_map[keyCode];
715 			table = B_OPTION_SHIFT_TABLE;
716 			break;
717 		case B_OPTION_KEY | B_CAPS_LOCK:
718 			offset = fKeys.option_caps_map[keyCode];
719 			table = B_OPTION_CAPS_TABLE;
720 			break;
721 		case B_OPTION_KEY | B_SHIFT_KEY | B_CAPS_LOCK:
722 			offset = fKeys.option_caps_shift_map[keyCode];
723 			table = B_OPTION_CAPS_SHIFT_TABLE;
724 			break;
725 		case B_CONTROL_KEY:
726 			offset = fKeys.control_map[keyCode];
727 			table = B_CONTROL_TABLE;
728 			break;
729 		default:
730 			offset = fKeys.normal_map[keyCode];
731 			table = B_NORMAL_TABLE;
732 			break;
733 	}
734 
735 	if (_table != NULL)
736 		*_table = table;
737 
738 	if (offset >= (int32)fCharsSize)
739 		return -1;
740 
741 	return offset;
742 }
743 
744 
745 bool
746 Keymap::_SetChars(int32 offset, const char* bytes, int32 numBytes)
747 {
748 	int32 oldNumBytes = fChars[offset];
749 
750 	if (oldNumBytes == numBytes
751 		&& !memcmp(&fChars[offset + 1], bytes, numBytes)) {
752 		// nothing to do
753 		return false;
754 	}
755 
756 	int32 diff = numBytes - oldNumBytes;
757 	if (diff != 0) {
758 		fCharsSize += diff;
759 
760 		if (diff > 0) {
761 			// make space for the new data
762 			char* chars = new(std::nothrow) char[fCharsSize];
763 			if (chars != NULL) {
764 				memcpy(chars, fChars, offset + oldNumBytes + 1);
765 				memcpy(&chars[offset + 1 + numBytes],
766 					&fChars[offset + 1 + oldNumBytes],
767 					fCharsSize - 2 - offset - diff);
768 				delete[] fChars;
769 				fChars = chars;
770 			} else
771 				return false;
772 		} else if (diff < 0) {
773 			// shrink table
774 			memmove(&fChars[offset + numBytes], &fChars[offset + oldNumBytes],
775 				fCharsSize - offset - 2 - diff);
776 		}
777 
778 		// update offsets
779 		int32* data = fKeys.control_map;
780 		int32 size = sizeof(fKeys.control_map) / 4 * 9
781 			+ sizeof(fKeys.acute_dead_key) / 4 * 5;
782 		for (int32 i = 0; i < size; i++) {
783 			if (data[i] > offset)
784 				data[i] += diff;
785 		}
786 	}
787 
788 	memcpy(&fChars[offset + 1], bytes, numBytes);
789 	fChars[offset] = numBytes;
790 
791 	return true;
792 }
793 
794 
795 uint8
796 Keymap::_GetDeadKeyIndex(int32 offset)
797 {
798 	if (fChars == NULL || offset <= 0)
799 		return 0;
800 
801 	uint32 numBytes = fChars[offset];
802 	if (!numBytes)
803 		return 0;
804 
805 	char chars[4];
806 	strncpy(chars, &fChars[offset + 1], numBytes);
807 	chars[numBytes] = 0;
808 
809 	int32 deadOffsets[] = {
810 		fKeys.acute_dead_key[1],
811 		fKeys.grave_dead_key[1],
812 		fKeys.circumflex_dead_key[1],
813 		fKeys.dieresis_dead_key[1],
814 		fKeys.tilde_dead_key[1]
815 	};
816 
817 	uint8 result = 0;
818 	for (int32 i = 0; i < 5; i++) {
819 		if (offset == deadOffsets[i]) {
820 			result = i + 1;
821 			break;
822 		}
823 
824 		uint32 deadNumBytes = fChars[deadOffsets[i]];
825 		if (!deadNumBytes)
826 			continue;
827 
828 		if (strncmp(chars, &fChars[deadOffsets[i] + 1], deadNumBytes) == 0) {
829 			result = i + 1;
830 			break;
831 		}
832 	}
833 
834 	return result;
835 }
836