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