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