1 /* 2 * Copyright 2009, 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::SetFont(const BFont& font) 81 { 82 fFont = font; 83 84 font_height fontHeight; 85 fFont.GetHeight(&fontHeight); 86 fBaseFontHeight = fontHeight.ascent + fontHeight.descent; 87 fBaseFontSize = fFont.Size(); 88 89 Invalidate(); 90 } 91 92 93 void 94 KeyboardLayoutView::AttachedToWindow() 95 { 96 SetViewColor(B_TRANSPARENT_COLOR); 97 98 SetFont(*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->IsDeadKey(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_BITMAP_ACCEPTS_VIEWS, B_RGBA32); 276 bitmap->Lock(); 277 278 BView* view = new BView(rect, "drag", 0, 0); 279 bitmap->AddChild(view); 280 281 _DrawKey(view, frame, key, frame, false); 282 283 view->Sync(); 284 bitmap->RemoveChild(view); 285 bitmap->Unlock(); 286 287 // Make it transparent 288 // TODO: is there a better way to do this? 289 uint8* bits = (uint8*)bitmap->Bits(); 290 for (int32 i = 0; i < bitmap->BitsLength(); i += 4) { 291 bits[i + 3] = 144; 292 } 293 294 BMessage drag(B_MIME_DATA); 295 drag.AddInt32("key", key->code); 296 297 char* string; 298 int32 numBytes; 299 fKeymap->GetChars(key->code, fModifiers, fDeadKey, &string, 300 &numBytes); 301 if (string != NULL) { 302 drag.AddData("text/plain", B_MIME_DATA, string, numBytes); 303 delete[] string; 304 } 305 306 DragMessage(&drag, bitmap, B_OP_ALPHA, offset); 307 fDragKey = key; 308 fDragModifiers = fModifiers; 309 310 fKeyState[key->code / 8] &= ~(1 << (7 - (key->code & 7))); 311 _InvalidateKey(key); 312 } 313 } 314 315 316 void 317 KeyboardLayoutView::Draw(BRect updateRect) 318 { 319 if (fOldSize != BSize(Bounds().Width(), Bounds().Height())) { 320 _InitOffscreen(); 321 _LayoutKeyboard(); 322 } 323 324 BView* view; 325 if (fOffscreenBitmap != NULL) { 326 view = fOffscreenView; 327 view->LockLooper(); 328 } else 329 view = this; 330 331 // Draw background 332 333 if (Parent()) 334 view->SetLowColor(Parent()->ViewColor()); 335 else 336 view->SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 337 338 view->FillRect(updateRect, B_SOLID_LOW); 339 340 // Draw keys 341 342 for (int32 i = 0; i < fLayout->CountKeys(); i++) { 343 Key* key = fLayout->KeyAt(i); 344 345 _DrawKey(view, updateRect, key, _FrameFor(key), 346 _IsKeyPressed(key->code)); 347 } 348 349 // Draw LED indicators 350 351 for (int32 i = 0; i < fLayout->CountIndicators(); i++) { 352 Indicator* indicator = fLayout->IndicatorAt(i); 353 354 _DrawIndicator(view, updateRect, indicator, _FrameFor(indicator->frame), 355 (fModifiers & indicator->modifier) != 0); 356 } 357 358 if (fOffscreenBitmap != NULL) { 359 view->Sync(); 360 view->UnlockLooper(); 361 362 DrawBitmapAsync(fOffscreenBitmap, BPoint(0, 0)); 363 } 364 } 365 366 367 void 368 KeyboardLayoutView::MessageReceived(BMessage* message) 369 { 370 if (message->WasDropped() && fEditable && fDropTarget != NULL 371 && fKeymap != NULL) { 372 int32 keyCode; 373 const char* data; 374 ssize_t size; 375 if (message->FindData("text/plain", B_MIME_DATA, 376 (const void**)&data, &size) == B_OK) { 377 // Automatically convert UTF-8 escaped strings (for example from 378 // CharacterMap) 379 int32 dataSize = 0; 380 uint8 buffer[16]; 381 if (size > 3 && data[0] == '\\' && data[1] == 'x') { 382 char tempBuffer[16]; 383 if (size > 15) 384 size = 15; 385 memcpy(tempBuffer, data, size); 386 tempBuffer[size] = '\0'; 387 data = tempBuffer; 388 389 while (size > 3 && data[0] == '\\' && data[1] == 'x') { 390 buffer[dataSize++] = strtoul(&data[2], NULL, 16); 391 if ((buffer[dataSize - 1] & 0x80) == 0) 392 break; 393 394 size -= 4; 395 data += 4; 396 } 397 data = (const char*)buffer; 398 } else if ((data[0] & 0xc0) != 0x80 && (data[0] & 0x80) != 0) { 399 // only accept the first character UTF-8 character 400 while (dataSize < size && (data[dataSize] & 0x80) != 0) { 401 dataSize++; 402 } 403 } else if ((data[0] & 0x80) == 0) { 404 // an ASCII character 405 dataSize = 1; 406 } else { 407 // no valid character 408 beep(); 409 return; 410 } 411 412 int32 buttons; 413 if (!message->IsSourceRemote() 414 && message->FindInt32("buttons", &buttons) == B_OK 415 && (buttons & B_SECONDARY_MOUSE_BUTTON) != 0 416 && message->FindInt32("key", &keyCode) == B_OK) { 417 // switch keys if the dropped object came from us 418 Key* key = _KeyForCode(keyCode); 419 if (key == NULL 420 || (key == fDropTarget && fDragModifiers == fModifiers)) 421 return; 422 423 char* string; 424 int32 numBytes; 425 fKeymap->GetChars(fDropTarget->code, fModifiers, fDeadKey, 426 &string, &numBytes); 427 if (string != NULL) { 428 // switch keys 429 fKeymap->SetKey(fDropTarget->code, fModifiers, fDeadKey, 430 (const char*)data, dataSize); 431 fKeymap->SetKey(key->code, fDragModifiers, fDeadKey, 432 string, numBytes); 433 delete[] string; 434 } else if (fKeymap->IsModifierKey(fDropTarget->code)) { 435 // switch key with modifier 436 fKeymap->SetModifier(key->code, 437 fKeymap->Modifier(fDropTarget->code)); 438 fKeymap->SetKey(fDropTarget->code, fModifiers, fDeadKey, 439 (const char*)data, dataSize); 440 } 441 } else { 442 // Send the old key to the target, so it's not lost entirely 443 _SendFakeKeyDown(fDropTarget); 444 445 fKeymap->SetKey(fDropTarget->code, fModifiers, fDeadKey, 446 (const char*)data, dataSize); 447 } 448 } else if (!message->IsSourceRemote() 449 && message->FindInt32("key", &keyCode) == B_OK) { 450 // Switch an unmapped key 451 452 Key* key = _KeyForCode(keyCode); 453 if (key != NULL && key == fDropTarget) 454 return; 455 456 uint32 modifier = fKeymap->Modifier(keyCode); 457 458 char* string; 459 int32 numBytes; 460 fKeymap->GetChars(fDropTarget->code, fModifiers, fDeadKey, 461 &string, &numBytes); 462 if (string != NULL) { 463 // switch key with modifier 464 fKeymap->SetModifier(fDropTarget->code, modifier); 465 fKeymap->SetKey(keyCode, fDragModifiers, fDeadKey, 466 string, numBytes); 467 delete[] string; 468 } else { 469 // switch modifier keys 470 fKeymap->SetModifier(keyCode, 471 fKeymap->Modifier(fDropTarget->code)); 472 fKeymap->SetModifier(fDropTarget->code, modifier); 473 } 474 475 _InvalidateKey(fDragKey); 476 } 477 478 _InvalidateKey(fDropTarget); 479 fDropTarget = NULL; 480 fDropPoint.x = -1; 481 return; 482 } 483 484 switch (message->what) { 485 case B_UNMAPPED_KEY_DOWN: 486 case B_UNMAPPED_KEY_UP: 487 _KeyChanged(message); 488 break; 489 490 case B_MODIFIERS_CHANGED: 491 { 492 int32 newModifiers; 493 if (message->FindInt32("modifiers", &newModifiers) == B_OK 494 && fModifiers != newModifiers) { 495 fModifiers = newModifiers; 496 _EvaluateDropTarget(fDropPoint); 497 if (Window()->IsActive()) 498 Invalidate(); 499 } 500 break; 501 } 502 503 default: 504 BView::MessageReceived(message); 505 break; 506 } 507 } 508 509 510 void 511 KeyboardLayoutView::_InitOffscreen() 512 { 513 delete fOffscreenBitmap; 514 fOffscreenView = NULL; 515 516 fOffscreenBitmap = new(std::nothrow) BBitmap(Bounds(), 517 B_BITMAP_ACCEPTS_VIEWS, B_RGB32); 518 if (fOffscreenBitmap != NULL && fOffscreenBitmap->IsValid()) { 519 fOffscreenBitmap->Lock(); 520 fOffscreenView = new(std::nothrow) BView(Bounds(), "offscreen view", 521 0, 0); 522 if (fOffscreenView != NULL) { 523 if (Parent() != NULL) { 524 fOffscreenView->SetViewColor(Parent()->ViewColor()); 525 } else { 526 fOffscreenView->SetViewColor( 527 ui_color(B_PANEL_BACKGROUND_COLOR)); 528 } 529 530 fOffscreenView->SetLowColor(fOffscreenView->ViewColor()); 531 fOffscreenBitmap->AddChild(fOffscreenView); 532 } 533 fOffscreenBitmap->Unlock(); 534 } 535 536 if (fOffscreenView == NULL) { 537 // something went wrong 538 delete fOffscreenBitmap; 539 fOffscreenBitmap = NULL; 540 } 541 } 542 543 544 void 545 KeyboardLayoutView::_LayoutKeyboard() 546 { 547 float factorX = Bounds().Width() / fLayout->Bounds().Width(); 548 float factorY = Bounds().Height() / fLayout->Bounds().Height(); 549 550 fFactor = min_c(factorX, factorY); 551 fOffset = BPoint((Bounds().Width() - fLayout->Bounds().Width() 552 * fFactor) / 2, 553 (Bounds().Height() - fLayout->Bounds().Height() * fFactor) / 2); 554 555 if (fLayout->DefaultKeySize().width < 11) 556 fGap = 1; 557 else 558 fGap = 2; 559 560 fOldSize.width = Bounds().Width(); 561 fOldSize.height = Bounds().Height(); 562 } 563 564 565 void 566 KeyboardLayoutView::_DrawKeyButton(BView* view, BRect& rect, BRect updateRect, 567 rgb_color base, rgb_color background, bool pressed) 568 { 569 be_control_look->DrawButtonFrame(view, rect, updateRect, base, 570 background, pressed ? BControlLook::B_ACTIVATED : 0); 571 be_control_look->DrawButtonBackground(view, rect, updateRect, 572 base, pressed ? BControlLook::B_ACTIVATED : 0); 573 } 574 575 576 void 577 KeyboardLayoutView::_DrawKey(BView* view, BRect updateRect, const Key* key, 578 BRect rect, bool pressed) 579 { 580 rgb_color base = key->dark ? kDarkColor : kBrightColor; 581 rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR); 582 key_kind keyKind = kNormalKey; 583 int32 deadKey = 0; 584 bool secondDeadKey = false; 585 bool isDeadKeyEnabled = true; 586 587 char text[32]; 588 if (fKeymap != NULL) { 589 _GetKeyLabel(key, text, sizeof(text), keyKind); 590 deadKey = fKeymap->IsDeadKey(key->code, fModifiers, &isDeadKeyEnabled); 591 secondDeadKey = fKeymap->IsDeadSecondKey(key->code, fModifiers, 592 fDeadKey); 593 } else { 594 // Show the key code if there is no keymap 595 snprintf(text, sizeof(text), "%02lx", key->code); 596 } 597 598 _SetFontSize(view, keyKind); 599 600 if (secondDeadKey) 601 base = kSecondDeadKeyColor; 602 else if (deadKey > 0 && isDeadKeyEnabled) 603 base = kDeadKeyColor; 604 605 if (key->shape == kRectangleKeyShape) { 606 _DrawKeyButton(view, rect, updateRect, base, background, pressed); 607 608 rect.InsetBy(1, 1); 609 610 be_control_look->DrawLabel(view, text, rect, updateRect, 611 base, 0, BAlignment(B_ALIGN_CENTER, B_ALIGN_MIDDLE)); 612 } else if (key->shape == kEnterKeyShape) { 613 BRegion region(rect); 614 BRect originalRect = rect; 615 BRect missingRect = rect; 616 617 // TODO: for some reason, this does not always equal the bottom of 618 // the other keys... 619 missingRect.top = floorf(rect.top 620 + fLayout->DefaultKeySize().height * fFactor - fGap - 1); 621 missingRect.right = floorf(missingRect.left 622 + (key->frame.Width() - key->second_row) * fFactor - fGap - 2); 623 region.Exclude(missingRect); 624 view->ConstrainClippingRegion(®ion); 625 626 _DrawKeyButton(view, rect, updateRect, base, background, pressed); 627 628 rect.left = missingRect.right; 629 be_control_look->DrawLabel(view, text, rect, updateRect, 630 base, 0, BAlignment(B_ALIGN_CENTER, B_ALIGN_MIDDLE)); 631 632 missingRect.right--; 633 missingRect.top -= 2; 634 region.Set(missingRect); 635 view->ConstrainClippingRegion(®ion); 636 637 rect = originalRect; 638 rect.bottom = missingRect.top + 2; 639 _DrawKeyButton(view, rect, updateRect, base, background, pressed); 640 641 missingRect.left = missingRect.right; 642 missingRect.right++; 643 missingRect.top += 2; 644 region.Set(missingRect); 645 view->ConstrainClippingRegion(®ion); 646 647 rect = originalRect; 648 rect.left = missingRect.right - 2; 649 rect.top = missingRect.top - 2; 650 _DrawKeyButton(view, rect, updateRect, base, background, pressed); 651 652 view->ConstrainClippingRegion(NULL); 653 } 654 } 655 656 657 void 658 KeyboardLayoutView::_DrawIndicator(BView* view, BRect updateRect, 659 const Indicator* indicator, BRect rect, bool lit) 660 { 661 float rectTop = rect.top; 662 rect.top += 2 * rect.Height() / 3; 663 664 const char* label = NULL; 665 if (indicator->modifier == B_CAPS_LOCK) 666 label = "caps"; 667 else if (indicator->modifier == B_NUM_LOCK) 668 label = "num"; 669 else if (indicator->modifier == B_SCROLL_LOCK) 670 label = "scroll"; 671 if (label != NULL) { 672 _SetFontSize(view, kIndicator); 673 674 font_height fontHeight; 675 GetFontHeight(&fontHeight); 676 if (ceilf(rect.top - fontHeight.ascent + fontHeight.descent - 2) 677 >= rectTop) { 678 view->SetHighColor(0, 0, 0); 679 view->SetLowColor(ViewColor()); 680 681 BString text(label); 682 view->TruncateString(&text, B_TRUNCATE_END, rect.Width()); 683 view->DrawString(text.String(), 684 BPoint(ceilf(rect.left + (rect.Width() 685 - StringWidth(text.String())) / 2), 686 ceilf(rect.top - fontHeight.descent - 2))); 687 } 688 } 689 690 rect.left += rect.Width() / 4; 691 rect.right -= rect.Width() / 3; 692 693 rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR); 694 rgb_color base = lit ? kLitIndicatorColor : kDarkColor; 695 696 be_control_look->DrawButtonFrame(view, rect, updateRect, base, 697 background, BControlLook::B_DISABLED); 698 be_control_look->DrawButtonBackground(view, rect, updateRect, 699 base, BControlLook::B_DISABLED); 700 } 701 702 703 const char* 704 KeyboardLayoutView::_SpecialKeyLabel(const key_map& map, uint32 code) 705 { 706 if (code == map.caps_key) 707 return "CAPS LOCK"; 708 if (code == map.scroll_key) 709 return "SCROLL"; 710 if (code == map.num_key) 711 return "NUM LOCK"; 712 if (code == map.left_shift_key || code == map.right_shift_key) 713 return "SHIFT"; 714 if (code == map.left_command_key || code == map.right_command_key) 715 return "COMMAND"; 716 if (code == map.left_control_key || code == map.right_control_key) 717 return "CONTROL"; 718 if (code == map.left_option_key || code == map.right_option_key) 719 return "OPTION"; 720 if (code == map.menu_key) 721 return "MENU"; 722 if (code == B_PRINT_KEY) 723 return "PRINT"; 724 if (code == B_PAUSE_KEY) 725 return "PAUSE"; 726 727 return NULL; 728 } 729 730 731 const char* 732 KeyboardLayoutView::_SpecialMappedKeySymbol(const char* bytes, size_t numBytes) 733 { 734 if (numBytes != 1) 735 return NULL; 736 737 if (bytes[0] == B_TAB) 738 return "\xe2\x86\xb9"; 739 if (bytes[0] == B_ENTER) 740 return "\xe2\x86\xb5"; 741 if (bytes[0] == B_BACKSPACE) 742 return "\xe2\x8c\xab"; 743 744 if (bytes[0] == B_UP_ARROW) 745 return "\xe2\x86\x91"; 746 if (bytes[0] == B_LEFT_ARROW) 747 return "\xe2\x86\x90"; 748 if (bytes[0] == B_DOWN_ARROW) 749 return "\xe2\x86\x93"; 750 if (bytes[0] == B_RIGHT_ARROW) 751 return "\xe2\x86\x92"; 752 753 return NULL; 754 } 755 756 757 const char* 758 KeyboardLayoutView::_SpecialMappedKeyLabel(const char* bytes, size_t numBytes) 759 { 760 if (numBytes != 1) 761 return NULL; 762 763 if (bytes[0] == B_ESCAPE) 764 return "ESC"; 765 766 if (bytes[0] == B_INSERT) 767 return "INS"; 768 if (bytes[0] == B_DELETE) 769 return "DEL"; 770 if (bytes[0] == B_HOME) 771 return "HOME"; 772 if (bytes[0] == B_END) 773 return "END"; 774 if (bytes[0] == B_PAGE_UP) 775 return "PAGE \xe2\x86\x91"; 776 if (bytes[0] == B_PAGE_DOWN) 777 return "PAGE \xe2\x86\x93"; 778 779 return NULL; 780 } 781 782 783 bool 784 KeyboardLayoutView::_FunctionKeyLabel(uint32 code, char* text, size_t textSize) 785 { 786 if (code >= B_F1_KEY && code <= B_F12_KEY) { 787 snprintf(text, textSize, "F%ld", code + 1 - B_F1_KEY); 788 return true; 789 } 790 791 return false; 792 } 793 794 795 void 796 KeyboardLayoutView::_GetKeyLabel(const Key* key, char* text, size_t textSize, 797 key_kind& keyKind) 798 { 799 const key_map& map = fKeymap->Map(); 800 keyKind = kNormalKey; 801 text[0] = '\0'; 802 803 const char* special = _SpecialKeyLabel(map, key->code); 804 if (special != NULL) { 805 strlcpy(text, special, textSize); 806 keyKind = kSpecialKey; 807 return; 808 } 809 810 if (_FunctionKeyLabel(key->code, text, textSize)) { 811 keyKind = kSpecialKey; 812 return; 813 } 814 815 char* bytes = NULL; 816 int32 numBytes; 817 fKeymap->GetChars(key->code, fModifiers, fDeadKey, &bytes, 818 &numBytes); 819 if (bytes != NULL) { 820 special = _SpecialMappedKeyLabel(bytes, numBytes); 821 if (special != NULL) { 822 strlcpy(text, special, textSize); 823 keyKind = kSpecialKey; 824 return; 825 } 826 827 special = _SpecialMappedKeySymbol(bytes, numBytes); 828 if (special != NULL) { 829 strlcpy(text, special, textSize); 830 keyKind = kSymbolKey; 831 return; 832 } 833 834 bool hasGlyphs; 835 fFont.GetHasGlyphs(bytes, 1, &hasGlyphs); 836 if (hasGlyphs) 837 strlcpy(text, bytes, sizeof(text)); 838 839 delete[] bytes; 840 } 841 } 842 843 844 bool 845 KeyboardLayoutView::_IsKeyPressed(uint32 code) 846 { 847 if (fDropTarget != NULL && fDropTarget->code == (int32)code) 848 return true; 849 850 return _KeyState(code); 851 } 852 853 854 bool 855 KeyboardLayoutView::_KeyState(uint32 code) const 856 { 857 if (code >= 16 * 8) 858 return false; 859 860 return (fKeyState[code / 8] & (1 << (7 - (code & 7)))) != 0; 861 } 862 863 864 void 865 KeyboardLayoutView::_SetKeyState(uint32 code, bool pressed) 866 { 867 if (code >= 16 * 8) 868 return; 869 870 if (pressed) 871 fKeyState[code / 8] |= (1 << (7 - (code & 7))); 872 else 873 fKeyState[code / 8] &= ~(1 << (7 - (code & 7))); 874 } 875 876 877 Key* 878 KeyboardLayoutView::_KeyForCode(uint32 code) 879 { 880 // TODO: have a lookup array 881 882 for (int32 i = 0; i < fLayout->CountKeys(); i++) { 883 Key* key = fLayout->KeyAt(i); 884 if (key->code == (int32)code) 885 return key; 886 } 887 888 return NULL; 889 } 890 891 892 void 893 KeyboardLayoutView::_InvalidateKey(uint32 code) 894 { 895 _InvalidateKey(_KeyForCode(code)); 896 } 897 898 899 void 900 KeyboardLayoutView::_InvalidateKey(const Key* key) 901 { 902 if (key != NULL) 903 Invalidate(_FrameFor(key)); 904 } 905 906 907 /*! Updates the fDeadKey member, and invalidates the view if needed. 908 909 \return true if the view has been invalidated. 910 */ 911 bool 912 KeyboardLayoutView::_HandleDeadKey(uint32 key, int32 modifiers) 913 { 914 if (fKeymap == NULL || fKeymap->IsModifierKey(key)) 915 return false; 916 917 bool isEnabled = false; 918 int32 deadKey = fKeymap->IsDeadKey(key, modifiers, &isEnabled); 919 if (fDeadKey != deadKey) { 920 if (isEnabled) { 921 Invalidate(); 922 fDeadKey = deadKey; 923 return true; 924 } 925 } else if (fDeadKey != 0) { 926 Invalidate(); 927 fDeadKey = 0; 928 return true; 929 } 930 931 return false; 932 } 933 934 935 void 936 KeyboardLayoutView::_KeyChanged(const BMessage* message) 937 { 938 const uint8* state; 939 ssize_t size; 940 int32 key; 941 if (message->FindData("states", B_UINT8_TYPE, (const void**)&state, &size) 942 != B_OK 943 || message->FindInt32("key", &key) != B_OK) 944 return; 945 946 // Update key state, and invalidate change keys 947 948 bool checkSingle = true; 949 950 if (message->what == B_KEY_DOWN || message->what == B_UNMAPPED_KEY_DOWN) { 951 if (_HandleDeadKey(key, fModifiers)) 952 checkSingle = false; 953 954 if (_KeyForCode(key) == NULL) 955 printf("no key for code %ld\n", key); 956 } 957 958 for (int32 i = 0; i < 16; i++) { 959 if (fKeyState[i] != state[i]) { 960 uint8 diff = fKeyState[i] ^ state[i]; 961 fKeyState[i] = state[i]; 962 963 if (!checkSingle || !Window()->IsActive()) 964 continue; 965 966 for (int32 j = 7; diff != 0; j--, diff >>= 1) { 967 if (diff & 1) { 968 _InvalidateKey(i * 8 + j); 969 } 970 } 971 } 972 } 973 } 974 975 976 Key* 977 KeyboardLayoutView::_KeyAt(BPoint point) 978 { 979 // Find key candidate 980 981 BPoint keyPoint = point; 982 keyPoint -= fOffset; 983 keyPoint.x /= fFactor; 984 keyPoint.y /= fFactor; 985 986 for (int32 i = 0; i < fLayout->CountKeys(); i++) { 987 Key* key = fLayout->KeyAt(i); 988 if (key->frame.Contains(keyPoint)) { 989 BRect frame = _FrameFor(key); 990 if (frame.Contains(point)) 991 return key; 992 993 return NULL; 994 } 995 } 996 997 return NULL; 998 } 999 1000 1001 BRect 1002 KeyboardLayoutView::_FrameFor(BRect keyFrame) 1003 { 1004 BRect rect; 1005 rect.left = ceilf(keyFrame.left * fFactor); 1006 rect.right = floorf((keyFrame.Width()) * fFactor + rect.left - fGap - 1); 1007 rect.top = ceilf(keyFrame.top * fFactor); 1008 rect.bottom = floorf((keyFrame.Height()) * fFactor + rect.top - fGap - 1); 1009 rect.OffsetBy(fOffset); 1010 1011 return rect; 1012 } 1013 1014 1015 BRect 1016 KeyboardLayoutView::_FrameFor(const Key* key) 1017 { 1018 return _FrameFor(key->frame); 1019 } 1020 1021 1022 void 1023 KeyboardLayoutView::_SetFontSize(BView* view, key_kind keyKind) 1024 { 1025 BSize size = fLayout->DefaultKeySize(); 1026 float fontSize = fBaseFontSize; 1027 if (fBaseFontHeight >= size.height * fFactor * 0.5) { 1028 fontSize *= (size.height * fFactor * 0.5) / fBaseFontHeight; 1029 if (fontSize < 8) 1030 fontSize = 8; 1031 } 1032 1033 switch (keyKind) { 1034 case kNormalKey: 1035 fFont.SetSize(fontSize); 1036 view->SetFont(&fFont); 1037 break; 1038 case kSpecialKey: 1039 fSpecialFont.SetSize(fontSize * 0.7); 1040 view->SetFont(&fSpecialFont); 1041 break; 1042 case kSymbolKey: 1043 fSpecialFont.SetSize(fontSize * 1.6); 1044 view->SetFont(&fSpecialFont); 1045 break; 1046 1047 case kIndicator: 1048 { 1049 BFont font; 1050 font.SetSize(fontSize * 0.8); 1051 view->SetFont(&font); 1052 break; 1053 } 1054 } 1055 } 1056 1057 1058 void 1059 KeyboardLayoutView::_EvaluateDropTarget(BPoint point) 1060 { 1061 fDropTarget = _KeyAt(point); 1062 if (fDropTarget != NULL) { 1063 if (fDropTarget == fDragKey && fModifiers == fDragModifiers) 1064 fDropTarget = NULL; 1065 else 1066 _InvalidateKey(fDropTarget); 1067 } 1068 } 1069 1070 1071 void 1072 KeyboardLayoutView::_SendFakeKeyDown(const Key* key) 1073 { 1074 BMessage message(B_KEY_DOWN); 1075 message.AddInt64("when", system_time()); 1076 message.AddData("states", B_UINT8_TYPE, &fKeyState, 1077 sizeof(fKeyState)); 1078 message.AddInt32("key", key->code); 1079 message.AddInt32("modifiers", fModifiers); 1080 message.AddPointer("keymap", fKeymap); 1081 1082 char* string; 1083 int32 numBytes; 1084 fKeymap->GetChars(key->code, fModifiers, fDeadKey, &string, 1085 &numBytes); 1086 if (string != NULL) { 1087 message.AddString("bytes", string); 1088 delete[] string; 1089 } 1090 1091 fKeymap->GetChars(key->code, 0, 0, &string, &numBytes); 1092 if (string != NULL) { 1093 message.AddInt32("raw_char", string[0]); 1094 message.AddInt8("byte", string[0]); 1095 delete[] string; 1096 } 1097 1098 fTarget.SendMessage(&message); 1099 } 1100