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