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, 4.0f, base, 568 background, pressed ? BControlLook::B_ACTIVATED : 0); 569 be_control_look->DrawButtonBackground(view, rect, updateRect, 4.0f, 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), "%02" B_PRIx32, 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 BRect topLeft = rect; 613 BRect topRight = rect; 614 BRect bottomLeft = rect; 615 BRect bottomRight = rect; 616 617 // TODO: for some reason, this does not always equal the bottom of 618 // the other keys... 619 bottomLeft.top = floorf(rect.top 620 + fLayout->DefaultKeySize().height * fFactor - fGap - 1); 621 bottomLeft.right = floorf(rect.left 622 + (key->frame.Width() - key->second_row) * fFactor - fGap - 2); 623 624 topLeft.bottom = bottomLeft.top; 625 topLeft.right = bottomLeft.right + 1; 626 // add one to make the borders meet 627 628 topRight.bottom = topLeft.bottom; 629 topRight.left = topLeft.right; 630 631 bottomRight.top = bottomLeft.top; 632 bottomRight.left = bottomLeft.right; 633 634 // draw top left corner 635 be_control_look->DrawButtonFrame(view, topLeft, updateRect, 636 4.0f, 0.0f, 4.0f, 0.0f, base, background, 637 pressed ? BControlLook::B_ACTIVATED : 0, 638 BControlLook::B_LEFT_BORDER | BControlLook::B_TOP_BORDER 639 | BControlLook::B_BOTTOM_BORDER); 640 be_control_look->DrawButtonBackground(view, topLeft, updateRect, 641 4.0f, 0.0f, 4.0f, 0.0f, base, 642 pressed ? BControlLook::B_ACTIVATED : 0, 643 BControlLook::B_LEFT_BORDER | BControlLook::B_TOP_BORDER 644 | BControlLook::B_BOTTOM_BORDER); 645 646 // draw top right corner 647 be_control_look->DrawButtonFrame(view, topRight, updateRect, 648 0.0f, 4.0f, 0.0f, 0.0f, base, background, 649 pressed ? BControlLook::B_ACTIVATED : 0, 650 BControlLook::B_TOP_BORDER | BControlLook::B_RIGHT_BORDER); 651 be_control_look->DrawButtonBackground(view, topRight, updateRect, 652 0.0f, 4.0f, 0.0f, 0.0f, base, 653 pressed ? BControlLook::B_ACTIVATED : 0, 654 BControlLook::B_TOP_BORDER | BControlLook::B_RIGHT_BORDER); 655 656 // draw bottom right corner 657 be_control_look->DrawButtonFrame(view, bottomRight, updateRect, 658 0.0f, 0.0f, 4.0f, 4.0f, base, background, 659 pressed ? BControlLook::B_ACTIVATED : 0, 660 BControlLook::B_LEFT_BORDER | BControlLook::B_RIGHT_BORDER 661 | BControlLook::B_BOTTOM_BORDER); 662 be_control_look->DrawButtonBackground(view, bottomRight, updateRect, 663 0.0f, 0.0f, 4.0f, 4.0f, base, 664 pressed ? BControlLook::B_ACTIVATED : 0, 665 BControlLook::B_LEFT_BORDER | BControlLook::B_RIGHT_BORDER 666 | BControlLook::B_BOTTOM_BORDER); 667 668 // clip out the bottom left corner 669 bottomLeft.right += 1; 670 bottomLeft.top -= 2; 671 BRegion region(rect); 672 region.Exclude(bottomLeft); 673 view->ConstrainClippingRegion(®ion); 674 675 // Fill in the rect with the background color 676 SetHighColor(background); 677 FillRect(rect); 678 679 // draw the button background 680 BRect bgRect = rect.InsetByCopy(2, 2); 681 be_control_look->DrawButtonBackground(view, bgRect, updateRect, 682 4.0f, 4.0f, 0.0f, 4.0f, base, 683 pressed ? BControlLook::B_ACTIVATED : 0); 684 685 rect.left = bottomLeft.right; 686 _GetAbbreviatedKeyLabelIfNeeded(view, rect, key, text, sizeof(text)); 687 688 // draw the button label 689 be_control_look->DrawLabel(view, text, rect, updateRect, 690 base, 0, BAlignment(B_ALIGN_CENTER, B_ALIGN_MIDDLE)); 691 692 // reset the clipping region 693 view->ConstrainClippingRegion(NULL); 694 } 695 } 696 697 698 void 699 KeyboardLayoutView::_DrawIndicator(BView* view, BRect updateRect, 700 const Indicator* indicator, BRect rect, bool lit) 701 { 702 float rectTop = rect.top; 703 rect.top += 2 * rect.Height() / 3; 704 705 const char* label = NULL; 706 if (indicator->modifier == B_CAPS_LOCK) 707 label = "caps"; 708 else if (indicator->modifier == B_NUM_LOCK) 709 label = "num"; 710 else if (indicator->modifier == B_SCROLL_LOCK) 711 label = "scroll"; 712 if (label != NULL) { 713 _SetFontSize(view, kIndicator); 714 715 font_height fontHeight; 716 GetFontHeight(&fontHeight); 717 if (ceilf(rect.top - fontHeight.ascent + fontHeight.descent - 2) 718 >= rectTop) { 719 view->SetHighColor(0, 0, 0); 720 view->SetLowColor(ViewColor()); 721 722 BString text(label); 723 view->TruncateString(&text, B_TRUNCATE_END, rect.Width()); 724 view->DrawString(text.String(), 725 BPoint(ceilf(rect.left + (rect.Width() 726 - StringWidth(text.String())) / 2), 727 ceilf(rect.top - fontHeight.descent - 2))); 728 } 729 } 730 731 rect.left += rect.Width() / 4; 732 rect.right -= rect.Width() / 3; 733 734 rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR); 735 rgb_color base = lit ? kLitIndicatorColor : kDarkColor; 736 737 be_control_look->DrawButtonFrame(view, rect, updateRect, base, 738 background, BControlLook::B_DISABLED); 739 be_control_look->DrawButtonBackground(view, rect, updateRect, 740 base, BControlLook::B_DISABLED); 741 } 742 743 744 const char* 745 KeyboardLayoutView::_SpecialKeyLabel(const key_map& map, uint32 code, 746 bool abbreviated) 747 { 748 if (code == map.caps_key) 749 return abbreviated ? "CAPS" : "CAPS LOCK"; 750 if (code == map.scroll_key) 751 return "SCROLL"; 752 if (code == map.num_key) 753 return abbreviated ? "NUM" : "NUM LOCK"; 754 if (code == map.left_shift_key || code == map.right_shift_key) 755 return "SHIFT"; 756 if (code == map.left_command_key || code == map.right_command_key) 757 return abbreviated ? "CMD" : "COMMAND"; 758 if (code == map.left_control_key || code == map.right_control_key) 759 return abbreviated ? "CTRL" : "CONTROL"; 760 if (code == map.left_option_key || code == map.right_option_key) 761 return abbreviated ? "OPT" : "OPTION"; 762 if (code == map.menu_key) 763 return "MENU"; 764 if (code == B_PRINT_KEY) 765 return "PRINT"; 766 if (code == B_PAUSE_KEY) 767 return "PAUSE"; 768 769 return NULL; 770 } 771 772 773 const char* 774 KeyboardLayoutView::_SpecialMappedKeySymbol(const char* bytes, size_t numBytes) 775 { 776 if (numBytes != 1) 777 return NULL; 778 779 if (bytes[0] == B_TAB) 780 return "\xe2\x86\xb9"; 781 if (bytes[0] == B_ENTER) 782 return "\xe2\x86\xb5"; 783 if (bytes[0] == B_BACKSPACE) 784 return "\xe2\x8c\xab"; 785 786 if (bytes[0] == B_UP_ARROW) 787 return "\xe2\x86\x91"; 788 if (bytes[0] == B_LEFT_ARROW) 789 return "\xe2\x86\x90"; 790 if (bytes[0] == B_DOWN_ARROW) 791 return "\xe2\x86\x93"; 792 if (bytes[0] == B_RIGHT_ARROW) 793 return "\xe2\x86\x92"; 794 795 return NULL; 796 } 797 798 799 const char* 800 KeyboardLayoutView::_SpecialMappedKeyLabel(const char* bytes, size_t numBytes, 801 bool abbreviated) 802 { 803 if (numBytes != 1) 804 return NULL; 805 806 if (bytes[0] == B_ESCAPE) 807 return "ESC"; 808 809 if (bytes[0] == B_INSERT) 810 return "INS"; 811 if (bytes[0] == B_DELETE) 812 return "DEL"; 813 if (bytes[0] == B_HOME) 814 return "HOME"; 815 if (bytes[0] == B_END) 816 return "END"; 817 if (bytes[0] == B_PAGE_UP) 818 return abbreviated ? "PG \xe2\x86\x91" : "PAGE \xe2\x86\x91"; 819 if (bytes[0] == B_PAGE_DOWN) 820 return abbreviated ? "PG \xe2\x86\x93" : "PAGE \xe2\x86\x93"; 821 822 return NULL; 823 } 824 825 826 bool 827 KeyboardLayoutView::_FunctionKeyLabel(uint32 code, char* text, size_t textSize) 828 { 829 if (code >= B_F1_KEY && code <= B_F12_KEY) { 830 snprintf(text, textSize, "F%" B_PRId32, code + 1 - B_F1_KEY); 831 return true; 832 } 833 834 return false; 835 } 836 837 838 void 839 KeyboardLayoutView::_GetAbbreviatedKeyLabelIfNeeded(BView* view, BRect rect, 840 const Key* key, char* text, size_t textSize) 841 { 842 if (floorf(rect.Width()) > ceilf(view->StringWidth(text))) 843 return; 844 845 // Check if we have a shorter version of this key 846 847 const key_map& map = fKeymap->Map(); 848 849 const char* special = _SpecialKeyLabel(map, key->code, true); 850 if (special != NULL) { 851 strlcpy(text, special, textSize); 852 return; 853 } 854 855 char* bytes = NULL; 856 int32 numBytes; 857 fKeymap->GetChars(key->code, fModifiers, fDeadKey, &bytes, &numBytes); 858 if (bytes != NULL) { 859 special = _SpecialMappedKeyLabel(bytes, numBytes, true); 860 if (special != NULL) 861 strlcpy(text, special, textSize); 862 863 delete[] bytes; 864 } 865 } 866 867 868 void 869 KeyboardLayoutView::_GetKeyLabel(const Key* key, char* text, size_t textSize, 870 key_kind& keyKind) 871 { 872 const key_map& map = fKeymap->Map(); 873 keyKind = kNormalKey; 874 text[0] = '\0'; 875 876 const char* special = _SpecialKeyLabel(map, key->code); 877 if (special != NULL) { 878 strlcpy(text, special, textSize); 879 keyKind = kSpecialKey; 880 return; 881 } 882 883 if (_FunctionKeyLabel(key->code, text, textSize)) { 884 keyKind = kSpecialKey; 885 return; 886 } 887 888 char* bytes = NULL; 889 int32 numBytes; 890 fKeymap->GetChars(key->code, fModifiers, fDeadKey, &bytes, &numBytes); 891 if (bytes != NULL) { 892 special = _SpecialMappedKeyLabel(bytes, numBytes); 893 if (special != NULL) { 894 strlcpy(text, special, textSize); 895 keyKind = kSpecialKey; 896 } else { 897 special = _SpecialMappedKeySymbol(bytes, numBytes); 898 if (special != NULL) { 899 strlcpy(text, special, textSize); 900 keyKind = kSymbolKey; 901 } else { 902 bool hasGlyphs; 903 fBaseFont.GetHasGlyphs(bytes, 1, &hasGlyphs); 904 if (hasGlyphs) 905 strlcpy(text, bytes, textSize); 906 } 907 } 908 909 delete[] bytes; 910 } 911 } 912 913 914 bool 915 KeyboardLayoutView::_IsKeyPressed(uint32 code) 916 { 917 if (fDropTarget != NULL && fDropTarget->code == (int32)code) 918 return true; 919 920 return _KeyState(code); 921 } 922 923 924 bool 925 KeyboardLayoutView::_KeyState(uint32 code) const 926 { 927 if (code >= 16 * 8) 928 return false; 929 930 return (fKeyState[code / 8] & (1 << (7 - (code & 7)))) != 0; 931 } 932 933 934 void 935 KeyboardLayoutView::_SetKeyState(uint32 code, bool pressed) 936 { 937 if (code >= 16 * 8) 938 return; 939 940 if (pressed) 941 fKeyState[code / 8] |= (1 << (7 - (code & 7))); 942 else 943 fKeyState[code / 8] &= ~(1 << (7 - (code & 7))); 944 } 945 946 947 Key* 948 KeyboardLayoutView::_KeyForCode(uint32 code) 949 { 950 // TODO: have a lookup array 951 952 for (int32 i = 0; i < fLayout->CountKeys(); i++) { 953 Key* key = fLayout->KeyAt(i); 954 if (key->code == (int32)code) 955 return key; 956 } 957 958 return NULL; 959 } 960 961 962 void 963 KeyboardLayoutView::_InvalidateKey(uint32 code) 964 { 965 _InvalidateKey(_KeyForCode(code)); 966 } 967 968 969 void 970 KeyboardLayoutView::_InvalidateKey(const Key* key) 971 { 972 if (key != NULL) 973 Invalidate(_FrameFor(key)); 974 } 975 976 977 /*! Updates the fDeadKey member, and invalidates the view if needed. 978 979 \return true if the view has been invalidated. 980 */ 981 bool 982 KeyboardLayoutView::_HandleDeadKey(uint32 key, int32 modifiers) 983 { 984 if (fKeymap == NULL || fKeymap->IsModifierKey(key)) 985 return false; 986 987 bool isEnabled = false; 988 int32 deadKey = fKeymap->DeadKey(key, modifiers, &isEnabled); 989 if (fDeadKey != deadKey) { 990 if (isEnabled) { 991 Invalidate(); 992 fDeadKey = deadKey; 993 return true; 994 } 995 } else if (fDeadKey != 0) { 996 Invalidate(); 997 fDeadKey = 0; 998 return true; 999 } 1000 1001 return false; 1002 } 1003 1004 1005 void 1006 KeyboardLayoutView::_KeyChanged(const BMessage* message) 1007 { 1008 const uint8* state; 1009 ssize_t size; 1010 int32 key; 1011 if (message->FindData("states", B_UINT8_TYPE, (const void**)&state, &size) 1012 != B_OK 1013 || message->FindInt32("key", &key) != B_OK) 1014 return; 1015 1016 // Update key state, and invalidate change keys 1017 1018 bool checkSingle = true; 1019 1020 if (message->what == B_KEY_DOWN || message->what == B_UNMAPPED_KEY_DOWN) { 1021 if (_HandleDeadKey(key, fModifiers)) 1022 checkSingle = false; 1023 1024 if (_KeyForCode(key) == NULL) 1025 printf("no key for code %" B_PRId32 "\n", key); 1026 } 1027 1028 for (int32 i = 0; i < 16; i++) { 1029 if (fKeyState[i] != state[i]) { 1030 uint8 diff = fKeyState[i] ^ state[i]; 1031 fKeyState[i] = state[i]; 1032 1033 if (!checkSingle || !Window()->IsActive()) 1034 continue; 1035 1036 for (int32 j = 7; diff != 0; j--, diff >>= 1) { 1037 if (diff & 1) { 1038 _InvalidateKey(i * 8 + j); 1039 } 1040 } 1041 } 1042 } 1043 } 1044 1045 1046 Key* 1047 KeyboardLayoutView::_KeyAt(BPoint point) 1048 { 1049 // Find key candidate 1050 1051 BPoint keyPoint = point; 1052 keyPoint -= fOffset; 1053 keyPoint.x /= fFactor; 1054 keyPoint.y /= fFactor; 1055 1056 for (int32 i = 0; i < fLayout->CountKeys(); i++) { 1057 Key* key = fLayout->KeyAt(i); 1058 if (key->frame.Contains(keyPoint)) { 1059 BRect frame = _FrameFor(key); 1060 if (frame.Contains(point)) 1061 return key; 1062 1063 return NULL; 1064 } 1065 } 1066 1067 return NULL; 1068 } 1069 1070 1071 BRect 1072 KeyboardLayoutView::_FrameFor(BRect keyFrame) 1073 { 1074 BRect rect; 1075 rect.left = ceilf(keyFrame.left * fFactor); 1076 rect.top = ceilf(keyFrame.top * fFactor); 1077 rect.right = floorf((keyFrame.Width()) * fFactor + rect.left - fGap - 1); 1078 rect.bottom = floorf((keyFrame.Height()) * fFactor + rect.top - fGap - 1); 1079 rect.OffsetBy(fOffset); 1080 1081 return rect; 1082 } 1083 1084 1085 BRect 1086 KeyboardLayoutView::_FrameFor(const Key* key) 1087 { 1088 return _FrameFor(key->frame); 1089 } 1090 1091 1092 void 1093 KeyboardLayoutView::_SetFontSize(BView* view, key_kind keyKind) 1094 { 1095 BSize size = fLayout->DefaultKeySize(); 1096 float fontSize = fBaseFontSize; 1097 if (fBaseFontHeight >= size.height * fFactor * 0.5) { 1098 fontSize *= (size.height * fFactor * 0.5) / fBaseFontHeight; 1099 if (fontSize < 8) 1100 fontSize = 8; 1101 } 1102 1103 switch (keyKind) { 1104 case kNormalKey: 1105 fBaseFont.SetSize(fontSize); 1106 view->SetFont(&fBaseFont); 1107 break; 1108 case kSpecialKey: 1109 fSpecialFont.SetSize(fontSize * 0.7); 1110 view->SetFont(&fSpecialFont); 1111 break; 1112 case kSymbolKey: 1113 fSpecialFont.SetSize(fontSize * 1.6); 1114 view->SetFont(&fSpecialFont); 1115 break; 1116 1117 case kIndicator: 1118 { 1119 BFont font; 1120 font.SetSize(fontSize * 0.8); 1121 view->SetFont(&font); 1122 break; 1123 } 1124 } 1125 } 1126 1127 1128 void 1129 KeyboardLayoutView::_EvaluateDropTarget(BPoint point) 1130 { 1131 fDropTarget = _KeyAt(point); 1132 if (fDropTarget != NULL) { 1133 if (fDropTarget == fDragKey && fModifiers == fDragModifiers) 1134 fDropTarget = NULL; 1135 else 1136 _InvalidateKey(fDropTarget); 1137 } 1138 } 1139 1140 1141 void 1142 KeyboardLayoutView::_SendFakeKeyDown(const Key* key) 1143 { 1144 BMessage message(B_KEY_DOWN); 1145 message.AddInt64("when", system_time()); 1146 message.AddData("states", B_UINT8_TYPE, &fKeyState, 1147 sizeof(fKeyState)); 1148 message.AddInt32("key", key->code); 1149 message.AddInt32("modifiers", fModifiers); 1150 message.AddPointer("keymap", fKeymap); 1151 1152 char* string; 1153 int32 numBytes; 1154 fKeymap->GetChars(key->code, fModifiers, fDeadKey, &string, 1155 &numBytes); 1156 if (string != NULL) { 1157 message.AddString("bytes", string); 1158 delete[] string; 1159 } 1160 1161 fKeymap->GetChars(key->code, 0, 0, &string, &numBytes); 1162 if (string != NULL) { 1163 message.AddInt32("raw_char", string[0]); 1164 message.AddInt8("byte", string[0]); 1165 delete[] string; 1166 } 1167 1168 fTarget.SendMessage(&message); 1169 } 1170