xref: /haiku/src/kits/interface/Slider.cpp (revision cb9c3e9cede30851ba29be2e955fb8cfcecef7ac)
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(&region);
973 		region.Exclude(thumbFrame);
974 		ConstrainClippingRegion(&region);
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