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