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