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