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