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