xref: /haiku/src/preferences/keymap/KeyboardLayoutView.cpp (revision 9a6a20d4689307142a7ed26a1437ba47e244e73f)
1 /*
2  * Copyright 2009-2010, Axel Dörfler, axeld@pinc-software.de.
3  * Copyright 2013-2014 Haiku, Inc. All rights reserved.
4  * Distributed under the terms of the MIT License.
5  *
6  * Authors:
7  *		Axel Dörfler, axeld@pinc-software.de
8  *		John Scipione, jscipione@gmail.com
9  */
10 
11 
12 #include "KeyboardLayoutView.h"
13 
14 #include <stdio.h>
15 #include <stdlib.h>
16 
17 #include <Beep.h>
18 #include <Bitmap.h>
19 #include <ControlLook.h>
20 #include <LayoutUtils.h>
21 #include <InputServerDevice.h>
22 #include <MenuItem.h>
23 #include <PopUpMenu.h>
24 #include <Region.h>
25 #include <Window.h>
26 
27 #include "Keymap.h"
28 #include "KeymapApplication.h"
29 
30 
31 #undef B_TRANSLATION_CONTEXT
32 #define B_TRANSLATION_CONTEXT "Keyboard Layout View"
33 
34 
35 static const rgb_color kDarkColor = {200, 200, 200, 255};
36 static const rgb_color kIdealSecondDeadKeyColor = {190, 190, 100, 255};
37 static const rgb_color kIdealDeadKeyColor = {102, 153, 205, 255};
38 static const rgb_color kLitIndicatorColor = {116, 212, 83, 255};
39 
40 
41 static bool
42 is_left_modifier_key(uint32 keyCode)
43 {
44 	return keyCode == 0x4b	// left shift
45 		|| keyCode == 0x5d	// left command
46 		|| keyCode == 0x5c	// left control
47 		|| keyCode == 0x66;	// left option
48 }
49 
50 
51 static bool
52 is_right_modifier_key(uint32 keyCode)
53 {
54 	return keyCode == 0x56	// right shift
55 		|| keyCode == 0x5f	// right command
56 		|| keyCode == 0x60	// right control
57 		|| keyCode == 0x67	// right option
58 		|| keyCode == 0x68;	// menu
59 }
60 
61 
62 static bool
63 is_lock_key(uint32 keyCode)
64 {
65 	return keyCode == 0x3b	// caps lock
66 		|| keyCode == 0x22	// num lock
67 		|| keyCode == 0x0f;	// scroll lock
68 }
69 
70 
71 static bool
72 is_mappable_to_modifier(uint32 keyCode)
73 {
74 	return is_left_modifier_key(keyCode)
75 		|| is_right_modifier_key(keyCode)
76 		|| is_lock_key(keyCode);
77 }
78 
79 
80 //	#pragma mark - KeyboardLayoutView
81 
82 
83 KeyboardLayoutView::KeyboardLayoutView(const char* name, BInputServerDevice* dev)
84 	:
85 	BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS | B_TRANSPARENT_BACKGROUND),
86 	fKeymap(NULL),
87 	fEditable(dev == NULL),
88 	fModifiers(0),
89 	fDeadKey(0),
90 	fButtons(0),
91 	fDragKey(NULL),
92 	fDropTarget(NULL),
93 	fOldSize(0, 0),
94 	fDevice(dev)
95 {
96 	fLayout = new KeyboardLayout;
97 	memset(fKeyState, 0, sizeof(fKeyState));
98 
99 	SetEventMask(B_KEYBOARD_EVENTS);
100 
101 	SetViewColor(B_TRANSPARENT_COLOR);
102 }
103 
104 
105 KeyboardLayoutView::~KeyboardLayoutView()
106 {
107 }
108 
109 
110 void
111 KeyboardLayoutView::SetKeyboardLayout(KeyboardLayout* layout)
112 {
113 	fLayout = layout;
114 	_LayoutKeyboard();
115 	Invalidate();
116 }
117 
118 
119 void
120 KeyboardLayoutView::SetKeymap(Keymap* keymap)
121 {
122 	fKeymap = keymap;
123 	Invalidate();
124 }
125 
126 
127 void
128 KeyboardLayoutView::SetTarget(BMessenger target)
129 {
130 	fTarget = target;
131 }
132 
133 
134 void
135 KeyboardLayoutView::SetBaseFont(const BFont& font)
136 {
137 	fBaseFont = font;
138 
139 	font_height fontHeight;
140 	fBaseFont.GetHeight(&fontHeight);
141 	fBaseFontHeight = fontHeight.ascent + fontHeight.descent;
142 	fBaseFontSize = fBaseFont.Size();
143 
144 	Invalidate();
145 }
146 
147 
148 void
149 KeyboardLayoutView::AttachedToWindow()
150 {
151 	SetBaseFont(*be_plain_font);
152 	fSpecialFont = *be_fixed_font;
153 	fModifiers = modifiers();
154 }
155 
156 
157 void
158 KeyboardLayoutView::FrameResized(float width, float height)
159 {
160 	_LayoutKeyboard();
161 }
162 
163 
164 void
165 KeyboardLayoutView::WindowActivated(bool active)
166 {
167 	if (active)
168 		Invalidate();
169 }
170 
171 
172 BSize
173 KeyboardLayoutView::MinSize()
174 {
175 	return BLayoutUtils::ComposeSize(ExplicitMinSize(), BSize(100, 50));
176 }
177 
178 
179 void
180 KeyboardLayoutView::KeyDown(const char* bytes, int32 numBytes)
181 {
182 	_KeyChanged(Window()->CurrentMessage());
183 }
184 
185 
186 void
187 KeyboardLayoutView::KeyUp(const char* bytes, int32 numBytes)
188 {
189 	_KeyChanged(Window()->CurrentMessage());
190 }
191 
192 
193 void
194 KeyboardLayoutView::MouseDown(BPoint point)
195 {
196 	fClickPoint = point;
197 	fDragKey = NULL;
198 	fDropPoint.x = -1;
199 
200 	int32 buttons = 0;
201 	if (Looper() != NULL && Looper()->CurrentMessage() != NULL)
202 		Looper()->CurrentMessage()->FindInt32("buttons", &buttons);
203 
204 	Key* key = _KeyAt(point);
205 	if (fKeymap == NULL || key == NULL) {
206 		fButtons = buttons;
207 		return;
208 	}
209 
210 	if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0
211 			|| ((buttons & B_PRIMARY_MOUSE_BUTTON) != 0
212 		&& (modifiers() & B_CONTROL_KEY) != 0)) {
213 		// secondary mouse button, pop up a swap context menu
214 		if (fEditable && !is_mappable_to_modifier(key->code)) {
215 			// ToDo: Pop up a list of alternative characters to map
216 			// the key to. Currently we only add an option to remove the
217 			// current key mapping.
218 			BPopUpMenu* alternativesPopUp = new BPopUpMenu(
219 				"Alternatives pop up", true, true, B_ITEMS_IN_COLUMN);
220 			BMessage* message = new BMessage(kMsgUpdateNormalKeys);
221 			message->AddUInt32("keyCode", key->code);
222 			message->AddBool("unset", true);
223 			alternativesPopUp->AddItem(new BMenuItem(B_TRANSLATE("Remove"),
224 				message));
225 			alternativesPopUp->SetAsyncAutoDestruct(true);
226 			if (alternativesPopUp->SetTargetForItems(Window()) == B_OK)
227 				alternativesPopUp->Go(ConvertToScreen(point), true);
228 		} else if (fEditable) {
229 			// pop up the modifier keys menu
230 			BPopUpMenu* modifiersPopUp = new BPopUpMenu("Modifiers pop up",
231 				true, true, B_ITEMS_IN_COLUMN);
232 			const key_map& map = fKeymap->Map();
233 			bool isLockKey = is_lock_key(key->code);
234 			BMenuItem* item = NULL;
235 
236 			if (is_left_modifier_key(key->code) || isLockKey) {
237 				item = _CreateSwapModifiersMenuItem(B_LEFT_SHIFT_KEY,
238 					isLockKey ? B_LEFT_SHIFT_KEY : B_SHIFT_KEY,
239 					map.left_shift_key, key->code);
240 				modifiersPopUp->AddItem(item);
241 				if (key->code == map.left_shift_key)
242 					item->SetMarked(true);
243 
244 				item = _CreateSwapModifiersMenuItem(B_LEFT_CONTROL_KEY,
245 					isLockKey ? B_LEFT_CONTROL_KEY : B_CONTROL_KEY,
246 					map.left_control_key, key->code);
247 				modifiersPopUp->AddItem(item);
248 				if (key->code == map.left_control_key)
249 					item->SetMarked(true);
250 
251 				item = _CreateSwapModifiersMenuItem(B_LEFT_OPTION_KEY,
252 					isLockKey ? B_LEFT_OPTION_KEY : B_OPTION_KEY,
253 					map.left_option_key, key->code);
254 				modifiersPopUp->AddItem(item);
255 				if (key->code == map.left_option_key)
256 					item->SetMarked(true);
257 
258 				item = _CreateSwapModifiersMenuItem(B_LEFT_COMMAND_KEY,
259 					isLockKey ? B_LEFT_COMMAND_KEY : B_COMMAND_KEY,
260 					map.left_command_key, key->code);
261 				modifiersPopUp->AddItem(item);
262 				if (key->code == map.left_command_key)
263 					item->SetMarked(true);
264 			}
265 
266 			if (is_right_modifier_key(key->code) || isLockKey) {
267 				if (isLockKey)
268 					modifiersPopUp->AddSeparatorItem();
269 
270 				item = _CreateSwapModifiersMenuItem(B_RIGHT_SHIFT_KEY,
271 					isLockKey ? B_RIGHT_SHIFT_KEY : B_SHIFT_KEY,
272 					map.right_shift_key, key->code);
273 				modifiersPopUp->AddItem(item);
274 				if (key->code == map.right_shift_key)
275 					item->SetMarked(true);
276 
277 				item = _CreateSwapModifiersMenuItem(B_RIGHT_CONTROL_KEY,
278 					isLockKey ? B_RIGHT_CONTROL_KEY : B_CONTROL_KEY,
279 					map.right_control_key, key->code);
280 				modifiersPopUp->AddItem(item);
281 				if (key->code == map.right_control_key)
282 					item->SetMarked(true);
283 			}
284 
285 			item = _CreateSwapModifiersMenuItem(B_MENU_KEY, B_MENU_KEY,
286 				map.menu_key, key->code);
287 			modifiersPopUp->AddItem(item);
288 			if (key->code == map.menu_key)
289 				item->SetMarked(true);
290 
291 			if (is_right_modifier_key(key->code) || isLockKey) {
292 				item = _CreateSwapModifiersMenuItem(B_RIGHT_OPTION_KEY,
293 					isLockKey ? B_RIGHT_OPTION_KEY : B_OPTION_KEY,
294 					map.right_option_key, key->code);
295 				modifiersPopUp->AddItem(item);
296 				if (key->code == map.right_option_key)
297 					item->SetMarked(true);
298 
299 				item = _CreateSwapModifiersMenuItem(B_RIGHT_COMMAND_KEY,
300 					isLockKey ? B_RIGHT_COMMAND_KEY : B_COMMAND_KEY,
301 					map.right_command_key, key->code);
302 				modifiersPopUp->AddItem(item);
303 				if (key->code == map.right_command_key)
304 					item->SetMarked(true);
305 			}
306 
307 			modifiersPopUp->AddSeparatorItem();
308 
309 			item = _CreateSwapModifiersMenuItem(B_CAPS_LOCK, B_CAPS_LOCK,
310 				map.caps_key, key->code);
311 			modifiersPopUp->AddItem(item);
312 			if (key->code == map.caps_key)
313 				item->SetMarked(true);
314 
315 			item = _CreateSwapModifiersMenuItem(B_NUM_LOCK, B_NUM_LOCK,
316 				map.num_key, key->code);
317 			modifiersPopUp->AddItem(item);
318 			if (key->code == map.num_key)
319 				item->SetMarked(true);
320 
321 			item = _CreateSwapModifiersMenuItem(B_SCROLL_LOCK, B_SCROLL_LOCK,
322 				map.scroll_key, key->code);
323 			modifiersPopUp->AddItem(item);
324 			if (key->code == map.scroll_key)
325 				item->SetMarked(true);
326 
327 			modifiersPopUp->SetAsyncAutoDestruct(true);
328 			if (modifiersPopUp->SetTargetForItems(Window()) == B_OK)
329 				modifiersPopUp->Go(ConvertToScreen(point), true);
330 		}
331 	} else if ((buttons & B_TERTIARY_MOUSE_BUTTON) != 0
332 		&& (fButtons & B_TERTIARY_MOUSE_BUTTON) == 0) {
333 		// tertiary mouse button, toggle the "deadness" of dead keys
334 		bool isEnabled = false;
335 		uint8 deadKey = fKeymap->DeadKey(key->code, fModifiers, &isEnabled);
336 		if (deadKey > 0) {
337 			fKeymap->SetDeadKeyEnabled(key->code, fModifiers, !isEnabled);
338 			_InvalidateKey(key);
339 		}
340 	} else {
341 		// primary mouse button
342 		if (fKeymap->IsModifierKey(key->code)) {
343 			if (_KeyState(key->code)) {
344 				uint32 modifier = fKeymap->Modifier(key->code);
345 				if ((modifier & modifiers()) == 0) {
346 					_SetKeyState(key->code, false);
347 					fModifiers &= ~modifier;
348 					Invalidate();
349 				}
350 			} else {
351 				_SetKeyState(key->code, true);
352 				fModifiers |= fKeymap->Modifier(key->code);
353 				Invalidate();
354 			}
355 
356 			// TODO: if possible, we could handle the lock keys for real
357 		} else {
358 			_SetKeyState(key->code, true);
359 			_InvalidateKey(key);
360 		}
361 	}
362 
363 	fButtons = buttons;
364 }
365 
366 
367 void
368 KeyboardLayoutView::MouseUp(BPoint point)
369 {
370 	Key* key = _KeyAt(fClickPoint);
371 
372 	int32 buttons = 0;
373 	if (Looper() != NULL && Looper()->CurrentMessage() != NULL)
374 		Looper()->CurrentMessage()->FindInt32("buttons", &buttons);
375 
376 	if (fKeymap == NULL || key == NULL) {
377 		fDragKey = NULL;
378 		return;
379 	}
380 
381 	if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0
382 		|| ((buttons & B_PRIMARY_MOUSE_BUTTON) != 0
383 			&& (modifiers() & B_CONTROL_KEY) != 0)) {
384 		; // do nothing
385 	} else if ((buttons & B_TERTIARY_MOUSE_BUTTON) != 0
386 		&& (fButtons & B_TERTIARY_MOUSE_BUTTON) == 0) {
387 		// toggle the "deadness" of dead keys via middle mouse button
388 		_SetKeyState(key->code, false);
389 		_InvalidateKey(key);
390 		fButtons = buttons;
391 	} else {
392 		// primary mouse button
393 		fButtons = buttons;
394 
395 		// modifier keys are sticky when used with the mouse
396 		if (fKeymap->IsModifierKey(key->code))
397 			return;
398 
399 		_SetKeyState(key->code, false);
400 
401 		if (_HandleDeadKey(key->code, fModifiers) && fDeadKey != 0)
402 			return;
403 
404 		_InvalidateKey(key);
405 
406 		if (fDragKey == NULL)
407 			_SendKeyDown(key);
408 	}
409 
410 	fDragKey = NULL;
411 }
412 
413 
414 void
415 KeyboardLayoutView::MouseMoved(BPoint point, uint32 transit,
416 	const BMessage* dragMessage)
417 {
418 	if (fKeymap == NULL)
419 		return;
420 
421 	// Ignore mouse-moved events if we are acting as a real input device.
422 	if (fDevice != NULL)
423 		return;
424 
425 	// prevent dragging for tertiary mouse button
426 	if ((fButtons & B_TERTIARY_MOUSE_BUTTON) != 0)
427 		return;
428 
429 	if (dragMessage != NULL) {
430 		if (fEditable) {
431 			_InvalidateKey(fDropTarget);
432 			fDropPoint = point;
433 
434 			_EvaluateDropTarget(point);
435 		}
436 
437 		return;
438 	}
439 
440 	int32 buttons;
441 	if (Window()->CurrentMessage() == NULL
442 		|| Window()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK
443 		|| buttons == 0) {
444 		return;
445 	}
446 
447 	if (fDragKey != NULL || !(fabs(point.x - fClickPoint.x) > 4
448 		|| fabs(point.y - fClickPoint.y) > 4)) {
449 		return;
450 	}
451 
452 	// start dragging
453 	Key* key = _KeyAt(fClickPoint);
454 	if (key == NULL)
455 		return;
456 
457 	BRect frame = _FrameFor(key);
458 	BPoint offset = fClickPoint - frame.LeftTop();
459 	frame.OffsetTo(B_ORIGIN);
460 
461 	BRect rect = frame;
462 	rect.right--;
463 	rect.bottom--;
464 	BBitmap* bitmap = new BBitmap(rect, B_RGBA32, true);
465 	bitmap->Lock();
466 
467 	BView* view = new BView(rect, "drag", B_FOLLOW_NONE, 0);
468 	bitmap->AddChild(view);
469 
470 	view->SetHighColor(0, 0, 0, 0);
471 	view->FillRect(view->Bounds());
472 	view->SetDrawingMode(B_OP_ALPHA);
473 	view->SetHighColor(0, 0, 0, 128);
474 	// set the level of transparency by value
475 	view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
476 	_DrawKey(view, frame, key, frame, false);
477 
478 	view->Sync();
479 	bitmap->Unlock();
480 
481 	BMessage drag(B_MIME_DATA);
482 	drag.AddInt32("key", key->code);
483 
484 	char* string;
485 	int32 numBytes;
486 	fKeymap->GetChars(key->code, fModifiers, fDeadKey, &string,
487 		&numBytes);
488 	if (string != NULL) {
489 		drag.AddData("text/plain", B_MIME_DATA, string, numBytes);
490 		delete[] string;
491 	}
492 
493 	DragMessage(&drag, bitmap, B_OP_ALPHA, offset);
494 	fDragKey = key;
495 	fDragModifiers = fModifiers;
496 
497 	fKeyState[key->code / 8] &= ~(1 << (7 - (key->code & 7)));
498 	_InvalidateKey(key);
499 }
500 
501 
502 void
503 KeyboardLayoutView::Draw(BRect updateRect)
504 {
505 	if (fOldSize != BSize(Bounds().Width(), Bounds().Height())) {
506 		_LayoutKeyboard();
507 	}
508 
509 	// Draw keys
510 
511 	for (int32 i = 0; i < fLayout->CountKeys(); i++) {
512 		Key* key = fLayout->KeyAt(i);
513 
514 		_DrawKey(this, updateRect, key, _FrameFor(key),
515 			_IsKeyPressed(key->code));
516 	}
517 
518 	// Draw LED indicators
519 
520 	for (int32 i = 0; i < fLayout->CountIndicators(); i++) {
521 		Indicator* indicator = fLayout->IndicatorAt(i);
522 
523 		_DrawIndicator(this, updateRect, indicator, _FrameFor(indicator->frame),
524 			(fModifiers & indicator->modifier) != 0);
525 	}
526 }
527 
528 
529 void
530 KeyboardLayoutView::MessageReceived(BMessage* message)
531 {
532 	if (message->WasDropped() && fEditable && fDropTarget != NULL
533 		&& fKeymap != NULL) {
534 		int32 keyCode;
535 		const char* data;
536 		ssize_t size;
537 		if (message->FindData("text/plain", B_MIME_DATA,
538 				(const void**)&data, &size) == B_OK) {
539 			// Automatically convert UTF-8 escaped strings (for example from
540 			// CharacterMap)
541 			int32 dataSize = 0;
542 			uint8 buffer[16];
543 			if (size > 3 && data[0] == '\\' && data[1] == 'x') {
544 				char tempBuffer[16];
545 				if (size > 15)
546 					size = 15;
547 				memcpy(tempBuffer, data, size);
548 				tempBuffer[size] = '\0';
549 				data = tempBuffer;
550 
551 				while (size > 3 && data[0] == '\\' && data[1] == 'x') {
552 					buffer[dataSize++] = strtoul(&data[2], NULL, 16);
553 					if ((buffer[dataSize - 1] & 0x80) == 0)
554 						break;
555 
556 					size -= 4;
557 					data += 4;
558 				}
559 				data = (const char*)buffer;
560 			} else if ((data[0] & 0xc0) != 0x80 && (data[0] & 0x80) != 0) {
561 				// only accept the first character UTF-8 character
562 				while (dataSize < size && (data[dataSize] & 0x80) != 0) {
563 					dataSize++;
564 				}
565 			} else if ((data[0] & 0x80) == 0) {
566 				// an ASCII character
567 				dataSize = 1;
568 			} else {
569 				// no valid character
570 				beep();
571 				return;
572 			}
573 
574 			int32 buttons;
575 			if (!message->IsSourceRemote()
576 				&& message->FindInt32("buttons", &buttons) == B_OK
577 				&& (buttons & B_PRIMARY_MOUSE_BUTTON) != 0
578 				&& message->FindInt32("key", &keyCode) == B_OK) {
579 				// switch keys if the dropped object came from us
580 				Key* key = _KeyForCode(keyCode);
581 				if (key == NULL
582 					|| (key == fDropTarget && fDragModifiers == fModifiers)) {
583 					return;
584 				}
585 
586 				char* string;
587 				int32 numBytes;
588 				fKeymap->GetChars(fDropTarget->code, fModifiers, fDeadKey,
589 					&string, &numBytes);
590 				if (string != NULL) {
591 					// switch keys
592 					fKeymap->SetKey(fDropTarget->code, fModifiers, fDeadKey,
593 						(const char*)data, dataSize);
594 					fKeymap->SetKey(key->code, fDragModifiers, fDeadKey,
595 						string, numBytes);
596 					delete[] string;
597 				} else if (fKeymap->IsModifierKey(fDropTarget->code)) {
598 					// switch key with modifier
599 					fKeymap->SetModifier(key->code,
600 						fKeymap->Modifier(fDropTarget->code));
601 					fKeymap->SetKey(fDropTarget->code, fModifiers, fDeadKey,
602 						(const char*)data, dataSize);
603 				}
604 			} else {
605 				// Send the old key to the target, so it's not lost entirely
606 				_SendKeyDown(fDropTarget);
607 
608 				fKeymap->SetKey(fDropTarget->code, fModifiers, fDeadKey,
609 					(const char*)data, dataSize);
610 			}
611 		} else if (!message->IsSourceRemote()
612 			&& message->FindInt32("key", &keyCode) == B_OK) {
613 			// Switch an unmapped key
614 
615 			Key* key = _KeyForCode(keyCode);
616 			if (key != NULL && key == fDropTarget)
617 				return;
618 
619 			uint32 modifier = fKeymap->Modifier(keyCode);
620 
621 			char* string;
622 			int32 numBytes;
623 			fKeymap->GetChars(fDropTarget->code, fModifiers, fDeadKey,
624 				&string, &numBytes);
625 			if (string != NULL) {
626 				// switch key with modifier
627 				fKeymap->SetModifier(fDropTarget->code, modifier);
628 				fKeymap->SetKey(keyCode, fDragModifiers, fDeadKey,
629 					string, numBytes);
630 				delete[] string;
631 			} else {
632 				// switch modifier keys
633 				fKeymap->SetModifier(keyCode,
634 					fKeymap->Modifier(fDropTarget->code));
635 				fKeymap->SetModifier(fDropTarget->code, modifier);
636 			}
637 
638 			_InvalidateKey(fDragKey);
639 		}
640 
641 		_InvalidateKey(fDropTarget);
642 		fDropTarget = NULL;
643 		fDropPoint.x = -1;
644 		return;
645 	}
646 
647 	switch (message->what) {
648 		case B_UNMAPPED_KEY_DOWN:
649 		case B_UNMAPPED_KEY_UP:
650 			_KeyChanged(message);
651 			break;
652 
653 		case B_MODIFIERS_CHANGED:
654 		{
655 			int32 newModifiers;
656 			if (message->FindInt32("modifiers", &newModifiers) == B_OK
657 				&& fModifiers != newModifiers) {
658 				fModifiers = newModifiers;
659 				_EvaluateDropTarget(fDropPoint);
660 				if (Window()->IsActive())
661 					Invalidate();
662 			}
663 			break;
664 		}
665 
666 		default:
667 			BView::MessageReceived(message);
668 			break;
669 	}
670 }
671 
672 
673 void
674 KeyboardLayoutView::_LayoutKeyboard()
675 {
676 	float factorX = Bounds().Width() / fLayout->Bounds().Width();
677 	float factorY = Bounds().Height() / fLayout->Bounds().Height();
678 
679 	fFactor = min_c(factorX, factorY);
680 	fOffset = BPoint(floorf((Bounds().Width() - fLayout->Bounds().Width()
681 			* fFactor) / 2),
682 		floorf((Bounds().Height() - fLayout->Bounds().Height() * fFactor) / 2));
683 
684 	if (fLayout->DefaultKeySize().width < 11)
685 		fGap = 1;
686 	else
687 		fGap = 2;
688 
689 	fOldSize.width = Bounds().Width();
690 	fOldSize.height = Bounds().Height();
691 }
692 
693 
694 void
695 KeyboardLayoutView::_DrawKeyButton(BView* view, BRect& rect, BRect updateRect,
696 	rgb_color base, rgb_color background, bool pressed)
697 {
698 	uint32 flags = pressed ? BControlLook::B_ACTIVATED : 0;
699 
700 	be_control_look->DrawButtonFrame(view, rect, updateRect, 4.0f, base,
701 		background, flags);
702 	be_control_look->DrawButtonBackground(view, rect, updateRect, 4.0f,
703 		base, flags);
704 }
705 
706 
707 void
708 KeyboardLayoutView::_DrawKey(BView* view, BRect updateRect, const Key* key,
709 	BRect rect, bool pressed)
710 {
711 	rgb_color base;
712 	if (ui_color(B_CONTROL_BACKGROUND_COLOR).IsLight()) {
713 		base = key->dark ? tint_color(ui_color(B_CONTROL_BACKGROUND_COLOR), B_DARKEN_1_TINT)
714 			: ui_color(B_CONTROL_BACKGROUND_COLOR);
715 	} else {
716 		base = key->dark ? tint_color(ui_color(B_CONTROL_BACKGROUND_COLOR), 0.8)
717 			: ui_color(B_CONTROL_BACKGROUND_COLOR);
718 	}
719 	rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR);
720 	rgb_color keyLabelColor = ui_color(B_CONTROL_TEXT_COLOR);
721 	key_kind keyKind = kNormalKey;
722 	int32 deadKey = 0;
723 	bool secondDeadKey = false;
724 	bool isDeadKeyEnabled = true;
725 
726 	char text[32];
727 	if (fKeymap != NULL) {
728 		_GetKeyLabel(key, text, sizeof(text), keyKind);
729 		deadKey = fKeymap->DeadKey(key->code, fModifiers, &isDeadKeyEnabled);
730 		secondDeadKey = fKeymap->IsDeadSecondKey(key->code, fModifiers,
731 			fDeadKey);
732 	} else {
733 		// Show the key code if there is no keymap
734 		snprintf(text, sizeof(text), "%02" B_PRIx32, key->code);
735 	}
736 
737 	_SetFontSize(view, keyKind);
738 
739 	uint32 flags = pressed ? BControlLook::B_ACTIVATED : 0;
740 
741 	if (secondDeadKey)
742 		base = mix_color(ui_color(B_CONTROL_BACKGROUND_COLOR), kIdealSecondDeadKeyColor, 100);
743 	else if (deadKey > 0 && isDeadKeyEnabled)
744 		base = mix_color(ui_color(B_CONTROL_BACKGROUND_COLOR), kIdealDeadKeyColor, 100);
745 
746 	if (key->shape == kRectangleKeyShape) {
747 		_DrawKeyButton(view, rect, updateRect, base, background, pressed);
748 
749 		rect.InsetBy(1, 1);
750 
751 		_GetAbbreviatedKeyLabelIfNeeded(view, rect, key, text, sizeof(text));
752 		be_control_look->DrawLabel(view, text, rect, updateRect,
753 			base, flags, BAlignment(B_ALIGN_CENTER, B_ALIGN_MIDDLE),
754 			&keyLabelColor);
755 	} else if (key->shape == kEnterKeyShape) {
756 		BRect topLeft = rect;
757 		BRect topRight = rect;
758 		BRect bottomLeft = rect;
759 		BRect bottomRight = rect;
760 
761 		// TODO: for some reason, this does not always equal the bottom of
762 		// the other keys...
763 		bottomLeft.top = floorf(rect.top
764 			+ fLayout->DefaultKeySize().height * fFactor - fGap - 1);
765 		bottomLeft.right = floorf(rect.left
766 			+ (key->frame.Width() - key->second_row) * fFactor - fGap - 2);
767 
768 		topLeft.bottom = bottomLeft.top;
769 		topLeft.right = bottomLeft.right + 1;
770 			// add one to make the borders meet
771 
772 		topRight.bottom = topLeft.bottom;
773 		topRight.left = topLeft.right;
774 
775 		bottomRight.top = bottomLeft.top;
776 		bottomRight.left = bottomLeft.right;
777 
778 		// draw top left corner
779 		be_control_look->DrawButtonFrame(view, topLeft, updateRect,
780 			4.0f, 0.0f, 4.0f, 0.0f, base, background, flags,
781 			BControlLook::B_LEFT_BORDER | BControlLook::B_TOP_BORDER
782 				| BControlLook::B_BOTTOM_BORDER);
783 		be_control_look->DrawButtonBackground(view, topLeft, updateRect,
784 			4.0f, 0.0f, 4.0f, 0.0f, base, flags,
785 			BControlLook::B_LEFT_BORDER | BControlLook::B_TOP_BORDER
786 				| BControlLook::B_BOTTOM_BORDER);
787 
788 		// draw top right corner
789 		be_control_look->DrawButtonFrame(view, topRight, updateRect,
790 			0.0f, 4.0f, 0.0f, 0.0f, base, background, flags,
791 			BControlLook::B_TOP_BORDER | BControlLook::B_RIGHT_BORDER);
792 		be_control_look->DrawButtonBackground(view, topRight, updateRect,
793 			0.0f, 4.0f, 0.0f, 0.0f, base, flags,
794 			BControlLook::B_TOP_BORDER | BControlLook::B_RIGHT_BORDER);
795 
796 		// draw bottom right corner
797 		be_control_look->DrawButtonFrame(view, bottomRight, updateRect,
798 			0.0f, 0.0f, 4.0f, 4.0f, base, background, flags,
799 			BControlLook::B_LEFT_BORDER | BControlLook::B_RIGHT_BORDER
800 				| BControlLook::B_BOTTOM_BORDER);
801 		be_control_look->DrawButtonBackground(view, bottomRight, updateRect,
802 			0.0f, 0.0f, 4.0f, 4.0f, base, flags,
803 			BControlLook::B_LEFT_BORDER | BControlLook::B_RIGHT_BORDER
804 				| BControlLook::B_BOTTOM_BORDER);
805 
806 		// clip out the bottom left corner
807 		bottomLeft.right += 1;
808 		bottomLeft.top -= 2;
809 		BRegion region(rect);
810 		region.Exclude(bottomLeft);
811 		view->ConstrainClippingRegion(&region);
812 
813 		// draw the button background
814 		BRect bgRect = rect.InsetByCopy(2, 2);
815 		be_control_look->DrawButtonBackground(view, bgRect, updateRect,
816 			4.0f, 4.0f, 0.0f, 4.0f, base, flags);
817 
818 		rect.left = bottomLeft.right;
819 		_GetAbbreviatedKeyLabelIfNeeded(view, rect, key, text, sizeof(text));
820 
821 		// draw the button label
822 		be_control_look->DrawLabel(view, text, rect, updateRect,
823 			base, flags, BAlignment(B_ALIGN_CENTER, B_ALIGN_MIDDLE),
824 			&keyLabelColor);
825 
826 		// reset the clipping region
827 		view->ConstrainClippingRegion(NULL);
828 	}
829 }
830 
831 
832 void
833 KeyboardLayoutView::_DrawIndicator(BView* view, BRect updateRect,
834 	const Indicator* indicator, BRect rect, bool lit)
835 {
836 	float rectTop = rect.top;
837 	rect.top += 2 * rect.Height() / 3;
838 
839 	const char* label = NULL;
840 	if (indicator->modifier == B_CAPS_LOCK)
841 		label = "caps";
842 	else if (indicator->modifier == B_NUM_LOCK)
843 		label = "num";
844 	else if (indicator->modifier == B_SCROLL_LOCK)
845 		label = "scroll";
846 	if (label != NULL) {
847 		_SetFontSize(view, kIndicator);
848 
849 		font_height fontHeight;
850 		GetFontHeight(&fontHeight);
851 		if (ceilf(rect.top - fontHeight.ascent + fontHeight.descent - 2)
852 				>= rectTop) {
853 			view->SetHighUIColor(B_PANEL_TEXT_COLOR);
854 			view->SetLowColor(ViewColor());
855 
856 			BString text(label);
857 			view->TruncateString(&text, B_TRUNCATE_END, rect.Width());
858 			view->DrawString(text.String(),
859 				BPoint(ceilf(rect.left + (rect.Width()
860 						- StringWidth(text.String())) / 2),
861 					ceilf(rect.top - fontHeight.descent - 2)));
862 		}
863 	}
864 
865 	rect.left += rect.Width() / 4;
866 	rect.right -= rect.Width() / 3;
867 
868 	rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR);
869 	rgb_color base = lit ? kLitIndicatorColor : kDarkColor;
870 
871 	be_control_look->DrawButtonFrame(view, rect, updateRect, base,
872 		background, BControlLook::B_DISABLED);
873 	be_control_look->DrawButtonBackground(view, rect, updateRect,
874 		base, BControlLook::B_DISABLED);
875 }
876 
877 
878 const char*
879 KeyboardLayoutView::_SpecialKeyLabel(const key_map& map, uint32 code,
880 	bool abbreviated)
881 {
882 	if (code == map.caps_key) {
883 		return abbreviated
884 			? B_TRANSLATE_COMMENT("CAPS", "Very short for 'caps lock'")
885 			: B_TRANSLATE("CAPS LOCK");
886 	}
887 	if (code == map.scroll_key)
888 		return B_TRANSLATE("SCROLL");
889 	if (code == map.num_key) {
890 		return abbreviated
891 			? B_TRANSLATE_COMMENT("NUM", "Very short for 'num lock'")
892 			: B_TRANSLATE("NUM LOCK");
893 	}
894 	if (code == map.left_shift_key || code == map.right_shift_key)
895 		return B_TRANSLATE("SHIFT");
896 	if (code == map.left_command_key || code == map.right_command_key) {
897 		return abbreviated
898 			? B_TRANSLATE_COMMENT("CMD", "Very short for 'command'")
899 			: B_TRANSLATE("COMMAND");
900 	}
901 	if (code == map.left_control_key || code == map.right_control_key) {
902 		return abbreviated
903 			? B_TRANSLATE_COMMENT("CTRL", "Very short for 'control'")
904 			: B_TRANSLATE("CONTROL");
905 	}
906 	if (code == map.left_option_key || code == map.right_option_key) {
907 		return abbreviated
908 			? B_TRANSLATE_COMMENT("OPT", "Very short for 'option'")
909 			: B_TRANSLATE("OPTION");
910 	}
911 	if (code == map.menu_key)
912 		return B_TRANSLATE("MENU");
913 	if (code == B_PRINT_KEY)
914 		return B_TRANSLATE("PRINT");
915 	if (code == B_PAUSE_KEY)
916 		return B_TRANSLATE("PAUSE");
917 
918 	return NULL;
919 }
920 
921 
922 const char*
923 KeyboardLayoutView::_SpecialMappedKeySymbol(const char* bytes, size_t numBytes)
924 {
925 	if (numBytes != 1)
926 		return NULL;
927 
928 	if (bytes[0] == B_TAB)
929 		return "\xe2\x86\xb9";
930 	if (bytes[0] == B_ENTER)
931 		return "\xe2\x8f\x8e";
932 	if (bytes[0] == B_BACKSPACE)
933 		return "\xe2\x8c\xab";
934 
935 	if (bytes[0] == B_UP_ARROW)
936 		return "\xe2\x86\x91";
937 	if (bytes[0] == B_LEFT_ARROW)
938 		return "\xe2\x86\x90";
939 	if (bytes[0] == B_DOWN_ARROW)
940 		return "\xe2\x86\x93";
941 	if (bytes[0] == B_RIGHT_ARROW)
942 		return "\xe2\x86\x92";
943 
944 	return NULL;
945 }
946 
947 
948 const char*
949 KeyboardLayoutView::_SpecialMappedKeyLabel(const char* bytes, size_t numBytes,
950 	bool abbreviated)
951 {
952 	if (numBytes != 1)
953 		return NULL;
954 	if (bytes[0] == B_ESCAPE)
955 		return B_TRANSLATE("ESC");
956 	if (bytes[0] == B_INSERT)
957 		return B_TRANSLATE("INS");
958 	if (bytes[0] == B_DELETE)
959 		return B_TRANSLATE("DEL");
960 	if (bytes[0] == B_HOME)
961 		return B_TRANSLATE("HOME");
962 	if (bytes[0] == B_END)
963 		return B_TRANSLATE("END");
964 	if (bytes[0] == B_PAGE_UP) {
965 		return abbreviated
966 			? B_TRANSLATE_COMMENT("PG \xe2\x86\x91",
967 				"Very short for 'page up'")
968 			: B_TRANSLATE("PAGE \xe2\x86\x91");
969 	}
970 	if (bytes[0] == B_PAGE_DOWN) {
971 		return abbreviated
972 			? B_TRANSLATE_COMMENT("PG \xe2\x86\x93",
973 				"Very short for 'page down'")
974 			: B_TRANSLATE("PAGE \xe2\x86\x93");
975 	}
976 
977 	return NULL;
978 }
979 
980 
981 bool
982 KeyboardLayoutView::_FunctionKeyLabel(uint32 code, char* text, size_t textSize)
983 {
984 	if (code >= B_F1_KEY && code <= B_F12_KEY) {
985 		snprintf(text, textSize, "F%" B_PRId32, code + 1 - B_F1_KEY);
986 		return true;
987 	}
988 
989 	return false;
990 }
991 
992 
993 void
994 KeyboardLayoutView::_GetAbbreviatedKeyLabelIfNeeded(BView* view, BRect rect,
995 	const Key* key, char* text, size_t textSize)
996 {
997 	if (floorf(rect.Width()) > ceilf(view->StringWidth(text)))
998 		return;
999 
1000 	// Check if we have a shorter version of this key
1001 
1002 	const key_map& map = fKeymap->Map();
1003 
1004 	const char* special = _SpecialKeyLabel(map, key->code, true);
1005 	if (special != NULL) {
1006 		strlcpy(text, special, textSize);
1007 		return;
1008 	}
1009 
1010 	char* bytes = NULL;
1011 	int32 numBytes;
1012 	fKeymap->GetChars(key->code, fModifiers, fDeadKey, &bytes, &numBytes);
1013 	if (bytes != NULL) {
1014 		special = _SpecialMappedKeyLabel(bytes, numBytes, true);
1015 		if (special != NULL)
1016 			strlcpy(text, special, textSize);
1017 
1018 		delete[] bytes;
1019 	}
1020 }
1021 
1022 
1023 void
1024 KeyboardLayoutView::_GetKeyLabel(const Key* key, char* text, size_t textSize,
1025 	key_kind& keyKind)
1026 {
1027 	const key_map& map = fKeymap->Map();
1028 	keyKind = kNormalKey;
1029 	text[0] = '\0';
1030 
1031 	const char* special = _SpecialKeyLabel(map, key->code);
1032 	if (special != NULL) {
1033 		strlcpy(text, special, textSize);
1034 		keyKind = kSpecialKey;
1035 		return;
1036 	}
1037 
1038 	if (_FunctionKeyLabel(key->code, text, textSize)) {
1039 		keyKind = kSpecialKey;
1040 		return;
1041 	}
1042 
1043 	char* bytes = NULL;
1044 	int32 numBytes;
1045 	fKeymap->GetChars(key->code, fModifiers, fDeadKey, &bytes, &numBytes);
1046 	if (bytes != NULL) {
1047 		special = _SpecialMappedKeyLabel(bytes, numBytes);
1048 		if (special != NULL) {
1049 			strlcpy(text, special, textSize);
1050 			keyKind = kSpecialKey;
1051 		} else {
1052 			special = _SpecialMappedKeySymbol(bytes, numBytes);
1053 			if (special != NULL) {
1054 				strlcpy(text, special, textSize);
1055 				keyKind = kSymbolKey;
1056 			} else {
1057 				bool hasGlyphs;
1058 				fBaseFont.GetHasGlyphs(bytes, 1, &hasGlyphs);
1059 				if (hasGlyphs)
1060 					strlcpy(text, bytes, textSize);
1061 			}
1062 		}
1063 
1064 		delete[] bytes;
1065 	}
1066 }
1067 
1068 
1069 bool
1070 KeyboardLayoutView::_IsKeyPressed(uint32 code)
1071 {
1072 	if (fDropTarget != NULL && fDropTarget->code == code)
1073 		return true;
1074 
1075 	return _KeyState(code);
1076 }
1077 
1078 
1079 bool
1080 KeyboardLayoutView::_KeyState(uint32 code) const
1081 {
1082 	if (code >= 16 * 8)
1083 		return false;
1084 
1085 	return (fKeyState[code / 8] & (1 << (7 - (code & 7)))) != 0;
1086 }
1087 
1088 
1089 void
1090 KeyboardLayoutView::_SetKeyState(uint32 code, bool pressed)
1091 {
1092 	if (code >= 16 * 8)
1093 		return;
1094 
1095 	if (pressed)
1096 		fKeyState[code / 8] |= (1 << (7 - (code & 7)));
1097 	else
1098 		fKeyState[code / 8] &= ~(1 << (7 - (code & 7)));
1099 }
1100 
1101 
1102 Key*
1103 KeyboardLayoutView::_KeyForCode(uint32 code)
1104 {
1105 	// TODO: have a lookup array
1106 
1107 	for (int32 i = 0; i < fLayout->CountKeys(); i++) {
1108 		Key* key = fLayout->KeyAt(i);
1109 		if (key->code == code)
1110 			return key;
1111 	}
1112 
1113 	return NULL;
1114 }
1115 
1116 
1117 void
1118 KeyboardLayoutView::_InvalidateKey(uint32 code)
1119 {
1120 	_InvalidateKey(_KeyForCode(code));
1121 }
1122 
1123 
1124 void
1125 KeyboardLayoutView::_InvalidateKey(const Key* key)
1126 {
1127 	if (key != NULL)
1128 		Invalidate(_FrameFor(key));
1129 }
1130 
1131 
1132 /*!	Updates the fDeadKey member, and invalidates the view if needed.
1133 
1134 	\return true if the view has been invalidated.
1135 */
1136 bool
1137 KeyboardLayoutView::_HandleDeadKey(uint32 key, int32 modifiers)
1138 {
1139 	if (fKeymap == NULL || fKeymap->IsModifierKey(key))
1140 		return false;
1141 
1142 	bool isEnabled = false;
1143 	int32 deadKey = fKeymap->DeadKey(key, modifiers, &isEnabled);
1144 	if (fDeadKey != deadKey && isEnabled) {
1145 		fDeadKey = deadKey;
1146 		Invalidate();
1147 		return true;
1148 	} else if (fDeadKey != 0) {
1149 		fDeadKey = 0;
1150 		Invalidate();
1151 		return true;
1152 	}
1153 
1154 	return false;
1155 }
1156 
1157 
1158 void
1159 KeyboardLayoutView::_KeyChanged(const BMessage* message)
1160 {
1161 	const uint8* state;
1162 	ssize_t size;
1163 	int32 key;
1164 	if (message->FindInt32("key", &key) != B_OK
1165 		|| message->FindData("states", B_UINT8_TYPE,
1166 			(const void**)&state, &size) != B_OK) {
1167 		return;
1168 	}
1169 
1170 	// Update key state, and invalidate change keys
1171 
1172 	bool checkSingle = true;
1173 
1174 	if (message->what == B_KEY_UP || message->what == B_UNMAPPED_KEY_UP) {
1175 		if (_HandleDeadKey(key, fModifiers))
1176 			checkSingle = false;
1177 
1178 		if (_KeyForCode(key) == NULL)
1179 			printf("no key for code %" B_PRId32 "\n", key);
1180 	}
1181 
1182 	for (int32 i = 0; i < 16; i++) {
1183 		if (fKeyState[i] != state[i]) {
1184 			uint8 diff = fKeyState[i] ^ state[i];
1185 			fKeyState[i] = state[i];
1186 
1187 			if (!checkSingle || !Window()->IsActive())
1188 				continue;
1189 
1190 			for (int32 j = 7; diff != 0; j--, diff >>= 1) {
1191 				if (diff & 1) {
1192 					_InvalidateKey(i * 8 + j);
1193 				}
1194 			}
1195 		}
1196 	}
1197 }
1198 
1199 
1200 Key*
1201 KeyboardLayoutView::_KeyAt(BPoint point)
1202 {
1203 	// Find key candidate
1204 
1205 	BPoint keyPoint = point;
1206 	keyPoint -= fOffset;
1207 	keyPoint.x /= fFactor;
1208 	keyPoint.y /= fFactor;
1209 
1210 	for (int32 i = fLayout->CountKeys() - 1; i >= 0; i--) {
1211 		Key* key = fLayout->KeyAt(i);
1212 		if (key->frame.Contains(keyPoint)) {
1213 			BRect frame = _FrameFor(key);
1214 			if (frame.Contains(point))
1215 				return key;
1216 
1217 			return NULL;
1218 		}
1219 	}
1220 
1221 	return NULL;
1222 }
1223 
1224 
1225 BRect
1226 KeyboardLayoutView::_FrameFor(BRect keyFrame)
1227 {
1228 	BRect rect;
1229 	rect.left	= ceilf(keyFrame.left * fFactor);
1230 	rect.top	= ceilf(keyFrame.top * fFactor);
1231 	rect.right	= floorf((keyFrame.Width()) * fFactor + rect.left - fGap - 1);
1232 	rect.bottom	= floorf((keyFrame.Height()) * fFactor + rect.top - fGap - 1);
1233 	rect.OffsetBy(fOffset);
1234 
1235 	return rect;
1236 }
1237 
1238 
1239 BRect
1240 KeyboardLayoutView::_FrameFor(const Key* key)
1241 {
1242 	return _FrameFor(key->frame);
1243 }
1244 
1245 
1246 void
1247 KeyboardLayoutView::_SetFontSize(BView* view, key_kind keyKind)
1248 {
1249 	BSize size = fLayout->DefaultKeySize();
1250 	float fontSize = fBaseFontSize;
1251 	if (fBaseFontHeight >= size.height * fFactor * 0.5) {
1252 		fontSize *= (size.height * fFactor * 0.5) / fBaseFontHeight;
1253 		if (fontSize < 8)
1254 			fontSize = 8;
1255 	}
1256 
1257 	switch (keyKind) {
1258 		case kNormalKey:
1259 			fBaseFont.SetSize(fontSize);
1260 			view->SetFont(&fBaseFont);
1261 			break;
1262 		case kSpecialKey:
1263 			fSpecialFont.SetSize(fontSize * 0.7);
1264 			view->SetFont(&fSpecialFont);
1265 			break;
1266 		case kSymbolKey:
1267 			fSpecialFont.SetSize(fontSize * 1.6);
1268 			view->SetFont(&fSpecialFont);
1269 			break;
1270 
1271 		case kIndicator:
1272 		{
1273 			BFont font;
1274 			font.SetSize(fontSize * 0.8);
1275 			view->SetFont(&font);
1276 			break;
1277 		}
1278 	}
1279 }
1280 
1281 
1282 void
1283 KeyboardLayoutView::_EvaluateDropTarget(BPoint point)
1284 {
1285 	fDropTarget = _KeyAt(point);
1286 	if (fDropTarget != NULL) {
1287 		if (fDropTarget == fDragKey && fModifiers == fDragModifiers)
1288 			fDropTarget = NULL;
1289 		else
1290 			_InvalidateKey(fDropTarget);
1291 	}
1292 }
1293 
1294 
1295 void
1296 KeyboardLayoutView::_SendKeyDown(const Key* key)
1297 {
1298 	BMessage message(B_KEY_DOWN);
1299 	message.AddInt64("when", system_time());
1300 	message.AddData("states", B_UINT8_TYPE, &fKeyState,
1301 		sizeof(fKeyState));
1302 	message.AddInt32("key", key->code);
1303 	message.AddInt32("modifiers", fModifiers);
1304 	message.AddInt32("be:key_repeat", 1);
1305 
1306 	if (fDevice == NULL)
1307 		message.AddPointer("keymap", fKeymap);
1308 
1309 	char* string;
1310 	int32 numBytes;
1311 	fKeymap->GetChars(key->code, fModifiers, fDeadKey, &string,
1312 		&numBytes);
1313 	if (string != NULL) {
1314 		message.AddString("bytes", string);
1315 		delete[] string;
1316 	}
1317 
1318 	fKeymap->GetChars(key->code, 0, 0, &string, &numBytes);
1319 	if (string != NULL) {
1320 		message.AddInt32("raw_char", string[0]);
1321 		message.AddInt8("byte", string[0]);
1322 		delete[] string;
1323 	}
1324 
1325 	if (fDevice == NULL) {
1326 		fTarget.SendMessage(&message);
1327 	} else {
1328 #if defined(VIRTUAL_KEYBOARD_DEVICE)
1329 		BMessage* deviceMessage = new BMessage(message);
1330 		if (fDevice->EnqueueMessage(deviceMessage) != B_OK)
1331 			delete deviceMessage;
1332 #endif
1333 	}
1334 }
1335 
1336 
1337 BMenuItem*
1338 KeyboardLayoutView::_CreateSwapModifiersMenuItem(uint32 modifier,
1339 	uint32 displayModifier, uint32 oldCode, uint32 newCode)
1340 {
1341 	int32 mask = B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY;
1342 	const char* oldName = _NameForModifier(oldCode == 0x00 ? modifier
1343 		: fKeymap->Modifier(oldCode) & ~mask, false);
1344 	const char* newName = _NameForModifier(newCode == 0x00 ? modifier
1345 		: fKeymap->Modifier(newCode) & ~mask, false);
1346 
1347 	BMessage* message = new BMessage(kMsgUpdateModifierKeys);
1348 	if (newName != NULL)
1349 		message->AddUInt32(newName, oldCode);
1350 
1351 	if (oldName != NULL)
1352 		message->AddUInt32(oldName, newCode);
1353 
1354 	if (oldCode == newCode)
1355 		message->AddBool("unset", true);
1356 
1357 	return new BMenuItem(_NameForModifier(displayModifier, true), message);
1358 }
1359 
1360 
1361 const char*
1362 KeyboardLayoutView::_NameForModifier(uint32 modifier, bool pretty)
1363 {
1364 	if (modifier == B_CAPS_LOCK)
1365 		return pretty ? B_TRANSLATE("Caps Lock") : "caps_key";
1366 	else if (modifier == B_NUM_LOCK)
1367 		return pretty ? B_TRANSLATE("Num Lock") : "num_key";
1368 	else if (modifier == B_SCROLL_LOCK)
1369 		return pretty ? B_TRANSLATE("Scroll Lock") : "scroll_key";
1370 	else if (modifier == B_SHIFT_KEY) {
1371 		return pretty ? B_TRANSLATE_COMMENT("Shift", "Shift key")
1372 			: "shift_key";
1373 	} else if (modifier == B_LEFT_SHIFT_KEY)
1374 		return pretty ? B_TRANSLATE("Left Shift") : "left_shift_key";
1375 	else if (modifier == B_RIGHT_SHIFT_KEY)
1376 		return pretty ? B_TRANSLATE("Right Shift") : "right_shift_key";
1377 	else if (modifier == B_COMMAND_KEY) {
1378 		return pretty ? B_TRANSLATE_COMMENT("Command", "Command key")
1379 			: "command_key";
1380 	} else if (modifier == B_LEFT_COMMAND_KEY)
1381 		return pretty ? B_TRANSLATE("Left Command") : "left_command_key";
1382 	else if (modifier == B_RIGHT_COMMAND_KEY)
1383 		return pretty ? B_TRANSLATE("Right Command") : "right_command_key";
1384 	else if (modifier == B_CONTROL_KEY) {
1385 		return pretty ? B_TRANSLATE_COMMENT("Control", "Control key")
1386 			: "control_key";
1387 	} else if (modifier == B_LEFT_CONTROL_KEY)
1388 		return pretty ? B_TRANSLATE("Left Control") : "left_control_key";
1389 	else if (modifier == B_RIGHT_CONTROL_KEY)
1390 		return pretty ? B_TRANSLATE("Right Control") : "right_control_key";
1391 	else if (modifier == B_OPTION_KEY) {
1392 		return pretty ? B_TRANSLATE_COMMENT("Option", "Option key")
1393 			: "option_key";
1394 	} else if (modifier == B_LEFT_OPTION_KEY)
1395 		return pretty ? B_TRANSLATE("Left Option") : "left_option_key";
1396 	else if (modifier == B_RIGHT_OPTION_KEY)
1397 		return pretty ? B_TRANSLATE("Right Option") : "right_option_key";
1398 	else if (modifier == B_MENU_KEY)
1399 		return pretty ? B_TRANSLATE_COMMENT("Menu", "Menu key") : "menu_key";
1400 
1401 	return NULL;
1402 }
1403