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