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