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