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 be_control_look->DrawButtonFrame(view, rect, updateRect, 4.0f, base, 703 background, pressed ? BControlLook::B_ACTIVATED : 0); 704 be_control_look->DrawButtonBackground(view, rect, updateRect, 4.0f, 705 base, pressed ? BControlLook::B_ACTIVATED : 0); 706 } 707 708 709 void 710 KeyboardLayoutView::_DrawKey(BView* view, BRect updateRect, const Key* key, 711 BRect rect, bool pressed) 712 { 713 rgb_color base = key->dark ? kDarkColor : kBrightColor; 714 rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR); 715 rgb_color keyLabelColor = make_color(0, 0, 0, 255); 716 key_kind keyKind = kNormalKey; 717 int32 deadKey = 0; 718 bool secondDeadKey = false; 719 bool isDeadKeyEnabled = true; 720 721 char text[32]; 722 if (fKeymap != NULL) { 723 _GetKeyLabel(key, text, sizeof(text), keyKind); 724 deadKey = fKeymap->DeadKey(key->code, fModifiers, &isDeadKeyEnabled); 725 secondDeadKey = fKeymap->IsDeadSecondKey(key->code, fModifiers, 726 fDeadKey); 727 } else { 728 // Show the key code if there is no keymap 729 snprintf(text, sizeof(text), "%02" B_PRIx32, key->code); 730 } 731 732 _SetFontSize(view, keyKind); 733 734 if (secondDeadKey) 735 base = kSecondDeadKeyColor; 736 else if (deadKey > 0 && isDeadKeyEnabled) 737 base = kDeadKeyColor; 738 739 if (key->shape == kRectangleKeyShape) { 740 _DrawKeyButton(view, rect, updateRect, base, background, pressed); 741 742 rect.InsetBy(1, 1); 743 744 _GetAbbreviatedKeyLabelIfNeeded(view, rect, key, text, sizeof(text)); 745 be_control_look->DrawLabel(view, text, rect, updateRect, 746 base, 0, BAlignment(B_ALIGN_CENTER, B_ALIGN_MIDDLE), &keyLabelColor); 747 } else if (key->shape == kEnterKeyShape) { 748 BRect topLeft = rect; 749 BRect topRight = rect; 750 BRect bottomLeft = rect; 751 BRect bottomRight = rect; 752 753 // TODO: for some reason, this does not always equal the bottom of 754 // the other keys... 755 bottomLeft.top = floorf(rect.top 756 + fLayout->DefaultKeySize().height * fFactor - fGap - 1); 757 bottomLeft.right = floorf(rect.left 758 + (key->frame.Width() - key->second_row) * fFactor - fGap - 2); 759 760 topLeft.bottom = bottomLeft.top; 761 topLeft.right = bottomLeft.right + 1; 762 // add one to make the borders meet 763 764 topRight.bottom = topLeft.bottom; 765 topRight.left = topLeft.right; 766 767 bottomRight.top = bottomLeft.top; 768 bottomRight.left = bottomLeft.right; 769 770 // draw top left corner 771 be_control_look->DrawButtonFrame(view, topLeft, updateRect, 772 4.0f, 0.0f, 4.0f, 0.0f, base, background, 773 pressed ? BControlLook::B_ACTIVATED : 0, 774 BControlLook::B_LEFT_BORDER | BControlLook::B_TOP_BORDER 775 | BControlLook::B_BOTTOM_BORDER); 776 be_control_look->DrawButtonBackground(view, topLeft, updateRect, 777 4.0f, 0.0f, 4.0f, 0.0f, base, 778 pressed ? BControlLook::B_ACTIVATED : 0, 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, 785 pressed ? BControlLook::B_ACTIVATED : 0, 786 BControlLook::B_TOP_BORDER | BControlLook::B_RIGHT_BORDER); 787 be_control_look->DrawButtonBackground(view, topRight, updateRect, 788 0.0f, 4.0f, 0.0f, 0.0f, base, 789 pressed ? BControlLook::B_ACTIVATED : 0, 790 BControlLook::B_TOP_BORDER | BControlLook::B_RIGHT_BORDER); 791 792 // draw bottom right corner 793 be_control_look->DrawButtonFrame(view, bottomRight, updateRect, 794 0.0f, 0.0f, 4.0f, 4.0f, base, background, 795 pressed ? BControlLook::B_ACTIVATED : 0, 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, 800 pressed ? BControlLook::B_ACTIVATED : 0, 801 BControlLook::B_LEFT_BORDER | BControlLook::B_RIGHT_BORDER 802 | BControlLook::B_BOTTOM_BORDER); 803 804 // clip out the bottom left corner 805 bottomLeft.right += 1; 806 bottomLeft.top -= 2; 807 BRegion region(rect); 808 region.Exclude(bottomLeft); 809 view->ConstrainClippingRegion(®ion); 810 811 // draw the button background 812 BRect bgRect = rect.InsetByCopy(2, 2); 813 be_control_look->DrawButtonBackground(view, bgRect, updateRect, 814 4.0f, 4.0f, 0.0f, 4.0f, base, 815 pressed ? BControlLook::B_ACTIVATED : 0); 816 817 rect.left = bottomLeft.right; 818 _GetAbbreviatedKeyLabelIfNeeded(view, rect, key, text, sizeof(text)); 819 820 // draw the button label 821 be_control_look->DrawLabel(view, text, rect, updateRect, 822 base, 0, BAlignment(B_ALIGN_CENTER, B_ALIGN_MIDDLE), &keyLabelColor); 823 824 // reset the clipping region 825 view->ConstrainClippingRegion(NULL); 826 } 827 } 828 829 830 void 831 KeyboardLayoutView::_DrawIndicator(BView* view, BRect updateRect, 832 const Indicator* indicator, BRect rect, bool lit) 833 { 834 float rectTop = rect.top; 835 rect.top += 2 * rect.Height() / 3; 836 837 const char* label = NULL; 838 if (indicator->modifier == B_CAPS_LOCK) 839 label = "caps"; 840 else if (indicator->modifier == B_NUM_LOCK) 841 label = "num"; 842 else if (indicator->modifier == B_SCROLL_LOCK) 843 label = "scroll"; 844 if (label != NULL) { 845 _SetFontSize(view, kIndicator); 846 847 font_height fontHeight; 848 GetFontHeight(&fontHeight); 849 if (ceilf(rect.top - fontHeight.ascent + fontHeight.descent - 2) 850 >= rectTop) { 851 view->SetHighColor(0, 0, 0); 852 view->SetLowColor(ViewColor()); 853 854 BString text(label); 855 view->TruncateString(&text, B_TRUNCATE_END, rect.Width()); 856 view->DrawString(text.String(), 857 BPoint(ceilf(rect.left + (rect.Width() 858 - StringWidth(text.String())) / 2), 859 ceilf(rect.top - fontHeight.descent - 2))); 860 } 861 } 862 863 rect.left += rect.Width() / 4; 864 rect.right -= rect.Width() / 3; 865 866 rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR); 867 rgb_color base = lit ? kLitIndicatorColor : kDarkColor; 868 869 be_control_look->DrawButtonFrame(view, rect, updateRect, base, 870 background, BControlLook::B_DISABLED); 871 be_control_look->DrawButtonBackground(view, rect, updateRect, 872 base, BControlLook::B_DISABLED); 873 } 874 875 876 const char* 877 KeyboardLayoutView::_SpecialKeyLabel(const key_map& map, uint32 code, 878 bool abbreviated) 879 { 880 if (code == map.caps_key) { 881 return abbreviated 882 ? B_TRANSLATE_COMMENT("CAPS", "Very short for 'caps lock'") 883 : B_TRANSLATE("CAPS LOCK"); 884 } 885 if (code == map.scroll_key) 886 return B_TRANSLATE("SCROLL"); 887 if (code == map.num_key) { 888 return abbreviated 889 ? B_TRANSLATE_COMMENT("NUM", "Very short for 'num lock'") 890 : B_TRANSLATE("NUM LOCK"); 891 } 892 if (code == map.left_shift_key || code == map.right_shift_key) 893 return B_TRANSLATE("SHIFT"); 894 if (code == map.left_command_key || code == map.right_command_key) { 895 return abbreviated 896 ? B_TRANSLATE_COMMENT("CMD", "Very short for 'command'") 897 : B_TRANSLATE("COMMAND"); 898 } 899 if (code == map.left_control_key || code == map.right_control_key) { 900 return abbreviated 901 ? B_TRANSLATE_COMMENT("CTRL", "Very short for 'control'") 902 : B_TRANSLATE("CONTROL"); 903 } 904 if (code == map.left_option_key || code == map.right_option_key) { 905 return abbreviated 906 ? B_TRANSLATE_COMMENT("OPT", "Very short for 'option'") 907 : B_TRANSLATE("OPTION"); 908 } 909 if (code == map.menu_key) 910 return B_TRANSLATE("MENU"); 911 if (code == B_PRINT_KEY) 912 return B_TRANSLATE("PRINT"); 913 if (code == B_PAUSE_KEY) 914 return B_TRANSLATE("PAUSE"); 915 916 return NULL; 917 } 918 919 920 const char* 921 KeyboardLayoutView::_SpecialMappedKeySymbol(const char* bytes, size_t numBytes) 922 { 923 if (numBytes != 1) 924 return NULL; 925 926 if (bytes[0] == B_TAB) 927 return "\xe2\x86\xb9"; 928 if (bytes[0] == B_ENTER) 929 return "\xe2\x86\xb5"; 930 if (bytes[0] == B_BACKSPACE) 931 return "\xe2\x8c\xab"; 932 933 if (bytes[0] == B_UP_ARROW) 934 return "\xe2\x86\x91"; 935 if (bytes[0] == B_LEFT_ARROW) 936 return "\xe2\x86\x90"; 937 if (bytes[0] == B_DOWN_ARROW) 938 return "\xe2\x86\x93"; 939 if (bytes[0] == B_RIGHT_ARROW) 940 return "\xe2\x86\x92"; 941 942 return NULL; 943 } 944 945 946 const char* 947 KeyboardLayoutView::_SpecialMappedKeyLabel(const char* bytes, size_t numBytes, 948 bool abbreviated) 949 { 950 if (numBytes != 1) 951 return NULL; 952 if (bytes[0] == B_ESCAPE) 953 return B_TRANSLATE("ESC"); 954 if (bytes[0] == B_INSERT) 955 return B_TRANSLATE("INS"); 956 if (bytes[0] == B_DELETE) 957 return B_TRANSLATE("DEL"); 958 if (bytes[0] == B_HOME) 959 return B_TRANSLATE("HOME"); 960 if (bytes[0] == B_END) 961 return B_TRANSLATE("END"); 962 if (bytes[0] == B_PAGE_UP) { 963 return abbreviated 964 ? B_TRANSLATE_COMMENT("PG \xe2\x86\x91", 965 "Very short for 'page up'") 966 : B_TRANSLATE("PAGE \xe2\x86\x91"); 967 } 968 if (bytes[0] == B_PAGE_DOWN) { 969 return abbreviated 970 ? B_TRANSLATE_COMMENT("PG \xe2\x86\x93", 971 "Very short for 'page down'") 972 : B_TRANSLATE("PAGE \xe2\x86\x93"); 973 } 974 975 return NULL; 976 } 977 978 979 bool 980 KeyboardLayoutView::_FunctionKeyLabel(uint32 code, char* text, size_t textSize) 981 { 982 if (code >= B_F1_KEY && code <= B_F12_KEY) { 983 snprintf(text, textSize, "F%" B_PRId32, code + 1 - B_F1_KEY); 984 return true; 985 } 986 987 return false; 988 } 989 990 991 void 992 KeyboardLayoutView::_GetAbbreviatedKeyLabelIfNeeded(BView* view, BRect rect, 993 const Key* key, char* text, size_t textSize) 994 { 995 if (floorf(rect.Width()) > ceilf(view->StringWidth(text))) 996 return; 997 998 // Check if we have a shorter version of this key 999 1000 const key_map& map = fKeymap->Map(); 1001 1002 const char* special = _SpecialKeyLabel(map, key->code, true); 1003 if (special != NULL) { 1004 strlcpy(text, special, textSize); 1005 return; 1006 } 1007 1008 char* bytes = NULL; 1009 int32 numBytes; 1010 fKeymap->GetChars(key->code, fModifiers, fDeadKey, &bytes, &numBytes); 1011 if (bytes != NULL) { 1012 special = _SpecialMappedKeyLabel(bytes, numBytes, true); 1013 if (special != NULL) 1014 strlcpy(text, special, textSize); 1015 1016 delete[] bytes; 1017 } 1018 } 1019 1020 1021 void 1022 KeyboardLayoutView::_GetKeyLabel(const Key* key, char* text, size_t textSize, 1023 key_kind& keyKind) 1024 { 1025 const key_map& map = fKeymap->Map(); 1026 keyKind = kNormalKey; 1027 text[0] = '\0'; 1028 1029 const char* special = _SpecialKeyLabel(map, key->code); 1030 if (special != NULL) { 1031 strlcpy(text, special, textSize); 1032 keyKind = kSpecialKey; 1033 return; 1034 } 1035 1036 if (_FunctionKeyLabel(key->code, text, textSize)) { 1037 keyKind = kSpecialKey; 1038 return; 1039 } 1040 1041 char* bytes = NULL; 1042 int32 numBytes; 1043 fKeymap->GetChars(key->code, fModifiers, fDeadKey, &bytes, &numBytes); 1044 if (bytes != NULL) { 1045 special = _SpecialMappedKeyLabel(bytes, numBytes); 1046 if (special != NULL) { 1047 strlcpy(text, special, textSize); 1048 keyKind = kSpecialKey; 1049 } else { 1050 special = _SpecialMappedKeySymbol(bytes, numBytes); 1051 if (special != NULL) { 1052 strlcpy(text, special, textSize); 1053 keyKind = kSymbolKey; 1054 } else { 1055 bool hasGlyphs; 1056 fBaseFont.GetHasGlyphs(bytes, 1, &hasGlyphs); 1057 if (hasGlyphs) 1058 strlcpy(text, bytes, textSize); 1059 } 1060 } 1061 1062 delete[] bytes; 1063 } 1064 } 1065 1066 1067 bool 1068 KeyboardLayoutView::_IsKeyPressed(uint32 code) 1069 { 1070 if (fDropTarget != NULL && fDropTarget->code == code) 1071 return true; 1072 1073 return _KeyState(code); 1074 } 1075 1076 1077 bool 1078 KeyboardLayoutView::_KeyState(uint32 code) const 1079 { 1080 if (code >= 16 * 8) 1081 return false; 1082 1083 return (fKeyState[code / 8] & (1 << (7 - (code & 7)))) != 0; 1084 } 1085 1086 1087 void 1088 KeyboardLayoutView::_SetKeyState(uint32 code, bool pressed) 1089 { 1090 if (code >= 16 * 8) 1091 return; 1092 1093 if (pressed) 1094 fKeyState[code / 8] |= (1 << (7 - (code & 7))); 1095 else 1096 fKeyState[code / 8] &= ~(1 << (7 - (code & 7))); 1097 } 1098 1099 1100 Key* 1101 KeyboardLayoutView::_KeyForCode(uint32 code) 1102 { 1103 // TODO: have a lookup array 1104 1105 for (int32 i = 0; i < fLayout->CountKeys(); i++) { 1106 Key* key = fLayout->KeyAt(i); 1107 if (key->code == code) 1108 return key; 1109 } 1110 1111 return NULL; 1112 } 1113 1114 1115 void 1116 KeyboardLayoutView::_InvalidateKey(uint32 code) 1117 { 1118 _InvalidateKey(_KeyForCode(code)); 1119 } 1120 1121 1122 void 1123 KeyboardLayoutView::_InvalidateKey(const Key* key) 1124 { 1125 if (key != NULL) 1126 Invalidate(_FrameFor(key)); 1127 } 1128 1129 1130 /*! Updates the fDeadKey member, and invalidates the view if needed. 1131 1132 \return true if the view has been invalidated. 1133 */ 1134 bool 1135 KeyboardLayoutView::_HandleDeadKey(uint32 key, int32 modifiers) 1136 { 1137 if (fKeymap == NULL || fKeymap->IsModifierKey(key)) 1138 return false; 1139 1140 bool isEnabled = false; 1141 int32 deadKey = fKeymap->DeadKey(key, modifiers, &isEnabled); 1142 if (fDeadKey != deadKey && isEnabled) { 1143 fDeadKey = deadKey; 1144 Invalidate(); 1145 return true; 1146 } else if (fDeadKey != 0) { 1147 fDeadKey = 0; 1148 Invalidate(); 1149 return true; 1150 } 1151 1152 return false; 1153 } 1154 1155 1156 void 1157 KeyboardLayoutView::_KeyChanged(const BMessage* message) 1158 { 1159 const uint8* state; 1160 ssize_t size; 1161 int32 key; 1162 if (message->FindInt32("key", &key) != B_OK 1163 || message->FindData("states", B_UINT8_TYPE, 1164 (const void**)&state, &size) != B_OK) { 1165 return; 1166 } 1167 1168 // Update key state, and invalidate change keys 1169 1170 bool checkSingle = true; 1171 1172 if (message->what == B_KEY_UP || message->what == B_UNMAPPED_KEY_UP) { 1173 if (_HandleDeadKey(key, fModifiers)) 1174 checkSingle = false; 1175 1176 if (_KeyForCode(key) == NULL) 1177 printf("no key for code %" B_PRId32 "\n", key); 1178 } 1179 1180 for (int32 i = 0; i < 16; i++) { 1181 if (fKeyState[i] != state[i]) { 1182 uint8 diff = fKeyState[i] ^ state[i]; 1183 fKeyState[i] = state[i]; 1184 1185 if (!checkSingle || !Window()->IsActive()) 1186 continue; 1187 1188 for (int32 j = 7; diff != 0; j--, diff >>= 1) { 1189 if (diff & 1) { 1190 _InvalidateKey(i * 8 + j); 1191 } 1192 } 1193 } 1194 } 1195 } 1196 1197 1198 Key* 1199 KeyboardLayoutView::_KeyAt(BPoint point) 1200 { 1201 // Find key candidate 1202 1203 BPoint keyPoint = point; 1204 keyPoint -= fOffset; 1205 keyPoint.x /= fFactor; 1206 keyPoint.y /= fFactor; 1207 1208 for (int32 i = fLayout->CountKeys() - 1; i >= 0; i--) { 1209 Key* key = fLayout->KeyAt(i); 1210 if (key->frame.Contains(keyPoint)) { 1211 BRect frame = _FrameFor(key); 1212 if (frame.Contains(point)) 1213 return key; 1214 1215 return NULL; 1216 } 1217 } 1218 1219 return NULL; 1220 } 1221 1222 1223 BRect 1224 KeyboardLayoutView::_FrameFor(BRect keyFrame) 1225 { 1226 BRect rect; 1227 rect.left = ceilf(keyFrame.left * fFactor); 1228 rect.top = ceilf(keyFrame.top * fFactor); 1229 rect.right = floorf((keyFrame.Width()) * fFactor + rect.left - fGap - 1); 1230 rect.bottom = floorf((keyFrame.Height()) * fFactor + rect.top - fGap - 1); 1231 rect.OffsetBy(fOffset); 1232 1233 return rect; 1234 } 1235 1236 1237 BRect 1238 KeyboardLayoutView::_FrameFor(const Key* key) 1239 { 1240 return _FrameFor(key->frame); 1241 } 1242 1243 1244 void 1245 KeyboardLayoutView::_SetFontSize(BView* view, key_kind keyKind) 1246 { 1247 BSize size = fLayout->DefaultKeySize(); 1248 float fontSize = fBaseFontSize; 1249 if (fBaseFontHeight >= size.height * fFactor * 0.5) { 1250 fontSize *= (size.height * fFactor * 0.5) / fBaseFontHeight; 1251 if (fontSize < 8) 1252 fontSize = 8; 1253 } 1254 1255 switch (keyKind) { 1256 case kNormalKey: 1257 fBaseFont.SetSize(fontSize); 1258 view->SetFont(&fBaseFont); 1259 break; 1260 case kSpecialKey: 1261 fSpecialFont.SetSize(fontSize * 0.7); 1262 view->SetFont(&fSpecialFont); 1263 break; 1264 case kSymbolKey: 1265 fSpecialFont.SetSize(fontSize * 1.6); 1266 view->SetFont(&fSpecialFont); 1267 break; 1268 1269 case kIndicator: 1270 { 1271 BFont font; 1272 font.SetSize(fontSize * 0.8); 1273 view->SetFont(&font); 1274 break; 1275 } 1276 } 1277 } 1278 1279 1280 void 1281 KeyboardLayoutView::_EvaluateDropTarget(BPoint point) 1282 { 1283 fDropTarget = _KeyAt(point); 1284 if (fDropTarget != NULL) { 1285 if (fDropTarget == fDragKey && fModifiers == fDragModifiers) 1286 fDropTarget = NULL; 1287 else 1288 _InvalidateKey(fDropTarget); 1289 } 1290 } 1291 1292 1293 void 1294 KeyboardLayoutView::_SendFakeKeyDown(const Key* key) 1295 { 1296 BMessage message(B_KEY_DOWN); 1297 message.AddInt64("when", system_time()); 1298 message.AddData("states", B_UINT8_TYPE, &fKeyState, 1299 sizeof(fKeyState)); 1300 message.AddInt32("key", key->code); 1301 message.AddInt32("modifiers", fModifiers); 1302 message.AddPointer("keymap", fKeymap); 1303 1304 char* string; 1305 int32 numBytes; 1306 fKeymap->GetChars(key->code, fModifiers, fDeadKey, &string, 1307 &numBytes); 1308 if (string != NULL) { 1309 message.AddString("bytes", string); 1310 delete[] string; 1311 } 1312 1313 fKeymap->GetChars(key->code, 0, 0, &string, &numBytes); 1314 if (string != NULL) { 1315 message.AddInt32("raw_char", string[0]); 1316 message.AddInt8("byte", string[0]); 1317 delete[] string; 1318 } 1319 1320 fTarget.SendMessage(&message); 1321 } 1322 1323 1324 BMenuItem* 1325 KeyboardLayoutView::_CreateSwapModifiersMenuItem(uint32 modifier, 1326 uint32 displayModifier, uint32 oldCode, uint32 newCode) 1327 { 1328 int32 mask = B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY; 1329 const char* oldName = _NameForModifier(oldCode == 0x00 ? modifier 1330 : fKeymap->Modifier(oldCode) & ~mask, false); 1331 const char* newName = _NameForModifier(newCode == 0x00 ? modifier 1332 : fKeymap->Modifier(newCode) & ~mask, false); 1333 1334 BMessage* message = new BMessage(kMsgUpdateModifierKeys); 1335 if (newName != NULL) 1336 message->AddUInt32(newName, oldCode); 1337 1338 if (oldName != NULL) 1339 message->AddUInt32(oldName, newCode); 1340 1341 if (oldCode == newCode) 1342 message->AddBool("unset", true); 1343 1344 return new BMenuItem(_NameForModifier(displayModifier, true), message); 1345 } 1346 1347 1348 const char* 1349 KeyboardLayoutView::_NameForModifier(uint32 modifier, bool pretty) 1350 { 1351 if (modifier == B_CAPS_LOCK) 1352 return pretty ? B_TRANSLATE("Caps lock") : "caps_key"; 1353 else if (modifier == B_NUM_LOCK) 1354 return pretty ? B_TRANSLATE("Num lock") : "num_key"; 1355 else if (modifier == B_SCROLL_LOCK) 1356 return pretty ? B_TRANSLATE("Scroll lock") : "scroll_key"; 1357 else if (modifier == B_SHIFT_KEY) { 1358 return pretty ? B_TRANSLATE_COMMENT("Shift", "Shift key") 1359 : "shift_key"; 1360 } else if (modifier == B_LEFT_SHIFT_KEY) 1361 return pretty ? B_TRANSLATE("Left shift") : "left_shift_key"; 1362 else if (modifier == B_RIGHT_SHIFT_KEY) 1363 return pretty ? B_TRANSLATE("Right shift") : "right_shift_key"; 1364 else if (modifier == B_COMMAND_KEY) { 1365 return pretty ? B_TRANSLATE_COMMENT("Command", "Command key") 1366 : "command_key"; 1367 } else if (modifier == B_LEFT_COMMAND_KEY) 1368 return pretty ? B_TRANSLATE("Left command") : "left_command_key"; 1369 else if (modifier == B_RIGHT_COMMAND_KEY) 1370 return pretty ? B_TRANSLATE("Right command") : "right_command_key"; 1371 else if (modifier == B_CONTROL_KEY) { 1372 return pretty ? B_TRANSLATE_COMMENT("Control", "Control key") 1373 : "control_key"; 1374 } else if (modifier == B_LEFT_CONTROL_KEY) 1375 return pretty ? B_TRANSLATE("Left control") : "left_control_key"; 1376 else if (modifier == B_RIGHT_CONTROL_KEY) 1377 return pretty ? B_TRANSLATE("Right control") : "right_control_key"; 1378 else if (modifier == B_OPTION_KEY) { 1379 return pretty ? B_TRANSLATE_COMMENT("Option", "Option key") 1380 : "option_key"; 1381 } else if (modifier == B_LEFT_OPTION_KEY) 1382 return pretty ? B_TRANSLATE("Left option") : "left_option_key"; 1383 else if (modifier == B_RIGHT_OPTION_KEY) 1384 return pretty ? B_TRANSLATE("Right option") : "right_option_key"; 1385 else if (modifier == B_MENU_KEY) 1386 return pretty ? B_TRANSLATE_COMMENT("Menu", "Menu key") : "menu_key"; 1387 1388 return NULL; 1389 } 1390