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.
7 * Axel Dörfler, axeld@pinc-software.de
8 * John Scipione, jscipione@gmail.com
12 #include "KeyboardLayoutView.h"
19 #include <ControlLook.h>
20 #include <LayoutUtils.h>
22 #include <PopUpMenu.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};
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
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
63 is_lock_key(uint32 keyCode
)
65 return keyCode
== 0x3b // caps lock
66 || keyCode
== 0x22 // num lock
67 || keyCode
== 0x0f; // scroll lock
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
),
96 fLayout
= new KeyboardLayout
;
97 memset(fKeyState
, 0, sizeof(fKeyState
));
99 SetEventMask(B_KEYBOARD_EVENTS
);
103 KeyboardLayoutView::~KeyboardLayoutView()
105 delete fOffscreenBitmap
;
110 KeyboardLayoutView::SetKeyboardLayout(KeyboardLayout
* layout
)
120 KeyboardLayoutView::SetKeymap(Keymap
* keymap
)
128 KeyboardLayoutView::SetTarget(BMessenger target
)
135 KeyboardLayoutView::SetBaseFont(const BFont
& font
)
139 font_height fontHeight
;
140 fBaseFont
.GetHeight(&fontHeight
);
141 fBaseFontHeight
= fontHeight
.ascent
+ fontHeight
.descent
;
142 fBaseFontSize
= fBaseFont
.Size();
149 KeyboardLayoutView::AttachedToWindow()
151 SetViewColor(B_TRANSPARENT_COLOR
);
153 SetBaseFont(*be_plain_font
);
154 fSpecialFont
= *be_fixed_font
;
155 fModifiers
= modifiers();
160 KeyboardLayoutView::FrameResized(float width
, float height
)
168 KeyboardLayoutView::WindowActivated(bool active
)
176 KeyboardLayoutView::MinSize()
178 return BLayoutUtils::ComposeSize(ExplicitMinSize(), BSize(100, 50));
183 KeyboardLayoutView::KeyDown(const char* bytes
, int32 numBytes
)
185 _KeyChanged(Window()->CurrentMessage());
190 KeyboardLayoutView::KeyUp(const char* bytes
, int32 numBytes
)
192 _KeyChanged(Window()->CurrentMessage());
197 KeyboardLayoutView::MouseDown(BPoint point
)
204 if (Looper() != NULL
&& Looper()->CurrentMessage() != NULL
)
205 Looper()->CurrentMessage()->FindInt32("buttons", &buttons
);
207 Key
* key
= _KeyAt(point
);
208 if (fKeymap
== NULL
|| key
== NULL
) {
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"),
228 alternativesPopUp
->SetAsyncAutoDestruct(true);
229 if (alternativesPopUp
->SetTargetForItems(Window()) == B_OK
)
230 alternativesPopUp
->Go(ConvertToScreen(point
), true);
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
) {
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
);
340 fKeymap
->SetDeadKeyEnabled(key
->code
, fModifiers
, !isEnabled
);
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
;
354 _SetKeyState(key
->code
, true);
355 fModifiers
|= fKeymap
->Modifier(key
->code
);
359 // TODO: if possible, we could handle the lock keys for real
361 _SetKeyState(key
->code
, true);
371 KeyboardLayoutView::MouseUp(BPoint point
)
373 Key
* key
= _KeyAt(fClickPoint
);
376 if (Looper() != NULL
&& Looper()->CurrentMessage() != NULL
)
377 Looper()->CurrentMessage()->FindInt32("buttons", &buttons
);
379 if (fKeymap
== NULL
|| key
== NULL
) {
384 if ((buttons
& B_SECONDARY_MOUSE_BUTTON
) != 0
385 || ((buttons
& B_PRIMARY_MOUSE_BUTTON
) != 0
386 && (modifiers() & B_CONTROL_KEY
) != 0)) {
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);
395 // primary mouse button
398 // modifier keys are sticky when used with the mouse
399 if (fKeymap
->IsModifierKey(key
->code
))
402 _SetKeyState(key
->code
, false);
404 if (_HandleDeadKey(key
->code
, fModifiers
) && fDeadKey
!= 0)
409 if (fDragKey
== NULL
)
410 _SendFakeKeyDown(key
);
418 KeyboardLayoutView::MouseMoved(BPoint point
, uint32 transit
,
419 const BMessage
* dragMessage
)
424 // prevent dragging for tertiary mouse button
425 if ((fButtons
& B_TERTIARY_MOUSE_BUTTON
) != 0)
428 if (dragMessage
!= NULL
) {
430 _InvalidateKey(fDropTarget
);
433 _EvaluateDropTarget(point
);
440 if (Window()->CurrentMessage() == NULL
441 || Window()->CurrentMessage()->FindInt32("buttons", &buttons
) != B_OK
446 if (fDragKey
!= NULL
|| !(fabs(point
.x
- fClickPoint
.x
) > 4
447 || fabs(point
.y
- fClickPoint
.y
) > 4)) {
452 Key
* key
= _KeyAt(fClickPoint
);
456 BRect frame
= _FrameFor(key
);
457 BPoint offset
= fClickPoint
- frame
.LeftTop();
458 frame
.OffsetTo(B_ORIGIN
);
463 BBitmap
* bitmap
= new BBitmap(rect
, B_RGBA32
, true);
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);
480 BMessage
drag(B_MIME_DATA
);
481 drag
.AddInt32("key", key
->code
);
485 fKeymap
->GetChars(key
->code
, fModifiers
, fDeadKey
, &string
,
487 if (string
!= NULL
) {
488 drag
.AddData("text/plain", B_MIME_DATA
, string
, numBytes
);
492 DragMessage(&drag
, bitmap
, B_OP_ALPHA
, offset
);
494 fDragModifiers
= fModifiers
;
496 fKeyState
[key
->code
/ 8] &= ~(1 << (7 - (key
->code
& 7)));
502 KeyboardLayoutView::Draw(BRect updateRect
)
504 if (fOldSize
!= BSize(Bounds().Width(), Bounds().Height())) {
510 if (fOffscreenBitmap
!= NULL
) {
511 view
= fOffscreenView
;
519 view
->SetLowColor(Parent()->ViewColor());
521 view
->SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR
));
523 view
->FillRect(updateRect
, B_SOLID_LOW
);
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
) {
545 view
->UnlockLooper();
547 DrawBitmapAsync(fOffscreenBitmap
, BPoint(0, 0));
553 KeyboardLayoutView::MessageReceived(BMessage
* message
)
555 if (message
->WasDropped() && fEditable
&& fDropTarget
!= NULL
556 && fKeymap
!= NULL
) {
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
566 if (size
> 3 && data
[0] == '\\' && data
[1] == 'x') {
570 memcpy(tempBuffer
, data
, size
);
571 tempBuffer
[size
] = '\0';
574 while (size
> 3 && data
[0] == '\\' && data
[1] == 'x') {
575 buffer
[dataSize
++] = strtoul(&data
[2], NULL
, 16);
576 if ((buffer
[dataSize
- 1] & 0x80) == 0)
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) {
588 } else if ((data
[0] & 0x80) == 0) {
589 // an ASCII character
592 // no valid character
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
);
605 || (key
== fDropTarget
&& fDragModifiers
== fModifiers
)) {
611 fKeymap
->GetChars(fDropTarget
->code
, fModifiers
, fDeadKey
,
613 if (string
!= NULL
) {
615 fKeymap
->SetKey(fDropTarget
->code
, fModifiers
, fDeadKey
,
616 (const char*)data
, dataSize
);
617 fKeymap
->SetKey(key
->code
, fDragModifiers
, fDeadKey
,
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
);
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
)
642 uint32 modifier
= fKeymap
->Modifier(keyCode
);
646 fKeymap
->GetChars(fDropTarget
->code
, fModifiers
, fDeadKey
,
648 if (string
!= NULL
) {
649 // switch key with modifier
650 fKeymap
->SetModifier(fDropTarget
->code
, modifier
);
651 fKeymap
->SetKey(keyCode
, fDragModifiers
, fDeadKey
,
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
);
670 switch (message
->what
) {
671 case B_UNMAPPED_KEY_DOWN
:
672 case B_UNMAPPED_KEY_UP
:
673 _KeyChanged(message
);
676 case B_MODIFIERS_CHANGED
:
679 if (message
->FindInt32("modifiers", &newModifiers
) == B_OK
680 && fModifiers
!= newModifiers
) {
681 fModifiers
= newModifiers
;
682 _EvaluateDropTarget(fDropPoint
);
683 if (Window()->IsActive())
690 BView::MessageReceived(message
);
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",
708 if (fOffscreenView
!= NULL
) {
709 if (Parent() != NULL
) {
710 fOffscreenView
->SetViewColor(Parent()->ViewColor());
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
;
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()
739 (Bounds().Height() - fLayout
->Bounds().Height() * fFactor
) / 2);
741 if (fLayout
->DefaultKeySize().width
< 11)
746 fOldSize
.width
= Bounds().Width();
747 fOldSize
.height
= Bounds().Height();
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);
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
;
770 bool secondDeadKey
= false;
771 bool isDeadKeyEnabled
= true;
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
,
780 // Show the key code if there is no keymap
781 snprintf(text
, sizeof(text
), "%02" B_PRIx32
, key
->code
);
784 _SetFontSize(view
, keyKind
);
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
);
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
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;
859 BRegion
region(rect
);
860 region
.Exclude(bottomLeft
);
861 view
->ConstrainClippingRegion(®ion
);
863 // Fill in the rect with the background color
864 SetHighColor(background
);
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
);
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
)
896 else if (indicator
->modifier
== B_NUM_LOCK
)
898 else if (indicator
->modifier
== B_SCROLL_LOCK
)
901 _SetFontSize(view
, kIndicator
);
903 font_height fontHeight
;
904 GetFontHeight(&fontHeight
);
905 if (ceilf(rect
.top
- fontHeight
.ascent
+ fontHeight
.descent
- 2)
907 view
->SetHighColor(0, 0, 0);
908 view
->SetLowColor(ViewColor());
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
);
933 KeyboardLayoutView::_SpecialKeyLabel(const key_map
& map
, uint32 code
,
936 if (code
== map
.caps_key
)
937 return abbreviated
? "CAPS" : "CAPS LOCK";
938 if (code
== map
.scroll_key
)
940 if (code
== map
.num_key
)
941 return abbreviated
? "NUM" : "NUM LOCK";
942 if (code
== map
.left_shift_key
|| code
== map
.right_shift_key
)
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
)
952 if (code
== B_PRINT_KEY
)
954 if (code
== B_PAUSE_KEY
)
962 KeyboardLayoutView::_SpecialMappedKeySymbol(const char* bytes
, size_t numBytes
)
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";
988 KeyboardLayoutView::_SpecialMappedKeyLabel(const char* bytes
, size_t numBytes
,
994 if (bytes
[0] == B_ESCAPE
)
997 if (bytes
[0] == B_INSERT
)
999 if (bytes
[0] == B_DELETE
)
1001 if (bytes
[0] == B_HOME
)
1003 if (bytes
[0] == B_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";
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
);
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
)))
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
);
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
);
1057 KeyboardLayoutView::_GetKeyLabel(const Key
* key
, char* text
, size_t textSize
,
1060 const key_map
& map
= fKeymap
->Map();
1061 keyKind
= kNormalKey
;
1064 const char* special
= _SpecialKeyLabel(map
, key
->code
);
1065 if (special
!= NULL
) {
1066 strlcpy(text
, special
, textSize
);
1067 keyKind
= kSpecialKey
;
1071 if (_FunctionKeyLabel(key
->code
, text
, textSize
)) {
1072 keyKind
= kSpecialKey
;
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
;
1085 special
= _SpecialMappedKeySymbol(bytes
, numBytes
);
1086 if (special
!= NULL
) {
1087 strlcpy(text
, special
, textSize
);
1088 keyKind
= kSymbolKey
;
1091 fBaseFont
.GetHasGlyphs(bytes
, 1, &hasGlyphs
);
1093 strlcpy(text
, bytes
, textSize
);
1103 KeyboardLayoutView::_IsKeyPressed(uint32 code
)
1105 if (fDropTarget
!= NULL
&& fDropTarget
->code
== code
)
1108 return _KeyState(code
);
1113 KeyboardLayoutView::_KeyState(uint32 code
) const
1118 return (fKeyState
[code
/ 8] & (1 << (7 - (code
& 7)))) != 0;
1123 KeyboardLayoutView::_SetKeyState(uint32 code
, bool pressed
)
1129 fKeyState
[code
/ 8] |= (1 << (7 - (code
& 7)));
1131 fKeyState
[code
/ 8] &= ~(1 << (7 - (code
& 7)));
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
)
1151 KeyboardLayoutView::_InvalidateKey(uint32 code
)
1153 _InvalidateKey(_KeyForCode(code
));
1158 KeyboardLayoutView::_InvalidateKey(const Key
* key
)
1161 Invalidate(_FrameFor(key
));
1165 /*! Updates the fDeadKey member, and invalidates the view if needed.
1167 \return true if the view has been invalidated.
1170 KeyboardLayoutView::_HandleDeadKey(uint32 key
, int32 modifiers
)
1172 if (fKeymap
== NULL
|| fKeymap
->IsModifierKey(key
))
1175 bool isEnabled
= false;
1176 int32 deadKey
= fKeymap
->DeadKey(key
, modifiers
, &isEnabled
);
1177 if (fDeadKey
!= deadKey
&& isEnabled
) {
1181 } else if (fDeadKey
!= 0) {
1192 KeyboardLayoutView::_KeyChanged(const BMessage
* message
)
1197 if (message
->FindInt32("key", &key
) != B_OK
1198 || message
->FindData("states", B_UINT8_TYPE
,
1199 (const void**)&state
, &size
) != B_OK
) {
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())
1223 for (int32 j
= 7; diff
!= 0; j
--, diff
>>= 1) {
1225 _InvalidateKey(i
* 8 + j
);
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
))
1259 KeyboardLayoutView::_FrameFor(BRect keyFrame
)
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
);
1273 KeyboardLayoutView::_FrameFor(const Key
* key
)
1275 return _FrameFor(key
->frame
);
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
;
1292 fBaseFont
.SetSize(fontSize
);
1293 view
->SetFont(&fBaseFont
);
1296 fSpecialFont
.SetSize(fontSize
* 0.7);
1297 view
->SetFont(&fSpecialFont
);
1300 fSpecialFont
.SetSize(fontSize
* 1.6);
1301 view
->SetFont(&fSpecialFont
);
1307 font
.SetSize(fontSize
* 0.8);
1308 view
->SetFont(&font
);
1316 KeyboardLayoutView::_EvaluateDropTarget(BPoint point
)
1318 fDropTarget
= _KeyAt(point
);
1319 if (fDropTarget
!= NULL
) {
1320 if (fDropTarget
== fDragKey
&& fModifiers
== fDragModifiers
)
1323 _InvalidateKey(fDropTarget
);
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
,
1335 message
.AddInt32("key", key
->code
);
1336 message
.AddInt32("modifiers", fModifiers
);
1337 message
.AddPointer("keymap", fKeymap
);
1341 fKeymap
->GetChars(key
->code
, fModifiers
, fDeadKey
, &string
,
1343 if (string
!= NULL
) {
1344 message
.AddString("bytes", 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]);
1355 fTarget
.SendMessage(&message
);
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
);
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")
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")
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")
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")
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";