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