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