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