fat: Greatly simplify and clean up dosfs_get_file_map().
[haiku.git] / src / preferences / keymap / KeyboardLayoutView.cpp
blob0eec89afb646b37441200c9869d6099f611901ca
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.
6 * Authors:
7 * Axel Dörfler, axeld@pinc-software.de
8 * John Scipione, jscipione@gmail.com
9 */
12 #include "KeyboardLayoutView.h"
14 #include <stdio.h>
15 #include <stdlib.h>
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>
26 #include "Keymap.h"
27 #include "KeymapApplication.h"
30 #undef B_TRANSLATION_CONTEXT
31 #define B_TRANSLATION_CONTEXT "Keyboard Layout View"
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};
41 static bool
42 is_left_modifier_key(uint32 keyCode)
44 return keyCode == 0x4b // left shift
45 || keyCode == 0x5d // left command
46 || keyCode == 0x5c // left control
47 || keyCode == 0x66; // left option
51 static bool
52 is_right_modifier_key(uint32 keyCode)
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
62 static bool
63 is_lock_key(uint32 keyCode)
65 return keyCode == 0x3b // caps lock
66 || keyCode == 0x22 // num lock
67 || keyCode == 0x0f; // scroll lock
71 static bool
72 is_mappable_to_modifier(uint32 keyCode)
74 return is_left_modifier_key(keyCode)
75 || is_right_modifier_key(keyCode)
76 || is_lock_key(keyCode);
80 // #pragma mark - KeyboardLayoutView
83 KeyboardLayoutView::KeyboardLayoutView(const char* name)
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)
96 fLayout = new KeyboardLayout;
97 memset(fKeyState, 0, sizeof(fKeyState));
99 SetEventMask(B_KEYBOARD_EVENTS);
103 KeyboardLayoutView::~KeyboardLayoutView()
105 delete fOffscreenBitmap;
109 void
110 KeyboardLayoutView::SetKeyboardLayout(KeyboardLayout* layout)
112 fLayout = layout;
113 _InitOffscreen();
114 _LayoutKeyboard();
115 Invalidate();
119 void
120 KeyboardLayoutView::SetKeymap(Keymap* keymap)
122 fKeymap = keymap;
123 Invalidate();
127 void
128 KeyboardLayoutView::SetTarget(BMessenger target)
130 fTarget = target;
134 void
135 KeyboardLayoutView::SetBaseFont(const BFont& font)
137 fBaseFont = font;
139 font_height fontHeight;
140 fBaseFont.GetHeight(&fontHeight);
141 fBaseFontHeight = fontHeight.ascent + fontHeight.descent;
142 fBaseFontSize = fBaseFont.Size();
144 Invalidate();
148 void
149 KeyboardLayoutView::AttachedToWindow()
151 SetViewColor(B_TRANSPARENT_COLOR);
153 SetBaseFont(*be_plain_font);
154 fSpecialFont = *be_fixed_font;
155 fModifiers = modifiers();
159 void
160 KeyboardLayoutView::FrameResized(float width, float height)
162 _InitOffscreen();
163 _LayoutKeyboard();
167 void
168 KeyboardLayoutView::WindowActivated(bool active)
170 if (active)
171 Invalidate();
175 BSize
176 KeyboardLayoutView::MinSize()
178 return BLayoutUtils::ComposeSize(ExplicitMinSize(), BSize(100, 50));
182 void
183 KeyboardLayoutView::KeyDown(const char* bytes, int32 numBytes)
185 _KeyChanged(Window()->CurrentMessage());
189 void
190 KeyboardLayoutView::KeyUp(const char* bytes, int32 numBytes)
192 _KeyChanged(Window()->CurrentMessage());
196 void
197 KeyboardLayoutView::MouseDown(BPoint point)
199 fClickPoint = point;
200 fDragKey = NULL;
201 fDropPoint.x = -1;
203 int32 buttons = 0;
204 if (Looper() != NULL && Looper()->CurrentMessage() != NULL)
205 Looper()->CurrentMessage()->FindInt32("buttons", &buttons);
207 Key* key = _KeyAt(point);
208 if (fKeymap == NULL || key == NULL) {
209 fButtons = buttons;
210 return;
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;
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);
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);
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);
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);
269 if (is_right_modifier_key(key->code) || isLockKey) {
270 if (isLockKey)
271 modifiersPopUp->AddSeparatorItem();
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);
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);
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);
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);
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);
310 modifiersPopUp->AddSeparatorItem();
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);
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);
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);
330 modifiersPopUp->SetAsyncAutoDestruct(true);
331 if (modifiersPopUp->SetTargetForItems(Window()) == B_OK)
332 modifiersPopUp->Go(ConvertToScreen(point), true);
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);
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();
353 } else {
354 _SetKeyState(key->code, true);
355 fModifiers |= fKeymap->Modifier(key->code);
356 Invalidate();
359 // TODO: if possible, we could handle the lock keys for real
360 } else {
361 _SetKeyState(key->code, true);
362 _InvalidateKey(key);
366 fButtons = buttons;
370 void
371 KeyboardLayoutView::MouseUp(BPoint point)
373 Key* key = _KeyAt(fClickPoint);
375 int32 buttons = 0;
376 if (Looper() != NULL && Looper()->CurrentMessage() != NULL)
377 Looper()->CurrentMessage()->FindInt32("buttons", &buttons);
379 if (fKeymap == NULL || key == NULL) {
380 fDragKey = NULL;
381 return;
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;
398 // modifier keys are sticky when used with the mouse
399 if (fKeymap->IsModifierKey(key->code))
400 return;
402 _SetKeyState(key->code, false);
404 if (_HandleDeadKey(key->code, fModifiers) && fDeadKey != 0)
405 return;
407 _InvalidateKey(key);
409 if (fDragKey == NULL)
410 _SendFakeKeyDown(key);
413 fDragKey = NULL;
417 void
418 KeyboardLayoutView::MouseMoved(BPoint point, uint32 transit,
419 const BMessage* dragMessage)
421 if (fKeymap == NULL)
422 return;
424 // prevent dragging for tertiary mouse button
425 if ((fButtons & B_TERTIARY_MOUSE_BUTTON) != 0)
426 return;
428 if (dragMessage != NULL) {
429 if (fEditable) {
430 _InvalidateKey(fDropTarget);
431 fDropPoint = point;
433 _EvaluateDropTarget(point);
436 return;
439 int32 buttons;
440 if (Window()->CurrentMessage() == NULL
441 || Window()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK
442 || buttons == 0) {
443 return;
446 if (fDragKey != NULL || !(fabs(point.x - fClickPoint.x) > 4
447 || fabs(point.y - fClickPoint.y) > 4)) {
448 return;
451 // start dragging
452 Key* key = _KeyAt(fClickPoint);
453 if (key == NULL)
454 return;
456 BRect frame = _FrameFor(key);
457 BPoint offset = fClickPoint - frame.LeftTop();
458 frame.OffsetTo(B_ORIGIN);
460 BRect rect = frame;
461 rect.right--;
462 rect.bottom--;
463 BBitmap* bitmap = new BBitmap(rect, B_RGBA32, true);
464 bitmap->Lock();
466 BView* view = new BView(rect, "drag", B_FOLLOW_NONE, 0);
467 bitmap->AddChild(view);
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);
477 view->Sync();
478 bitmap->Unlock();
480 BMessage drag(B_MIME_DATA);
481 drag.AddInt32("key", key->code);
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;
492 DragMessage(&drag, bitmap, B_OP_ALPHA, offset);
493 fDragKey = key;
494 fDragModifiers = fModifiers;
496 fKeyState[key->code / 8] &= ~(1 << (7 - (key->code & 7)));
497 _InvalidateKey(key);
501 void
502 KeyboardLayoutView::Draw(BRect updateRect)
504 if (fOldSize != BSize(Bounds().Width(), Bounds().Height())) {
505 _InitOffscreen();
506 _LayoutKeyboard();
509 BView* view;
510 if (fOffscreenBitmap != NULL) {
511 view = fOffscreenView;
512 view->LockLooper();
513 } else
514 view = this;
516 // Draw background
518 if (Parent())
519 view->SetLowColor(Parent()->ViewColor());
520 else
521 view->SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
523 view->FillRect(updateRect, B_SOLID_LOW);
525 // Draw keys
527 for (int32 i = 0; i < fLayout->CountKeys(); i++) {
528 Key* key = fLayout->KeyAt(i);
530 _DrawKey(view, updateRect, key, _FrameFor(key),
531 _IsKeyPressed(key->code));
534 // Draw LED indicators
536 for (int32 i = 0; i < fLayout->CountIndicators(); i++) {
537 Indicator* indicator = fLayout->IndicatorAt(i);
539 _DrawIndicator(view, updateRect, indicator, _FrameFor(indicator->frame),
540 (fModifiers & indicator->modifier) != 0);
543 if (fOffscreenBitmap != NULL) {
544 view->Sync();
545 view->UnlockLooper();
547 DrawBitmapAsync(fOffscreenBitmap, BPoint(0, 0));
552 void
553 KeyboardLayoutView::MessageReceived(BMessage* message)
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;
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;
579 size -= 4;
580 data += 4;
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++;
588 } else if ((data[0] & 0x80) == 0) {
589 // an ASCII character
590 dataSize = 1;
591 } else {
592 // no valid character
593 beep();
594 return;
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;
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);
627 } else {
628 // Send the old key to the target, so it's not lost entirely
629 _SendFakeKeyDown(fDropTarget);
631 fKeymap->SetKey(fDropTarget->code, fModifiers, fDeadKey,
632 (const char*)data, dataSize);
634 } else if (!message->IsSourceRemote()
635 && message->FindInt32("key", &keyCode) == B_OK) {
636 // Switch an unmapped key
638 Key* key = _KeyForCode(keyCode);
639 if (key != NULL && key == fDropTarget)
640 return;
642 uint32 modifier = fKeymap->Modifier(keyCode);
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);
661 _InvalidateKey(fDragKey);
664 _InvalidateKey(fDropTarget);
665 fDropTarget = NULL;
666 fDropPoint.x = -1;
667 return;
670 switch (message->what) {
671 case B_UNMAPPED_KEY_DOWN:
672 case B_UNMAPPED_KEY_UP:
673 _KeyChanged(message);
674 break;
676 case B_MODIFIERS_CHANGED:
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();
686 break;
689 default:
690 BView::MessageReceived(message);
691 break;
696 void
697 KeyboardLayoutView::_InitOffscreen()
699 delete fOffscreenBitmap;
700 fOffscreenView = NULL;
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));
716 fOffscreenView->SetLowColor(fOffscreenView->ViewColor());
717 fOffscreenBitmap->AddChild(fOffscreenView);
719 fOffscreenBitmap->Unlock();
722 if (fOffscreenView == NULL) {
723 // something went wrong
724 delete fOffscreenBitmap;
725 fOffscreenBitmap = NULL;
730 void
731 KeyboardLayoutView::_LayoutKeyboard()
733 float factorX = Bounds().Width() / fLayout->Bounds().Width();
734 float factorY = Bounds().Height() / fLayout->Bounds().Height();
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);
741 if (fLayout->DefaultKeySize().width < 11)
742 fGap = 1;
743 else
744 fGap = 2;
746 fOldSize.width = Bounds().Width();
747 fOldSize.height = Bounds().Height();
751 void
752 KeyboardLayoutView::_DrawKeyButton(BView* view, BRect& rect, BRect updateRect,
753 rgb_color base, rgb_color background, bool pressed)
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);
762 void
763 KeyboardLayoutView::_DrawKey(BView* view, BRect updateRect, const Key* key,
764 BRect rect, bool pressed)
766 rgb_color base = key->dark ? kDarkColor : kBrightColor;
767 rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR);
768 key_kind keyKind = kNormalKey;
769 int32 deadKey = 0;
770 bool secondDeadKey = false;
771 bool isDeadKeyEnabled = true;
773 char text[32];
774 if (fKeymap != NULL) {
775 _GetKeyLabel(key, text, sizeof(text), keyKind);
776 deadKey = fKeymap->DeadKey(key->code, fModifiers, &isDeadKeyEnabled);
777 secondDeadKey = fKeymap->IsDeadSecondKey(key->code, fModifiers,
778 fDeadKey);
779 } else {
780 // Show the key code if there is no keymap
781 snprintf(text, sizeof(text), "%02" B_PRIx32, key->code);
784 _SetFontSize(view, keyKind);
786 if (secondDeadKey)
787 base = kSecondDeadKeyColor;
788 else if (deadKey > 0 && isDeadKeyEnabled)
789 base = kDeadKeyColor;
791 if (key->shape == kRectangleKeyShape) {
792 _DrawKeyButton(view, rect, updateRect, base, background, pressed);
794 rect.InsetBy(1, 1);
796 _GetAbbreviatedKeyLabelIfNeeded(view, rect, key, text, sizeof(text));
797 be_control_look->DrawLabel(view, text, rect, updateRect,
798 base, 0, BAlignment(B_ALIGN_CENTER, B_ALIGN_MIDDLE));
799 } else if (key->shape == kEnterKeyShape) {
800 BRect topLeft = rect;
801 BRect topRight = rect;
802 BRect bottomLeft = rect;
803 BRect bottomRight = rect;
805 // TODO: for some reason, this does not always equal the bottom of
806 // the other keys...
807 bottomLeft.top = floorf(rect.top
808 + fLayout->DefaultKeySize().height * fFactor - fGap - 1);
809 bottomLeft.right = floorf(rect.left
810 + (key->frame.Width() - key->second_row) * fFactor - fGap - 2);
812 topLeft.bottom = bottomLeft.top;
813 topLeft.right = bottomLeft.right + 1;
814 // add one to make the borders meet
816 topRight.bottom = topLeft.bottom;
817 topRight.left = topLeft.right;
819 bottomRight.top = bottomLeft.top;
820 bottomRight.left = bottomLeft.right;
822 // draw top left corner
823 be_control_look->DrawButtonFrame(view, topLeft, updateRect,
824 4.0f, 0.0f, 4.0f, 0.0f, base, background,
825 pressed ? BControlLook::B_ACTIVATED : 0,
826 BControlLook::B_LEFT_BORDER | BControlLook::B_TOP_BORDER
827 | BControlLook::B_BOTTOM_BORDER);
828 be_control_look->DrawButtonBackground(view, topLeft, updateRect,
829 4.0f, 0.0f, 4.0f, 0.0f, base,
830 pressed ? BControlLook::B_ACTIVATED : 0,
831 BControlLook::B_LEFT_BORDER | BControlLook::B_TOP_BORDER
832 | BControlLook::B_BOTTOM_BORDER);
834 // draw top right corner
835 be_control_look->DrawButtonFrame(view, topRight, updateRect,
836 0.0f, 4.0f, 0.0f, 0.0f, base, background,
837 pressed ? BControlLook::B_ACTIVATED : 0,
838 BControlLook::B_TOP_BORDER | BControlLook::B_RIGHT_BORDER);
839 be_control_look->DrawButtonBackground(view, topRight, updateRect,
840 0.0f, 4.0f, 0.0f, 0.0f, base,
841 pressed ? BControlLook::B_ACTIVATED : 0,
842 BControlLook::B_TOP_BORDER | BControlLook::B_RIGHT_BORDER);
844 // draw bottom right corner
845 be_control_look->DrawButtonFrame(view, bottomRight, updateRect,
846 0.0f, 0.0f, 4.0f, 4.0f, base, background,
847 pressed ? BControlLook::B_ACTIVATED : 0,
848 BControlLook::B_LEFT_BORDER | BControlLook::B_RIGHT_BORDER
849 | BControlLook::B_BOTTOM_BORDER);
850 be_control_look->DrawButtonBackground(view, bottomRight, updateRect,
851 0.0f, 0.0f, 4.0f, 4.0f, base,
852 pressed ? BControlLook::B_ACTIVATED : 0,
853 BControlLook::B_LEFT_BORDER | BControlLook::B_RIGHT_BORDER
854 | BControlLook::B_BOTTOM_BORDER);
856 // clip out the bottom left corner
857 bottomLeft.right += 1;
858 bottomLeft.top -= 2;
859 BRegion region(rect);
860 region.Exclude(bottomLeft);
861 view->ConstrainClippingRegion(&region);
863 // Fill in the rect with the background color
864 SetHighColor(background);
865 FillRect(rect);
867 // draw the button background
868 BRect bgRect = rect.InsetByCopy(2, 2);
869 be_control_look->DrawButtonBackground(view, bgRect, updateRect,
870 4.0f, 4.0f, 0.0f, 4.0f, base,
871 pressed ? BControlLook::B_ACTIVATED : 0);
873 rect.left = bottomLeft.right;
874 _GetAbbreviatedKeyLabelIfNeeded(view, rect, key, text, sizeof(text));
876 // draw the button label
877 be_control_look->DrawLabel(view, text, rect, updateRect,
878 base, 0, BAlignment(B_ALIGN_CENTER, B_ALIGN_MIDDLE));
880 // reset the clipping region
881 view->ConstrainClippingRegion(NULL);
886 void
887 KeyboardLayoutView::_DrawIndicator(BView* view, BRect updateRect,
888 const Indicator* indicator, BRect rect, bool lit)
890 float rectTop = rect.top;
891 rect.top += 2 * rect.Height() / 3;
893 const char* label = NULL;
894 if (indicator->modifier == B_CAPS_LOCK)
895 label = "caps";
896 else if (indicator->modifier == B_NUM_LOCK)
897 label = "num";
898 else if (indicator->modifier == B_SCROLL_LOCK)
899 label = "scroll";
900 if (label != NULL) {
901 _SetFontSize(view, kIndicator);
903 font_height fontHeight;
904 GetFontHeight(&fontHeight);
905 if (ceilf(rect.top - fontHeight.ascent + fontHeight.descent - 2)
906 >= rectTop) {
907 view->SetHighColor(0, 0, 0);
908 view->SetLowColor(ViewColor());
910 BString text(label);
911 view->TruncateString(&text, B_TRUNCATE_END, rect.Width());
912 view->DrawString(text.String(),
913 BPoint(ceilf(rect.left + (rect.Width()
914 - StringWidth(text.String())) / 2),
915 ceilf(rect.top - fontHeight.descent - 2)));
919 rect.left += rect.Width() / 4;
920 rect.right -= rect.Width() / 3;
922 rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR);
923 rgb_color base = lit ? kLitIndicatorColor : kDarkColor;
925 be_control_look->DrawButtonFrame(view, rect, updateRect, base,
926 background, BControlLook::B_DISABLED);
927 be_control_look->DrawButtonBackground(view, rect, updateRect,
928 base, BControlLook::B_DISABLED);
932 const char*
933 KeyboardLayoutView::_SpecialKeyLabel(const key_map& map, uint32 code,
934 bool abbreviated)
936 if (code == map.caps_key)
937 return abbreviated ? "CAPS" : "CAPS LOCK";
938 if (code == map.scroll_key)
939 return "SCROLL";
940 if (code == map.num_key)
941 return abbreviated ? "NUM" : "NUM LOCK";
942 if (code == map.left_shift_key || code == map.right_shift_key)
943 return "SHIFT";
944 if (code == map.left_command_key || code == map.right_command_key)
945 return abbreviated ? "CMD" : "COMMAND";
946 if (code == map.left_control_key || code == map.right_control_key)
947 return abbreviated ? "CTRL" : "CONTROL";
948 if (code == map.left_option_key || code == map.right_option_key)
949 return abbreviated ? "OPT" : "OPTION";
950 if (code == map.menu_key)
951 return "MENU";
952 if (code == B_PRINT_KEY)
953 return "PRINT";
954 if (code == B_PAUSE_KEY)
955 return "PAUSE";
957 return NULL;
961 const char*
962 KeyboardLayoutView::_SpecialMappedKeySymbol(const char* bytes, size_t numBytes)
964 if (numBytes != 1)
965 return NULL;
967 if (bytes[0] == B_TAB)
968 return "\xe2\x86\xb9";
969 if (bytes[0] == B_ENTER)
970 return "\xe2\x86\xb5";
971 if (bytes[0] == B_BACKSPACE)
972 return "\xe2\x8c\xab";
974 if (bytes[0] == B_UP_ARROW)
975 return "\xe2\x86\x91";
976 if (bytes[0] == B_LEFT_ARROW)
977 return "\xe2\x86\x90";
978 if (bytes[0] == B_DOWN_ARROW)
979 return "\xe2\x86\x93";
980 if (bytes[0] == B_RIGHT_ARROW)
981 return "\xe2\x86\x92";
983 return NULL;
987 const char*
988 KeyboardLayoutView::_SpecialMappedKeyLabel(const char* bytes, size_t numBytes,
989 bool abbreviated)
991 if (numBytes != 1)
992 return NULL;
994 if (bytes[0] == B_ESCAPE)
995 return "ESC";
997 if (bytes[0] == B_INSERT)
998 return "INS";
999 if (bytes[0] == B_DELETE)
1000 return "DEL";
1001 if (bytes[0] == B_HOME)
1002 return "HOME";
1003 if (bytes[0] == B_END)
1004 return "END";
1005 if (bytes[0] == B_PAGE_UP)
1006 return abbreviated ? "PG \xe2\x86\x91" : "PAGE \xe2\x86\x91";
1007 if (bytes[0] == B_PAGE_DOWN)
1008 return abbreviated ? "PG \xe2\x86\x93" : "PAGE \xe2\x86\x93";
1010 return NULL;
1014 bool
1015 KeyboardLayoutView::_FunctionKeyLabel(uint32 code, char* text, size_t textSize)
1017 if (code >= B_F1_KEY && code <= B_F12_KEY) {
1018 snprintf(text, textSize, "F%" B_PRId32, code + 1 - B_F1_KEY);
1019 return true;
1022 return false;
1026 void
1027 KeyboardLayoutView::_GetAbbreviatedKeyLabelIfNeeded(BView* view, BRect rect,
1028 const Key* key, char* text, size_t textSize)
1030 if (floorf(rect.Width()) > ceilf(view->StringWidth(text)))
1031 return;
1033 // Check if we have a shorter version of this key
1035 const key_map& map = fKeymap->Map();
1037 const char* special = _SpecialKeyLabel(map, key->code, true);
1038 if (special != NULL) {
1039 strlcpy(text, special, textSize);
1040 return;
1043 char* bytes = NULL;
1044 int32 numBytes;
1045 fKeymap->GetChars(key->code, fModifiers, fDeadKey, &bytes, &numBytes);
1046 if (bytes != NULL) {
1047 special = _SpecialMappedKeyLabel(bytes, numBytes, true);
1048 if (special != NULL)
1049 strlcpy(text, special, textSize);
1051 delete[] bytes;
1056 void
1057 KeyboardLayoutView::_GetKeyLabel(const Key* key, char* text, size_t textSize,
1058 key_kind& keyKind)
1060 const key_map& map = fKeymap->Map();
1061 keyKind = kNormalKey;
1062 text[0] = '\0';
1064 const char* special = _SpecialKeyLabel(map, key->code);
1065 if (special != NULL) {
1066 strlcpy(text, special, textSize);
1067 keyKind = kSpecialKey;
1068 return;
1071 if (_FunctionKeyLabel(key->code, text, textSize)) {
1072 keyKind = kSpecialKey;
1073 return;
1076 char* bytes = NULL;
1077 int32 numBytes;
1078 fKeymap->GetChars(key->code, fModifiers, fDeadKey, &bytes, &numBytes);
1079 if (bytes != NULL) {
1080 special = _SpecialMappedKeyLabel(bytes, numBytes);
1081 if (special != NULL) {
1082 strlcpy(text, special, textSize);
1083 keyKind = kSpecialKey;
1084 } else {
1085 special = _SpecialMappedKeySymbol(bytes, numBytes);
1086 if (special != NULL) {
1087 strlcpy(text, special, textSize);
1088 keyKind = kSymbolKey;
1089 } else {
1090 bool hasGlyphs;
1091 fBaseFont.GetHasGlyphs(bytes, 1, &hasGlyphs);
1092 if (hasGlyphs)
1093 strlcpy(text, bytes, textSize);
1097 delete[] bytes;
1102 bool
1103 KeyboardLayoutView::_IsKeyPressed(uint32 code)
1105 if (fDropTarget != NULL && fDropTarget->code == code)
1106 return true;
1108 return _KeyState(code);
1112 bool
1113 KeyboardLayoutView::_KeyState(uint32 code) const
1115 if (code >= 16 * 8)
1116 return false;
1118 return (fKeyState[code / 8] & (1 << (7 - (code & 7)))) != 0;
1122 void
1123 KeyboardLayoutView::_SetKeyState(uint32 code, bool pressed)
1125 if (code >= 16 * 8)
1126 return;
1128 if (pressed)
1129 fKeyState[code / 8] |= (1 << (7 - (code & 7)));
1130 else
1131 fKeyState[code / 8] &= ~(1 << (7 - (code & 7)));
1135 Key*
1136 KeyboardLayoutView::_KeyForCode(uint32 code)
1138 // TODO: have a lookup array
1140 for (int32 i = 0; i < fLayout->CountKeys(); i++) {
1141 Key* key = fLayout->KeyAt(i);
1142 if (key->code == code)
1143 return key;
1146 return NULL;
1150 void
1151 KeyboardLayoutView::_InvalidateKey(uint32 code)
1153 _InvalidateKey(_KeyForCode(code));
1157 void
1158 KeyboardLayoutView::_InvalidateKey(const Key* key)
1160 if (key != NULL)
1161 Invalidate(_FrameFor(key));
1165 /*! Updates the fDeadKey member, and invalidates the view if needed.
1167 \return true if the view has been invalidated.
1169 bool
1170 KeyboardLayoutView::_HandleDeadKey(uint32 key, int32 modifiers)
1172 if (fKeymap == NULL || fKeymap->IsModifierKey(key))
1173 return false;
1175 bool isEnabled = false;
1176 int32 deadKey = fKeymap->DeadKey(key, modifiers, &isEnabled);
1177 if (fDeadKey != deadKey && isEnabled) {
1178 fDeadKey = deadKey;
1179 Invalidate();
1180 return true;
1181 } else if (fDeadKey != 0) {
1182 fDeadKey = 0;
1183 Invalidate();
1184 return true;
1187 return false;
1191 void
1192 KeyboardLayoutView::_KeyChanged(const BMessage* message)
1194 const uint8* state;
1195 ssize_t size;
1196 int32 key;
1197 if (message->FindInt32("key", &key) != B_OK
1198 || message->FindData("states", B_UINT8_TYPE,
1199 (const void**)&state, &size) != B_OK) {
1200 return;
1203 // Update key state, and invalidate change keys
1205 bool checkSingle = true;
1207 if (message->what == B_KEY_UP || message->what == B_UNMAPPED_KEY_UP) {
1208 if (_HandleDeadKey(key, fModifiers))
1209 checkSingle = false;
1211 if (_KeyForCode(key) == NULL)
1212 printf("no key for code %" B_PRId32 "\n", key);
1215 for (int32 i = 0; i < 16; i++) {
1216 if (fKeyState[i] != state[i]) {
1217 uint8 diff = fKeyState[i] ^ state[i];
1218 fKeyState[i] = state[i];
1220 if (!checkSingle || !Window()->IsActive())
1221 continue;
1223 for (int32 j = 7; diff != 0; j--, diff >>= 1) {
1224 if (diff & 1) {
1225 _InvalidateKey(i * 8 + j);
1233 Key*
1234 KeyboardLayoutView::_KeyAt(BPoint point)
1236 // Find key candidate
1238 BPoint keyPoint = point;
1239 keyPoint -= fOffset;
1240 keyPoint.x /= fFactor;
1241 keyPoint.y /= fFactor;
1243 for (int32 i = fLayout->CountKeys() - 1; i >= 0; i--) {
1244 Key* key = fLayout->KeyAt(i);
1245 if (key->frame.Contains(keyPoint)) {
1246 BRect frame = _FrameFor(key);
1247 if (frame.Contains(point))
1248 return key;
1250 return NULL;
1254 return NULL;
1258 BRect
1259 KeyboardLayoutView::_FrameFor(BRect keyFrame)
1261 BRect rect;
1262 rect.left = ceilf(keyFrame.left * fFactor);
1263 rect.top = ceilf(keyFrame.top * fFactor);
1264 rect.right = floorf((keyFrame.Width()) * fFactor + rect.left - fGap - 1);
1265 rect.bottom = floorf((keyFrame.Height()) * fFactor + rect.top - fGap - 1);
1266 rect.OffsetBy(fOffset);
1268 return rect;
1272 BRect
1273 KeyboardLayoutView::_FrameFor(const Key* key)
1275 return _FrameFor(key->frame);
1279 void
1280 KeyboardLayoutView::_SetFontSize(BView* view, key_kind keyKind)
1282 BSize size = fLayout->DefaultKeySize();
1283 float fontSize = fBaseFontSize;
1284 if (fBaseFontHeight >= size.height * fFactor * 0.5) {
1285 fontSize *= (size.height * fFactor * 0.5) / fBaseFontHeight;
1286 if (fontSize < 8)
1287 fontSize = 8;
1290 switch (keyKind) {
1291 case kNormalKey:
1292 fBaseFont.SetSize(fontSize);
1293 view->SetFont(&fBaseFont);
1294 break;
1295 case kSpecialKey:
1296 fSpecialFont.SetSize(fontSize * 0.7);
1297 view->SetFont(&fSpecialFont);
1298 break;
1299 case kSymbolKey:
1300 fSpecialFont.SetSize(fontSize * 1.6);
1301 view->SetFont(&fSpecialFont);
1302 break;
1304 case kIndicator:
1306 BFont font;
1307 font.SetSize(fontSize * 0.8);
1308 view->SetFont(&font);
1309 break;
1315 void
1316 KeyboardLayoutView::_EvaluateDropTarget(BPoint point)
1318 fDropTarget = _KeyAt(point);
1319 if (fDropTarget != NULL) {
1320 if (fDropTarget == fDragKey && fModifiers == fDragModifiers)
1321 fDropTarget = NULL;
1322 else
1323 _InvalidateKey(fDropTarget);
1328 void
1329 KeyboardLayoutView::_SendFakeKeyDown(const Key* key)
1331 BMessage message(B_KEY_DOWN);
1332 message.AddInt64("when", system_time());
1333 message.AddData("states", B_UINT8_TYPE, &fKeyState,
1334 sizeof(fKeyState));
1335 message.AddInt32("key", key->code);
1336 message.AddInt32("modifiers", fModifiers);
1337 message.AddPointer("keymap", fKeymap);
1339 char* string;
1340 int32 numBytes;
1341 fKeymap->GetChars(key->code, fModifiers, fDeadKey, &string,
1342 &numBytes);
1343 if (string != NULL) {
1344 message.AddString("bytes", string);
1345 delete[] string;
1348 fKeymap->GetChars(key->code, 0, 0, &string, &numBytes);
1349 if (string != NULL) {
1350 message.AddInt32("raw_char", string[0]);
1351 message.AddInt8("byte", string[0]);
1352 delete[] string;
1355 fTarget.SendMessage(&message);
1359 BMenuItem*
1360 KeyboardLayoutView::_CreateSwapModifiersMenuItem(uint32 modifier,
1361 uint32 displayModifier, uint32 oldCode, uint32 newCode)
1363 int32 mask = B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY;
1364 const char* oldName = _NameForModifier(oldCode == 0x00 ? modifier
1365 : fKeymap->Modifier(oldCode) & ~mask, false);
1366 const char* newName = _NameForModifier(newCode == 0x00 ? modifier
1367 : fKeymap->Modifier(newCode) & ~mask, false);
1369 BMessage* message = new BMessage(kMsgUpdateModifierKeys);
1370 if (newName != NULL)
1371 message->AddUInt32(newName, oldCode);
1373 if (oldName != NULL)
1374 message->AddUInt32(oldName, newCode);
1376 if (oldCode == newCode)
1377 message->AddBool("unset", true);
1379 return new BMenuItem(_NameForModifier(displayModifier, true), message);
1383 const char*
1384 KeyboardLayoutView::_NameForModifier(uint32 modifier, bool pretty)
1386 if (modifier == B_CAPS_LOCK)
1387 return pretty ? B_TRANSLATE("Caps lock") : "caps_key";
1388 else if (modifier == B_NUM_LOCK)
1389 return pretty ? B_TRANSLATE("Num lock") : "num_key";
1390 else if (modifier == B_SCROLL_LOCK)
1391 return pretty ? B_TRANSLATE("Scroll lock") : "scroll_key";
1392 else if (modifier == B_SHIFT_KEY) {
1393 return pretty ? B_TRANSLATE_COMMENT("Shift", "Shift key")
1394 : "shift_key";
1395 } else if (modifier == B_LEFT_SHIFT_KEY)
1396 return pretty ? B_TRANSLATE("Left shift") : "left_shift_key";
1397 else if (modifier == B_RIGHT_SHIFT_KEY)
1398 return pretty ? B_TRANSLATE("Right shift") : "right_shift_key";
1399 else if (modifier == B_COMMAND_KEY) {
1400 return pretty ? B_TRANSLATE_COMMENT("Command", "Command key")
1401 : "command_key";
1402 } else if (modifier == B_LEFT_COMMAND_KEY)
1403 return pretty ? B_TRANSLATE("Left command") : "left_command_key";
1404 else if (modifier == B_RIGHT_COMMAND_KEY)
1405 return pretty ? B_TRANSLATE("Right command") : "right_command_key";
1406 else if (modifier == B_CONTROL_KEY) {
1407 return pretty ? B_TRANSLATE_COMMENT("Control", "Control key")
1408 : "control_key";
1409 } else if (modifier == B_LEFT_CONTROL_KEY)
1410 return pretty ? B_TRANSLATE("Left control") : "left_control_key";
1411 else if (modifier == B_RIGHT_CONTROL_KEY)
1412 return pretty ? B_TRANSLATE("Right control") : "right_control_key";
1413 else if (modifier == B_OPTION_KEY) {
1414 return pretty ? B_TRANSLATE_COMMENT("Option", "Option key")
1415 : "option_key";
1416 } else if (modifier == B_LEFT_OPTION_KEY)
1417 return pretty ? B_TRANSLATE("Left option") : "left_option_key";
1418 else if (modifier == B_RIGHT_OPTION_KEY)
1419 return pretty ? B_TRANSLATE("Right option") : "right_option_key";
1420 else if (modifier == B_MENU_KEY)
1421 return pretty ? B_TRANSLATE_COMMENT("Menu", "Menu key") : "menu_key";
1423 return NULL;