1/* 2 * Copyright 2004 DarkWyrm <darkwyrm@earthlink.net> 3 * Copyright 2013 FeemanLou 4 * Copyright 2014-2015 Haiku, Inc. All rights reserved. 5 * 6 * Distributed under the terms of the MIT license. 7 * 8 * Originally written by DarkWyrm <darkwyrm@earthlink.net> 9 * Updated by FreemanLou as part of Google GCI 2013 10 * 11 * Authors: 12 * DarkWyrm, darkwyrm@earthlink.net 13 * FeemanLou 14 * John Scipione, jscipione@gmail.com 15 */ 16 17 18#include <AbstractSpinner.h> 19 20#include <algorithm> 21 22#include <AbstractLayoutItem.h> 23#include <Alignment.h> 24#include <ControlLook.h> 25#include <Font.h> 26#include <GradientLinear.h> 27#include <LayoutItem.h> 28#include <LayoutUtils.h> 29#include <Message.h> 30#include <MessageFilter.h> 31#include <MessageRunner.h> 32#include <Point.h> 33#include <PropertyInfo.h> 34#include <TextView.h> 35#include <View.h> 36#include <Window.h> 37 38 39static const float kFrameMargin = 2.0f; 40 41const char* const kFrameField = "BAbstractSpinner:layoutItem:frame"; 42const char* const kLabelItemField = "BAbstractSpinner:labelItem"; 43const char* const kTextViewItemField = "BAbstractSpinner:textViewItem"; 44 45 46static property_info sProperties[] = { 47 { 48 "Align", 49 { B_GET_PROPERTY, 0 }, 50 { B_DIRECT_SPECIFIER, 0 }, 51 "Returns the alignment of the spinner label.", 52 0, 53 { B_INT32_TYPE } 54 }, 55 { 56 "Align", 57 { B_SET_PROPERTY, 0 }, 58 { B_DIRECT_SPECIFIER, 0}, 59 "Sets the alignment of the spinner label.", 60 0, 61 { B_INT32_TYPE } 62 }, 63 64 { 65 "ButtonStyle", 66 { B_GET_PROPERTY, 0 }, 67 { B_DIRECT_SPECIFIER, 0 }, 68 "Returns the style of the spinner buttons.", 69 0, 70 { B_INT32_TYPE } 71 }, 72 { 73 "ButtonStyle", 74 { B_SET_PROPERTY, 0 }, 75 { B_DIRECT_SPECIFIER, 0}, 76 "Sets the style of the spinner buttons.", 77 0, 78 { B_INT32_TYPE } 79 }, 80 81 { 82 "Divider", 83 { B_GET_PROPERTY, 0 }, 84 { B_DIRECT_SPECIFIER, 0 }, 85 "Returns the divider position of the spinner.", 86 0, 87 { B_FLOAT_TYPE } 88 }, 89 { 90 "Divider", 91 { B_SET_PROPERTY, 0 }, 92 { B_DIRECT_SPECIFIER, 0}, 93 "Sets the divider position of the spinner.", 94 0, 95 { B_FLOAT_TYPE } 96 }, 97 98 { 99 "Enabled", 100 { B_GET_PROPERTY, 0 }, 101 { B_DIRECT_SPECIFIER, 0 }, 102 "Returns whether or not the spinner is enabled.", 103 0, 104 { B_BOOL_TYPE } 105 }, 106 { 107 "Enabled", 108 { B_SET_PROPERTY, 0 }, 109 { B_DIRECT_SPECIFIER, 0}, 110 "Sets whether or not the spinner is enabled.", 111 0, 112 { B_BOOL_TYPE } 113 }, 114 115 { 116 "Label", 117 { B_GET_PROPERTY, 0 }, 118 { B_DIRECT_SPECIFIER, 0 }, 119 "Returns the spinner label.", 120 0, 121 { B_STRING_TYPE } 122 }, 123 { 124 "Label", 125 { B_SET_PROPERTY, 0 }, 126 { B_DIRECT_SPECIFIER, 0}, 127 "Sets the spinner label.", 128 0, 129 { B_STRING_TYPE } 130 }, 131 132 { 133 "Message", 134 { B_GET_PROPERTY, 0 }, 135 { B_DIRECT_SPECIFIER, 0 }, 136 "Returns the spinner invocation message.", 137 0, 138 { B_MESSAGE_TYPE } 139 }, 140 { 141 "Message", 142 { B_SET_PROPERTY, 0 }, 143 { B_DIRECT_SPECIFIER, 0}, 144 "Sets the spinner invocation message.", 145 0, 146 { B_MESSAGE_TYPE } 147 }, 148 149 { 0 } 150}; 151 152 153typedef enum { 154 SPINNER_INCREMENT, 155 SPINNER_DECREMENT 156} spinner_direction; 157 158 159class SpinnerButton : public BView { 160public: 161 SpinnerButton(BRect frame, const char* name, 162 spinner_direction direction); 163 virtual ~SpinnerButton(); 164 165 virtual void AttachedToWindow(); 166 virtual void DetachedFromWindow(); 167 virtual void Draw(BRect updateRect); 168 virtual void MouseDown(BPoint where); 169 virtual void MouseUp(BPoint where); 170 virtual void MouseMoved(BPoint where, uint32 transit, 171 const BMessage* message); 172 virtual void MessageReceived(BMessage* message); 173 174 bool IsEnabled() const { return fIsEnabled; } 175 virtual void SetEnabled(bool enable) { fIsEnabled = enable; }; 176 177private: 178 spinner_direction fSpinnerDirection; 179 BAbstractSpinner* fParent; 180 bool fIsEnabled; 181 bool fIsMouseDown; 182 bool fIsMouseOver; 183 BMessageRunner* fRepeater; 184}; 185 186 187class SpinnerTextView : public BTextView { 188public: 189 SpinnerTextView(BRect rect, BRect textRect); 190 virtual ~SpinnerTextView(); 191 192 virtual void AttachedToWindow(); 193 virtual void DetachedFromWindow(); 194 virtual void KeyDown(const char* bytes, int32 numBytes); 195 virtual void MakeFocus(bool focus); 196 197private: 198 BAbstractSpinner* fParent; 199}; 200 201 202class BAbstractSpinner::LabelLayoutItem : public BAbstractLayoutItem { 203public: 204 LabelLayoutItem(BAbstractSpinner* parent); 205 LabelLayoutItem(BMessage* archive); 206 207 virtual bool IsVisible(); 208 virtual void SetVisible(bool visible); 209 210 virtual BRect Frame(); 211 virtual void SetFrame(BRect frame); 212 213 void SetParent(BAbstractSpinner* parent); 214 virtual BView* View(); 215 216 virtual BSize BaseMinSize(); 217 virtual BSize BaseMaxSize(); 218 virtual BSize BasePreferredSize(); 219 virtual BAlignment BaseAlignment(); 220 221 BRect FrameInParent() const; 222 223 virtual status_t Archive(BMessage* into, bool deep = true) const; 224 static BArchivable* Instantiate(BMessage* from); 225 226private: 227 BAbstractSpinner* fParent; 228 BRect fFrame; 229}; 230 231 232class BAbstractSpinner::TextViewLayoutItem : public BAbstractLayoutItem { 233public: 234 TextViewLayoutItem(BAbstractSpinner* parent); 235 TextViewLayoutItem(BMessage* archive); 236 237 virtual bool IsVisible(); 238 virtual void SetVisible(bool visible); 239 240 virtual BRect Frame(); 241 virtual void SetFrame(BRect frame); 242 243 void SetParent(BAbstractSpinner* parent); 244 virtual BView* View(); 245 246 virtual BSize BaseMinSize(); 247 virtual BSize BaseMaxSize(); 248 virtual BSize BasePreferredSize(); 249 virtual BAlignment BaseAlignment(); 250 251 BRect FrameInParent() const; 252 253 virtual status_t Archive(BMessage* into, bool deep = true) const; 254 static BArchivable* Instantiate(BMessage* from); 255 256private: 257 BAbstractSpinner* fParent; 258 BRect fFrame; 259}; 260 261 262struct BAbstractSpinner::LayoutData { 263 LayoutData(float width, float height) 264 : 265 label_layout_item(NULL), 266 text_view_layout_item(NULL), 267 label_width(0), 268 label_height(0), 269 text_view_width(0), 270 text_view_height(0), 271 previous_width(width), 272 previous_height(height), 273 valid(false) 274 { 275 } 276 277 LabelLayoutItem* label_layout_item; 278 TextViewLayoutItem* text_view_layout_item; 279 280 font_height font_info; 281 282 float label_width; 283 float label_height; 284 float text_view_width; 285 float text_view_height; 286 287 float previous_width; 288 float previous_height; 289 290 BSize min; 291 BAlignment alignment; 292 293 bool valid; 294}; 295 296 297// #pragma mark - SpinnerButton 298 299 300SpinnerButton::SpinnerButton(BRect frame, const char* name, 301 spinner_direction direction) 302 : 303 BView(frame, name, B_FOLLOW_RIGHT | B_FOLLOW_TOP, B_WILL_DRAW), 304 fSpinnerDirection(direction), 305 fParent(NULL), 306 fIsEnabled(true), 307 fIsMouseDown(false), 308 fIsMouseOver(false), 309 fRepeater(NULL) 310{ 311} 312 313 314SpinnerButton::~SpinnerButton() 315{ 316 delete fRepeater; 317} 318 319 320void 321SpinnerButton::AttachedToWindow() 322{ 323 fParent = static_cast<BAbstractSpinner*>(Parent()); 324 325 AdoptParentColors(); 326 BView::AttachedToWindow(); 327} 328 329 330void 331SpinnerButton::DetachedFromWindow() 332{ 333 fParent = NULL; 334 335 BView::DetachedFromWindow(); 336} 337 338 339void 340SpinnerButton::Draw(BRect updateRect) 341{ 342 BRect rect(Bounds()); 343 if (!rect.IsValid() || !rect.Intersects(updateRect)) 344 return; 345 346 BView::Draw(updateRect); 347 348 float frameTint = fIsEnabled ? B_DARKEN_1_TINT : B_NO_TINT; 349 350 float fgTint; 351 if (!fIsEnabled) 352 fgTint = B_DARKEN_1_TINT; 353 else if (fIsMouseDown) 354 fgTint = B_DARKEN_MAX_TINT; 355 else 356 fgTint = 1.777f; // 216 --> 48.2 (48) 357 358 float bgTint; 359 if (fIsEnabled && fIsMouseOver) 360 bgTint = B_DARKEN_1_TINT; 361 else 362 bgTint = B_NO_TINT; 363 364 rgb_color bgColor = ui_color(B_PANEL_BACKGROUND_COLOR); 365 if (bgColor.red + bgColor.green + bgColor.blue <= 128 * 3) { 366 // if dark background make the tint lighter 367 frameTint = 2.0f - frameTint; 368 fgTint = 2.0f - fgTint; 369 bgTint = 2.0f - bgTint; 370 } 371 372 uint32 borders = be_control_look->B_TOP_BORDER 373 | be_control_look->B_BOTTOM_BORDER; 374 375 if (fSpinnerDirection == SPINNER_INCREMENT) 376 borders |= be_control_look->B_RIGHT_BORDER; 377 else 378 borders |= be_control_look->B_LEFT_BORDER; 379 380 uint32 flags = fIsMouseDown ? BControlLook::B_ACTIVATED : 0; 381 flags |= !fIsEnabled ? BControlLook::B_DISABLED : 0; 382 383 // draw the button 384 be_control_look->DrawButtonFrame(this, rect, updateRect, 385 tint_color(bgColor, frameTint), bgColor, flags, borders); 386 be_control_look->DrawButtonBackground(this, rect, updateRect, 387 tint_color(bgColor, bgTint), flags, borders); 388 389 switch (fParent->ButtonStyle()) { 390 case SPINNER_BUTTON_HORIZONTAL_ARROWS: 391 { 392 int32 arrowDirection = fSpinnerDirection == SPINNER_INCREMENT 393 ? be_control_look->B_RIGHT_ARROW 394 : be_control_look->B_LEFT_ARROW; 395 396 rect.InsetBy(0.0f, 1.0f); 397 be_control_look->DrawArrowShape(this, rect, updateRect, bgColor, 398 arrowDirection, 0, fgTint); 399 break; 400 } 401 402 case SPINNER_BUTTON_VERTICAL_ARROWS: 403 { 404 int32 arrowDirection = fSpinnerDirection == SPINNER_INCREMENT 405 ? be_control_look->B_UP_ARROW 406 : be_control_look->B_DOWN_ARROW; 407 408 rect.InsetBy(0.0f, 1.0f); 409 be_control_look->DrawArrowShape(this, rect, updateRect, bgColor, 410 arrowDirection, 0, fgTint); 411 break; 412 } 413 414 default: 415 case SPINNER_BUTTON_PLUS_MINUS: 416 { 417 BFont font; 418 fParent->GetFont(&font); 419 float inset = floorf(font.Size() / 4); 420 rect.InsetBy(inset, inset); 421 422 if (rect.IntegerWidth() % 2 != 0) 423 rect.right -= 1; 424 425 if (rect.IntegerHeight() % 2 != 0) 426 rect.bottom -= 1; 427 428 SetHighColor(tint_color(bgColor, fgTint)); 429 430 // draw the +/- 431 float halfHeight = floorf(rect.Height() / 2); 432 StrokeLine(BPoint(rect.left, rect.top + halfHeight), 433 BPoint(rect.right, rect.top + halfHeight)); 434 if (fSpinnerDirection == SPINNER_INCREMENT) { 435 float halfWidth = floorf(rect.Width() / 2); 436 StrokeLine(BPoint(rect.left + halfWidth, rect.top + 1), 437 BPoint(rect.left + halfWidth, rect.bottom - 1)); 438 } 439 } 440 } 441} 442 443 444void 445SpinnerButton::MouseDown(BPoint where) 446{ 447 if (fIsEnabled) { 448 fIsMouseDown = true; 449 fSpinnerDirection == SPINNER_INCREMENT 450 ? fParent->Increment() 451 : fParent->Decrement(); 452 Invalidate(); 453 BMessage repeatMessage('rept'); 454 SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY); 455 fRepeater = new BMessageRunner(BMessenger(this), repeatMessage, 456 200000); 457 } 458 459 BView::MouseDown(where); 460} 461 462 463void 464SpinnerButton::MouseMoved(BPoint where, uint32 transit, 465 const BMessage* message) 466{ 467 switch (transit) { 468 case B_ENTERED_VIEW: 469 case B_INSIDE_VIEW: 470 { 471 BPoint where; 472 uint32 buttons; 473 GetMouse(&where, &buttons); 474 fIsMouseOver = Bounds().Contains(where) && buttons == 0; 475 476 break; 477 } 478 479 case B_EXITED_VIEW: 480 case B_OUTSIDE_VIEW: 481 fIsMouseOver = false; 482 MouseUp(Bounds().LeftTop()); 483 break; 484 } 485 486 BView::MouseMoved(where, transit, message); 487} 488 489 490void 491SpinnerButton::MouseUp(BPoint where) 492{ 493 fIsMouseDown = false; 494 delete fRepeater; 495 fRepeater = NULL; 496 Invalidate(); 497 498 BView::MouseUp(where); 499} 500 501 502void 503SpinnerButton::MessageReceived(BMessage* message) 504{ 505 switch (message->what) { 506 case 'rept': 507 { 508 if (fIsMouseDown && fRepeater != NULL) { 509 fSpinnerDirection == SPINNER_INCREMENT 510 ? fParent->Increment() 511 : fParent->Decrement(); 512 } 513 514 break; 515 } 516 517 default: 518 BView::MessageReceived(message); 519 } 520} 521 522 523// #pragma mark - SpinnerTextView 524 525 526SpinnerTextView::SpinnerTextView(BRect rect, BRect textRect) 527 : 528 BTextView(rect, "textview", textRect, B_FOLLOW_ALL, 529 B_WILL_DRAW | B_NAVIGABLE), 530 fParent(NULL) 531{ 532 MakeResizable(true); 533} 534 535 536SpinnerTextView::~SpinnerTextView() 537{ 538} 539 540 541void 542SpinnerTextView::AttachedToWindow() 543{ 544 fParent = static_cast<BAbstractSpinner*>(Parent()); 545 546 BTextView::AttachedToWindow(); 547} 548 549 550void 551SpinnerTextView::DetachedFromWindow() 552{ 553 fParent = NULL; 554 555 BTextView::DetachedFromWindow(); 556} 557 558 559void 560SpinnerTextView::KeyDown(const char* bytes, int32 numBytes) 561{ 562 if (fParent == NULL) { 563 BTextView::KeyDown(bytes, numBytes); 564 return; 565 } 566 567 switch (bytes[0]) { 568 case B_ENTER: 569 case B_SPACE: 570 fParent->SetValueFromText(); 571 break; 572 573 case B_TAB: 574 fParent->KeyDown(bytes, numBytes); 575 break; 576 577 case B_LEFT_ARROW: 578 if (fParent->ButtonStyle() == SPINNER_BUTTON_HORIZONTAL_ARROWS 579 && (modifiers() & B_CONTROL_KEY) != 0) { 580 // need to hold down control, otherwise can't move cursor 581 fParent->Decrement(); 582 } else 583 BTextView::KeyDown(bytes, numBytes); 584 break; 585 586 case B_UP_ARROW: 587 if (fParent->ButtonStyle() != SPINNER_BUTTON_HORIZONTAL_ARROWS) 588 fParent->Increment(); 589 else 590 BTextView::KeyDown(bytes, numBytes); 591 break; 592 593 case B_RIGHT_ARROW: 594 if (fParent->ButtonStyle() == SPINNER_BUTTON_HORIZONTAL_ARROWS 595 && (modifiers() & B_CONTROL_KEY) != 0) { 596 // need to hold down control, otherwise can't move cursor 597 fParent->Increment(); 598 } else 599 BTextView::KeyDown(bytes, numBytes); 600 break; 601 602 case B_DOWN_ARROW: 603 if (fParent->ButtonStyle() != SPINNER_BUTTON_HORIZONTAL_ARROWS) 604 fParent->Decrement(); 605 else 606 BTextView::KeyDown(bytes, numBytes); 607 break; 608 609 default: 610 BTextView::KeyDown(bytes, numBytes); 611 break; 612 } 613} 614 615 616void 617SpinnerTextView::MakeFocus(bool focus) 618{ 619 BTextView::MakeFocus(focus); 620 621 if (fParent == NULL) 622 return; 623 624 if (focus) 625 SelectAll(); 626 else 627 fParent->SetValueFromText(); 628 629 fParent->_DrawTextView(fParent->Bounds()); 630} 631 632 633// #pragma mark - BAbstractSpinner::LabelLayoutItem 634 635 636BAbstractSpinner::LabelLayoutItem::LabelLayoutItem(BAbstractSpinner* parent) 637 : 638 fParent(parent), 639 fFrame() 640{ 641} 642 643 644BAbstractSpinner::LabelLayoutItem::LabelLayoutItem(BMessage* from) 645 : 646 BAbstractLayoutItem(from), 647 fParent(NULL), 648 fFrame() 649{ 650 from->FindRect(kFrameField, &fFrame); 651} 652 653 654bool 655BAbstractSpinner::LabelLayoutItem::IsVisible() 656{ 657 return !fParent->IsHidden(fParent); 658} 659 660 661void 662BAbstractSpinner::LabelLayoutItem::SetVisible(bool visible) 663{ 664} 665 666 667BRect 668BAbstractSpinner::LabelLayoutItem::Frame() 669{ 670 return fFrame; 671} 672 673 674void 675BAbstractSpinner::LabelLayoutItem::SetFrame(BRect frame) 676{ 677 fFrame = frame; 678 fParent->_UpdateFrame(); 679} 680 681 682void 683BAbstractSpinner::LabelLayoutItem::SetParent(BAbstractSpinner* parent) 684{ 685 fParent = parent; 686} 687 688 689BView* 690BAbstractSpinner::LabelLayoutItem::View() 691{ 692 return fParent; 693} 694 695 696BSize 697BAbstractSpinner::LabelLayoutItem::BaseMinSize() 698{ 699 fParent->_ValidateLayoutData(); 700 701 if (fParent->Label() == NULL) 702 return BSize(-1.0f, -1.0f); 703 704 return BSize(fParent->fLayoutData->label_width 705 + be_control_look->DefaultLabelSpacing(), 706 fParent->fLayoutData->label_height); 707} 708 709 710BSize 711BAbstractSpinner::LabelLayoutItem::BaseMaxSize() 712{ 713 return BaseMinSize(); 714} 715 716 717BSize 718BAbstractSpinner::LabelLayoutItem::BasePreferredSize() 719{ 720 return BaseMinSize(); 721} 722 723 724BAlignment 725BAbstractSpinner::LabelLayoutItem::BaseAlignment() 726{ 727 return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT); 728} 729 730 731BRect 732BAbstractSpinner::LabelLayoutItem::FrameInParent() const 733{ 734 return fFrame.OffsetByCopy(-fParent->Frame().left, -fParent->Frame().top); 735} 736 737 738status_t 739BAbstractSpinner::LabelLayoutItem::Archive(BMessage* into, bool deep) const 740{ 741 BArchiver archiver(into); 742 status_t result = BAbstractLayoutItem::Archive(into, deep); 743 744 if (result == B_OK) 745 result = into->AddRect(kFrameField, fFrame); 746 747 return archiver.Finish(result); 748} 749 750 751BArchivable* 752BAbstractSpinner::LabelLayoutItem::Instantiate(BMessage* from) 753{ 754 if (validate_instantiation(from, "BAbstractSpinner::LabelLayoutItem")) 755 return new LabelLayoutItem(from); 756 757 return NULL; 758} 759 760 761// #pragma mark - BAbstractSpinner::TextViewLayoutItem 762 763 764BAbstractSpinner::TextViewLayoutItem::TextViewLayoutItem(BAbstractSpinner* parent) 765 : 766 fParent(parent), 767 fFrame() 768{ 769 SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 770} 771 772 773BAbstractSpinner::TextViewLayoutItem::TextViewLayoutItem(BMessage* from) 774 : 775 BAbstractLayoutItem(from), 776 fParent(NULL), 777 fFrame() 778{ 779 from->FindRect(kFrameField, &fFrame); 780} 781 782 783bool 784BAbstractSpinner::TextViewLayoutItem::IsVisible() 785{ 786 return !fParent->IsHidden(fParent); 787} 788 789 790void 791BAbstractSpinner::TextViewLayoutItem::SetVisible(bool visible) 792{ 793 // not allowed 794} 795 796 797BRect 798BAbstractSpinner::TextViewLayoutItem::Frame() 799{ 800 return fFrame; 801} 802 803 804void 805BAbstractSpinner::TextViewLayoutItem::SetFrame(BRect frame) 806{ 807 fFrame = frame; 808 fParent->_UpdateFrame(); 809} 810 811 812void 813BAbstractSpinner::TextViewLayoutItem::SetParent(BAbstractSpinner* parent) 814{ 815 fParent = parent; 816} 817 818 819BView* 820BAbstractSpinner::TextViewLayoutItem::View() 821{ 822 return fParent; 823} 824 825 826BSize 827BAbstractSpinner::TextViewLayoutItem::BaseMinSize() 828{ 829 fParent->_ValidateLayoutData(); 830 831 BSize size(fParent->fLayoutData->text_view_width, 832 fParent->fLayoutData->text_view_height); 833 834 return size; 835} 836 837 838BSize 839BAbstractSpinner::TextViewLayoutItem::BaseMaxSize() 840{ 841 return BaseMinSize(); 842} 843 844 845BSize 846BAbstractSpinner::TextViewLayoutItem::BasePreferredSize() 847{ 848 return BaseMinSize(); 849} 850 851 852BAlignment 853BAbstractSpinner::TextViewLayoutItem::BaseAlignment() 854{ 855 return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT); 856} 857 858 859BRect 860BAbstractSpinner::TextViewLayoutItem::FrameInParent() const 861{ 862 return fFrame.OffsetByCopy(-fParent->Frame().left, -fParent->Frame().top); 863} 864 865 866status_t 867BAbstractSpinner::TextViewLayoutItem::Archive(BMessage* into, bool deep) const 868{ 869 BArchiver archiver(into); 870 status_t result = BAbstractLayoutItem::Archive(into, deep); 871 872 if (result == B_OK) 873 result = into->AddRect(kFrameField, fFrame); 874 875 return archiver.Finish(result); 876} 877 878 879BArchivable* 880BAbstractSpinner::TextViewLayoutItem::Instantiate(BMessage* from) 881{ 882 if (validate_instantiation(from, "BAbstractSpinner::TextViewLayoutItem")) 883 return new LabelLayoutItem(from); 884 885 return NULL; 886} 887 888 889// #pragma mark - BAbstractSpinner 890 891 892BAbstractSpinner::BAbstractSpinner(BRect frame, const char* name, const char* label, 893 BMessage* message, uint32 resizingMode, uint32 flags) 894 : 895 BControl(frame, name, label, message, resizingMode, 896 flags | B_WILL_DRAW | B_FRAME_EVENTS) 897{ 898 _InitObject(); 899} 900 901 902BAbstractSpinner::BAbstractSpinner(const char* name, const char* label, BMessage* message, 903 uint32 flags) 904 : 905 BControl(name, label, message, flags | B_WILL_DRAW | B_FRAME_EVENTS) 906{ 907 _InitObject(); 908} 909 910 911BAbstractSpinner::BAbstractSpinner(BMessage* data) 912 : 913 BControl(data), 914 fButtonStyle(SPINNER_BUTTON_PLUS_MINUS) 915{ 916 _InitObject(); 917 918 if (data->FindInt32("_align") != B_OK) 919 fAlignment = B_ALIGN_LEFT; 920 921 if (data->FindInt32("_button_style") != B_OK) 922 fButtonStyle = SPINNER_BUTTON_PLUS_MINUS; 923 924 if (data->FindInt32("_divider") != B_OK) 925 fDivider = 0.0f; 926} 927 928 929BAbstractSpinner::~BAbstractSpinner() 930{ 931 delete fLayoutData; 932 fLayoutData = NULL; 933} 934 935 936BArchivable* 937BAbstractSpinner::Instantiate(BMessage* data) 938{ 939 // cannot instantiate an abstract spinner 940 return NULL; 941} 942 943 944status_t 945BAbstractSpinner::Archive(BMessage* data, bool deep) const 946{ 947 status_t status = BControl::Archive(data, deep); 948 data->AddString("class", "Spinner"); 949 950 if (status == B_OK) 951 status = data->AddInt32("_align", fAlignment); 952 953 if (status == B_OK) 954 data->AddInt32("_button_style", fButtonStyle); 955 956 if (status == B_OK) 957 status = data->AddFloat("_divider", fDivider); 958 959 return status; 960} 961 962 963status_t 964BAbstractSpinner::GetSupportedSuites(BMessage* message) 965{ 966 message->AddString("suites", "suite/vnd.Haiku-spinner"); 967 968 BPropertyInfo prop_info(sProperties); 969 message->AddFlat("messages", &prop_info); 970 971 return BControl::GetSupportedSuites(message); 972} 973 974 975BHandler* 976BAbstractSpinner::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier, 977 int32 form, const char* property) 978{ 979 return BControl::ResolveSpecifier(message, index, specifier, form, 980 property); 981} 982 983 984void 985BAbstractSpinner::AttachedToWindow() 986{ 987 if (!Messenger().IsValid()) 988 SetTarget(Window()); 989 990 BControl::SetValue(Value()); 991 // sets the text and enables or disables the arrows 992 993 _UpdateTextViewColors(IsEnabled()); 994 fTextView->MakeEditable(IsEnabled()); 995 996 BControl::AttachedToWindow(); 997} 998 999 1000void 1001BAbstractSpinner::Draw(BRect updateRect) 1002{ 1003 _DrawLabel(updateRect); 1004 _DrawTextView(updateRect); 1005 fIncrement->Invalidate(); 1006 fDecrement->Invalidate(); 1007} 1008 1009 1010void 1011BAbstractSpinner::FrameResized(float width, float height) 1012{ 1013 BControl::FrameResized(width, height); 1014 1015 // TODO: this causes flickering still... 1016 1017 // changes in width 1018 1019 BRect bounds = Bounds(); 1020 1021 if (bounds.Width() > fLayoutData->previous_width) { 1022 // invalidate the region between the old and the new right border 1023 BRect rect = bounds; 1024 rect.left += fLayoutData->previous_width - kFrameMargin; 1025 rect.right--; 1026 Invalidate(rect); 1027 } else if (bounds.Width() < fLayoutData->previous_width) { 1028 // invalidate the region of the new right border 1029 BRect rect = bounds; 1030 rect.left = rect.right - kFrameMargin; 1031 Invalidate(rect); 1032 } 1033 1034 // changes in height 1035 1036 if (bounds.Height() > fLayoutData->previous_height) { 1037 // invalidate the region between the old and the new bottom border 1038 BRect rect = bounds; 1039 rect.top += fLayoutData->previous_height - kFrameMargin; 1040 rect.bottom--; 1041 Invalidate(rect); 1042 // invalidate label area 1043 rect = bounds; 1044 rect.right = fDivider; 1045 Invalidate(rect); 1046 } else if (bounds.Height() < fLayoutData->previous_height) { 1047 // invalidate the region of the new bottom border 1048 BRect rect = bounds; 1049 rect.top = rect.bottom - kFrameMargin; 1050 Invalidate(rect); 1051 // invalidate label area 1052 rect = bounds; 1053 rect.right = fDivider; 1054 Invalidate(rect); 1055 } 1056 1057 fLayoutData->previous_width = bounds.Width(); 1058 fLayoutData->previous_height = bounds.Height(); 1059} 1060 1061 1062void 1063BAbstractSpinner::ValueChanged() 1064{ 1065 // hook method - does nothing 1066} 1067 1068 1069void 1070BAbstractSpinner::MessageReceived(BMessage* message) 1071{ 1072 if (!IsEnabled() && message->what == B_COLORS_UPDATED) 1073 _UpdateTextViewColors(false); 1074 1075 BControl::MessageReceived(message); 1076} 1077 1078 1079void 1080BAbstractSpinner::MakeFocus(bool focus) 1081{ 1082 fTextView->MakeFocus(focus); 1083} 1084 1085 1086void 1087BAbstractSpinner::ResizeToPreferred() 1088{ 1089 BControl::ResizeToPreferred(); 1090 1091 const char* label = Label(); 1092 if (label != NULL) { 1093 fDivider = ceilf(StringWidth(label)) 1094 + be_control_look->DefaultLabelSpacing(); 1095 } else 1096 fDivider = 0.0f; 1097 1098 _LayoutTextView(); 1099} 1100 1101 1102void 1103BAbstractSpinner::SetFlags(uint32 flags) 1104{ 1105 // If the textview is navigable, set it to not navigable if needed, 1106 // else if it is not navigable, set it to navigable if needed 1107 if (fTextView->Flags() & B_NAVIGABLE) { 1108 if (!(flags & B_NAVIGABLE)) 1109 fTextView->SetFlags(fTextView->Flags() & ~B_NAVIGABLE); 1110 } else { 1111 if (flags & B_NAVIGABLE) 1112 fTextView->SetFlags(fTextView->Flags() | B_NAVIGABLE); 1113 } 1114 1115 // Don't make this one navigable 1116 flags &= ~B_NAVIGABLE; 1117 1118 BControl::SetFlags(flags); 1119} 1120 1121 1122void 1123BAbstractSpinner::WindowActivated(bool active) 1124{ 1125 _DrawTextView(fTextView->Frame()); 1126} 1127 1128 1129void 1130BAbstractSpinner::SetAlignment(alignment align) 1131{ 1132 fAlignment = align; 1133} 1134 1135 1136void 1137BAbstractSpinner::SetButtonStyle(spinner_button_style buttonStyle) 1138{ 1139 fButtonStyle = buttonStyle; 1140} 1141 1142 1143void 1144BAbstractSpinner::SetDivider(float position) 1145{ 1146 position = roundf(position); 1147 1148 float delta = fDivider - position; 1149 if (delta == 0.0f) 1150 return; 1151 1152 fDivider = position; 1153 1154 if ((Flags() & B_SUPPORTS_LAYOUT) != 0) { 1155 // We should never get here, since layout support means, we also 1156 // layout the divider, and don't use this method at all. 1157 Relayout(); 1158 } else { 1159 _LayoutTextView(); 1160 Invalidate(); 1161 } 1162} 1163 1164 1165void 1166BAbstractSpinner::SetEnabled(bool enable) 1167{ 1168 if (IsEnabled() == enable) 1169 return; 1170 1171 BControl::SetEnabled(enable); 1172 1173 fTextView->MakeEditable(enable); 1174 if (enable) 1175 fTextView->SetFlags(fTextView->Flags() | B_NAVIGABLE); 1176 else 1177 fTextView->SetFlags(fTextView->Flags() & ~B_NAVIGABLE); 1178 1179 _UpdateTextViewColors(enable); 1180 fTextView->Invalidate(); 1181 1182 _LayoutTextView(); 1183 Invalidate(); 1184 if (Window() != NULL) 1185 Window()->UpdateIfNeeded(); 1186} 1187 1188 1189void 1190BAbstractSpinner::SetLabel(const char* label) 1191{ 1192 BControl::SetLabel(label); 1193 1194 if (Window() != NULL) 1195 Window()->UpdateIfNeeded(); 1196} 1197 1198 1199bool 1200BAbstractSpinner::IsDecrementEnabled() const 1201{ 1202 return fDecrement->IsEnabled(); 1203} 1204 1205 1206void 1207BAbstractSpinner::SetDecrementEnabled(bool enable) 1208{ 1209 if (IsDecrementEnabled() == enable) 1210 return; 1211 1212 fDecrement->SetEnabled(enable); 1213 fDecrement->Invalidate(); 1214} 1215 1216 1217bool 1218BAbstractSpinner::IsIncrementEnabled() const 1219{ 1220 return fIncrement->IsEnabled(); 1221} 1222 1223 1224void 1225BAbstractSpinner::SetIncrementEnabled(bool enable) 1226{ 1227 if (IsIncrementEnabled() == enable) 1228 return; 1229 1230 fIncrement->SetEnabled(enable); 1231 fIncrement->Invalidate(); 1232} 1233 1234 1235BSize 1236BAbstractSpinner::MinSize() 1237{ 1238 _ValidateLayoutData(); 1239 return BLayoutUtils::ComposeSize(ExplicitMinSize(), fLayoutData->min); 1240} 1241 1242 1243BSize 1244BAbstractSpinner::MaxSize() 1245{ 1246 _ValidateLayoutData(); 1247 1248 BSize max = fLayoutData->min; 1249 max.width = B_SIZE_UNLIMITED; 1250 1251 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max); 1252} 1253 1254 1255BSize 1256BAbstractSpinner::PreferredSize() 1257{ 1258 _ValidateLayoutData(); 1259 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), 1260 fLayoutData->min); 1261} 1262 1263 1264BAlignment 1265BAbstractSpinner::LayoutAlignment() 1266{ 1267 _ValidateLayoutData(); 1268 return BLayoutUtils::ComposeAlignment(ExplicitAlignment(), 1269 BAlignment(B_ALIGN_LEFT, B_ALIGN_VERTICAL_CENTER)); 1270} 1271 1272 1273BLayoutItem* 1274BAbstractSpinner::CreateLabelLayoutItem() 1275{ 1276 if (fLayoutData->label_layout_item == NULL) 1277 fLayoutData->label_layout_item = new LabelLayoutItem(this); 1278 1279 return fLayoutData->label_layout_item; 1280} 1281 1282 1283BLayoutItem* 1284BAbstractSpinner::CreateTextViewLayoutItem() 1285{ 1286 if (fLayoutData->text_view_layout_item == NULL) 1287 fLayoutData->text_view_layout_item = new TextViewLayoutItem(this); 1288 1289 return fLayoutData->text_view_layout_item; 1290} 1291 1292 1293BTextView* 1294BAbstractSpinner::TextView() const 1295{ 1296 return dynamic_cast<BTextView*>(fTextView); 1297} 1298 1299 1300// #pragma mark - BAbstractSpinner protected methods 1301 1302 1303status_t 1304BAbstractSpinner::AllArchived(BMessage* into) const 1305{ 1306 status_t result; 1307 if ((result = BControl::AllArchived(into)) != B_OK) 1308 return result; 1309 1310 BArchiver archiver(into); 1311 1312 BArchivable* textViewItem = fLayoutData->text_view_layout_item; 1313 if (archiver.IsArchived(textViewItem)) 1314 result = archiver.AddArchivable(kTextViewItemField, textViewItem); 1315 1316 if (result != B_OK) 1317 return result; 1318 1319 BArchivable* labelBarItem = fLayoutData->label_layout_item; 1320 if (archiver.IsArchived(labelBarItem)) 1321 result = archiver.AddArchivable(kLabelItemField, labelBarItem); 1322 1323 return result; 1324} 1325 1326 1327status_t 1328BAbstractSpinner::AllUnarchived(const BMessage* from) 1329{ 1330 BUnarchiver unarchiver(from); 1331 1332 status_t result = B_OK; 1333 if ((result = BControl::AllUnarchived(from)) != B_OK) 1334 return result; 1335 1336 if (unarchiver.IsInstantiated(kTextViewItemField)) { 1337 TextViewLayoutItem*& textViewItem 1338 = fLayoutData->text_view_layout_item; 1339 result = unarchiver.FindObject(kTextViewItemField, 1340 BUnarchiver::B_DONT_ASSUME_OWNERSHIP, textViewItem); 1341 1342 if (result == B_OK) 1343 textViewItem->SetParent(this); 1344 else 1345 return result; 1346 } 1347 1348 if (unarchiver.IsInstantiated(kLabelItemField)) { 1349 LabelLayoutItem*& labelItem = fLayoutData->label_layout_item; 1350 result = unarchiver.FindObject(kLabelItemField, 1351 BUnarchiver::B_DONT_ASSUME_OWNERSHIP, labelItem); 1352 1353 if (result == B_OK) 1354 labelItem->SetParent(this); 1355 } 1356 1357 return result; 1358} 1359 1360 1361void 1362BAbstractSpinner::DoLayout() 1363{ 1364 if ((Flags() & B_SUPPORTS_LAYOUT) == 0) 1365 return; 1366 1367 if (GetLayout()) { 1368 BControl::DoLayout(); 1369 return; 1370 } 1371 1372 _ValidateLayoutData(); 1373 1374 BSize size(Bounds().Size()); 1375 if (size.width < fLayoutData->min.width) 1376 size.width = fLayoutData->min.width; 1377 1378 if (size.height < fLayoutData->min.height) 1379 size.height = fLayoutData->min.height; 1380 1381 float divider = 0; 1382 if (fLayoutData->label_layout_item != NULL 1383 && fLayoutData->text_view_layout_item != NULL 1384 && fLayoutData->label_layout_item->Frame().IsValid() 1385 && fLayoutData->text_view_layout_item->Frame().IsValid()) { 1386 divider = fLayoutData->text_view_layout_item->Frame().left 1387 - fLayoutData->label_layout_item->Frame().left; 1388 } else if (fLayoutData->label_width > 0) { 1389 divider = fLayoutData->label_width 1390 + be_control_look->DefaultLabelSpacing(); 1391 } 1392 fDivider = divider; 1393 1394 BRect dirty(fTextView->Frame()); 1395 _LayoutTextView(); 1396 1397 // invalidate dirty region 1398 dirty = dirty | fTextView->Frame(); 1399 dirty = dirty | fIncrement->Frame(); 1400 dirty = dirty | fDecrement->Frame(); 1401 1402 Invalidate(dirty); 1403} 1404 1405 1406void 1407BAbstractSpinner::LayoutInvalidated(bool descendants) 1408{ 1409 if (fLayoutData != NULL) 1410 fLayoutData->valid = false; 1411} 1412 1413 1414// #pragma mark - BAbstractSpinner private methods 1415 1416 1417void 1418BAbstractSpinner::_DrawLabel(BRect updateRect) 1419{ 1420 BRect rect(Bounds()); 1421 rect.right = fDivider; 1422 if (!rect.IsValid() || !rect.Intersects(updateRect)) 1423 return; 1424 1425 _ValidateLayoutData(); 1426 1427 const char* label = Label(); 1428 if (label == NULL) 1429 return; 1430 1431 // horizontal position 1432 float x; 1433 switch (fAlignment) { 1434 case B_ALIGN_RIGHT: 1435 x = fDivider - fLayoutData->label_width - 3.0f; 1436 break; 1437 1438 case B_ALIGN_CENTER: 1439 x = fDivider - roundf(fLayoutData->label_width / 2.0f); 1440 break; 1441 1442 default: 1443 x = 0.0f; 1444 break; 1445 } 1446 1447 // vertical position 1448 font_height& fontHeight = fLayoutData->font_info; 1449 float y = rect.top 1450 + roundf((rect.Height() + 1.0f - fontHeight.ascent 1451 - fontHeight.descent) / 2.0f) 1452 + fontHeight.ascent; 1453 1454 uint32 flags = be_control_look->Flags(this); 1455 1456 // erase the is control flag before drawing the label so that the label 1457 // will get drawn using B_PANEL_TEXT_COLOR. 1458 flags &= ~BControlLook::B_IS_CONTROL; 1459 1460 be_control_look->DrawLabel(this, label, LowColor(), flags, BPoint(x, y)); 1461} 1462 1463 1464void 1465BAbstractSpinner::_DrawTextView(BRect updateRect) 1466{ 1467 BRect rect = fTextView->Frame(); 1468 rect.InsetBy(-kFrameMargin, -kFrameMargin); 1469 if (!rect.IsValid() || !rect.Intersects(updateRect)) 1470 return; 1471 1472 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 1473 uint32 flags = 0; 1474 if (!IsEnabled()) 1475 flags |= BControlLook::B_DISABLED; 1476 1477 if (fTextView->IsFocus() && Window()->IsActive()) 1478 flags |= BControlLook::B_FOCUSED; 1479 1480 be_control_look->DrawTextControlBorder(this, rect, updateRect, base, 1481 flags); 1482} 1483 1484 1485void 1486BAbstractSpinner::_InitObject() 1487{ 1488 fAlignment = B_ALIGN_LEFT; 1489 fButtonStyle = SPINNER_BUTTON_PLUS_MINUS; 1490 1491 if (Label() != NULL) { 1492 fDivider = StringWidth(Label()) 1493 + be_control_look->DefaultLabelSpacing(); 1494 } else 1495 fDivider = 0.0f; 1496 1497 BControl::SetEnabled(true); 1498 BControl::SetValue(0); 1499 1500 BRect rect(Bounds()); 1501 fLayoutData = new LayoutData(rect.Width(), rect.Height()); 1502 1503 rect.left = fDivider; 1504 rect.InsetBy(kFrameMargin, kFrameMargin); 1505 rect.right -= rect.Height() * 2 + kFrameMargin * 2 + 1.0f; 1506 BRect textRect(rect.OffsetToCopy(B_ORIGIN)); 1507 1508 fTextView = new SpinnerTextView(rect, textRect); 1509 AddChild(fTextView); 1510 1511 rect.InsetBy(0.0f, -kFrameMargin); 1512 1513 rect.left = rect.right + kFrameMargin * 2; 1514 rect.right = rect.left + rect.Height() - kFrameMargin * 2; 1515 1516 fDecrement = new SpinnerButton(rect, "decrement", SPINNER_DECREMENT); 1517 AddChild(fDecrement); 1518 1519 rect.left = rect.right + 1.0f; 1520 rect.right = rect.left + rect.Height() - kFrameMargin * 2; 1521 1522 fIncrement = new SpinnerButton(rect, "increment", SPINNER_INCREMENT); 1523 AddChild(fIncrement); 1524 1525 uint32 navigableFlags = Flags() & B_NAVIGABLE; 1526 if (navigableFlags != 0) 1527 BControl::SetFlags(Flags() & ~B_NAVIGABLE); 1528} 1529 1530 1531void 1532BAbstractSpinner::_LayoutTextView() 1533{ 1534 BRect rect; 1535 if (fLayoutData->text_view_layout_item != NULL) { 1536 rect = fLayoutData->text_view_layout_item->FrameInParent(); 1537 } else { 1538 rect = Bounds(); 1539 rect.left = fDivider; 1540 } 1541 rect.InsetBy(kFrameMargin, kFrameMargin); 1542 rect.right -= rect.Height() * 2 + kFrameMargin * 2 + 1.0f; 1543 1544 fTextView->MoveTo(rect.left, rect.top); 1545 fTextView->ResizeTo(rect.Width(), rect.Height()); 1546 fTextView->SetTextRect(rect.OffsetToCopy(B_ORIGIN)); 1547 1548 rect.InsetBy(0.0f, -kFrameMargin); 1549 1550 rect.left = rect.right + kFrameMargin * 2; 1551 rect.right = rect.left + rect.Height() - kFrameMargin * 2; 1552 1553 fDecrement->ResizeTo(rect.Width(), rect.Height()); 1554 fDecrement->MoveTo(rect.LeftTop()); 1555 1556 rect.left = rect.right + 1.0f; 1557 rect.right = rect.left + rect.Height() - kFrameMargin * 2; 1558 1559 fIncrement->ResizeTo(rect.Width(), rect.Height()); 1560 fIncrement->MoveTo(rect.LeftTop()); 1561} 1562 1563 1564void 1565BAbstractSpinner::_UpdateFrame() 1566{ 1567 if (fLayoutData->label_layout_item == NULL 1568 || fLayoutData->text_view_layout_item == NULL) { 1569 return; 1570 } 1571 1572 BRect labelFrame = fLayoutData->label_layout_item->Frame(); 1573 BRect textViewFrame = fLayoutData->text_view_layout_item->Frame(); 1574 1575 if (!labelFrame.IsValid() || !textViewFrame.IsValid()) 1576 return; 1577 1578 // update divider 1579 fDivider = textViewFrame.left - labelFrame.left; 1580 1581 BRect frame = textViewFrame | labelFrame; 1582 MoveTo(frame.left, frame.top); 1583 BSize oldSize = Bounds().Size(); 1584 ResizeTo(frame.Width(), frame.Height()); 1585 BSize newSize = Bounds().Size(); 1586 1587 // If the size changes, ResizeTo() will trigger a relayout, otherwise 1588 // we need to do that explicitly. 1589 if (newSize != oldSize) 1590 Relayout(); 1591} 1592 1593 1594void 1595BAbstractSpinner::_UpdateTextViewColors(bool enable) 1596{ 1597 // Mimick BTextControl's appearance. 1598 rgb_color textColor = ui_color(B_DOCUMENT_TEXT_COLOR); 1599 1600 if (enable) { 1601 fTextView->SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR); 1602 fTextView->SetLowUIColor(ViewUIColor()); 1603 } else { 1604 rgb_color color = ui_color(B_DOCUMENT_BACKGROUND_COLOR); 1605 color = disable_color(ViewColor(), color); 1606 textColor = disable_color(textColor, ViewColor()); 1607 1608 fTextView->SetViewColor(color); 1609 fTextView->SetLowColor(color); 1610 } 1611 1612 BFont font; 1613 fTextView->GetFontAndColor(0, &font); 1614 fTextView->SetFontAndColor(&font, B_FONT_ALL, &textColor); 1615} 1616 1617 1618void 1619BAbstractSpinner::_ValidateLayoutData() 1620{ 1621 if (fLayoutData->valid) 1622 return; 1623 1624 font_height& fontHeight = fLayoutData->font_info; 1625 GetFontHeight(&fontHeight); 1626 1627 if (Label() != NULL) { 1628 fLayoutData->label_width = StringWidth(Label()); 1629 fLayoutData->label_height = ceilf(fontHeight.ascent 1630 + fontHeight.descent + fontHeight.leading); 1631 } else { 1632 fLayoutData->label_width = 0; 1633 fLayoutData->label_height = 0; 1634 } 1635 1636 float divider = 0; 1637 if (fLayoutData->label_width > 0) { 1638 divider = ceilf(fLayoutData->label_width 1639 + be_control_look->DefaultLabelSpacing()); 1640 } 1641 1642 if ((Flags() & B_SUPPORTS_LAYOUT) == 0) 1643 divider = std::max(divider, fDivider); 1644 1645 float minTextWidth = fTextView->StringWidth("99999"); 1646 1647 float textViewHeight = fTextView->LineHeight(0) + kFrameMargin * 2; 1648 float textViewWidth = minTextWidth + textViewHeight * 2; 1649 1650 fLayoutData->text_view_width = textViewWidth; 1651 fLayoutData->text_view_height = textViewHeight; 1652 1653 BSize min(textViewWidth, textViewHeight); 1654 if (divider > 0.0f) 1655 min.width += divider; 1656 1657 if (fLayoutData->label_height > min.height) 1658 min.height = fLayoutData->label_height; 1659 1660 fLayoutData->min = min; 1661 fLayoutData->valid = true; 1662 1663 ResetLayoutInvalidation(); 1664} 1665 1666 1667// FBC padding 1668 1669void BAbstractSpinner::_ReservedAbstractSpinner20() {} 1670void BAbstractSpinner::_ReservedAbstractSpinner19() {} 1671void BAbstractSpinner::_ReservedAbstractSpinner18() {} 1672void BAbstractSpinner::_ReservedAbstractSpinner17() {} 1673void BAbstractSpinner::_ReservedAbstractSpinner16() {} 1674void BAbstractSpinner::_ReservedAbstractSpinner15() {} 1675void BAbstractSpinner::_ReservedAbstractSpinner14() {} 1676void BAbstractSpinner::_ReservedAbstractSpinner13() {} 1677void BAbstractSpinner::_ReservedAbstractSpinner12() {} 1678void BAbstractSpinner::_ReservedAbstractSpinner11() {} 1679void BAbstractSpinner::_ReservedAbstractSpinner10() {} 1680void BAbstractSpinner::_ReservedAbstractSpinner9() {} 1681void BAbstractSpinner::_ReservedAbstractSpinner8() {} 1682void BAbstractSpinner::_ReservedAbstractSpinner7() {} 1683void BAbstractSpinner::_ReservedAbstractSpinner6() {} 1684void BAbstractSpinner::_ReservedAbstractSpinner5() {} 1685void BAbstractSpinner::_ReservedAbstractSpinner4() {} 1686void BAbstractSpinner::_ReservedAbstractSpinner3() {} 1687void BAbstractSpinner::_ReservedAbstractSpinner2() {} 1688void BAbstractSpinner::_ReservedAbstractSpinner1() {} 1689