1 /* 2 * Copyright 2001-2009, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Marc Flerackers (mflerackers@androme.be) 7 * Stephan Aßmus <superstippi@gmx.de> 8 * Axel Dörfler, axeld@pinc-software.de 9 */ 10 11 12 #include <Slider.h> 13 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 18 #include <Bitmap.h> 19 #include <ControlLook.h> 20 #include <Errors.h> 21 #include <LayoutUtils.h> 22 #include <Message.h> 23 #include <Region.h> 24 #include <String.h> 25 #include <Window.h> 26 27 #include <binary_compatibility/Interface.h> 28 29 30 #define USE_OFF_SCREEN_VIEW 0 31 32 33 BSlider::BSlider(BRect frame, const char* name, const char* label, 34 BMessage* message, int32 minValue, int32 maxValue, 35 thumb_style thumbType, uint32 resizingMode, uint32 flags) 36 : BControl(frame, name, label, message, resizingMode, flags), 37 fModificationMessage(NULL), 38 fSnoozeAmount(20000), 39 40 fMinLimitLabel(NULL), 41 fMaxLimitLabel(NULL), 42 43 fMinValue(minValue), 44 fMaxValue(maxValue), 45 fKeyIncrementValue(1), 46 47 fHashMarkCount(0), 48 fHashMarks(B_HASH_MARKS_NONE), 49 50 fStyle(thumbType), 51 52 fOrientation(B_HORIZONTAL), 53 fBarThickness(6.0) 54 { 55 _InitBarColor(); 56 57 _InitObject(); 58 SetValue(0); 59 } 60 61 62 BSlider::BSlider(BRect frame, const char *name, const char *label, 63 BMessage *message, int32 minValue, int32 maxValue, 64 orientation posture, thumb_style thumbType, uint32 resizingMode, 65 uint32 flags) 66 : BControl(frame, name, label, message, resizingMode, flags), 67 fModificationMessage(NULL), 68 fSnoozeAmount(20000), 69 70 fMinLimitLabel(NULL), 71 fMaxLimitLabel(NULL), 72 73 fMinValue(minValue), 74 fMaxValue(maxValue), 75 fKeyIncrementValue(1), 76 77 fHashMarkCount(0), 78 fHashMarks(B_HASH_MARKS_NONE), 79 80 fStyle(thumbType), 81 82 fOrientation(posture), 83 fBarThickness(6.0) 84 { 85 _InitBarColor(); 86 87 _InitObject(); 88 SetValue(0); 89 } 90 91 92 BSlider::BSlider(const char *name, const char *label, BMessage *message, 93 int32 minValue, int32 maxValue, orientation posture, 94 thumb_style thumbType, uint32 flags) 95 : BControl(name, label, message, flags), 96 fModificationMessage(NULL), 97 fSnoozeAmount(20000), 98 99 fMinLimitLabel(NULL), 100 fMaxLimitLabel(NULL), 101 102 fMinValue(minValue), 103 fMaxValue(maxValue), 104 fKeyIncrementValue(1), 105 106 fHashMarkCount(0), 107 fHashMarks(B_HASH_MARKS_NONE), 108 109 fStyle(thumbType), 110 111 fOrientation(posture), 112 fBarThickness(6.0) 113 { 114 _InitBarColor(); 115 116 _InitObject(); 117 SetValue(0); 118 } 119 120 121 BSlider::BSlider(BMessage *archive) 122 : BControl(archive) 123 { 124 fModificationMessage = NULL; 125 126 if (archive->HasMessage("_mod_msg")) { 127 BMessage* message = new BMessage; 128 129 archive->FindMessage("_mod_msg", message); 130 131 SetModificationMessage(message); 132 } 133 134 if (archive->FindInt32("_sdelay", &fSnoozeAmount) != B_OK) 135 SetSnoozeAmount(20000); 136 137 rgb_color color; 138 if (archive->FindInt32("_fcolor", (int32 *)&color) == B_OK) 139 UseFillColor(true, &color); 140 else 141 UseFillColor(false); 142 143 int32 orient; 144 if (archive->FindInt32("_orient", &orient) == B_OK) 145 fOrientation = (orientation)orient; 146 else 147 fOrientation = B_HORIZONTAL; 148 149 fMinLimitLabel = NULL; 150 fMaxLimitLabel = NULL; 151 152 const char* minlbl = NULL; 153 const char* maxlbl = NULL; 154 155 archive->FindString("_minlbl", &minlbl); 156 archive->FindString("_maxlbl", &maxlbl); 157 158 SetLimitLabels(minlbl, maxlbl); 159 160 if (archive->FindInt32("_min", &fMinValue) != B_OK) 161 fMinValue = 0; 162 163 if (archive->FindInt32("_max", &fMaxValue) != B_OK) 164 fMaxValue = 100; 165 166 if (archive->FindInt32("_incrementvalue", &fKeyIncrementValue) != B_OK) 167 fKeyIncrementValue = 1; 168 169 if (archive->FindInt32("_hashcount", &fHashMarkCount) != B_OK) 170 fHashMarkCount = 11; 171 172 int16 hashloc; 173 if (archive->FindInt16("_hashloc", &hashloc) == B_OK) 174 fHashMarks = (hash_mark_location)hashloc; 175 else 176 fHashMarks = B_HASH_MARKS_NONE; 177 178 int16 sstyle; 179 if (archive->FindInt16("_sstyle", &sstyle) == B_OK) 180 fStyle = (thumb_style)sstyle; 181 else 182 fStyle = B_BLOCK_THUMB; 183 184 if (archive->FindInt32("_bcolor", (int32 *)&color) != B_OK) 185 color = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_4_TINT); 186 SetBarColor(color); 187 188 float bthickness; 189 if (archive->FindFloat("_bthickness", &bthickness) == B_OK) 190 fBarThickness = bthickness; 191 else 192 fBarThickness = 6.0f; 193 194 _InitObject(); 195 } 196 197 198 BSlider::~BSlider() 199 { 200 #if USE_OFF_SCREEN_VIEW 201 delete fOffScreenBits; 202 #endif 203 204 delete fModificationMessage; 205 free(fMinLimitLabel); 206 free(fMaxLimitLabel); 207 } 208 209 210 void 211 BSlider::_InitBarColor() 212 { 213 if (be_control_look != NULL) { 214 SetBarColor(be_control_look->SliderBarColor( 215 ui_color(B_PANEL_BACKGROUND_COLOR))); 216 } else { 217 SetBarColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 218 B_DARKEN_4_TINT)); 219 } 220 221 UseFillColor(false, NULL); 222 } 223 224 225 void 226 BSlider::_InitObject() 227 { 228 fLocation.x = 0; 229 fLocation.y = 0; 230 fInitialLocation.x = 0; 231 fInitialLocation.y = 0; 232 233 #if USE_OFF_SCREEN_VIEW 234 fOffScreenBits = NULL; 235 fOffScreenView = NULL; 236 #endif 237 238 fUpdateText = NULL; 239 fMinSize.Set(-1, -1); 240 fMaxUpdateTextWidth = -1.0; 241 } 242 243 244 BArchivable* 245 BSlider::Instantiate(BMessage *archive) 246 { 247 if (validate_instantiation(archive, "BSlider")) 248 return new BSlider(archive); 249 250 return NULL; 251 } 252 253 254 status_t 255 BSlider::Archive(BMessage *archive, bool deep) const 256 { 257 status_t ret = BControl::Archive(archive, deep); 258 259 if (ModificationMessage() && ret == B_OK) 260 ret = archive->AddMessage("_mod_msg", ModificationMessage()); 261 262 if (ret == B_OK) 263 ret = archive->AddInt32("_sdelay", fSnoozeAmount); 264 if (ret == B_OK) 265 ret = archive->AddInt32("_bcolor", (const uint32 &)fBarColor); 266 267 if (FillColor(NULL) && ret == B_OK) 268 ret = archive->AddInt32("_fcolor", (const uint32 &)fFillColor); 269 270 if (ret == B_OK && fMinLimitLabel) 271 ret = archive->AddString("_minlbl", fMinLimitLabel); 272 273 if (ret == B_OK && fMaxLimitLabel) 274 ret = archive->AddString("_maxlbl", fMaxLimitLabel); 275 276 if (ret == B_OK) 277 ret = archive->AddInt32("_min", fMinValue); 278 if (ret == B_OK) 279 ret = archive->AddInt32("_max", fMaxValue); 280 281 if (ret == B_OK) 282 ret = archive->AddInt32("_incrementvalue", fKeyIncrementValue); 283 if (ret == B_OK) 284 ret = archive->AddInt32("_hashcount", fHashMarkCount); 285 if (ret == B_OK) 286 ret = archive->AddInt16("_hashloc", fHashMarks); 287 if (ret == B_OK) 288 ret = archive->AddInt16("_sstyle", fStyle); 289 if (ret == B_OK) 290 ret = archive->AddInt32("_orient", fOrientation); 291 if (ret == B_OK) 292 ret = archive->AddFloat("_bthickness", fBarThickness); 293 294 return ret; 295 } 296 297 298 status_t 299 BSlider::Perform(perform_code code, void* _data) 300 { 301 switch (code) { 302 case PERFORM_CODE_MIN_SIZE: 303 ((perform_data_min_size*)_data)->return_value 304 = BSlider::MinSize(); 305 return B_OK; 306 case PERFORM_CODE_MAX_SIZE: 307 ((perform_data_max_size*)_data)->return_value 308 = BSlider::MaxSize(); 309 return B_OK; 310 case PERFORM_CODE_PREFERRED_SIZE: 311 ((perform_data_preferred_size*)_data)->return_value 312 = BSlider::PreferredSize(); 313 return B_OK; 314 case PERFORM_CODE_LAYOUT_ALIGNMENT: 315 ((perform_data_layout_alignment*)_data)->return_value 316 = BSlider::LayoutAlignment(); 317 return B_OK; 318 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH: 319 ((perform_data_has_height_for_width*)_data)->return_value 320 = BSlider::HasHeightForWidth(); 321 return B_OK; 322 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH: 323 { 324 perform_data_get_height_for_width* data 325 = (perform_data_get_height_for_width*)_data; 326 BSlider::GetHeightForWidth(data->width, &data->min, &data->max, 327 &data->preferred); 328 return B_OK; 329 } 330 case PERFORM_CODE_SET_LAYOUT: 331 { 332 perform_data_set_layout* data = (perform_data_set_layout*)_data; 333 BSlider::SetLayout(data->layout); 334 return B_OK; 335 } 336 case PERFORM_CODE_INVALIDATE_LAYOUT: 337 { 338 perform_data_invalidate_layout* data 339 = (perform_data_invalidate_layout*)_data; 340 BSlider::InvalidateLayout(data->descendants); 341 return B_OK; 342 } 343 case PERFORM_CODE_DO_LAYOUT: 344 { 345 BSlider::DoLayout(); 346 return B_OK; 347 } 348 } 349 350 return BControl::Perform(code, _data); 351 } 352 353 354 void 355 BSlider::WindowActivated(bool state) 356 { 357 BControl::WindowActivated(state); 358 } 359 360 361 void 362 BSlider::AttachedToWindow() 363 { 364 ResizeToPreferred(); 365 366 #if USE_OFF_SCREEN_VIEW 367 BRect bounds(Bounds()); 368 369 if (!fOffScreenView) { 370 fOffScreenView = new BView(bounds, "", B_FOLLOW_ALL, B_WILL_DRAW); 371 372 BFont font; 373 GetFont(&font); 374 fOffScreenView->SetFont(&font); 375 } 376 377 if (!fOffScreenBits) { 378 fOffScreenBits = new BBitmap(bounds, B_RGBA32, true, false); 379 380 if (fOffScreenBits && fOffScreenView) 381 fOffScreenBits->AddChild(fOffScreenView); 382 383 } else if (fOffScreenView) 384 fOffScreenBits->AddChild(fOffScreenView); 385 #endif // USE_OFF_SCREEN_VIEW 386 387 BControl::AttachedToWindow(); 388 389 BView* view = OffscreenView(); 390 if (view && view->LockLooper()) { 391 view->SetViewColor(B_TRANSPARENT_COLOR); 392 view->SetLowColor(LowColor()); 393 view->UnlockLooper(); 394 } 395 396 int32 value = Value(); 397 SetValue(value); 398 // makes sure the value is within valid bounds 399 _SetLocationForValue(Value()); 400 // makes sure the location is correct 401 UpdateTextChanged(); 402 } 403 404 405 void 406 BSlider::AllAttached() 407 { 408 BControl::AllAttached(); 409 } 410 411 412 void 413 BSlider::AllDetached() 414 { 415 BControl::AllDetached(); 416 } 417 418 419 void 420 BSlider::DetachedFromWindow() 421 { 422 BControl::DetachedFromWindow(); 423 424 #if USE_OFF_SCREEN_VIEW 425 if (fOffScreenBits) { 426 delete fOffScreenBits; 427 fOffScreenBits = NULL; 428 fOffScreenView = NULL; 429 } 430 #endif 431 } 432 433 434 void 435 BSlider::MessageReceived(BMessage *msg) 436 { 437 BControl::MessageReceived(msg); 438 } 439 440 441 void 442 BSlider::FrameMoved(BPoint new_position) 443 { 444 BControl::FrameMoved(new_position); 445 } 446 447 448 void 449 BSlider::FrameResized(float w,float h) 450 { 451 BControl::FrameResized(w, h); 452 453 BRect bounds(Bounds()); 454 455 if (bounds.right <= 0.0f || bounds.bottom <= 0.0f) 456 return; 457 458 #if USE_OFF_SCREEN_VIEW 459 if (fOffScreenBits) { 460 fOffScreenBits->RemoveChild(fOffScreenView); 461 delete fOffScreenBits; 462 463 fOffScreenView->ResizeTo(bounds.Width(), bounds.Height()); 464 465 fOffScreenBits = new BBitmap(Bounds(), B_RGBA32, true, false); 466 fOffScreenBits->AddChild(fOffScreenView); 467 } 468 #endif 469 470 Invalidate(); 471 } 472 473 474 void 475 BSlider::KeyDown(const char *bytes, int32 numBytes) 476 { 477 if (!IsEnabled() || IsHidden()) 478 return; 479 480 int32 newValue = Value(); 481 482 switch (bytes[0]) { 483 case B_LEFT_ARROW: 484 case B_DOWN_ARROW: 485 newValue -= KeyIncrementValue(); 486 break; 487 488 case B_RIGHT_ARROW: 489 case B_UP_ARROW: 490 newValue += KeyIncrementValue(); 491 break; 492 493 case B_HOME: 494 newValue = fMinValue; 495 break; 496 case B_END: 497 newValue = fMaxValue; 498 break; 499 500 default: 501 BControl::KeyDown(bytes, numBytes); 502 return; 503 } 504 505 if (newValue < fMinValue) 506 newValue = fMinValue; 507 if (newValue > fMaxValue) 508 newValue = fMaxValue; 509 510 if (newValue != Value()) { 511 fInitialLocation = _Location(); 512 SetValue(newValue); 513 InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED); 514 } 515 } 516 517 void 518 BSlider::KeyUp(const char *bytes, int32 numBytes) 519 { 520 if (fInitialLocation != _Location()) { 521 // The last KeyDown event triggered the modification message or no 522 // notification at all, we may also have sent the modification message 523 // continually while the user kept pressing the key. In either case, 524 // finish with the final message to make the behavior consistent with 525 // changing the value by mouse. 526 Invoke(); 527 } 528 } 529 530 531 /*! 532 Makes sure the \a point is within valid bounds. 533 Returns \c true if the relevant coordinate (depending on the orientation 534 of the slider) differs from \a comparePoint. 535 */ 536 bool 537 BSlider::_ConstrainPoint(BPoint& point, BPoint comparePoint) const 538 { 539 if (fOrientation == B_HORIZONTAL) { 540 if (point.x != comparePoint.x) { 541 if (point.x < _MinPosition()) 542 point.x = _MinPosition(); 543 else if (point.x > _MaxPosition()) 544 point.x = _MaxPosition(); 545 546 return true; 547 } 548 } else { 549 if (point.y != comparePoint.y) { 550 if (point.y > _MinPosition()) 551 point.y = _MinPosition(); 552 else if (point.y < _MaxPosition()) 553 point.y = _MaxPosition(); 554 555 return true; 556 } 557 } 558 559 return false; 560 } 561 562 563 void 564 BSlider::MouseDown(BPoint point) 565 { 566 if (!IsEnabled()) 567 return; 568 569 if (BarFrame().Contains(point) || ThumbFrame().Contains(point)) 570 fInitialLocation = _Location(); 571 572 uint32 buttons; 573 GetMouse(&point, &buttons, true); 574 575 _ConstrainPoint(point, fInitialLocation); 576 SetValue(ValueForPoint(point)); 577 578 if (_Location() != fInitialLocation) 579 InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED); 580 581 if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) { 582 SetTracking(true); 583 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY); 584 } else { 585 // synchronous mouse tracking 586 BPoint prevPoint; 587 588 while (buttons) { 589 prevPoint = point; 590 591 snooze(SnoozeAmount()); 592 GetMouse(&point, &buttons, true); 593 594 if (_ConstrainPoint(point, prevPoint)) { 595 int32 value = ValueForPoint(point); 596 if (value != Value()) { 597 SetValue(value); 598 InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED); 599 } 600 } 601 } 602 if (_Location() != fInitialLocation) 603 Invoke(); 604 } 605 } 606 607 608 void 609 BSlider::MouseUp(BPoint point) 610 { 611 if (IsTracking()) { 612 if (_Location() != fInitialLocation) 613 Invoke(); 614 615 SetTracking(false); 616 } else 617 BControl::MouseUp(point); 618 } 619 620 621 void 622 BSlider::MouseMoved(BPoint point, uint32 transit, const BMessage *message) 623 { 624 if (IsTracking()) { 625 if (_ConstrainPoint(point, _Location())) { 626 int32 value = ValueForPoint(point); 627 if (value != Value()) { 628 SetValue(value); 629 InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED); 630 } 631 } 632 } else 633 BControl::MouseMoved(point, transit, message); 634 } 635 636 637 void 638 BSlider::Pulse() 639 { 640 BControl::Pulse(); 641 } 642 643 644 void 645 BSlider::SetLabel(const char *label) 646 { 647 BControl::SetLabel(label); 648 } 649 650 651 void 652 BSlider::SetLimitLabels(const char *minLabel, const char *maxLabel) 653 { 654 free(fMinLimitLabel); 655 fMinLimitLabel = minLabel ? strdup(minLabel) : NULL; 656 657 free(fMaxLimitLabel); 658 fMaxLimitLabel = maxLabel ? strdup(maxLabel) : NULL; 659 660 InvalidateLayout(); 661 662 // TODO: This is for backwards compatibility and should 663 // probably be removed when breaking binary compatiblity. 664 // Applications like our own Mouse rely on this behavior. 665 if ((Flags() & B_SUPPORTS_LAYOUT) == 0) 666 ResizeToPreferred(); 667 668 Invalidate(); 669 } 670 671 672 const char* 673 BSlider::MinLimitLabel() const 674 { 675 return fMinLimitLabel; 676 } 677 678 679 const char* 680 BSlider::MaxLimitLabel() const 681 { 682 return fMaxLimitLabel; 683 } 684 685 686 void 687 BSlider::SetValue(int32 value) 688 { 689 if (value < fMinValue) 690 value = fMinValue; 691 if (value > fMaxValue) 692 value = fMaxValue; 693 694 if (value == Value()) 695 return; 696 697 _SetLocationForValue(value); 698 699 BRect oldThumbFrame = ThumbFrame(); 700 701 // While it would be enough to do this dependent on fUseFillColor, 702 // that doesn't work out if DrawBar() has been overridden by a sub class 703 if (fOrientation == B_HORIZONTAL) 704 oldThumbFrame.top = BarFrame().top; 705 else 706 oldThumbFrame.right = BarFrame().right; 707 708 BControl::SetValueNoUpdate(value); 709 BRect invalid = oldThumbFrame | ThumbFrame(); 710 711 if (Style() == B_TRIANGLE_THUMB) { 712 // 1) we need to take care of pixels touched because of 713 // anti-aliasing 714 // 2) we need to update the region with the focus mark as well 715 // (a method BSlider::FocusMarkFrame() would be nice as well) 716 if (fOrientation == B_HORIZONTAL) { 717 if (IsFocus()) 718 invalid.bottom += 2; 719 invalid.InsetBy(-1, 0); 720 } else { 721 if (IsFocus()) 722 invalid.left -= 2; 723 invalid.InsetBy(0, -1); 724 } 725 } 726 727 Invalidate(invalid); 728 729 UpdateTextChanged(); 730 } 731 732 733 int32 734 BSlider::ValueForPoint(BPoint location) const 735 { 736 float min; 737 float max; 738 float position; 739 if (fOrientation == B_HORIZONTAL) { 740 min = _MinPosition(); 741 max = _MaxPosition(); 742 position = location.x; 743 } else { 744 max = _MinPosition(); 745 min = _MaxPosition(); 746 position = min + (max - location.y); 747 } 748 749 if (position < min) 750 position = min; 751 if (position > max) 752 position = max; 753 754 return (int32)roundf(((position - min) * (fMaxValue - fMinValue) / (max - min)) + fMinValue); 755 } 756 757 758 void 759 BSlider::SetPosition(float position) 760 { 761 if (position <= 0.0f) 762 BControl::SetValue(fMinValue); 763 else if (position >= 1.0f) 764 BControl::SetValue(fMaxValue); 765 else 766 BControl::SetValue((int32)(position * (fMaxValue - fMinValue) + fMinValue)); 767 } 768 769 770 float 771 BSlider::Position() const 772 { 773 float range = (float)(fMaxValue - fMinValue); 774 if (range == 0.0f) 775 range = 1.0f; 776 777 return (float)(Value() - fMinValue) / range; 778 } 779 780 781 void 782 BSlider::SetEnabled(bool on) 783 { 784 BControl::SetEnabled(on); 785 } 786 787 788 void 789 BSlider::GetLimits(int32 *minimum, int32 *maximum) const 790 { 791 if (minimum != NULL) 792 *minimum = fMinValue; 793 if (maximum != NULL) 794 *maximum = fMaxValue; 795 } 796 797 798 // #pragma mark - drawing 799 800 801 void 802 BSlider::Draw(BRect updateRect) 803 { 804 // clear out background 805 BRegion background(updateRect); 806 background.Exclude(BarFrame()); 807 bool drawBackground = true; 808 if (Parent() && (Parent()->Flags() & B_DRAW_ON_CHILDREN) != 0) { 809 // This view is embedded somewhere, most likely the Tracker Desktop 810 // shelf. 811 drawBackground = false; 812 } 813 814 // ToDo: the triangle thumb doesn't delete its background, so we still have 815 // to do it Note, this also creates a different behaviour for subclasses, 816 // depending on the thumb style - if possible this should be avoided. 817 if (Style() == B_BLOCK_THUMB) { 818 BRect thumbFrame = ThumbFrame(); 819 if (be_control_look != NULL) { 820 // fill background where shadow will be... 821 // TODO: Such drawint dependent behavior should be moved into 822 // BControlLook of course. 823 thumbFrame.right--; 824 thumbFrame.bottom--; 825 } 826 background.Exclude(thumbFrame); 827 } 828 829 #if USE_OFF_SCREEN_VIEW 830 if (!fOffScreenBits) 831 return; 832 833 if (fOffScreenBits->Lock()) { 834 fOffScreenView->SetViewColor(ViewColor()); 835 fOffScreenView->SetLowColor(LowColor()); 836 #endif 837 838 if (drawBackground && background.Frame().IsValid()) 839 OffscreenView()->FillRegion(&background, B_SOLID_LOW); 840 841 #if USE_OFF_SCREEN_VIEW 842 fOffScreenView->Sync(); 843 fOffScreenBits->Unlock(); 844 } 845 #endif 846 847 DrawSlider(); 848 } 849 850 851 void 852 BSlider::DrawSlider() 853 { 854 if (LockLooper()) { 855 #if USE_OFF_SCREEN_VIEW 856 if (!fOffScreenBits) 857 return; 858 if (fOffScreenBits->Lock()) { 859 #endif 860 DrawBar(); 861 DrawHashMarks(); 862 DrawThumb(); 863 DrawFocusMark(); 864 DrawText(); 865 866 #if USE_OFF_SCREEN_VIEW 867 fOffScreenView->Sync(); 868 fOffScreenBits->Unlock(); 869 870 DrawBitmap(fOffScreenBits, B_ORIGIN); 871 } 872 #endif 873 UnlockLooper(); 874 } 875 } 876 877 878 void 879 BSlider::DrawBar() 880 { 881 BRect frame = BarFrame(); 882 BView *view = OffscreenView(); 883 884 if (be_control_look != NULL) { 885 uint32 flags = be_control_look->Flags(this); 886 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 887 rgb_color rightFillColor = fBarColor; 888 rgb_color leftFillColor = fUseFillColor ? fFillColor : fBarColor; 889 be_control_look->DrawSliderBar(view, frame, frame, base, leftFillColor, 890 rightFillColor, Position(), flags, fOrientation); 891 return; 892 } 893 894 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR); 895 rgb_color lightenmax; 896 rgb_color darken1; 897 rgb_color darken2; 898 rgb_color darkenmax; 899 900 rgb_color barColor; 901 rgb_color fillColor; 902 903 if (IsEnabled()) { 904 lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT); 905 darken1 = tint_color(no_tint, B_DARKEN_1_TINT); 906 darken2 = tint_color(no_tint, B_DARKEN_2_TINT); 907 darkenmax = tint_color(no_tint, B_DARKEN_MAX_TINT); 908 barColor = fBarColor; 909 fillColor = fFillColor; 910 } else { 911 lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT); 912 darken1 = no_tint; 913 darken2 = tint_color(no_tint, B_DARKEN_1_TINT); 914 darkenmax = tint_color(no_tint, B_DARKEN_3_TINT); 915 916 barColor.red = (fBarColor.red + no_tint.red) / 2; 917 barColor.green = (fBarColor.green + no_tint.green) / 2; 918 barColor.blue = (fBarColor.blue + no_tint.blue) / 2; 919 barColor.alpha = 255; 920 921 fillColor.red = (fFillColor.red + no_tint.red) / 2; 922 fillColor.green = (fFillColor.green + no_tint.green) / 2; 923 fillColor.blue = (fFillColor.blue + no_tint.blue) / 2; 924 fillColor.alpha = 255; 925 } 926 927 // exclude the block thumb from the bar filling 928 929 BRect lowerFrame = frame.InsetByCopy(1, 1); 930 lowerFrame.top++; 931 lowerFrame.left++; 932 BRect upperFrame = lowerFrame; 933 BRect thumbFrame; 934 935 if (Style() == B_BLOCK_THUMB) { 936 thumbFrame = ThumbFrame(); 937 938 if (fOrientation == B_HORIZONTAL) { 939 lowerFrame.right = thumbFrame.left; 940 upperFrame.left = thumbFrame.right; 941 } else { 942 lowerFrame.top = thumbFrame.bottom; 943 upperFrame.bottom = thumbFrame.top; 944 } 945 } else if (fUseFillColor) { 946 if (fOrientation == B_HORIZONTAL) { 947 lowerFrame.right = floor(lowerFrame.left - 1 + Position() 948 * (lowerFrame.Width() + 1)); 949 upperFrame.left = lowerFrame.right; 950 } else { 951 lowerFrame.top = floor(lowerFrame.bottom + 1 - Position() 952 * (lowerFrame.Height() + 1)); 953 upperFrame.bottom = lowerFrame.top; 954 } 955 } 956 957 view->SetHighColor(barColor); 958 view->FillRect(upperFrame); 959 960 if (Style() == B_BLOCK_THUMB || fUseFillColor) { 961 if (fUseFillColor) 962 view->SetHighColor(fillColor); 963 view->FillRect(lowerFrame); 964 } 965 966 if (Style() == B_BLOCK_THUMB) { 967 // We don't want to stroke the lines over the thumb 968 969 PushState(); 970 971 BRegion region; 972 GetClippingRegion(®ion); 973 region.Exclude(thumbFrame); 974 ConstrainClippingRegion(®ion); 975 } 976 977 view->SetHighColor(darken1); 978 view->StrokeLine(BPoint(frame.left, frame.top), 979 BPoint(frame.left + 1.0f, frame.top)); 980 view->StrokeLine(BPoint(frame.left, frame.bottom), 981 BPoint(frame.left + 1.0f, frame.bottom)); 982 view->StrokeLine(BPoint(frame.right - 1.0f, frame.top), 983 BPoint(frame.right, frame.top)); 984 985 view->SetHighColor(darken2); 986 view->StrokeLine(BPoint(frame.left + 1.0f, frame.top), 987 BPoint(frame.right - 1.0f, frame.top)); 988 view->StrokeLine(BPoint(frame.left, frame.bottom - 1.0f), 989 BPoint(frame.left, frame.top + 1.0f)); 990 991 view->SetHighColor(lightenmax); 992 view->StrokeLine(BPoint(frame.left + 1.0f, frame.bottom), 993 BPoint(frame.right, frame.bottom)); 994 view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f), 995 BPoint(frame.right, frame.top + 1.0f)); 996 997 frame.InsetBy(1.0f, 1.0f); 998 999 view->SetHighColor(darkenmax); 1000 view->StrokeLine(BPoint(frame.left, frame.bottom), 1001 BPoint(frame.left, frame.top)); 1002 view->StrokeLine(BPoint(frame.left + 1.0f, frame.top), 1003 BPoint(frame.right, frame.top)); 1004 1005 if (Style() == B_BLOCK_THUMB) 1006 PopState(); 1007 } 1008 1009 1010 void 1011 BSlider::DrawHashMarks() 1012 { 1013 if (fHashMarks == B_HASH_MARKS_NONE) 1014 return; 1015 1016 BRect frame = HashMarksFrame(); 1017 BView* view = OffscreenView(); 1018 1019 if (be_control_look) { 1020 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 1021 uint32 flags = be_control_look->Flags(this); 1022 be_control_look->DrawSliderHashMarks(view, frame, frame, base, 1023 fHashMarkCount, fHashMarks, flags, fOrientation); 1024 return; 1025 } 1026 1027 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR); 1028 rgb_color lightenmax; 1029 rgb_color darken2; 1030 1031 if (IsEnabled()) { 1032 lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT); 1033 darken2 = tint_color(no_tint, B_DARKEN_2_TINT); 1034 } else { 1035 lightenmax = tint_color(no_tint, B_LIGHTEN_2_TINT); 1036 darken2 = tint_color(no_tint, B_DARKEN_1_TINT); 1037 } 1038 1039 float pos = _MinPosition(); 1040 int32 hashMarkCount = max_c(fHashMarkCount, 2); 1041 // draw at least two hashmarks at min/max if 1042 // fHashMarks != B_HASH_MARKS_NONE 1043 float factor = (_MaxPosition() - pos) / (hashMarkCount - 1); 1044 1045 if (fHashMarks & B_HASH_MARKS_TOP) { 1046 1047 view->BeginLineArray(hashMarkCount * 2); 1048 1049 if (fOrientation == B_HORIZONTAL) { 1050 for (int32 i = 0; i < hashMarkCount; i++) { 1051 view->AddLine(BPoint(pos, frame.top), 1052 BPoint(pos, frame.top + 5), darken2); 1053 view->AddLine(BPoint(pos + 1, frame.top), 1054 BPoint(pos + 1, frame.top + 5), lightenmax); 1055 1056 pos += factor; 1057 } 1058 } else { 1059 for (int32 i = 0; i < hashMarkCount; i++) { 1060 view->AddLine(BPoint(frame.left, pos), 1061 BPoint(frame.left + 5, pos), darken2); 1062 view->AddLine(BPoint(frame.left, pos + 1), 1063 BPoint(frame.left + 5, pos + 1), lightenmax); 1064 1065 pos += factor; 1066 } 1067 } 1068 1069 view->EndLineArray(); 1070 } 1071 1072 pos = _MinPosition(); 1073 1074 if (fHashMarks & B_HASH_MARKS_BOTTOM) { 1075 1076 view->BeginLineArray(hashMarkCount * 2); 1077 1078 if (fOrientation == B_HORIZONTAL) { 1079 for (int32 i = 0; i < hashMarkCount; i++) { 1080 view->AddLine(BPoint(pos, frame.bottom - 5), 1081 BPoint(pos, frame.bottom), darken2); 1082 view->AddLine(BPoint(pos + 1, frame.bottom - 5), 1083 BPoint(pos + 1, frame.bottom), lightenmax); 1084 1085 pos += factor; 1086 } 1087 } else { 1088 for (int32 i = 0; i < hashMarkCount; i++) { 1089 view->AddLine(BPoint(frame.right - 5, pos), 1090 BPoint(frame.right, pos), darken2); 1091 view->AddLine(BPoint(frame.right - 5, pos + 1), 1092 BPoint(frame.right, pos + 1), lightenmax); 1093 1094 pos += factor; 1095 } 1096 } 1097 1098 view->EndLineArray(); 1099 } 1100 } 1101 1102 1103 void 1104 BSlider::DrawThumb() 1105 { 1106 if (Style() == B_BLOCK_THUMB) 1107 _DrawBlockThumb(); 1108 else 1109 _DrawTriangleThumb(); 1110 } 1111 1112 1113 void 1114 BSlider::DrawFocusMark() 1115 { 1116 if (!IsFocus()) 1117 return; 1118 1119 OffscreenView()->SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 1120 1121 BRect frame = ThumbFrame(); 1122 1123 if (fStyle == B_BLOCK_THUMB) { 1124 frame.left += 2.0f; 1125 frame.top += 2.0f; 1126 frame.right -= 3.0f; 1127 frame.bottom -= 3.0f; 1128 OffscreenView()->StrokeRect(frame); 1129 } else { 1130 if (fOrientation == B_HORIZONTAL) { 1131 OffscreenView()->StrokeLine(BPoint(frame.left, frame.bottom + 2.0f), 1132 BPoint(frame.right, frame.bottom + 2.0f)); 1133 } else { 1134 OffscreenView()->StrokeLine(BPoint(frame.left - 2.0f, frame.top), 1135 BPoint(frame.left - 2.0f, frame.bottom)); 1136 } 1137 } 1138 } 1139 1140 1141 void 1142 BSlider::DrawText() 1143 { 1144 BRect bounds(Bounds()); 1145 BView *view = OffscreenView(); 1146 1147 rgb_color base = LowColor(); 1148 uint32 flags = 0; 1149 if (be_control_look == NULL) { 1150 if (IsEnabled()) { 1151 view->SetHighColor(0, 0, 0); 1152 } else { 1153 view->SetHighColor(tint_color(LowColor(), B_DISABLED_LABEL_TINT)); 1154 } 1155 } else 1156 flags = be_control_look->Flags(this); 1157 1158 font_height fontHeight; 1159 GetFontHeight(&fontHeight); 1160 if (Orientation() == B_HORIZONTAL) { 1161 if (Label()) { 1162 if (be_control_look == NULL) { 1163 view->DrawString(Label(), 1164 BPoint(0.0, ceilf(fontHeight.ascent))); 1165 } else { 1166 be_control_look->DrawLabel(view, Label(), base, flags, 1167 BPoint(0.0, ceilf(fontHeight.ascent))); 1168 } 1169 } 1170 1171 // the update text is updated in SetValue() only 1172 if (fUpdateText != NULL) { 1173 if (be_control_look == NULL) { 1174 view->DrawString(fUpdateText, BPoint(bounds.right 1175 - StringWidth(fUpdateText), ceilf(fontHeight.ascent))); 1176 } else { 1177 be_control_look->DrawLabel(view, fUpdateText, base, flags, 1178 BPoint(bounds.right - StringWidth(fUpdateText), 1179 ceilf(fontHeight.ascent))); 1180 } 1181 } 1182 1183 if (fMinLimitLabel) { 1184 if (be_control_look == NULL) { 1185 view->DrawString(fMinLimitLabel, BPoint(0.0, bounds.bottom 1186 - fontHeight.descent)); 1187 } else { 1188 be_control_look->DrawLabel(view, fMinLimitLabel, base, flags, 1189 BPoint(0.0, bounds.bottom - fontHeight.descent)); 1190 } 1191 } 1192 1193 if (fMaxLimitLabel) { 1194 if (be_control_look == NULL) { 1195 view->DrawString(fMaxLimitLabel, BPoint(bounds.right 1196 - StringWidth(fMaxLimitLabel), bounds.bottom 1197 - fontHeight.descent)); 1198 } else { 1199 be_control_look->DrawLabel(view, fMaxLimitLabel, base, flags, 1200 BPoint(bounds.right - StringWidth(fMaxLimitLabel), 1201 bounds.bottom - fontHeight.descent)); 1202 } 1203 } 1204 } else { 1205 float lineHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent) 1206 + ceilf(fontHeight.leading); 1207 float baseLine = ceilf(fontHeight.ascent); 1208 1209 if (Label()) { 1210 if (be_control_look == NULL) { 1211 view->DrawString(Label(), BPoint((bounds.Width() 1212 - StringWidth(Label())) / 2.0, baseLine)); 1213 } else { 1214 be_control_look->DrawLabel(view, Label(), base, flags, 1215 BPoint((bounds.Width() - StringWidth(Label())) / 2.0, 1216 baseLine)); 1217 } 1218 baseLine += lineHeight; 1219 } 1220 1221 if (fMaxLimitLabel) { 1222 if (be_control_look == NULL) { 1223 view->DrawString(fMaxLimitLabel, BPoint((bounds.Width() 1224 - StringWidth(fMaxLimitLabel)) / 2.0, baseLine)); 1225 } else { 1226 be_control_look->DrawLabel(view, fMaxLimitLabel, base, flags, 1227 BPoint((bounds.Width() 1228 - StringWidth(fMaxLimitLabel)) / 2.0, baseLine)); 1229 } 1230 } 1231 1232 baseLine = bounds.bottom - ceilf(fontHeight.descent); 1233 1234 if (fMinLimitLabel) { 1235 if (be_control_look == NULL) { 1236 view->DrawString(fMinLimitLabel, BPoint((bounds.Width() 1237 - StringWidth(fMinLimitLabel)) / 2.0, baseLine)); 1238 } else { 1239 be_control_look->DrawLabel(view, fMinLimitLabel, base, flags, 1240 BPoint((bounds.Width() 1241 - StringWidth(fMinLimitLabel)) / 2.0, baseLine)); 1242 } 1243 baseLine -= lineHeight; 1244 } 1245 1246 if (fUpdateText != NULL) { 1247 if (be_control_look == NULL) { 1248 view->DrawString(fUpdateText, BPoint((bounds.Width() 1249 - StringWidth(fUpdateText)) / 2.0, baseLine)); 1250 } else { 1251 be_control_look->DrawLabel(view, fUpdateText, base, flags, 1252 BPoint((bounds.Width() 1253 - StringWidth(fUpdateText)) / 2.0, baseLine)); 1254 } 1255 } 1256 } 1257 } 1258 1259 1260 // #pragma mark - 1261 1262 1263 const char* 1264 BSlider::UpdateText() const 1265 { 1266 return NULL; 1267 } 1268 1269 1270 void 1271 BSlider::UpdateTextChanged() 1272 { 1273 // update text label 1274 float oldWidth = 0.0; 1275 if (fUpdateText != NULL) 1276 oldWidth = StringWidth(fUpdateText); 1277 1278 const char* oldUpdateText = fUpdateText; 1279 fUpdateText = UpdateText(); 1280 bool updateTextOnOff = (fUpdateText == NULL && oldUpdateText != NULL) 1281 || (fUpdateText != NULL && oldUpdateText == NULL); 1282 1283 float newWidth = 0.0; 1284 if (fUpdateText != NULL) 1285 newWidth = StringWidth(fUpdateText); 1286 1287 float width = ceilf(max_c(newWidth, oldWidth)) + 2.0f; 1288 if (width != 0) { 1289 font_height fontHeight; 1290 GetFontHeight(&fontHeight); 1291 1292 float height = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent); 1293 float lineHeight = height + ceilf(fontHeight.leading); 1294 BRect invalid(Bounds()); 1295 if (fOrientation == B_HORIZONTAL) 1296 invalid = BRect(invalid.right - width, 0, invalid.right, height); 1297 else { 1298 if (!updateTextOnOff) { 1299 invalid.left = (invalid.left + invalid.right - width) / 2; 1300 invalid.right = invalid.left + width; 1301 if (fMinLimitLabel) 1302 invalid.bottom -= lineHeight; 1303 invalid.top = invalid.bottom - height; 1304 } 1305 } 1306 Invalidate(invalid); 1307 } 1308 1309 float oldMaxUpdateTextWidth = fMaxUpdateTextWidth; 1310 fMaxUpdateTextWidth = MaxUpdateTextWidth(); 1311 if (oldMaxUpdateTextWidth != fMaxUpdateTextWidth) 1312 InvalidateLayout(); 1313 } 1314 1315 1316 BRect 1317 BSlider::BarFrame() const 1318 { 1319 BRect frame(Bounds()); 1320 1321 font_height fontHeight; 1322 GetFontHeight(&fontHeight); 1323 1324 float textHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent); 1325 float leading = ceilf(fontHeight.leading); 1326 1327 float thumbInset; 1328 if (fStyle == B_BLOCK_THUMB) 1329 thumbInset = 8.0; 1330 else 1331 thumbInset = 7.0; 1332 1333 if (Orientation() == B_HORIZONTAL) { 1334 frame.left = thumbInset; 1335 frame.top = 6.0 + (Label() || fUpdateText ? textHeight + 4.0 : 0.0); 1336 frame.right -= thumbInset; 1337 frame.bottom = frame.top + fBarThickness; 1338 } else { 1339 frame.left = floorf((frame.Width() - fBarThickness) / 2.0); 1340 frame.top = thumbInset; 1341 if (Label()) 1342 frame.top += textHeight; 1343 if (fMaxLimitLabel) { 1344 frame.top += textHeight; 1345 if (Label()) 1346 frame.top += leading; 1347 } 1348 1349 frame.right = frame.left + fBarThickness; 1350 frame.bottom = frame.bottom - thumbInset; 1351 if (fMinLimitLabel) 1352 frame.bottom -= textHeight; 1353 if (fUpdateText) { 1354 frame.bottom -= textHeight; 1355 if (fMinLimitLabel) 1356 frame.bottom -= leading; 1357 } 1358 } 1359 1360 return frame; 1361 } 1362 1363 1364 BRect 1365 BSlider::HashMarksFrame() const 1366 { 1367 BRect frame(BarFrame()); 1368 1369 if (fOrientation == B_HORIZONTAL) { 1370 frame.top -= 6.0; 1371 frame.bottom += 6.0; 1372 } else { 1373 frame.left -= 6.0; 1374 frame.right += 6.0; 1375 } 1376 1377 return frame; 1378 } 1379 1380 1381 BRect 1382 BSlider::ThumbFrame() const 1383 { 1384 // TODO: The slider looks really ugly and broken when it is too little. 1385 // I would suggest using BarFrame() here to get the top and bottom coords 1386 // and spread them further apart for the thumb 1387 1388 BRect frame = Bounds(); 1389 1390 font_height fontHeight; 1391 GetFontHeight(&fontHeight); 1392 1393 float textHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent); 1394 1395 if (fStyle == B_BLOCK_THUMB) { 1396 if (Orientation() == B_HORIZONTAL) { 1397 frame.left = floorf(Position() * (_MaxPosition() 1398 - _MinPosition()) + _MinPosition()) - 8; 1399 frame.top = 2 + (Label() || fUpdateText ? textHeight + 4 : 0); 1400 frame.right = frame.left + 17; 1401 frame.bottom = frame.top + fBarThickness + 7; 1402 } else { 1403 frame.left = floor((frame.Width() - fBarThickness) / 2) - 4; 1404 frame.top = floorf(Position() * (_MaxPosition() 1405 - _MinPosition()) + _MinPosition()) - 8; 1406 frame.right = frame.left + fBarThickness + 7; 1407 frame.bottom = frame.top + 17; 1408 } 1409 } else { 1410 if (Orientation() == B_HORIZONTAL) { 1411 frame.left = floorf(Position() * (_MaxPosition() 1412 - _MinPosition()) + _MinPosition()) - 6; 1413 frame.right = frame.left + 12; 1414 frame.top = 3 + fBarThickness + (Label() ? textHeight + 4 : 0); 1415 frame.bottom = frame.top + 8; 1416 } else { 1417 frame.left = floorf((frame.Width() + fBarThickness) / 2) - 3; 1418 frame.top = floorf(Position() * (_MaxPosition() 1419 - _MinPosition())) + _MinPosition() - 6; 1420 frame.right = frame.left + 8; 1421 frame.bottom = frame.top + 12; 1422 } 1423 } 1424 1425 return frame; 1426 } 1427 1428 1429 void 1430 BSlider::SetFlags(uint32 flags) 1431 { 1432 BControl::SetFlags(flags); 1433 } 1434 1435 1436 void 1437 BSlider::SetResizingMode(uint32 mode) 1438 { 1439 BControl::SetResizingMode(mode); 1440 } 1441 1442 1443 void 1444 BSlider::GetPreferredSize(float* _width, float* _height) 1445 { 1446 BSize preferredSize = PreferredSize(); 1447 1448 if (_width) { 1449 // *_width = preferredSize.width; 1450 // NOTE: For compatibility reasons, the BSlider never shrinks 1451 // horizontally. This only affects applications which do not 1452 // use the new layout system. 1453 *_width = max_c(Bounds().Width(), preferredSize.width); 1454 } 1455 1456 if (_height) 1457 *_height = preferredSize.height; 1458 } 1459 1460 1461 void 1462 BSlider::ResizeToPreferred() 1463 { 1464 BControl::ResizeToPreferred(); 1465 } 1466 1467 1468 status_t 1469 BSlider::Invoke(BMessage* message) 1470 { 1471 return BControl::Invoke(message); 1472 } 1473 1474 1475 BHandler* 1476 BSlider::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier, 1477 int32 command, const char *property) 1478 { 1479 return BControl::ResolveSpecifier(message, index, specifier, command, 1480 property); 1481 } 1482 1483 1484 status_t 1485 BSlider::GetSupportedSuites(BMessage* message) 1486 { 1487 return BControl::GetSupportedSuites(message); 1488 } 1489 1490 1491 void 1492 BSlider::SetModificationMessage(BMessage* message) 1493 { 1494 delete fModificationMessage; 1495 fModificationMessage = message; 1496 } 1497 1498 1499 BMessage* 1500 BSlider::ModificationMessage() const 1501 { 1502 return fModificationMessage; 1503 } 1504 1505 1506 void 1507 BSlider::SetSnoozeAmount(int32 snoozeTime) 1508 { 1509 if (snoozeTime < 10000) 1510 snoozeTime = 10000; 1511 else if (snoozeTime > 1000000) 1512 snoozeTime = 1000000; 1513 1514 fSnoozeAmount = snoozeTime; 1515 } 1516 1517 1518 int32 1519 BSlider::SnoozeAmount() const 1520 { 1521 return fSnoozeAmount; 1522 } 1523 1524 1525 void 1526 BSlider::SetKeyIncrementValue(int32 incrementValue) 1527 { 1528 fKeyIncrementValue = incrementValue; 1529 } 1530 1531 1532 int32 1533 BSlider::KeyIncrementValue() const 1534 { 1535 return fKeyIncrementValue; 1536 } 1537 1538 1539 void 1540 BSlider::SetHashMarkCount(int32 hashMarkCount) 1541 { 1542 fHashMarkCount = hashMarkCount; 1543 Invalidate(); 1544 } 1545 1546 1547 int32 1548 BSlider::HashMarkCount() const 1549 { 1550 return fHashMarkCount; 1551 } 1552 1553 1554 void 1555 BSlider::SetHashMarks(hash_mark_location where) 1556 { 1557 fHashMarks = where; 1558 // TODO: enable if the hashmark look is influencing the control size! 1559 // InvalidateLayout(); 1560 Invalidate(); 1561 } 1562 1563 1564 hash_mark_location 1565 BSlider::HashMarks() const 1566 { 1567 return fHashMarks; 1568 } 1569 1570 1571 void 1572 BSlider::SetStyle(thumb_style style) 1573 { 1574 fStyle = style; 1575 InvalidateLayout(); 1576 Invalidate(); 1577 } 1578 1579 1580 thumb_style 1581 BSlider::Style() const 1582 { 1583 return fStyle; 1584 } 1585 1586 1587 void 1588 BSlider::SetBarColor(rgb_color barColor) 1589 { 1590 fBarColor = barColor; 1591 Invalidate(BarFrame()); 1592 } 1593 1594 1595 rgb_color 1596 BSlider::BarColor() const 1597 { 1598 return fBarColor; 1599 } 1600 1601 1602 void 1603 BSlider::UseFillColor(bool useFill, const rgb_color* barColor) 1604 { 1605 fUseFillColor = useFill; 1606 1607 if (useFill && barColor) 1608 fFillColor = *barColor; 1609 1610 Invalidate(BarFrame()); 1611 } 1612 1613 1614 bool 1615 BSlider::FillColor(rgb_color* barColor) const 1616 { 1617 if (barColor && fUseFillColor) 1618 *barColor = fFillColor; 1619 1620 return fUseFillColor; 1621 } 1622 1623 1624 BView* 1625 BSlider::OffscreenView() const 1626 { 1627 #if USE_OFF_SCREEN_VIEW 1628 return fOffScreenView; 1629 #else 1630 return (BView*)this; 1631 #endif 1632 } 1633 1634 1635 orientation 1636 BSlider::Orientation() const 1637 { 1638 return fOrientation; 1639 } 1640 1641 1642 void 1643 BSlider::SetOrientation(orientation posture) 1644 { 1645 if (fOrientation == posture) 1646 return; 1647 1648 fOrientation = posture; 1649 InvalidateLayout(); 1650 Invalidate(); 1651 } 1652 1653 1654 float 1655 BSlider::BarThickness() const 1656 { 1657 return fBarThickness; 1658 } 1659 1660 1661 void 1662 BSlider::SetBarThickness(float thickness) 1663 { 1664 if (thickness < 1.0) 1665 thickness = 1.0; 1666 else 1667 thickness = roundf(thickness); 1668 1669 if (thickness != fBarThickness) { 1670 // calculate invalid barframe and extend by hashmark size 1671 float hInset = 0.0; 1672 float vInset = 0.0; 1673 if (fOrientation == B_HORIZONTAL) 1674 vInset = -6.0; 1675 else 1676 hInset = -6.0; 1677 BRect invalid = BarFrame().InsetByCopy(hInset, vInset) | ThumbFrame(); 1678 1679 fBarThickness = thickness; 1680 1681 invalid = invalid | BarFrame().InsetByCopy(hInset, vInset) 1682 | ThumbFrame(); 1683 Invalidate(invalid); 1684 InvalidateLayout(); 1685 } 1686 } 1687 1688 1689 void 1690 BSlider::SetFont(const BFont *font, uint32 properties) 1691 { 1692 BControl::SetFont(font, properties); 1693 1694 #if USE_OFF_SCREEN_VIEW 1695 if (fOffScreenView && fOffScreenBits) { 1696 if (fOffScreenBits->Lock()) { 1697 fOffScreenView->SetFont(font, properties); 1698 fOffScreenBits->Unlock(); 1699 } 1700 } 1701 #endif 1702 1703 InvalidateLayout(); 1704 } 1705 1706 1707 void 1708 BSlider::SetLimits(int32 minimum, int32 maximum) 1709 { 1710 if (minimum <= maximum) { 1711 fMinValue = minimum; 1712 fMaxValue = maximum; 1713 1714 int32 value = Value(); 1715 value = max_c(minimum, value); 1716 value = min_c(maximum, value); 1717 1718 if (value != Value()) { 1719 SetValue(value); 1720 } 1721 } 1722 } 1723 1724 1725 float 1726 BSlider::MaxUpdateTextWidth() 1727 { 1728 // very simplistic implementation that assumes the string will be widest 1729 // at the maximum value 1730 int32 value = Value(); 1731 SetValueNoUpdate(fMaxValue); 1732 float width = StringWidth(UpdateText()); 1733 SetValueNoUpdate(value); 1734 // in case the derived class uses a fixed buffer, the contents 1735 // should be reset for the old value 1736 UpdateText(); 1737 return width; 1738 } 1739 1740 1741 // #pragma mark - layout related 1742 1743 1744 void 1745 BSlider::InvalidateLayout(bool descendants) 1746 { 1747 // invalidate cached preferred size 1748 fMinSize.Set(-1, -1); 1749 1750 BControl::InvalidateLayout(descendants); 1751 } 1752 1753 1754 BSize 1755 BSlider::MinSize() 1756 { 1757 return BLayoutUtils::ComposeSize(ExplicitMinSize(), 1758 _ValidateMinSize()); 1759 } 1760 1761 1762 BSize 1763 BSlider::MaxSize() 1764 { 1765 BSize maxSize = _ValidateMinSize(); 1766 if (fOrientation == B_HORIZONTAL) 1767 maxSize.width = B_SIZE_UNLIMITED; 1768 else 1769 maxSize.height = B_SIZE_UNLIMITED; 1770 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), maxSize); 1771 } 1772 1773 1774 BSize 1775 BSlider::PreferredSize() 1776 { 1777 BSize preferredSize = _ValidateMinSize(); 1778 if (fOrientation == B_HORIZONTAL) 1779 preferredSize.width = max_c(100.0, preferredSize.width); 1780 else 1781 preferredSize.height = max_c(100.0, preferredSize.height); 1782 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), preferredSize); 1783 } 1784 1785 1786 // #pragma mark - private 1787 1788 void 1789 BSlider::_DrawBlockThumb() 1790 { 1791 BRect frame = ThumbFrame(); 1792 BView *view = OffscreenView(); 1793 1794 if (be_control_look != NULL) { 1795 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 1796 uint32 flags = be_control_look->Flags(this); 1797 be_control_look->DrawSliderThumb(view, frame, frame, base, flags, 1798 fOrientation); 1799 return; 1800 } 1801 1802 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR); 1803 rgb_color lighten2; 1804 rgb_color lighten1; 1805 rgb_color darken2; 1806 rgb_color darken3; 1807 rgb_color darkenmax; 1808 1809 if (IsEnabled()) { 1810 lighten2 = tint_color(no_tint, B_LIGHTEN_2_TINT); 1811 lighten1 = no_tint; 1812 darken2 = tint_color(no_tint, B_DARKEN_2_TINT); 1813 darken3 = tint_color(no_tint, B_DARKEN_3_TINT); 1814 darkenmax = tint_color(no_tint, B_DARKEN_MAX_TINT); 1815 } else { 1816 lighten2 = tint_color(no_tint, B_LIGHTEN_2_TINT); 1817 lighten1 = tint_color(no_tint, B_LIGHTEN_1_TINT); 1818 darken2 = tint_color(no_tint, (B_NO_TINT + B_DARKEN_1_TINT) / 2.0); 1819 darken3 = tint_color(no_tint, B_DARKEN_1_TINT); 1820 darkenmax = tint_color(no_tint, B_DARKEN_3_TINT); 1821 } 1822 1823 // blank background for shadow 1824 // ToDo: this also draws over the hash marks (though it's not *that* noticeable) 1825 view->SetHighColor(no_tint); 1826 view->StrokeLine(BPoint(frame.left, frame.top), 1827 BPoint(frame.left, frame.top)); 1828 1829 BRect barFrame = BarFrame(); 1830 if (barFrame.right >= frame.right) { 1831 // leave out barFrame from shadow background clearing 1832 view->StrokeLine(BPoint(frame.right, frame.top), 1833 BPoint(frame.right, barFrame.top - 1.0f)); 1834 view->StrokeLine(BPoint(frame.right, barFrame.bottom + 1.0f), 1835 BPoint(frame.right, frame.bottom)); 1836 } else { 1837 view->StrokeLine(BPoint(frame.right, frame.top), 1838 BPoint(frame.right, frame.bottom)); 1839 } 1840 1841 view->StrokeLine(BPoint(frame.left, frame.bottom), 1842 BPoint(frame.right - 1.0f, frame.bottom)); 1843 view->StrokeLine(BPoint(frame.left, frame.bottom - 1.0f), 1844 BPoint(frame.left, frame.bottom - 1.0f)); 1845 view->StrokeLine(BPoint(frame.right - 1.0f, frame.top), 1846 BPoint(frame.right - 1.0f, frame.top)); 1847 1848 // Outline (top, left) 1849 view->SetHighColor(darken3); 1850 view->StrokeLine(BPoint(frame.left, frame.bottom - 2.0f), 1851 BPoint(frame.left, frame.top + 1.0f)); 1852 view->StrokeLine(BPoint(frame.left + 1.0f, frame.top), 1853 BPoint(frame.right - 2.0f, frame.top)); 1854 1855 // Shadow 1856 view->SetHighColor(0, 0, 0, IsEnabled() ? 100 : 50); 1857 view->SetDrawingMode(B_OP_ALPHA); 1858 view->StrokeLine(BPoint(frame.right, frame.top + 2.0f), 1859 BPoint(frame.right, frame.bottom - 1.0f)); 1860 view->StrokeLine(BPoint(frame.left + 2.0f, frame.bottom), 1861 BPoint(frame.right - 1.0f, frame.bottom)); 1862 1863 view->SetDrawingMode(B_OP_COPY); 1864 view->SetHighColor(darken3); 1865 view->StrokeLine(BPoint(frame.right - 1.0f, frame.bottom - 1.0f), 1866 BPoint(frame.right - 1.0f, frame.bottom - 1.0f)); 1867 1868 1869 // First bevel 1870 frame.InsetBy(1.0f, 1.0f); 1871 1872 view->SetHighColor(darkenmax); 1873 view->StrokeLine(BPoint(frame.left, frame.bottom), 1874 BPoint(frame.right - 1.0f, frame.bottom)); 1875 view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f), 1876 BPoint(frame.right, frame.top)); 1877 1878 view->SetHighColor(lighten2); 1879 view->StrokeLine(BPoint(frame.left, frame.top), 1880 BPoint(frame.left, frame.bottom - 1.0f)); 1881 view->StrokeLine(BPoint(frame.left + 1.0f, frame.top), 1882 BPoint(frame.right - 1.0f, frame.top)); 1883 1884 frame.InsetBy(1.0f, 1.0f); 1885 1886 view->FillRect(BRect(frame.left, frame.top, frame.right - 1.0f, frame.bottom - 1.0f)); 1887 1888 // Second bevel and center dots 1889 view->SetHighColor(darken2); 1890 view->StrokeLine(BPoint(frame.left, frame.bottom), 1891 BPoint(frame.right, frame.bottom)); 1892 view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f), 1893 BPoint(frame.right, frame.top)); 1894 1895 if (Orientation() == B_HORIZONTAL) { 1896 view->StrokeLine(BPoint(frame.left + 6.0f, frame.top + 2.0f), 1897 BPoint(frame.left + 6.0f, frame.top + 2.0f)); 1898 view->StrokeLine(BPoint(frame.left + 6.0f, frame.top + 4.0f), 1899 BPoint(frame.left + 6.0f, frame.top + 4.0f)); 1900 view->StrokeLine(BPoint(frame.left + 6.0f, frame.top + 6.0f), 1901 BPoint(frame.left + 6.0f, frame.top + 6.0f)); 1902 } else { 1903 view->StrokeLine(BPoint(frame.left + 2.0f, frame.top + 6.0f), 1904 BPoint(frame.left + 2.0f, frame.top + 6.0f)); 1905 view->StrokeLine(BPoint(frame.left + 4.0f, frame.top + 6.0f), 1906 BPoint(frame.left + 4.0f, frame.top + 6.0f)); 1907 view->StrokeLine(BPoint(frame.left + 6.0f, frame.top + 6.0f), 1908 BPoint(frame.left + 6.0f, frame.top + 6.0f)); 1909 } 1910 1911 frame.InsetBy(1.0f, 1.0f); 1912 1913 // Third bevel 1914 view->SetHighColor(lighten1); 1915 view->StrokeLine(BPoint(frame.left, frame.bottom), 1916 BPoint(frame.right, frame.bottom)); 1917 view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f), 1918 BPoint(frame.right, frame.top)); 1919 } 1920 1921 1922 void 1923 BSlider::_DrawTriangleThumb() 1924 { 1925 BRect frame = ThumbFrame(); 1926 BView *view = OffscreenView(); 1927 1928 if (be_control_look != NULL) { 1929 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 1930 uint32 flags = be_control_look->Flags(this); 1931 be_control_look->DrawSliderTriangle(view, frame, frame, base, flags, 1932 fOrientation); 1933 return; 1934 } 1935 1936 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR); 1937 rgb_color lightenmax; 1938 rgb_color lighten1; 1939 rgb_color darken2; 1940 rgb_color darken3; 1941 rgb_color darkenmax; 1942 1943 if (IsEnabled()) { 1944 lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT); 1945 lighten1 = no_tint; 1946 darken2 = tint_color(no_tint, B_DARKEN_2_TINT); 1947 darken3 = tint_color(no_tint, B_DARKEN_3_TINT); 1948 darkenmax = tint_color(no_tint, B_DARKEN_MAX_TINT); 1949 } else { 1950 lightenmax = tint_color(no_tint, B_LIGHTEN_2_TINT); 1951 lighten1 = tint_color(no_tint, B_LIGHTEN_1_TINT); 1952 darken2 = tint_color(no_tint, (B_NO_TINT + B_DARKEN_1_TINT) / 2); 1953 darken3 = tint_color(no_tint, B_DARKEN_1_TINT); 1954 darkenmax = tint_color(no_tint, B_DARKEN_3_TINT); 1955 } 1956 1957 if (Orientation() == B_HORIZONTAL) { 1958 view->SetHighColor(lighten1); 1959 view->FillTriangle( 1960 BPoint(frame.left + 1, frame.bottom - 3), 1961 BPoint((frame.left + frame.right) / 2, frame.top + 1), 1962 BPoint(frame.right - 1, frame.bottom - 3)); 1963 1964 view->SetHighColor(no_tint); 1965 view->StrokeLine(BPoint(frame.right - 2, frame.bottom - 3), 1966 BPoint(frame.left + 3, frame.bottom - 3)); 1967 1968 view->SetHighColor(darkenmax); 1969 view->StrokeLine(BPoint(frame.left, frame.bottom - 1), 1970 BPoint(frame.right, frame.bottom - 1)); 1971 view->StrokeLine(BPoint(frame.right, frame.bottom - 2), 1972 BPoint((frame.left + frame.right) / 2, frame.top)); 1973 1974 view->SetHighColor(darken2); 1975 view->StrokeLine(BPoint(frame.right - 1, frame.bottom - 2), 1976 BPoint(frame.left + 1, frame.bottom - 2)); 1977 view->SetHighColor(darken3); 1978 view->StrokeLine(BPoint(frame.left, frame.bottom - 2), 1979 BPoint((frame.left + frame.right) / 2 - 1, frame.top + 1)); 1980 1981 view->SetHighColor(lightenmax); 1982 view->StrokeLine(BPoint(frame.left + 2, frame.bottom - 3), 1983 BPoint((frame.left + frame.right) / 2, frame.top + 1)); 1984 1985 // Shadow 1986 view->SetHighColor(0, 0, 0, IsEnabled() ? 80 : 40); 1987 view->SetDrawingMode(B_OP_ALPHA); 1988 view->StrokeLine(BPoint(frame.left + 1, frame.bottom), 1989 BPoint(frame.right, frame.bottom)); 1990 } else { 1991 view->SetHighColor(lighten1); 1992 view->FillTriangle( 1993 BPoint(frame.left, (frame.top + frame.bottom) / 2), 1994 BPoint(frame.right - 1, frame.top + 1), 1995 BPoint(frame.right - 1, frame.bottom - 1)); 1996 1997 view->SetHighColor(darkenmax); 1998 view->StrokeLine(BPoint(frame.right - 1, frame.top), 1999 BPoint(frame.right - 1, frame.bottom)); 2000 view->StrokeLine(BPoint(frame.right - 1, frame.bottom), 2001 BPoint(frame.right - 2, frame.bottom)); 2002 2003 view->SetHighColor(darken2); 2004 view->StrokeLine(BPoint(frame.right - 2, frame.top + 2), 2005 BPoint(frame.right - 2, frame.bottom - 1)); 2006 view->StrokeLine( 2007 BPoint(frame.left, (frame.top + frame.bottom) / 2), 2008 BPoint(frame.right - 2, frame.top)); 2009 view->SetHighColor(darken3); 2010 view->StrokeLine( 2011 BPoint(frame.left + 1, (frame.top + frame.bottom) / 2 + 1), 2012 BPoint(frame.right - 3, frame.bottom - 1)); 2013 2014 view->SetHighColor(lightenmax); 2015 view->StrokeLine( 2016 BPoint(frame.left + 1, (frame.top + frame.bottom) / 2), 2017 BPoint(frame.right - 2, frame.top + 1)); 2018 2019 // Shadow 2020 view->SetHighColor(0, 0, 0, IsEnabled() ? 80 : 40); 2021 view->SetDrawingMode(B_OP_ALPHA); 2022 view->StrokeLine(BPoint(frame.right, frame.top + 1), 2023 BPoint(frame.right, frame.bottom)); 2024 } 2025 2026 view->SetDrawingMode(B_OP_COPY); 2027 } 2028 2029 2030 BPoint 2031 BSlider::_Location() const 2032 { 2033 return fLocation; 2034 } 2035 2036 2037 void 2038 BSlider::_SetLocationForValue(int32 value) 2039 { 2040 BPoint loc; 2041 float range = (float)(fMaxValue - fMinValue); 2042 if (range == 0) 2043 range = 1; 2044 2045 float pos = (float)(value - fMinValue) / range * 2046 (_MaxPosition() - _MinPosition()); 2047 2048 if (fOrientation == B_HORIZONTAL) { 2049 loc.x = ceil(_MinPosition() + pos); 2050 loc.y = 0; 2051 } else { 2052 loc.x = 0; 2053 loc.y = floor(_MaxPosition() - pos); 2054 } 2055 fLocation = loc; 2056 } 2057 2058 2059 float 2060 BSlider::_MinPosition() const 2061 { 2062 if (fOrientation == B_HORIZONTAL) 2063 return BarFrame().left + 1.0f; 2064 2065 return BarFrame().bottom - 1.0f; 2066 } 2067 2068 2069 float 2070 BSlider::_MaxPosition() const 2071 { 2072 if (fOrientation == B_HORIZONTAL) 2073 return BarFrame().right - 1.0f; 2074 2075 return BarFrame().top + 1.0f; 2076 } 2077 2078 2079 BSize 2080 BSlider::_ValidateMinSize() 2081 { 2082 if (fMinSize.width >= 0) { 2083 // the preferred size is up to date 2084 return fMinSize; 2085 } 2086 2087 font_height fontHeight; 2088 GetFontHeight(&fontHeight); 2089 2090 float width = 0.0; 2091 float height = 0.0; 2092 2093 if (fMaxUpdateTextWidth < 0.0) 2094 fMaxUpdateTextWidth = MaxUpdateTextWidth(); 2095 2096 if (Orientation() == B_HORIZONTAL) { 2097 height = 12.0 + fBarThickness; 2098 int32 rows = 0; 2099 2100 float labelWidth = 0; 2101 int32 labelRows = 0; 2102 float labelSpacing = StringWidth("M") * 2; 2103 if (Label()) { 2104 labelWidth = StringWidth(Label()); 2105 labelRows = 1; 2106 } 2107 if (fMaxUpdateTextWidth > 0.0) { 2108 if (labelWidth > 0) 2109 labelWidth += labelSpacing; 2110 labelWidth += fMaxUpdateTextWidth; 2111 labelRows = 1; 2112 } 2113 rows += labelRows; 2114 2115 if (MinLimitLabel()) 2116 width = StringWidth(MinLimitLabel()); 2117 if (MaxLimitLabel()) { 2118 // some space between the labels 2119 if (MinLimitLabel()) 2120 width += labelSpacing; 2121 2122 width += StringWidth(MaxLimitLabel()); 2123 } 2124 2125 if (labelWidth > width) 2126 width = labelWidth; 2127 if (width < 32.0) 2128 width = 32.0; 2129 2130 if (MinLimitLabel() || MaxLimitLabel()) 2131 rows++; 2132 2133 height += rows * (ceilf(fontHeight.ascent) 2134 + ceilf(fontHeight.descent) + 4.0); 2135 } else { 2136 // B_VERTICAL 2137 width = 12.0 + fBarThickness; 2138 height = 32.0; 2139 2140 float lineHeightNoLeading = ceilf(fontHeight.ascent) 2141 + ceilf(fontHeight.descent); 2142 float lineHeight = lineHeightNoLeading + ceilf(fontHeight.leading); 2143 2144 // find largest label 2145 float labelWidth = 0; 2146 if (Label()) { 2147 labelWidth = StringWidth(Label()); 2148 height += lineHeightNoLeading; 2149 } 2150 if (MaxLimitLabel()) { 2151 labelWidth = max_c(labelWidth, StringWidth(MaxLimitLabel())); 2152 height += Label() ? lineHeight : lineHeightNoLeading; 2153 } 2154 if (MinLimitLabel()) { 2155 labelWidth = max_c(labelWidth, StringWidth(MinLimitLabel())); 2156 height += lineHeightNoLeading; 2157 } 2158 if (fMaxUpdateTextWidth > 0.0) { 2159 labelWidth = max_c(labelWidth, fMaxUpdateTextWidth); 2160 height += MinLimitLabel() ? lineHeight : lineHeightNoLeading; 2161 } 2162 2163 width = max_c(labelWidth, width); 2164 } 2165 2166 fMinSize.width = width; 2167 fMinSize.height = height; 2168 2169 ResetLayoutInvalidation(); 2170 2171 return fMinSize; 2172 } 2173 2174 2175 // #pragma mark - FBC padding 2176 2177 void BSlider::_ReservedSlider6() {} 2178 void BSlider::_ReservedSlider7() {} 2179 void BSlider::_ReservedSlider8() {} 2180 void BSlider::_ReservedSlider9() {} 2181 void BSlider::_ReservedSlider10() {} 2182 void BSlider::_ReservedSlider11() {} 2183 void BSlider::_ReservedSlider12() {} 2184 2185 2186 BSlider & 2187 BSlider::operator=(const BSlider &) 2188 { 2189 return *this; 2190 } 2191 2192 2193 // #pragma mark - BeOS compatibility 2194 2195 2196 #if __GNUC__ < 3 2197 2198 extern "C" void 2199 GetLimits__7BSliderPlT1(BSlider* slider, int32* minimum, int32* maximum) 2200 { 2201 slider->GetLimits(minimum, maximum); 2202 } 2203 2204 2205 extern "C" void 2206 _ReservedSlider4__7BSlider(BSlider *slider, int32 minimum, int32 maximum) 2207 { 2208 slider->BSlider::SetLimits(minimum, maximum); 2209 } 2210 2211 extern "C" float 2212 _ReservedSlider5__7BSlider(BSlider *slider) 2213 { 2214 return slider->BSlider::MaxUpdateTextWidth(); 2215 } 2216 2217 2218 extern "C" void 2219 _ReservedSlider1__7BSlider(BSlider* slider, orientation _orientation) 2220 { 2221 slider->BSlider::SetOrientation(_orientation); 2222 } 2223 2224 2225 extern "C" void 2226 _ReservedSlider2__7BSlider(BSlider* slider, float thickness) 2227 { 2228 slider->BSlider::SetBarThickness(thickness); 2229 } 2230 2231 2232 extern "C" void 2233 _ReservedSlider3__7BSlider(BSlider* slider, const BFont* font, 2234 uint32 properties) 2235 { 2236 slider->BSlider::SetFont(font, properties); 2237 } 2238 2239 #endif // __GNUC__ < 3 2240