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