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 939 ? B_TRANSLATE_COMMENT("CAPS", "Very short for 'caps lock'") 940 : B_TRANSLATE("CAPS LOCK"); 941 } 942 if (code == map.scroll_key) 943 return B_TRANSLATE("SCROLL"); 944 if (code == map.num_key) { 945 return abbreviated 946 ? B_TRANSLATE_COMMENT("NUM", "Very short for 'num lock'") 947 : B_TRANSLATE("NUM LOCK"); 948 } 949 if (code == map.left_shift_key || code == map.right_shift_key) 950 return B_TRANSLATE("SHIFT"); 951 if (code == map.left_command_key || code == map.right_command_key) { 952 return abbreviated 953 ? B_TRANSLATE_COMMENT("CMD", "Very short for 'command'") 954 : B_TRANSLATE("COMMAND"); 955 } 956 if (code == map.left_control_key || code == map.right_control_key) { 957 return abbreviated 958 ? B_TRANSLATE_COMMENT("CTRL", "Very short for 'control'") 959 : B_TRANSLATE("CONTROL"); 960 } 961 if (code == map.left_option_key || code == map.right_option_key) { 962 return abbreviated 963 ? B_TRANSLATE_COMMENT("OPT", "Very short for 'option'") 964 : B_TRANSLATE("OPTION"); 965 } 966 if (code == map.menu_key) 967 return B_TRANSLATE("MENU"); 968 if (code == B_PRINT_KEY) 969 return B_TRANSLATE("PRINT"); 970 if (code == B_PAUSE_KEY) 971 return B_TRANSLATE("PAUSE"); 972 973 return NULL; 974 } 975 976 977 const char* 978 KeyboardLayoutView::_SpecialMappedKeySymbol(const char* bytes, size_t numBytes) 979 { 980 if (numBytes != 1) 981 return NULL; 982 983 if (bytes[0] == B_TAB) 984 return "\xe2\x86\xb9"; 985 if (bytes[0] == B_ENTER) 986 return "\xe2\x86\xb5"; 987 if (bytes[0] == B_BACKSPACE) 988 return "\xe2\x8c\xab"; 989 990 if (bytes[0] == B_UP_ARROW) 991 return "\xe2\x86\x91"; 992 if (bytes[0] == B_LEFT_ARROW) 993 return "\xe2\x86\x90"; 994 if (bytes[0] == B_DOWN_ARROW) 995 return "\xe2\x86\x93"; 996 if (bytes[0] == B_RIGHT_ARROW) 997 return "\xe2\x86\x92"; 998 999 return NULL; 1000 } 1001 1002 1003 const char* 1004 KeyboardLayoutView::_SpecialMappedKeyLabel(const char* bytes, size_t numBytes, 1005 bool abbreviated) 1006 { 1007 if (numBytes != 1) 1008 return NULL; 1009 if (bytes[0] == B_ESCAPE) 1010 return B_TRANSLATE("ESC"); 1011 if (bytes[0] == B_INSERT) 1012 return B_TRANSLATE("INS"); 1013 if (bytes[0] == B_DELETE) 1014 return B_TRANSLATE("DEL"); 1015 if (bytes[0] == B_HOME) 1016 return B_TRANSLATE("HOME"); 1017 if (bytes[0] == B_END) 1018 return B_TRANSLATE("END"); 1019 if (bytes[0] == B_PAGE_UP) { 1020 return abbreviated 1021 ? B_TRANSLATE_COMMENT("PG \xe2\x86\x91", 1022 "Very short for 'page up'") 1023 : B_TRANSLATE("PAGE \xe2\x86\x91"); 1024 } 1025 if (bytes[0] == B_PAGE_DOWN) { 1026 return abbreviated 1027 ? B_TRANSLATE_COMMENT("PG \xe2\x86\x93", 1028 "Very short for 'page down'") 1029 : B_TRANSLATE("PAGE \xe2\x86\x93"); 1030 } 1031 1032 return NULL; 1033 } 1034 1035 1036 bool 1037 KeyboardLayoutView::_FunctionKeyLabel(uint32 code, char* text, size_t textSize) 1038 { 1039 if (code >= B_F1_KEY && code <= B_F12_KEY) { 1040 snprintf(text, textSize, "F%" B_PRId32, code + 1 - B_F1_KEY); 1041 return true; 1042 } 1043 1044 return false; 1045 } 1046 1047 1048 void 1049 KeyboardLayoutView::_GetAbbreviatedKeyLabelIfNeeded(BView* view, BRect rect, 1050 const Key* key, char* text, size_t textSize) 1051 { 1052 if (floorf(rect.Width()) > ceilf(view->StringWidth(text))) 1053 return; 1054 1055 // Check if we have a shorter version of this key 1056 1057 const key_map& map = fKeymap->Map(); 1058 1059 const char* special = _SpecialKeyLabel(map, key->code, true); 1060 if (special != NULL) { 1061 strlcpy(text, special, textSize); 1062 return; 1063 } 1064 1065 char* bytes = NULL; 1066 int32 numBytes; 1067 fKeymap->GetChars(key->code, fModifiers, fDeadKey, &bytes, &numBytes); 1068 if (bytes != NULL) { 1069 special = _SpecialMappedKeyLabel(bytes, numBytes, true); 1070 if (special != NULL) 1071 strlcpy(text, special, textSize); 1072 1073 delete[] bytes; 1074 } 1075 } 1076 1077 1078 void 1079 KeyboardLayoutView::_GetKeyLabel(const Key* key, char* text, size_t textSize, 1080 key_kind& keyKind) 1081 { 1082 const key_map& map = fKeymap->Map(); 1083 keyKind = kNormalKey; 1084 text[0] = '\0'; 1085 1086 const char* special = _SpecialKeyLabel(map, key->code); 1087 if (special != NULL) { 1088 strlcpy(text, special, textSize); 1089 keyKind = kSpecialKey; 1090 return; 1091 } 1092 1093 if (_FunctionKeyLabel(key->code, text, textSize)) { 1094 keyKind = kSpecialKey; 1095 return; 1096 } 1097 1098 char* bytes = NULL; 1099 int32 numBytes; 1100 fKeymap->GetChars(key->code, fModifiers, fDeadKey, &bytes, &numBytes); 1101 if (bytes != NULL) { 1102 special = _SpecialMappedKeyLabel(bytes, numBytes); 1103 if (special != NULL) { 1104 strlcpy(text, special, textSize); 1105 keyKind = kSpecialKey; 1106 } else { 1107 special = _SpecialMappedKeySymbol(bytes, numBytes); 1108 if (special != NULL) { 1109 strlcpy(text, special, textSize); 1110 keyKind = kSymbolKey; 1111 } else { 1112 bool hasGlyphs; 1113 fBaseFont.GetHasGlyphs(bytes, 1, &hasGlyphs); 1114 if (hasGlyphs) 1115 strlcpy(text, bytes, textSize); 1116 } 1117 } 1118 1119 delete[] bytes; 1120 } 1121 } 1122 1123 1124 bool 1125 KeyboardLayoutView::_IsKeyPressed(uint32 code) 1126 { 1127 if (fDropTarget != NULL && fDropTarget->code == code) 1128 return true; 1129 1130 return _KeyState(code); 1131 } 1132 1133 1134 bool 1135 KeyboardLayoutView::_KeyState(uint32 code) const 1136 { 1137 if (code >= 16 * 8) 1138 return false; 1139 1140 return (fKeyState[code / 8] & (1 << (7 - (code & 7)))) != 0; 1141 } 1142 1143 1144 void 1145 KeyboardLayoutView::_SetKeyState(uint32 code, bool pressed) 1146 { 1147 if (code >= 16 * 8) 1148 return; 1149 1150 if (pressed) 1151 fKeyState[code / 8] |= (1 << (7 - (code & 7))); 1152 else 1153 fKeyState[code / 8] &= ~(1 << (7 - (code & 7))); 1154 } 1155 1156 1157 Key* 1158 KeyboardLayoutView::_KeyForCode(uint32 code) 1159 { 1160 // TODO: have a lookup array 1161 1162 for (int32 i = 0; i < fLayout->CountKeys(); i++) { 1163 Key* key = fLayout->KeyAt(i); 1164 if (key->code == code) 1165 return key; 1166 } 1167 1168 return NULL; 1169 } 1170 1171 1172 void 1173 KeyboardLayoutView::_InvalidateKey(uint32 code) 1174 { 1175 _InvalidateKey(_KeyForCode(code)); 1176 } 1177 1178 1179 void 1180 KeyboardLayoutView::_InvalidateKey(const Key* key) 1181 { 1182 if (key != NULL) 1183 Invalidate(_FrameFor(key)); 1184 } 1185 1186 1187 /*! Updates the fDeadKey member, and invalidates the view if needed. 1188 1189 \return true if the view has been invalidated. 1190 */ 1191 bool 1192 KeyboardLayoutView::_HandleDeadKey(uint32 key, int32 modifiers) 1193 { 1194 if (fKeymap == NULL || fKeymap->IsModifierKey(key)) 1195 return false; 1196 1197 bool isEnabled = false; 1198 int32 deadKey = fKeymap->DeadKey(key, modifiers, &isEnabled); 1199 if (fDeadKey != deadKey && isEnabled) { 1200 fDeadKey = deadKey; 1201 Invalidate(); 1202 return true; 1203 } else if (fDeadKey != 0) { 1204 fDeadKey = 0; 1205 Invalidate(); 1206 return true; 1207 } 1208 1209 return false; 1210 } 1211 1212 1213 void 1214 KeyboardLayoutView::_KeyChanged(const BMessage* message) 1215 { 1216 const uint8* state; 1217 ssize_t size; 1218 int32 key; 1219 if (message->FindInt32("key", &key) != B_OK 1220 || message->FindData("states", B_UINT8_TYPE, 1221 (const void**)&state, &size) != B_OK) { 1222 return; 1223 } 1224 1225 // Update key state, and invalidate change keys 1226 1227 bool checkSingle = true; 1228 1229 if (message->what == B_KEY_UP || message->what == B_UNMAPPED_KEY_UP) { 1230 if (_HandleDeadKey(key, fModifiers)) 1231 checkSingle = false; 1232 1233 if (_KeyForCode(key) == NULL) 1234 printf("no key for code %" B_PRId32 "\n", key); 1235 } 1236 1237 for (int32 i = 0; i < 16; i++) { 1238 if (fKeyState[i] != state[i]) { 1239 uint8 diff = fKeyState[i] ^ state[i]; 1240 fKeyState[i] = state[i]; 1241 1242 if (!checkSingle || !Window()->IsActive()) 1243 continue; 1244 1245 for (int32 j = 7; diff != 0; j--, diff >>= 1) { 1246 if (diff & 1) { 1247 _InvalidateKey(i * 8 + j); 1248 } 1249 } 1250 } 1251 } 1252 } 1253 1254 1255 Key* 1256 KeyboardLayoutView::_KeyAt(BPoint point) 1257 { 1258 // Find key candidate 1259 1260 BPoint keyPoint = point; 1261 keyPoint -= fOffset; 1262 keyPoint.x /= fFactor; 1263 keyPoint.y /= fFactor; 1264 1265 for (int32 i = fLayout->CountKeys() - 1; i >= 0; i--) { 1266 Key* key = fLayout->KeyAt(i); 1267 if (key->frame.Contains(keyPoint)) { 1268 BRect frame = _FrameFor(key); 1269 if (frame.Contains(point)) 1270 return key; 1271 1272 return NULL; 1273 } 1274 } 1275 1276 return NULL; 1277 } 1278 1279 1280 BRect 1281 KeyboardLayoutView::_FrameFor(BRect keyFrame) 1282 { 1283 BRect rect; 1284 rect.left = ceilf(keyFrame.left * fFactor); 1285 rect.top = ceilf(keyFrame.top * fFactor); 1286 rect.right = floorf((keyFrame.Width()) * fFactor + rect.left - fGap - 1); 1287 rect.bottom = floorf((keyFrame.Height()) * fFactor + rect.top - fGap - 1); 1288 rect.OffsetBy(fOffset); 1289 1290 return rect; 1291 } 1292 1293 1294 BRect 1295 KeyboardLayoutView::_FrameFor(const Key* key) 1296 { 1297 return _FrameFor(key->frame); 1298 } 1299 1300 1301 void 1302 KeyboardLayoutView::_SetFontSize(BView* view, key_kind keyKind) 1303 { 1304 BSize size = fLayout->DefaultKeySize(); 1305 float fontSize = fBaseFontSize; 1306 if (fBaseFontHeight >= size.height * fFactor * 0.5) { 1307 fontSize *= (size.height * fFactor * 0.5) / fBaseFontHeight; 1308 if (fontSize < 8) 1309 fontSize = 8; 1310 } 1311 1312 switch (keyKind) { 1313 case kNormalKey: 1314 fBaseFont.SetSize(fontSize); 1315 view->SetFont(&fBaseFont); 1316 break; 1317 case kSpecialKey: 1318 fSpecialFont.SetSize(fontSize * 0.7); 1319 view->SetFont(&fSpecialFont); 1320 break; 1321 case kSymbolKey: 1322 fSpecialFont.SetSize(fontSize * 1.6); 1323 view->SetFont(&fSpecialFont); 1324 break; 1325 1326 case kIndicator: 1327 { 1328 BFont font; 1329 font.SetSize(fontSize * 0.8); 1330 view->SetFont(&font); 1331 break; 1332 } 1333 } 1334 } 1335 1336 1337 void 1338 KeyboardLayoutView::_EvaluateDropTarget(BPoint point) 1339 { 1340 fDropTarget = _KeyAt(point); 1341 if (fDropTarget != NULL) { 1342 if (fDropTarget == fDragKey && fModifiers == fDragModifiers) 1343 fDropTarget = NULL; 1344 else 1345 _InvalidateKey(fDropTarget); 1346 } 1347 } 1348 1349 1350 void 1351 KeyboardLayoutView::_SendFakeKeyDown(const Key* key) 1352 { 1353 BMessage message(B_KEY_DOWN); 1354 message.AddInt64("when", system_time()); 1355 message.AddData("states", B_UINT8_TYPE, &fKeyState, 1356 sizeof(fKeyState)); 1357 message.AddInt32("key", key->code); 1358 message.AddInt32("modifiers", fModifiers); 1359 message.AddPointer("keymap", fKeymap); 1360 1361 char* string; 1362 int32 numBytes; 1363 fKeymap->GetChars(key->code, fModifiers, fDeadKey, &string, 1364 &numBytes); 1365 if (string != NULL) { 1366 message.AddString("bytes", string); 1367 delete[] string; 1368 } 1369 1370 fKeymap->GetChars(key->code, 0, 0, &string, &numBytes); 1371 if (string != NULL) { 1372 message.AddInt32("raw_char", string[0]); 1373 message.AddInt8("byte", string[0]); 1374 delete[] string; 1375 } 1376 1377 fTarget.SendMessage(&message); 1378 } 1379 1380 1381 BMenuItem* 1382 KeyboardLayoutView::_CreateSwapModifiersMenuItem(uint32 modifier, 1383 uint32 displayModifier, uint32 oldCode, uint32 newCode) 1384 { 1385 int32 mask = B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY; 1386 const char* oldName = _NameForModifier(oldCode == 0x00 ? modifier 1387 : fKeymap->Modifier(oldCode) & ~mask, false); 1388 const char* newName = _NameForModifier(newCode == 0x00 ? modifier 1389 : fKeymap->Modifier(newCode) & ~mask, false); 1390 1391 BMessage* message = new BMessage(kMsgUpdateModifierKeys); 1392 if (newName != NULL) 1393 message->AddUInt32(newName, oldCode); 1394 1395 if (oldName != NULL) 1396 message->AddUInt32(oldName, newCode); 1397 1398 if (oldCode == newCode) 1399 message->AddBool("unset", true); 1400 1401 return new BMenuItem(_NameForModifier(displayModifier, true), message); 1402 } 1403 1404 1405 const char* 1406 KeyboardLayoutView::_NameForModifier(uint32 modifier, bool pretty) 1407 { 1408 if (modifier == B_CAPS_LOCK) 1409 return pretty ? B_TRANSLATE("Caps lock") : "caps_key"; 1410 else if (modifier == B_NUM_LOCK) 1411 return pretty ? B_TRANSLATE("Num lock") : "num_key"; 1412 else if (modifier == B_SCROLL_LOCK) 1413 return pretty ? B_TRANSLATE("Scroll lock") : "scroll_key"; 1414 else if (modifier == B_SHIFT_KEY) { 1415 return pretty ? B_TRANSLATE_COMMENT("Shift", "Shift key") 1416 : "shift_key"; 1417 } else if (modifier == B_LEFT_SHIFT_KEY) 1418 return pretty ? B_TRANSLATE("Left shift") : "left_shift_key"; 1419 else if (modifier == B_RIGHT_SHIFT_KEY) 1420 return pretty ? B_TRANSLATE("Right shift") : "right_shift_key"; 1421 else if (modifier == B_COMMAND_KEY) { 1422 return pretty ? B_TRANSLATE_COMMENT("Command", "Command key") 1423 : "command_key"; 1424 } else if (modifier == B_LEFT_COMMAND_KEY) 1425 return pretty ? B_TRANSLATE("Left command") : "left_command_key"; 1426 else if (modifier == B_RIGHT_COMMAND_KEY) 1427 return pretty ? B_TRANSLATE("Right command") : "right_command_key"; 1428 else if (modifier == B_CONTROL_KEY) { 1429 return pretty ? B_TRANSLATE_COMMENT("Control", "Control key") 1430 : "control_key"; 1431 } else if (modifier == B_LEFT_CONTROL_KEY) 1432 return pretty ? B_TRANSLATE("Left control") : "left_control_key"; 1433 else if (modifier == B_RIGHT_CONTROL_KEY) 1434 return pretty ? B_TRANSLATE("Right control") : "right_control_key"; 1435 else if (modifier == B_OPTION_KEY) { 1436 return pretty ? B_TRANSLATE_COMMENT("Option", "Option key") 1437 : "option_key"; 1438 } else if (modifier == B_LEFT_OPTION_KEY) 1439 return pretty ? B_TRANSLATE("Left option") : "left_option_key"; 1440 else if (modifier == B_RIGHT_OPTION_KEY) 1441 return pretty ? B_TRANSLATE("Right option") : "right_option_key"; 1442 else if (modifier == B_MENU_KEY) 1443 return pretty ? B_TRANSLATE_COMMENT("Menu", "Menu key") : "menu_key"; 1444 1445 return NULL; 1446 } 1447