1 /* 2 * Copyright 2009-2010, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "KeyboardLayoutView.h" 8 9 #include <stdio.h> 10 #include <stdlib.h> 11 12 #include <Beep.h> 13 #include <Bitmap.h> 14 #include <ControlLook.h> 15 #include <LayoutUtils.h> 16 #include <Region.h> 17 #include <Window.h> 18 19 #include "Keymap.h" 20 21 22 static const rgb_color kBrightColor = {230, 230, 230, 255}; 23 static const rgb_color kDarkColor = {200, 200, 200, 255}; 24 static const rgb_color kSecondDeadKeyColor = {240, 240, 150, 255}; 25 static const rgb_color kDeadKeyColor = {152, 203, 255, 255}; 26 static const rgb_color kLitIndicatorColor = {116, 212, 83, 255}; 27 28 29 KeyboardLayoutView::KeyboardLayoutView(const char* name) 30 : 31 BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS), 32 fOffscreenBitmap(NULL), 33 fKeymap(NULL), 34 fEditable(true), 35 fModifiers(0), 36 fDeadKey(0), 37 fButtons(0), 38 fDragKey(NULL), 39 fDropTarget(NULL), 40 fOldSize(0, 0) 41 { 42 fLayout = new KeyboardLayout; 43 memset(fKeyState, 0, sizeof(fKeyState)); 44 45 SetEventMask(B_KEYBOARD_EVENTS); 46 } 47 48 49 KeyboardLayoutView::~KeyboardLayoutView() 50 { 51 delete fOffscreenBitmap; 52 } 53 54 55 void 56 KeyboardLayoutView::SetKeyboardLayout(KeyboardLayout* layout) 57 { 58 fLayout = layout; 59 _LayoutKeyboard(); 60 Invalidate(); 61 } 62 63 64 void 65 KeyboardLayoutView::SetKeymap(Keymap* keymap) 66 { 67 fKeymap = keymap; 68 Invalidate(); 69 } 70 71 72 void 73 KeyboardLayoutView::SetTarget(BMessenger target) 74 { 75 fTarget = target; 76 } 77 78 79 void 80 KeyboardLayoutView::SetBaseFont(const BFont& font) 81 { 82 fBaseFont = font; 83 84 font_height fontHeight; 85 fBaseFont.GetHeight(&fontHeight); 86 fBaseFontHeight = fontHeight.ascent + fontHeight.descent; 87 fBaseFontSize = fBaseFont.Size(); 88 89 Invalidate(); 90 } 91 92 93 void 94 KeyboardLayoutView::AttachedToWindow() 95 { 96 SetViewColor(B_TRANSPARENT_COLOR); 97 98 SetBaseFont(*be_plain_font); 99 fSpecialFont = *be_fixed_font; 100 fModifiers = modifiers(); 101 } 102 103 104 void 105 KeyboardLayoutView::FrameResized(float width, float height) 106 { 107 _InitOffscreen(); 108 _LayoutKeyboard(); 109 } 110 111 112 void 113 KeyboardLayoutView::WindowActivated(bool active) 114 { 115 if (active) 116 Invalidate(); 117 } 118 119 120 BSize 121 KeyboardLayoutView::MinSize() 122 { 123 return BLayoutUtils::ComposeSize(ExplicitMinSize(), BSize(100, 50)); 124 } 125 126 127 void 128 KeyboardLayoutView::KeyDown(const char* bytes, int32 numBytes) 129 { 130 _KeyChanged(Window()->CurrentMessage()); 131 } 132 133 134 void 135 KeyboardLayoutView::KeyUp(const char* bytes, int32 numBytes) 136 { 137 _KeyChanged(Window()->CurrentMessage()); 138 } 139 140 141 void 142 KeyboardLayoutView::MouseDown(BPoint point) 143 { 144 fClickPoint = point; 145 fDragKey = NULL; 146 fDropPoint.x = -1; 147 148 Key* key = _KeyAt(point); 149 if (key == NULL) 150 return; 151 152 int32 buttons = 0; 153 if (Looper() != NULL && Looper()->CurrentMessage() != NULL) 154 Looper()->CurrentMessage()->FindInt32("buttons", &buttons); 155 156 if ((buttons & B_TERTIARY_MOUSE_BUTTON) != 0 157 && (fButtons & B_TERTIARY_MOUSE_BUTTON) == 0) { 158 // toggle the "deadness" of dead keys via middle mouse button 159 if (fKeymap != NULL) { 160 bool isEnabled = false; 161 uint8 deadKey 162 = fKeymap->DeadKey(key->code, fModifiers, &isEnabled); 163 if (deadKey > 0) { 164 fKeymap->SetDeadKeyEnabled(key->code, fModifiers, !isEnabled); 165 _InvalidateKey(key); 166 } 167 } 168 } else { 169 if (fKeymap != NULL && fKeymap->IsModifierKey(key->code)) { 170 if (_KeyState(key->code)) { 171 uint32 modifier = fKeymap->Modifier(key->code); 172 if ((modifier & modifiers()) == 0) { 173 _SetKeyState(key->code, false); 174 fModifiers &= ~modifier; 175 Invalidate(); 176 } 177 } else { 178 _SetKeyState(key->code, true); 179 fModifiers |= fKeymap->Modifier(key->code); 180 Invalidate(); 181 } 182 183 // TODO: if possible, we could handle the lock keys for real 184 } else { 185 _SetKeyState(key->code, true); 186 _InvalidateKey(key); 187 } 188 } 189 190 fButtons = buttons; 191 } 192 193 194 void 195 KeyboardLayoutView::MouseUp(BPoint point) 196 { 197 Key* key = _KeyAt(fClickPoint); 198 199 int32 buttons = 0; 200 if (Looper() != NULL && Looper()->CurrentMessage() != NULL) 201 Looper()->CurrentMessage()->FindInt32("buttons", &buttons); 202 203 if (key != NULL) { 204 if ((fButtons & B_TERTIARY_MOUSE_BUTTON) != 0 205 && (buttons & B_TERTIARY_MOUSE_BUTTON) == 0) { 206 _SetKeyState(key->code, false); 207 _InvalidateKey(key); 208 fButtons = buttons; 209 } else { 210 fButtons = buttons; 211 212 // modifier keys are sticky when used with the mouse 213 if (fKeymap != NULL && fKeymap->IsModifierKey(key->code)) 214 return; 215 216 _SetKeyState(key->code, false); 217 218 if (_HandleDeadKey(key->code, fModifiers) && fDeadKey != 0) 219 return; 220 221 _InvalidateKey(key); 222 223 if (fDragKey == NULL && fKeymap != NULL) { 224 // Send fake key down message to target 225 _SendFakeKeyDown(key); 226 } 227 } 228 } 229 fDragKey = NULL; 230 } 231 232 233 void 234 KeyboardLayoutView::MouseMoved(BPoint point, uint32 transit, 235 const BMessage* dragMessage) 236 { 237 if (fKeymap == NULL) 238 return; 239 240 // prevent dragging for tertiary mouse button 241 if ((fButtons & B_TERTIARY_MOUSE_BUTTON) != 0) 242 return; 243 244 if (dragMessage != NULL) { 245 if (fEditable) { 246 _InvalidateKey(fDropTarget); 247 fDropPoint = point; 248 249 _EvaluateDropTarget(point); 250 } 251 252 return; 253 } 254 255 int32 buttons; 256 if (Window()->CurrentMessage() == NULL 257 || Window()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK 258 || buttons == 0) 259 return; 260 261 if (fDragKey == NULL && (fabs(point.x - fClickPoint.x) > 4 262 || fabs(point.y - fClickPoint.y) > 4)) { 263 // start dragging 264 Key* key = _KeyAt(fClickPoint); 265 if (key == NULL) 266 return; 267 268 BRect frame = _FrameFor(key); 269 BPoint offset = fClickPoint - frame.LeftTop(); 270 frame.OffsetTo(B_ORIGIN); 271 272 BRect rect = frame; 273 rect.right--; 274 rect.bottom--; 275 BBitmap* bitmap = new BBitmap(rect, B_RGBA32, true); 276 bitmap->Lock(); 277 278 BView* view = new BView(rect, "drag", B_FOLLOW_NONE, 0); 279 bitmap->AddChild(view); 280 281 view->SetHighColor(0, 0, 0, 0); 282 view->FillRect(view->Bounds()); 283 view->SetDrawingMode(B_OP_ALPHA); 284 view->SetHighColor(0, 0, 0, 128); 285 // set the level of transparency by value 286 view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE); 287 _DrawKey(view, frame, key, frame, false); 288 289 view->Sync(); 290 bitmap->Unlock(); 291 292 BMessage drag(B_MIME_DATA); 293 drag.AddInt32("key", key->code); 294 295 char* string; 296 int32 numBytes; 297 fKeymap->GetChars(key->code, fModifiers, fDeadKey, &string, 298 &numBytes); 299 if (string != NULL) { 300 drag.AddData("text/plain", B_MIME_DATA, string, numBytes); 301 delete[] string; 302 } 303 304 DragMessage(&drag, bitmap, B_OP_ALPHA, offset); 305 fDragKey = key; 306 fDragModifiers = fModifiers; 307 308 fKeyState[key->code / 8] &= ~(1 << (7 - (key->code & 7))); 309 _InvalidateKey(key); 310 } 311 } 312 313 314 void 315 KeyboardLayoutView::Draw(BRect updateRect) 316 { 317 if (fOldSize != BSize(Bounds().Width(), Bounds().Height())) { 318 _InitOffscreen(); 319 _LayoutKeyboard(); 320 } 321 322 BView* view; 323 if (fOffscreenBitmap != NULL) { 324 view = fOffscreenView; 325 view->LockLooper(); 326 } else 327 view = this; 328 329 // Draw background 330 331 if (Parent()) 332 view->SetLowColor(Parent()->ViewColor()); 333 else 334 view->SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 335 336 view->FillRect(updateRect, B_SOLID_LOW); 337 338 // Draw keys 339 340 for (int32 i = 0; i < fLayout->CountKeys(); i++) { 341 Key* key = fLayout->KeyAt(i); 342 343 _DrawKey(view, updateRect, key, _FrameFor(key), 344 _IsKeyPressed(key->code)); 345 } 346 347 // Draw LED indicators 348 349 for (int32 i = 0; i < fLayout->CountIndicators(); i++) { 350 Indicator* indicator = fLayout->IndicatorAt(i); 351 352 _DrawIndicator(view, updateRect, indicator, _FrameFor(indicator->frame), 353 (fModifiers & indicator->modifier) != 0); 354 } 355 356 if (fOffscreenBitmap != NULL) { 357 view->Sync(); 358 view->UnlockLooper(); 359 360 DrawBitmapAsync(fOffscreenBitmap, BPoint(0, 0)); 361 } 362 } 363 364 365 void 366 KeyboardLayoutView::MessageReceived(BMessage* message) 367 { 368 if (message->WasDropped() && fEditable && fDropTarget != NULL 369 && fKeymap != NULL) { 370 int32 keyCode; 371 const char* data; 372 ssize_t size; 373 if (message->FindData("text/plain", B_MIME_DATA, 374 (const void**)&data, &size) == B_OK) { 375 // Automatically convert UTF-8 escaped strings (for example from 376 // CharacterMap) 377 int32 dataSize = 0; 378 uint8 buffer[16]; 379 if (size > 3 && data[0] == '\\' && data[1] == 'x') { 380 char tempBuffer[16]; 381 if (size > 15) 382 size = 15; 383 memcpy(tempBuffer, data, size); 384 tempBuffer[size] = '\0'; 385 data = tempBuffer; 386 387 while (size > 3 && data[0] == '\\' && data[1] == 'x') { 388 buffer[dataSize++] = strtoul(&data[2], NULL, 16); 389 if ((buffer[dataSize - 1] & 0x80) == 0) 390 break; 391 392 size -= 4; 393 data += 4; 394 } 395 data = (const char*)buffer; 396 } else if ((data[0] & 0xc0) != 0x80 && (data[0] & 0x80) != 0) { 397 // only accept the first character UTF-8 character 398 while (dataSize < size && (data[dataSize] & 0x80) != 0) { 399 dataSize++; 400 } 401 } else if ((data[0] & 0x80) == 0) { 402 // an ASCII character 403 dataSize = 1; 404 } else { 405 // no valid character 406 beep(); 407 return; 408 } 409 410 int32 buttons; 411 if (!message->IsSourceRemote() 412 && message->FindInt32("buttons", &buttons) == B_OK 413 && (buttons & B_SECONDARY_MOUSE_BUTTON) != 0 414 && message->FindInt32("key", &keyCode) == B_OK) { 415 // switch keys if the dropped object came from us 416 Key* key = _KeyForCode(keyCode); 417 if (key == NULL 418 || (key == fDropTarget && fDragModifiers == fModifiers)) 419 return; 420 421 char* string; 422 int32 numBytes; 423 fKeymap->GetChars(fDropTarget->code, fModifiers, fDeadKey, 424 &string, &numBytes); 425 if (string != NULL) { 426 // switch keys 427 fKeymap->SetKey(fDropTarget->code, fModifiers, fDeadKey, 428 (const char*)data, dataSize); 429 fKeymap->SetKey(key->code, fDragModifiers, fDeadKey, 430 string, numBytes); 431 delete[] string; 432 } else if (fKeymap->IsModifierKey(fDropTarget->code)) { 433 // switch key with modifier 434 fKeymap->SetModifier(key->code, 435 fKeymap->Modifier(fDropTarget->code)); 436 fKeymap->SetKey(fDropTarget->code, fModifiers, fDeadKey, 437 (const char*)data, dataSize); 438 } 439 } else { 440 // Send the old key to the target, so it's not lost entirely 441 _SendFakeKeyDown(fDropTarget); 442 443 fKeymap->SetKey(fDropTarget->code, fModifiers, fDeadKey, 444 (const char*)data, dataSize); 445 } 446 } else if (!message->IsSourceRemote() 447 && message->FindInt32("key", &keyCode) == B_OK) { 448 // Switch an unmapped key 449 450 Key* key = _KeyForCode(keyCode); 451 if (key != NULL && key == fDropTarget) 452 return; 453 454 uint32 modifier = fKeymap->Modifier(keyCode); 455 456 char* string; 457 int32 numBytes; 458 fKeymap->GetChars(fDropTarget->code, fModifiers, fDeadKey, 459 &string, &numBytes); 460 if (string != NULL) { 461 // switch key with modifier 462 fKeymap->SetModifier(fDropTarget->code, modifier); 463 fKeymap->SetKey(keyCode, fDragModifiers, fDeadKey, 464 string, numBytes); 465 delete[] string; 466 } else { 467 // switch modifier keys 468 fKeymap->SetModifier(keyCode, 469 fKeymap->Modifier(fDropTarget->code)); 470 fKeymap->SetModifier(fDropTarget->code, modifier); 471 } 472 473 _InvalidateKey(fDragKey); 474 } 475 476 _InvalidateKey(fDropTarget); 477 fDropTarget = NULL; 478 fDropPoint.x = -1; 479 return; 480 } 481 482 switch (message->what) { 483 case B_UNMAPPED_KEY_DOWN: 484 case B_UNMAPPED_KEY_UP: 485 _KeyChanged(message); 486 break; 487 488 case B_MODIFIERS_CHANGED: 489 { 490 int32 newModifiers; 491 if (message->FindInt32("modifiers", &newModifiers) == B_OK 492 && fModifiers != newModifiers) { 493 fModifiers = newModifiers; 494 _EvaluateDropTarget(fDropPoint); 495 if (Window()->IsActive()) 496 Invalidate(); 497 } 498 break; 499 } 500 501 default: 502 BView::MessageReceived(message); 503 break; 504 } 505 } 506 507 508 void 509 KeyboardLayoutView::_InitOffscreen() 510 { 511 delete fOffscreenBitmap; 512 fOffscreenView = NULL; 513 514 fOffscreenBitmap = new(std::nothrow) BBitmap(Bounds(), 515 B_BITMAP_ACCEPTS_VIEWS, B_RGB32); 516 if (fOffscreenBitmap != NULL && fOffscreenBitmap->IsValid()) { 517 fOffscreenBitmap->Lock(); 518 fOffscreenView = new(std::nothrow) BView(Bounds(), "offscreen view", 519 0, 0); 520 if (fOffscreenView != NULL) { 521 if (Parent() != NULL) { 522 fOffscreenView->SetViewColor(Parent()->ViewColor()); 523 } else { 524 fOffscreenView->SetViewColor( 525 ui_color(B_PANEL_BACKGROUND_COLOR)); 526 } 527 528 fOffscreenView->SetLowColor(fOffscreenView->ViewColor()); 529 fOffscreenBitmap->AddChild(fOffscreenView); 530 } 531 fOffscreenBitmap->Unlock(); 532 } 533 534 if (fOffscreenView == NULL) { 535 // something went wrong 536 delete fOffscreenBitmap; 537 fOffscreenBitmap = NULL; 538 } 539 } 540 541 542 void 543 KeyboardLayoutView::_LayoutKeyboard() 544 { 545 float factorX = Bounds().Width() / fLayout->Bounds().Width(); 546 float factorY = Bounds().Height() / fLayout->Bounds().Height(); 547 548 fFactor = min_c(factorX, factorY); 549 fOffset = BPoint((Bounds().Width() - fLayout->Bounds().Width() 550 * fFactor) / 2, 551 (Bounds().Height() - fLayout->Bounds().Height() * fFactor) / 2); 552 553 if (fLayout->DefaultKeySize().width < 11) 554 fGap = 1; 555 else 556 fGap = 2; 557 558 fOldSize.width = Bounds().Width(); 559 fOldSize.height = Bounds().Height(); 560 } 561 562 563 void 564 KeyboardLayoutView::_DrawKeyButton(BView* view, BRect& rect, BRect updateRect, 565 rgb_color base, rgb_color background, bool pressed) 566 { 567 be_control_look->DrawButtonFrame(view, rect, updateRect, base, 568 background, pressed ? BControlLook::B_ACTIVATED : 0); 569 be_control_look->DrawButtonBackground(view, rect, updateRect, 570 base, pressed ? BControlLook::B_ACTIVATED : 0); 571 } 572 573 574 void 575 KeyboardLayoutView::_DrawKey(BView* view, BRect updateRect, const Key* key, 576 BRect rect, bool pressed) 577 { 578 rgb_color base = key->dark ? kDarkColor : kBrightColor; 579 rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR); 580 key_kind keyKind = kNormalKey; 581 int32 deadKey = 0; 582 bool secondDeadKey = false; 583 bool isDeadKeyEnabled = true; 584 585 char text[32]; 586 if (fKeymap != NULL) { 587 _GetKeyLabel(key, text, sizeof(text), keyKind); 588 deadKey = fKeymap->DeadKey(key->code, fModifiers, &isDeadKeyEnabled); 589 secondDeadKey = fKeymap->IsDeadSecondKey(key->code, fModifiers, 590 fDeadKey); 591 } else { 592 // Show the key code if there is no keymap 593 snprintf(text, sizeof(text), "%02lx", key->code); 594 } 595 596 _SetFontSize(view, keyKind); 597 598 if (secondDeadKey) 599 base = kSecondDeadKeyColor; 600 else if (deadKey > 0 && isDeadKeyEnabled) 601 base = kDeadKeyColor; 602 603 if (key->shape == kRectangleKeyShape) { 604 _DrawKeyButton(view, rect, updateRect, base, background, pressed); 605 606 rect.InsetBy(1, 1); 607 608 _GetAbbreviatedKeyLabelIfNeeded(view, rect, key, text, sizeof(text)); 609 be_control_look->DrawLabel(view, text, rect, updateRect, 610 base, 0, BAlignment(B_ALIGN_CENTER, B_ALIGN_MIDDLE)); 611 } else if (key->shape == kEnterKeyShape) { 612 BRegion region(rect); 613 BRect originalRect = rect; 614 BRect missingRect = rect; 615 616 // TODO: for some reason, this does not always equal the bottom of 617 // the other keys... 618 missingRect.top = floorf(rect.top 619 + fLayout->DefaultKeySize().height * fFactor - fGap - 1); 620 missingRect.right = floorf(missingRect.left 621 + (key->frame.Width() - key->second_row) * fFactor - fGap - 2); 622 region.Exclude(missingRect); 623 view->ConstrainClippingRegion(®ion); 624 625 _DrawKeyButton(view, rect, updateRect, base, background, pressed); 626 627 rect.left = missingRect.right; 628 _GetAbbreviatedKeyLabelIfNeeded(view, rect, key, text, sizeof(text)); 629 630 be_control_look->DrawLabel(view, text, rect, updateRect, 631 base, 0, BAlignment(B_ALIGN_CENTER, B_ALIGN_MIDDLE)); 632 633 missingRect.right--; 634 missingRect.top -= 2; 635 region.Set(missingRect); 636 view->ConstrainClippingRegion(®ion); 637 638 rect = originalRect; 639 rect.bottom = missingRect.top + 2; 640 _DrawKeyButton(view, rect, updateRect, base, background, pressed); 641 642 missingRect.left = missingRect.right; 643 missingRect.right++; 644 missingRect.top += 2; 645 region.Set(missingRect); 646 view->ConstrainClippingRegion(®ion); 647 648 rect = originalRect; 649 rect.left = missingRect.right - 2; 650 rect.top = missingRect.top - 2; 651 _DrawKeyButton(view, rect, updateRect, base, background, pressed); 652 653 view->ConstrainClippingRegion(NULL); 654 } 655 } 656 657 658 void 659 KeyboardLayoutView::_DrawIndicator(BView* view, BRect updateRect, 660 const Indicator* indicator, BRect rect, bool lit) 661 { 662 float rectTop = rect.top; 663 rect.top += 2 * rect.Height() / 3; 664 665 const char* label = NULL; 666 if (indicator->modifier == B_CAPS_LOCK) 667 label = "caps"; 668 else if (indicator->modifier == B_NUM_LOCK) 669 label = "num"; 670 else if (indicator->modifier == B_SCROLL_LOCK) 671 label = "scroll"; 672 if (label != NULL) { 673 _SetFontSize(view, kIndicator); 674 675 font_height fontHeight; 676 GetFontHeight(&fontHeight); 677 if (ceilf(rect.top - fontHeight.ascent + fontHeight.descent - 2) 678 >= rectTop) { 679 view->SetHighColor(0, 0, 0); 680 view->SetLowColor(ViewColor()); 681 682 BString text(label); 683 view->TruncateString(&text, B_TRUNCATE_END, rect.Width()); 684 view->DrawString(text.String(), 685 BPoint(ceilf(rect.left + (rect.Width() 686 - StringWidth(text.String())) / 2), 687 ceilf(rect.top - fontHeight.descent - 2))); 688 } 689 } 690 691 rect.left += rect.Width() / 4; 692 rect.right -= rect.Width() / 3; 693 694 rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR); 695 rgb_color base = lit ? kLitIndicatorColor : kDarkColor; 696 697 be_control_look->DrawButtonFrame(view, rect, updateRect, base, 698 background, BControlLook::B_DISABLED); 699 be_control_look->DrawButtonBackground(view, rect, updateRect, 700 base, BControlLook::B_DISABLED); 701 } 702 703 704 const char* 705 KeyboardLayoutView::_SpecialKeyLabel(const key_map& map, uint32 code, 706 bool abbreviated) 707 { 708 if (code == map.caps_key) 709 return abbreviated ? "CAPS" : "CAPS LOCK"; 710 if (code == map.scroll_key) 711 return "SCROLL"; 712 if (code == map.num_key) 713 return abbreviated ? "NUM" : "NUM LOCK"; 714 if (code == map.left_shift_key || code == map.right_shift_key) 715 return "SHIFT"; 716 if (code == map.left_command_key || code == map.right_command_key) 717 return abbreviated ? "CMD" : "COMMAND"; 718 if (code == map.left_control_key || code == map.right_control_key) 719 return abbreviated ? "CTRL" : "CONTROL"; 720 if (code == map.left_option_key || code == map.right_option_key) 721 return abbreviated ? "OPT" : "OPTION"; 722 if (code == map.menu_key) 723 return "MENU"; 724 if (code == B_PRINT_KEY) 725 return "PRINT"; 726 if (code == B_PAUSE_KEY) 727 return "PAUSE"; 728 729 return NULL; 730 } 731 732 733 const char* 734 KeyboardLayoutView::_SpecialMappedKeySymbol(const char* bytes, size_t numBytes) 735 { 736 if (numBytes != 1) 737 return NULL; 738 739 if (bytes[0] == B_TAB) 740 return "\xe2\x86\xb9"; 741 if (bytes[0] == B_ENTER) 742 return "\xe2\x86\xb5"; 743 if (bytes[0] == B_BACKSPACE) 744 return "\xe2\x8c\xab"; 745 746 if (bytes[0] == B_UP_ARROW) 747 return "\xe2\x86\x91"; 748 if (bytes[0] == B_LEFT_ARROW) 749 return "\xe2\x86\x90"; 750 if (bytes[0] == B_DOWN_ARROW) 751 return "\xe2\x86\x93"; 752 if (bytes[0] == B_RIGHT_ARROW) 753 return "\xe2\x86\x92"; 754 755 return NULL; 756 } 757 758 759 const char* 760 KeyboardLayoutView::_SpecialMappedKeyLabel(const char* bytes, size_t numBytes, 761 bool abbreviated) 762 { 763 if (numBytes != 1) 764 return NULL; 765 766 if (bytes[0] == B_ESCAPE) 767 return "ESC"; 768 769 if (bytes[0] == B_INSERT) 770 return "INS"; 771 if (bytes[0] == B_DELETE) 772 return "DEL"; 773 if (bytes[0] == B_HOME) 774 return "HOME"; 775 if (bytes[0] == B_END) 776 return "END"; 777 if (bytes[0] == B_PAGE_UP) 778 return abbreviated ? "PG \xe2\x86\x91" : "PAGE \xe2\x86\x91"; 779 if (bytes[0] == B_PAGE_DOWN) 780 return abbreviated ? "PG \xe2\x86\x93" : "PAGE \xe2\x86\x93"; 781 782 return NULL; 783 } 784 785 786 bool 787 KeyboardLayoutView::_FunctionKeyLabel(uint32 code, char* text, size_t textSize) 788 { 789 if (code >= B_F1_KEY && code <= B_F12_KEY) { 790 snprintf(text, textSize, "F%ld", code + 1 - B_F1_KEY); 791 return true; 792 } 793 794 return false; 795 } 796 797 798 void 799 KeyboardLayoutView::_GetAbbreviatedKeyLabelIfNeeded(BView* view, BRect rect, 800 const Key* key, char* text, size_t textSize) 801 { 802 if (floorf(rect.Width()) > ceilf(view->StringWidth(text))) 803 return; 804 805 // Check if we have a shorter version of this key 806 807 const key_map& map = fKeymap->Map(); 808 809 const char* special = _SpecialKeyLabel(map, key->code, true); 810 if (special != NULL) { 811 strlcpy(text, special, textSize); 812 return; 813 } 814 815 char* bytes = NULL; 816 int32 numBytes; 817 fKeymap->GetChars(key->code, fModifiers, fDeadKey, &bytes, &numBytes); 818 if (bytes != NULL) { 819 special = _SpecialMappedKeyLabel(bytes, numBytes, true); 820 if (special != NULL) 821 strlcpy(text, special, textSize); 822 823 delete[] bytes; 824 } 825 } 826 827 828 void 829 KeyboardLayoutView::_GetKeyLabel(const Key* key, char* text, size_t textSize, 830 key_kind& keyKind) 831 { 832 const key_map& map = fKeymap->Map(); 833 keyKind = kNormalKey; 834 text[0] = '\0'; 835 836 const char* special = _SpecialKeyLabel(map, key->code); 837 if (special != NULL) { 838 strlcpy(text, special, textSize); 839 keyKind = kSpecialKey; 840 return; 841 } 842 843 if (_FunctionKeyLabel(key->code, text, textSize)) { 844 keyKind = kSpecialKey; 845 return; 846 } 847 848 char* bytes = NULL; 849 int32 numBytes; 850 fKeymap->GetChars(key->code, fModifiers, fDeadKey, &bytes, &numBytes); 851 if (bytes != NULL) { 852 special = _SpecialMappedKeyLabel(bytes, numBytes); 853 if (special != NULL) { 854 strlcpy(text, special, textSize); 855 keyKind = kSpecialKey; 856 } else { 857 special = _SpecialMappedKeySymbol(bytes, numBytes); 858 if (special != NULL) { 859 strlcpy(text, special, textSize); 860 keyKind = kSymbolKey; 861 } else { 862 bool hasGlyphs; 863 fBaseFont.GetHasGlyphs(bytes, 1, &hasGlyphs); 864 if (hasGlyphs) 865 strlcpy(text, bytes, textSize); 866 } 867 } 868 869 delete[] bytes; 870 } 871 } 872 873 874 bool 875 KeyboardLayoutView::_IsKeyPressed(uint32 code) 876 { 877 if (fDropTarget != NULL && fDropTarget->code == (int32)code) 878 return true; 879 880 return _KeyState(code); 881 } 882 883 884 bool 885 KeyboardLayoutView::_KeyState(uint32 code) const 886 { 887 if (code >= 16 * 8) 888 return false; 889 890 return (fKeyState[code / 8] & (1 << (7 - (code & 7)))) != 0; 891 } 892 893 894 void 895 KeyboardLayoutView::_SetKeyState(uint32 code, bool pressed) 896 { 897 if (code >= 16 * 8) 898 return; 899 900 if (pressed) 901 fKeyState[code / 8] |= (1 << (7 - (code & 7))); 902 else 903 fKeyState[code / 8] &= ~(1 << (7 - (code & 7))); 904 } 905 906 907 Key* 908 KeyboardLayoutView::_KeyForCode(uint32 code) 909 { 910 // TODO: have a lookup array 911 912 for (int32 i = 0; i < fLayout->CountKeys(); i++) { 913 Key* key = fLayout->KeyAt(i); 914 if (key->code == (int32)code) 915 return key; 916 } 917 918 return NULL; 919 } 920 921 922 void 923 KeyboardLayoutView::_InvalidateKey(uint32 code) 924 { 925 _InvalidateKey(_KeyForCode(code)); 926 } 927 928 929 void 930 KeyboardLayoutView::_InvalidateKey(const Key* key) 931 { 932 if (key != NULL) 933 Invalidate(_FrameFor(key)); 934 } 935 936 937 /*! Updates the fDeadKey member, and invalidates the view if needed. 938 939 \return true if the view has been invalidated. 940 */ 941 bool 942 KeyboardLayoutView::_HandleDeadKey(uint32 key, int32 modifiers) 943 { 944 if (fKeymap == NULL || fKeymap->IsModifierKey(key)) 945 return false; 946 947 bool isEnabled = false; 948 int32 deadKey = fKeymap->DeadKey(key, modifiers, &isEnabled); 949 if (fDeadKey != deadKey) { 950 if (isEnabled) { 951 Invalidate(); 952 fDeadKey = deadKey; 953 return true; 954 } 955 } else if (fDeadKey != 0) { 956 Invalidate(); 957 fDeadKey = 0; 958 return true; 959 } 960 961 return false; 962 } 963 964 965 void 966 KeyboardLayoutView::_KeyChanged(const BMessage* message) 967 { 968 const uint8* state; 969 ssize_t size; 970 int32 key; 971 if (message->FindData("states", B_UINT8_TYPE, (const void**)&state, &size) 972 != B_OK 973 || message->FindInt32("key", &key) != B_OK) 974 return; 975 976 // Update key state, and invalidate change keys 977 978 bool checkSingle = true; 979 980 if (message->what == B_KEY_DOWN || message->what == B_UNMAPPED_KEY_DOWN) { 981 if (_HandleDeadKey(key, fModifiers)) 982 checkSingle = false; 983 984 if (_KeyForCode(key) == NULL) 985 printf("no key for code %ld\n", key); 986 } 987 988 for (int32 i = 0; i < 16; i++) { 989 if (fKeyState[i] != state[i]) { 990 uint8 diff = fKeyState[i] ^ state[i]; 991 fKeyState[i] = state[i]; 992 993 if (!checkSingle || !Window()->IsActive()) 994 continue; 995 996 for (int32 j = 7; diff != 0; j--, diff >>= 1) { 997 if (diff & 1) { 998 _InvalidateKey(i * 8 + j); 999 } 1000 } 1001 } 1002 } 1003 } 1004 1005 1006 Key* 1007 KeyboardLayoutView::_KeyAt(BPoint point) 1008 { 1009 // Find key candidate 1010 1011 BPoint keyPoint = point; 1012 keyPoint -= fOffset; 1013 keyPoint.x /= fFactor; 1014 keyPoint.y /= fFactor; 1015 1016 for (int32 i = 0; i < fLayout->CountKeys(); i++) { 1017 Key* key = fLayout->KeyAt(i); 1018 if (key->frame.Contains(keyPoint)) { 1019 BRect frame = _FrameFor(key); 1020 if (frame.Contains(point)) 1021 return key; 1022 1023 return NULL; 1024 } 1025 } 1026 1027 return NULL; 1028 } 1029 1030 1031 BRect 1032 KeyboardLayoutView::_FrameFor(BRect keyFrame) 1033 { 1034 BRect rect; 1035 rect.left = ceilf(keyFrame.left * fFactor); 1036 rect.right = floorf((keyFrame.Width()) * fFactor + rect.left - fGap - 1); 1037 rect.top = ceilf(keyFrame.top * fFactor); 1038 rect.bottom = floorf((keyFrame.Height()) * fFactor + rect.top - fGap - 1); 1039 rect.OffsetBy(fOffset); 1040 1041 return rect; 1042 } 1043 1044 1045 BRect 1046 KeyboardLayoutView::_FrameFor(const Key* key) 1047 { 1048 return _FrameFor(key->frame); 1049 } 1050 1051 1052 void 1053 KeyboardLayoutView::_SetFontSize(BView* view, key_kind keyKind) 1054 { 1055 BSize size = fLayout->DefaultKeySize(); 1056 float fontSize = fBaseFontSize; 1057 if (fBaseFontHeight >= size.height * fFactor * 0.5) { 1058 fontSize *= (size.height * fFactor * 0.5) / fBaseFontHeight; 1059 if (fontSize < 8) 1060 fontSize = 8; 1061 } 1062 1063 switch (keyKind) { 1064 case kNormalKey: 1065 fBaseFont.SetSize(fontSize); 1066 view->SetFont(&fBaseFont); 1067 break; 1068 case kSpecialKey: 1069 fSpecialFont.SetSize(fontSize * 0.7); 1070 view->SetFont(&fSpecialFont); 1071 break; 1072 case kSymbolKey: 1073 fSpecialFont.SetSize(fontSize * 1.6); 1074 view->SetFont(&fSpecialFont); 1075 break; 1076 1077 case kIndicator: 1078 { 1079 BFont font; 1080 font.SetSize(fontSize * 0.8); 1081 view->SetFont(&font); 1082 break; 1083 } 1084 } 1085 } 1086 1087 1088 void 1089 KeyboardLayoutView::_EvaluateDropTarget(BPoint point) 1090 { 1091 fDropTarget = _KeyAt(point); 1092 if (fDropTarget != NULL) { 1093 if (fDropTarget == fDragKey && fModifiers == fDragModifiers) 1094 fDropTarget = NULL; 1095 else 1096 _InvalidateKey(fDropTarget); 1097 } 1098 } 1099 1100 1101 void 1102 KeyboardLayoutView::_SendFakeKeyDown(const Key* key) 1103 { 1104 BMessage message(B_KEY_DOWN); 1105 message.AddInt64("when", system_time()); 1106 message.AddData("states", B_UINT8_TYPE, &fKeyState, 1107 sizeof(fKeyState)); 1108 message.AddInt32("key", key->code); 1109 message.AddInt32("modifiers", fModifiers); 1110 message.AddPointer("keymap", fKeymap); 1111 1112 char* string; 1113 int32 numBytes; 1114 fKeymap->GetChars(key->code, fModifiers, fDeadKey, &string, 1115 &numBytes); 1116 if (string != NULL) { 1117 message.AddString("bytes", string); 1118 delete[] string; 1119 } 1120 1121 fKeymap->GetChars(key->code, 0, 0, &string, &numBytes); 1122 if (string != NULL) { 1123 message.AddInt32("raw_char", string[0]); 1124 message.AddInt8("byte", string[0]); 1125 delete[] string; 1126 } 1127 1128 fTarget.SendMessage(&message); 1129 } 1130