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