xref: /haiku/src/kits/interface/Slider.cpp (revision c80809a3ab0b0a2ce53ea861a2b00ace24ff452d)
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.left = BarFrame().left;
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 anti-aliasing.
713 		// 2) We need to update the region with the focus mark as well. (A
714 		// method BSlider::FocusMarkFrame() would be nice as well.)
715 		if (fOrientation == B_HORIZONTAL) {
716 			if (IsFocus())
717 				invalid.bottom += 2;
718 			invalid.InsetBy(-1, 0);
719 		} else {
720 			if (IsFocus())
721 				invalid.left -= 2;
722 			invalid.InsetBy(0, -1);
723 		}
724 	}
725 
726 	Invalidate(invalid);
727 
728 	UpdateTextChanged();
729 }
730 
731 
732 int32
733 BSlider::ValueForPoint(BPoint location) const
734 {
735 	float min;
736 	float max;
737 	float position;
738 	if (fOrientation == B_HORIZONTAL) {
739 		min = _MinPosition();
740 		max = _MaxPosition();
741 		position = location.x;
742 	} else {
743 		max = _MinPosition();
744 		min = _MaxPosition();
745 		position = min + (max - location.y);
746 	}
747 
748 	if (position < min)
749 		position = min;
750 	if (position > max)
751 		position = max;
752 
753 	return (int32)roundf(((position - min) * (fMaxValue - fMinValue) / (max - min)) + fMinValue);
754 }
755 
756 
757 void
758 BSlider::SetPosition(float position)
759 {
760 	if (position <= 0.0f)
761 		BControl::SetValue(fMinValue);
762 	else if (position >= 1.0f)
763 		BControl::SetValue(fMaxValue);
764 	else
765 		BControl::SetValue((int32)(position * (fMaxValue - fMinValue) + fMinValue));
766 }
767 
768 
769 float
770 BSlider::Position() const
771 {
772 	float range = (float)(fMaxValue - fMinValue);
773 	if (range == 0.0f)
774 		range = 1.0f;
775 
776 	return (float)(Value() - fMinValue) / range;
777 }
778 
779 
780 void
781 BSlider::SetEnabled(bool on)
782 {
783 	BControl::SetEnabled(on);
784 }
785 
786 
787 void
788 BSlider::GetLimits(int32 *minimum, int32 *maximum) const
789 {
790 	if (minimum != NULL)
791 		*minimum = fMinValue;
792 	if (maximum != NULL)
793 		*maximum = fMaxValue;
794 }
795 
796 
797 // #pragma mark - drawing
798 
799 
800 void
801 BSlider::Draw(BRect updateRect)
802 {
803 	// clear out background
804 	BRegion background(updateRect);
805 	background.Exclude(BarFrame());
806 	bool drawBackground = true;
807 	if (Parent() && (Parent()->Flags() & B_DRAW_ON_CHILDREN) != 0) {
808 		// This view is embedded somewhere, most likely the Tracker Desktop
809 		// shelf.
810 		drawBackground = false;
811 	}
812 
813 #if USE_OFF_SCREEN_VIEW
814 	if (!fOffScreenBits)
815 		return;
816 
817 	if (fOffScreenBits->Lock()) {
818 		fOffScreenView->SetViewColor(ViewColor());
819 		fOffScreenView->SetLowColor(LowColor());
820 #endif
821 
822 		if (drawBackground && background.Frame().IsValid())
823 			OffscreenView()->FillRegion(&background, B_SOLID_LOW);
824 
825 #if USE_OFF_SCREEN_VIEW
826 		fOffScreenView->Sync();
827 		fOffScreenBits->Unlock();
828 	}
829 #endif
830 
831 	DrawSlider();
832 }
833 
834 
835 void
836 BSlider::DrawSlider()
837 {
838 	if (LockLooper()) {
839 #if USE_OFF_SCREEN_VIEW
840 		if (!fOffScreenBits)
841 			return;
842 		if (fOffScreenBits->Lock()) {
843 #endif
844 			DrawBar();
845 			DrawHashMarks();
846 			DrawThumb();
847 			DrawFocusMark();
848 			DrawText();
849 
850 #if USE_OFF_SCREEN_VIEW
851 			fOffScreenView->Sync();
852 			fOffScreenBits->Unlock();
853 
854 			DrawBitmap(fOffScreenBits, B_ORIGIN);
855 		}
856 #endif
857 		UnlockLooper();
858 	}
859 }
860 
861 
862 void
863 BSlider::DrawBar()
864 {
865 	BRect frame = BarFrame();
866 	BView *view = OffscreenView();
867 
868 	if (be_control_look != NULL) {
869 		uint32 flags = be_control_look->Flags(this);
870 		rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
871 		rgb_color rightFillColor = fBarColor;
872 		rgb_color leftFillColor = fUseFillColor ? fFillColor : fBarColor;
873 		be_control_look->DrawSliderBar(view, frame, frame, base, leftFillColor,
874 			rightFillColor, Position(), flags, fOrientation);
875 		return;
876 	}
877 
878 	rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR);
879 	rgb_color lightenmax;
880 	rgb_color darken1;
881 	rgb_color darken2;
882 	rgb_color darkenmax;
883 
884 	rgb_color barColor;
885 	rgb_color fillColor;
886 
887 	if (IsEnabled()) {
888 		lightenmax	= tint_color(no_tint, B_LIGHTEN_MAX_TINT);
889 		darken1		= tint_color(no_tint, B_DARKEN_1_TINT);
890 		darken2		= tint_color(no_tint, B_DARKEN_2_TINT);
891 		darkenmax	= tint_color(no_tint, B_DARKEN_MAX_TINT);
892 		barColor	= fBarColor;
893 		fillColor	= fFillColor;
894 	} else {
895 		lightenmax	= tint_color(no_tint, B_LIGHTEN_MAX_TINT);
896 		darken1		= no_tint;
897 		darken2		= tint_color(no_tint, B_DARKEN_1_TINT);
898 		darkenmax	= tint_color(no_tint, B_DARKEN_3_TINT);
899 
900 		barColor.red	= (fBarColor.red + no_tint.red) / 2;
901 		barColor.green	= (fBarColor.green + no_tint.green) / 2;
902 		barColor.blue	= (fBarColor.blue + no_tint.blue) / 2;
903 		barColor.alpha	= 255;
904 
905 		fillColor.red	= (fFillColor.red + no_tint.red) / 2;
906 		fillColor.green	= (fFillColor.green + no_tint.green) / 2;
907 		fillColor.blue	= (fFillColor.blue + no_tint.blue) / 2;
908 		fillColor.alpha	= 255;
909 	}
910 
911 	// exclude the block thumb from the bar filling
912 
913 	BRect lowerFrame = frame.InsetByCopy(1, 1);
914 	lowerFrame.top++;
915 	lowerFrame.left++;
916 	BRect upperFrame = lowerFrame;
917 	BRect thumbFrame;
918 
919 	if (Style() == B_BLOCK_THUMB) {
920 		thumbFrame = ThumbFrame();
921 
922 		if (fOrientation == B_HORIZONTAL) {
923 			lowerFrame.right = thumbFrame.left;
924 			upperFrame.left = thumbFrame.right;
925 		} else {
926 			lowerFrame.top = thumbFrame.bottom;
927 			upperFrame.bottom = thumbFrame.top;
928 		}
929 	} else if (fUseFillColor) {
930 		if (fOrientation == B_HORIZONTAL) {
931 			lowerFrame.right = floor(lowerFrame.left - 1 + Position()
932 				* (lowerFrame.Width() + 1));
933 			upperFrame.left = lowerFrame.right;
934 		} else {
935 			lowerFrame.top = floor(lowerFrame.bottom + 1 - Position()
936 				* (lowerFrame.Height() + 1));
937 			upperFrame.bottom = lowerFrame.top;
938 		}
939 	}
940 
941 	view->SetHighColor(barColor);
942 	view->FillRect(upperFrame);
943 
944 	if (Style() == B_BLOCK_THUMB || fUseFillColor) {
945 		if (fUseFillColor)
946 			view->SetHighColor(fillColor);
947 		view->FillRect(lowerFrame);
948 	}
949 
950 	if (Style() == B_BLOCK_THUMB) {
951 		// We don't want to stroke the lines over the thumb
952 
953 		PushState();
954 
955 		BRegion region;
956 		GetClippingRegion(&region);
957 		region.Exclude(thumbFrame);
958 		ConstrainClippingRegion(&region);
959 	}
960 
961 	view->SetHighColor(darken1);
962 	view->StrokeLine(BPoint(frame.left, frame.top),
963 					 BPoint(frame.left + 1.0f, frame.top));
964 	view->StrokeLine(BPoint(frame.left, frame.bottom),
965 					 BPoint(frame.left + 1.0f, frame.bottom));
966 	view->StrokeLine(BPoint(frame.right - 1.0f, frame.top),
967 					 BPoint(frame.right, frame.top));
968 
969 	view->SetHighColor(darken2);
970 	view->StrokeLine(BPoint(frame.left + 1.0f, frame.top),
971 					 BPoint(frame.right - 1.0f, frame.top));
972 	view->StrokeLine(BPoint(frame.left, frame.bottom - 1.0f),
973 					 BPoint(frame.left, frame.top + 1.0f));
974 
975 	view->SetHighColor(lightenmax);
976 	view->StrokeLine(BPoint(frame.left + 1.0f, frame.bottom),
977 					 BPoint(frame.right, frame.bottom));
978 	view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f),
979 					 BPoint(frame.right, frame.top + 1.0f));
980 
981 	frame.InsetBy(1.0f, 1.0f);
982 
983 	view->SetHighColor(darkenmax);
984 	view->StrokeLine(BPoint(frame.left, frame.bottom),
985 					 BPoint(frame.left, frame.top));
986 	view->StrokeLine(BPoint(frame.left + 1.0f, frame.top),
987 					 BPoint(frame.right, frame.top));
988 
989 	if (Style() == B_BLOCK_THUMB)
990 		PopState();
991 }
992 
993 
994 void
995 BSlider::DrawHashMarks()
996 {
997 	if (fHashMarks == B_HASH_MARKS_NONE)
998 		return;
999 
1000 	BRect frame = HashMarksFrame();
1001 	BView* view = OffscreenView();
1002 
1003 	if (be_control_look) {
1004 		rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
1005 		uint32 flags = be_control_look->Flags(this);
1006 		be_control_look->DrawSliderHashMarks(view, frame, frame, base,
1007 			fHashMarkCount, fHashMarks, flags, fOrientation);
1008 		return;
1009 	}
1010 
1011 	rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR);
1012 	rgb_color lightenmax;
1013 	rgb_color darken2;
1014 
1015 	if (IsEnabled()) {
1016 		lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT);
1017 		darken2 = tint_color(no_tint, B_DARKEN_2_TINT);
1018 	} else {
1019 		lightenmax = tint_color(no_tint, B_LIGHTEN_2_TINT);
1020 		darken2 = tint_color(no_tint, B_DARKEN_1_TINT);
1021 	}
1022 
1023 	float pos = _MinPosition();
1024 	int32 hashMarkCount = max_c(fHashMarkCount, 2);
1025 		// draw at least two hashmarks at min/max if
1026 		// fHashMarks != B_HASH_MARKS_NONE
1027 	float factor = (_MaxPosition() - pos) / (hashMarkCount - 1);
1028 
1029 	if (fHashMarks & B_HASH_MARKS_TOP) {
1030 
1031 		view->BeginLineArray(hashMarkCount * 2);
1032 
1033 		if (fOrientation == B_HORIZONTAL) {
1034 			for (int32 i = 0; i < hashMarkCount; i++) {
1035 				view->AddLine(BPoint(pos, frame.top),
1036 							  BPoint(pos, frame.top + 5), darken2);
1037 				view->AddLine(BPoint(pos + 1, frame.top),
1038 							  BPoint(pos + 1, frame.top + 5), lightenmax);
1039 
1040 				pos += factor;
1041 			}
1042 		} else {
1043 			for (int32 i = 0; i < hashMarkCount; i++) {
1044 				view->AddLine(BPoint(frame.left, pos),
1045 							  BPoint(frame.left + 5, pos), darken2);
1046 				view->AddLine(BPoint(frame.left, pos + 1),
1047 							  BPoint(frame.left + 5, pos + 1), lightenmax);
1048 
1049 				pos += factor;
1050 			}
1051 		}
1052 
1053 		view->EndLineArray();
1054 	}
1055 
1056 	pos = _MinPosition();
1057 
1058 	if (fHashMarks & B_HASH_MARKS_BOTTOM) {
1059 
1060 		view->BeginLineArray(hashMarkCount * 2);
1061 
1062 		if (fOrientation == B_HORIZONTAL) {
1063 			for (int32 i = 0; i < hashMarkCount; i++) {
1064 				view->AddLine(BPoint(pos, frame.bottom - 5),
1065 							  BPoint(pos, frame.bottom), darken2);
1066 				view->AddLine(BPoint(pos + 1, frame.bottom - 5),
1067 							  BPoint(pos + 1, frame.bottom), lightenmax);
1068 
1069 				pos += factor;
1070 			}
1071 		} else {
1072 			for (int32 i = 0; i < hashMarkCount; i++) {
1073 				view->AddLine(BPoint(frame.right - 5, pos),
1074 							  BPoint(frame.right, pos), darken2);
1075 				view->AddLine(BPoint(frame.right - 5, pos + 1),
1076 							  BPoint(frame.right, pos + 1), lightenmax);
1077 
1078 				pos += factor;
1079 			}
1080 		}
1081 
1082 		view->EndLineArray();
1083 	}
1084 }
1085 
1086 
1087 void
1088 BSlider::DrawThumb()
1089 {
1090 	if (Style() == B_BLOCK_THUMB)
1091 		_DrawBlockThumb();
1092 	else
1093 		_DrawTriangleThumb();
1094 }
1095 
1096 
1097 void
1098 BSlider::DrawFocusMark()
1099 {
1100 	if (!IsFocus())
1101 		return;
1102 
1103 	OffscreenView()->SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
1104 
1105 	BRect frame = ThumbFrame();
1106 
1107 	if (fStyle == B_BLOCK_THUMB) {
1108 		frame.left += 2.0f;
1109 		frame.top += 2.0f;
1110 		frame.right -= 3.0f;
1111 		frame.bottom -= 3.0f;
1112 		OffscreenView()->StrokeRect(frame);
1113 	} else {
1114 		if (fOrientation == B_HORIZONTAL) {
1115 			OffscreenView()->StrokeLine(BPoint(frame.left, frame.bottom + 2.0f),
1116 				BPoint(frame.right, frame.bottom + 2.0f));
1117 		} else {
1118 			OffscreenView()->StrokeLine(BPoint(frame.left - 2.0f, frame.top),
1119 				BPoint(frame.left - 2.0f, frame.bottom));
1120 		}
1121 	}
1122 }
1123 
1124 
1125 void
1126 BSlider::DrawText()
1127 {
1128 	BRect bounds(Bounds());
1129 	BView *view = OffscreenView();
1130 
1131 	rgb_color base = LowColor();
1132 	uint32 flags = 0;
1133 	if (be_control_look == NULL) {
1134 		if (IsEnabled()) {
1135 			view->SetHighColor(0, 0, 0);
1136 		} else {
1137 			view->SetHighColor(tint_color(LowColor(), B_DISABLED_LABEL_TINT));
1138 		}
1139 	} else
1140  		flags = be_control_look->Flags(this);
1141 
1142 	font_height fontHeight;
1143 	GetFontHeight(&fontHeight);
1144 	if (Orientation() == B_HORIZONTAL) {
1145 		if (Label()) {
1146 			if (be_control_look == NULL) {
1147 				view->DrawString(Label(),
1148 					BPoint(0.0, ceilf(fontHeight.ascent)));
1149 			} else {
1150 				be_control_look->DrawLabel(view, Label(), base, flags,
1151 					BPoint(0.0, ceilf(fontHeight.ascent)));
1152 			}
1153 		}
1154 
1155 		// the update text is updated in SetValue() only
1156 		if (fUpdateText != NULL) {
1157 			if (be_control_look == NULL) {
1158 				view->DrawString(fUpdateText, BPoint(bounds.right
1159 					- StringWidth(fUpdateText), ceilf(fontHeight.ascent)));
1160 			} else {
1161 				be_control_look->DrawLabel(view, fUpdateText, base, flags,
1162 					BPoint(bounds.right - StringWidth(fUpdateText),
1163 						ceilf(fontHeight.ascent)));
1164 			}
1165 		}
1166 
1167 		if (fMinLimitLabel) {
1168 			if (be_control_look == NULL) {
1169 				view->DrawString(fMinLimitLabel, BPoint(0.0, bounds.bottom
1170 					- fontHeight.descent));
1171 			} else {
1172 				be_control_look->DrawLabel(view, fMinLimitLabel, base, flags,
1173 					BPoint(0.0, bounds.bottom - fontHeight.descent));
1174 			}
1175 		}
1176 
1177 		if (fMaxLimitLabel) {
1178 			if (be_control_look == NULL) {
1179 				view->DrawString(fMaxLimitLabel, BPoint(bounds.right
1180 					- StringWidth(fMaxLimitLabel), bounds.bottom
1181 					- fontHeight.descent));
1182 			} else {
1183 				be_control_look->DrawLabel(view, fMaxLimitLabel, base, flags,
1184 					BPoint(bounds.right - StringWidth(fMaxLimitLabel),
1185 						bounds.bottom - fontHeight.descent));
1186 			}
1187 		}
1188 	} else {
1189 		float lineHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent)
1190 			+ ceilf(fontHeight.leading);
1191 		float baseLine = ceilf(fontHeight.ascent);
1192 
1193 		if (Label()) {
1194 			if (be_control_look == NULL) {
1195 				view->DrawString(Label(), BPoint((bounds.Width()
1196 					- StringWidth(Label())) / 2.0, baseLine));
1197 			} else {
1198 				be_control_look->DrawLabel(view, Label(), base, flags,
1199 					BPoint((bounds.Width() - StringWidth(Label())) / 2.0,
1200 						baseLine));
1201 			}
1202 			baseLine += lineHeight;
1203 		}
1204 
1205 		if (fMaxLimitLabel) {
1206 			if (be_control_look == NULL) {
1207 				view->DrawString(fMaxLimitLabel, BPoint((bounds.Width()
1208 					- StringWidth(fMaxLimitLabel)) / 2.0, baseLine));
1209 			} else {
1210 				be_control_look->DrawLabel(view, fMaxLimitLabel, base, flags,
1211 					BPoint((bounds.Width()
1212 						- StringWidth(fMaxLimitLabel)) / 2.0, baseLine));
1213 			}
1214 		}
1215 
1216 		baseLine = bounds.bottom - ceilf(fontHeight.descent);
1217 
1218 		if (fMinLimitLabel) {
1219 			if (be_control_look == NULL) {
1220 				view->DrawString(fMinLimitLabel, BPoint((bounds.Width()
1221 					- StringWidth(fMinLimitLabel)) / 2.0, baseLine));
1222 			} else {
1223 				be_control_look->DrawLabel(view, fMinLimitLabel, base, flags,
1224 					BPoint((bounds.Width()
1225 						- StringWidth(fMinLimitLabel)) / 2.0, baseLine));
1226 			}
1227 			baseLine -= lineHeight;
1228 		}
1229 
1230 		if (fUpdateText != NULL) {
1231 			if (be_control_look == NULL) {
1232 				view->DrawString(fUpdateText, BPoint((bounds.Width()
1233 					- StringWidth(fUpdateText)) / 2.0, baseLine));
1234 			} else {
1235 				be_control_look->DrawLabel(view, fUpdateText, base, flags,
1236 					BPoint((bounds.Width()
1237 						- StringWidth(fUpdateText)) / 2.0, baseLine));
1238 			}
1239 		}
1240 	}
1241 }
1242 
1243 
1244 // #pragma mark -
1245 
1246 
1247 const char*
1248 BSlider::UpdateText() const
1249 {
1250 	return NULL;
1251 }
1252 
1253 
1254 void
1255 BSlider::UpdateTextChanged()
1256 {
1257 	// update text label
1258 	float oldWidth = 0.0;
1259 	if (fUpdateText != NULL)
1260 		oldWidth = StringWidth(fUpdateText);
1261 
1262 	const char* oldUpdateText = fUpdateText;
1263 	fUpdateText = UpdateText();
1264 	bool updateTextOnOff = (fUpdateText == NULL && oldUpdateText != NULL)
1265 		|| (fUpdateText != NULL && oldUpdateText == NULL);
1266 
1267 	float newWidth = 0.0;
1268 	if (fUpdateText != NULL)
1269 		newWidth = StringWidth(fUpdateText);
1270 
1271 	float width = ceilf(max_c(newWidth, oldWidth)) + 2.0f;
1272 	if (width != 0) {
1273 		font_height fontHeight;
1274 		GetFontHeight(&fontHeight);
1275 
1276 		float height = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent);
1277 		float lineHeight = height + ceilf(fontHeight.leading);
1278 		BRect invalid(Bounds());
1279 		if (fOrientation == B_HORIZONTAL)
1280 			invalid = BRect(invalid.right - width, 0, invalid.right, height);
1281 		else {
1282 			if (!updateTextOnOff) {
1283 				invalid.left = (invalid.left + invalid.right - width) / 2;
1284 				invalid.right = invalid.left + width;
1285 				if (fMinLimitLabel)
1286 					invalid.bottom -= lineHeight;
1287 				invalid.top = invalid.bottom - height;
1288 			}
1289 		}
1290 		Invalidate(invalid);
1291 	}
1292 
1293 	float oldMaxUpdateTextWidth = fMaxUpdateTextWidth;
1294 	fMaxUpdateTextWidth = MaxUpdateTextWidth();
1295 	if (oldMaxUpdateTextWidth != fMaxUpdateTextWidth)
1296 		InvalidateLayout();
1297 }
1298 
1299 
1300 BRect
1301 BSlider::BarFrame() const
1302 {
1303 	BRect frame(Bounds());
1304 
1305 	font_height fontHeight;
1306 	GetFontHeight(&fontHeight);
1307 
1308 	float textHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent);
1309 	float leading = ceilf(fontHeight.leading);
1310 
1311 	float thumbInset;
1312 	if (fStyle == B_BLOCK_THUMB)
1313 		thumbInset = 8.0;
1314 	else
1315 		thumbInset = 7.0;
1316 
1317 	if (Orientation() == B_HORIZONTAL) {
1318 		frame.left = thumbInset;
1319 		frame.top = 6.0 + (Label() || fUpdateText ? textHeight + 4.0 : 0.0);
1320 		frame.right -= thumbInset;
1321 		frame.bottom = frame.top + fBarThickness;
1322 	} else {
1323 		frame.left = floorf((frame.Width() - fBarThickness) / 2.0);
1324 		frame.top = thumbInset;
1325 		if (Label())
1326 			frame.top += textHeight;
1327 		if (fMaxLimitLabel) {
1328 			frame.top += textHeight;
1329 			if (Label())
1330 				frame.top += leading;
1331 		}
1332 
1333 		frame.right = frame.left + fBarThickness;
1334 		frame.bottom = frame.bottom - thumbInset;
1335 		if (fMinLimitLabel)
1336 			frame.bottom -= textHeight;
1337 		if (fUpdateText) {
1338 			frame.bottom -= textHeight;
1339 			if (fMinLimitLabel)
1340 				frame.bottom -= leading;
1341 		}
1342 	}
1343 
1344 	return frame;
1345 }
1346 
1347 
1348 BRect
1349 BSlider::HashMarksFrame() const
1350 {
1351 	BRect frame(BarFrame());
1352 
1353 	if (fOrientation == B_HORIZONTAL) {
1354 		frame.top -= 6.0;
1355 		frame.bottom += 6.0;
1356 	} else {
1357 		frame.left -= 6.0;
1358 		frame.right += 6.0;
1359 	}
1360 
1361 	return frame;
1362 }
1363 
1364 
1365 BRect
1366 BSlider::ThumbFrame() const
1367 {
1368 	// TODO: The slider looks really ugly and broken when it is too little.
1369 	// I would suggest using BarFrame() here to get the top and bottom coords
1370 	// and spread them further apart for the thumb
1371 
1372 	BRect frame = Bounds();
1373 
1374 	font_height fontHeight;
1375 	GetFontHeight(&fontHeight);
1376 
1377 	float textHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent);
1378 
1379 	if (fStyle == B_BLOCK_THUMB) {
1380 		if (Orientation() == B_HORIZONTAL) {
1381 			frame.left = floorf(Position() * (_MaxPosition()
1382 				- _MinPosition()) + _MinPosition()) - 8;
1383 			frame.top = 2 + (Label() || fUpdateText ? textHeight + 4 : 0);
1384 			frame.right = frame.left + 17;
1385 			frame.bottom = frame.top + fBarThickness + 7;
1386 		} else {
1387 			frame.left = floor((frame.Width() - fBarThickness) / 2) - 4;
1388 			frame.top = floorf(Position() * (_MaxPosition()
1389 				- _MinPosition()) + _MinPosition()) - 8;
1390 			frame.right = frame.left + fBarThickness + 7;
1391 			frame.bottom = frame.top + 17;
1392 		}
1393 	} else {
1394 		if (Orientation() == B_HORIZONTAL) {
1395 			frame.left = floorf(Position() * (_MaxPosition()
1396 				- _MinPosition()) + _MinPosition()) - 6;
1397 			frame.right = frame.left + 12;
1398 			frame.top = 3 + fBarThickness + (Label() ? textHeight + 4 : 0);
1399 			frame.bottom = frame.top + 8;
1400 		} else {
1401 			frame.left = floorf((frame.Width() + fBarThickness) / 2) - 3;
1402 			frame.top = floorf(Position() * (_MaxPosition()
1403 				- _MinPosition())) + _MinPosition() - 6;
1404 			frame.right = frame.left + 8;
1405 			frame.bottom = frame.top + 12;
1406 		}
1407 	}
1408 
1409 	return frame;
1410 }
1411 
1412 
1413 void
1414 BSlider::SetFlags(uint32 flags)
1415 {
1416 	BControl::SetFlags(flags);
1417 }
1418 
1419 
1420 void
1421 BSlider::SetResizingMode(uint32 mode)
1422 {
1423 	BControl::SetResizingMode(mode);
1424 }
1425 
1426 
1427 void
1428 BSlider::GetPreferredSize(float* _width, float* _height)
1429 {
1430 	BSize preferredSize = PreferredSize();
1431 
1432 	if (Orientation() == B_HORIZONTAL) {
1433 		if (_width != NULL) {
1434 			// NOTE: For compatibility reasons, a horizontal BSlider
1435 			// never shrinks horizontally. This only affects applications
1436 			// which do not use the new layout system.
1437 			*_width = max_c(Bounds().Width(), preferredSize.width);
1438 		}
1439 
1440 		if (_height != NULL)
1441 			*_height = preferredSize.height;
1442 	} else {
1443 		if (_width != NULL)
1444 			*_width = preferredSize.width;
1445 
1446 		if (_height != NULL) {
1447 			// NOTE: Similarly, a vertical BSlider never shrinks
1448 			// vertically. This only affects applications which do not
1449 			// use the new layout system.
1450 			*_height = max_c(Bounds().Height(), preferredSize.height);
1451 		}
1452 	}
1453 }
1454 
1455 
1456 void
1457 BSlider::ResizeToPreferred()
1458 {
1459 	BControl::ResizeToPreferred();
1460 }
1461 
1462 
1463 status_t
1464 BSlider::Invoke(BMessage* message)
1465 {
1466 	return BControl::Invoke(message);
1467 }
1468 
1469 
1470 BHandler*
1471 BSlider::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier,
1472 	int32 command, const char *property)
1473 {
1474 	return BControl::ResolveSpecifier(message, index, specifier, command,
1475 		property);
1476 }
1477 
1478 
1479 status_t
1480 BSlider::GetSupportedSuites(BMessage* message)
1481 {
1482 	return BControl::GetSupportedSuites(message);
1483 }
1484 
1485 
1486 void
1487 BSlider::SetModificationMessage(BMessage* message)
1488 {
1489 	delete fModificationMessage;
1490 	fModificationMessage = message;
1491 }
1492 
1493 
1494 BMessage*
1495 BSlider::ModificationMessage() const
1496 {
1497 	return fModificationMessage;
1498 }
1499 
1500 
1501 void
1502 BSlider::SetSnoozeAmount(int32 snoozeTime)
1503 {
1504 	if (snoozeTime < 10000)
1505 		snoozeTime = 10000;
1506 	else if (snoozeTime > 1000000)
1507 		snoozeTime = 1000000;
1508 
1509 	fSnoozeAmount = snoozeTime;
1510 }
1511 
1512 
1513 int32
1514 BSlider::SnoozeAmount() const
1515 {
1516 	return fSnoozeAmount;
1517 }
1518 
1519 
1520 void
1521 BSlider::SetKeyIncrementValue(int32 incrementValue)
1522 {
1523 	fKeyIncrementValue = incrementValue;
1524 }
1525 
1526 
1527 int32
1528 BSlider::KeyIncrementValue() const
1529 {
1530 	return fKeyIncrementValue;
1531 }
1532 
1533 
1534 void
1535 BSlider::SetHashMarkCount(int32 hashMarkCount)
1536 {
1537 	fHashMarkCount = hashMarkCount;
1538 	Invalidate();
1539 }
1540 
1541 
1542 int32
1543 BSlider::HashMarkCount() const
1544 {
1545 	return fHashMarkCount;
1546 }
1547 
1548 
1549 void
1550 BSlider::SetHashMarks(hash_mark_location where)
1551 {
1552 	fHashMarks = where;
1553 // TODO: enable if the hashmark look is influencing the control size!
1554 //	InvalidateLayout();
1555 	Invalidate();
1556 }
1557 
1558 
1559 hash_mark_location
1560 BSlider::HashMarks() const
1561 {
1562 	return fHashMarks;
1563 }
1564 
1565 
1566 void
1567 BSlider::SetStyle(thumb_style style)
1568 {
1569 	fStyle = style;
1570 	InvalidateLayout();
1571 	Invalidate();
1572 }
1573 
1574 
1575 thumb_style
1576 BSlider::Style() const
1577 {
1578 	return fStyle;
1579 }
1580 
1581 
1582 void
1583 BSlider::SetBarColor(rgb_color barColor)
1584 {
1585 	fBarColor = barColor;
1586 	Invalidate(BarFrame());
1587 }
1588 
1589 
1590 rgb_color
1591 BSlider::BarColor() const
1592 {
1593 	return fBarColor;
1594 }
1595 
1596 
1597 void
1598 BSlider::UseFillColor(bool useFill, const rgb_color* barColor)
1599 {
1600 	fUseFillColor = useFill;
1601 
1602 	if (useFill && barColor)
1603 		fFillColor = *barColor;
1604 
1605 	Invalidate(BarFrame());
1606 }
1607 
1608 
1609 bool
1610 BSlider::FillColor(rgb_color* barColor) const
1611 {
1612 	if (barColor && fUseFillColor)
1613 		*barColor = fFillColor;
1614 
1615 	return fUseFillColor;
1616 }
1617 
1618 
1619 BView*
1620 BSlider::OffscreenView() const
1621 {
1622 #if USE_OFF_SCREEN_VIEW
1623 	return fOffScreenView;
1624 #else
1625 	return (BView*)this;
1626 #endif
1627 }
1628 
1629 
1630 orientation
1631 BSlider::Orientation() const
1632 {
1633 	return fOrientation;
1634 }
1635 
1636 
1637 void
1638 BSlider::SetOrientation(orientation posture)
1639 {
1640 	if (fOrientation == posture)
1641 		return;
1642 
1643 	fOrientation = posture;
1644 	InvalidateLayout();
1645 	Invalidate();
1646 }
1647 
1648 
1649 float
1650 BSlider::BarThickness() const
1651 {
1652 	return fBarThickness;
1653 }
1654 
1655 
1656 void
1657 BSlider::SetBarThickness(float thickness)
1658 {
1659 	if (thickness < 1.0)
1660 		thickness = 1.0;
1661 	else
1662 		thickness = roundf(thickness);
1663 
1664 	if (thickness != fBarThickness) {
1665 		// calculate invalid barframe and extend by hashmark size
1666 		float hInset = 0.0;
1667 		float vInset = 0.0;
1668 		if (fOrientation == B_HORIZONTAL)
1669 			vInset = -6.0;
1670 		else
1671 			hInset = -6.0;
1672 		BRect invalid = BarFrame().InsetByCopy(hInset, vInset) | ThumbFrame();
1673 
1674 		fBarThickness = thickness;
1675 
1676 		invalid = invalid | BarFrame().InsetByCopy(hInset, vInset)
1677 			| ThumbFrame();
1678 		Invalidate(invalid);
1679 		InvalidateLayout();
1680 	}
1681 }
1682 
1683 
1684 void
1685 BSlider::SetFont(const BFont *font, uint32 properties)
1686 {
1687 	BControl::SetFont(font, properties);
1688 
1689 #if USE_OFF_SCREEN_VIEW
1690 	if (fOffScreenView && fOffScreenBits) {
1691 		if (fOffScreenBits->Lock()) {
1692 			fOffScreenView->SetFont(font, properties);
1693 			fOffScreenBits->Unlock();
1694 		}
1695 	}
1696 #endif
1697 
1698 	InvalidateLayout();
1699 }
1700 
1701 
1702 void
1703 BSlider::SetLimits(int32 minimum, int32 maximum)
1704 {
1705 	if (minimum <= maximum) {
1706 		fMinValue = minimum;
1707 		fMaxValue = maximum;
1708 
1709 		int32 value = Value();
1710 		value = max_c(minimum, value);
1711 		value = min_c(maximum, value);
1712 
1713 		if (value != Value()) {
1714 			SetValue(value);
1715 		}
1716 	}
1717 }
1718 
1719 
1720 float
1721 BSlider::MaxUpdateTextWidth()
1722 {
1723 	// very simplistic implementation that assumes the string will be widest
1724 	// at the maximum value
1725 	int32 value = Value();
1726 	SetValueNoUpdate(fMaxValue);
1727 	float width = StringWidth(UpdateText());
1728 	SetValueNoUpdate(value);
1729 	// in case the derived class uses a fixed buffer, the contents
1730 	// should be reset for the old value
1731 	UpdateText();
1732 	return width;
1733 }
1734 
1735 
1736 // #pragma mark - layout related
1737 
1738 
1739 void
1740 BSlider::InvalidateLayout(bool descendants)
1741 {
1742 	// invalidate cached preferred size
1743 	fMinSize.Set(-1, -1);
1744 
1745 	BControl::InvalidateLayout(descendants);
1746 }
1747 
1748 
1749 BSize
1750 BSlider::MinSize()
1751 {
1752 	return BLayoutUtils::ComposeSize(ExplicitMinSize(),
1753 		_ValidateMinSize());
1754 }
1755 
1756 
1757 BSize
1758 BSlider::MaxSize()
1759 {
1760 	BSize maxSize = _ValidateMinSize();
1761 	if (fOrientation == B_HORIZONTAL)
1762 		maxSize.width = B_SIZE_UNLIMITED;
1763 	else
1764 		maxSize.height = B_SIZE_UNLIMITED;
1765 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), maxSize);
1766 }
1767 
1768 
1769 BSize
1770 BSlider::PreferredSize()
1771 {
1772 	BSize preferredSize = _ValidateMinSize();
1773 	if (fOrientation == B_HORIZONTAL)
1774 		preferredSize.width = max_c(100.0, preferredSize.width);
1775 	else
1776 		preferredSize.height = max_c(100.0, preferredSize.height);
1777 	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), preferredSize);
1778 }
1779 
1780 
1781 // #pragma mark - private
1782 
1783 void
1784 BSlider::_DrawBlockThumb()
1785 {
1786 	BRect frame = ThumbFrame();
1787 	BView *view = OffscreenView();
1788 
1789 	if (be_control_look != NULL) {
1790 		rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
1791 		uint32 flags = be_control_look->Flags(this);
1792 		be_control_look->DrawSliderThumb(view, frame, frame, base, flags,
1793 			fOrientation);
1794 		return;
1795 	}
1796 
1797 	rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR);
1798 	rgb_color lighten2;
1799 	rgb_color lighten1;
1800 	rgb_color darken2;
1801 	rgb_color darken3;
1802 	rgb_color darkenmax;
1803 
1804 	if (IsEnabled()) {
1805 		lighten2	= tint_color(no_tint, B_LIGHTEN_2_TINT);
1806 		lighten1	= no_tint;
1807 		darken2		= tint_color(no_tint, B_DARKEN_2_TINT);
1808 		darken3		= tint_color(no_tint, B_DARKEN_3_TINT);
1809 		darkenmax	= tint_color(no_tint, B_DARKEN_MAX_TINT);
1810 	} else {
1811 		lighten2	= tint_color(no_tint, B_LIGHTEN_2_TINT);
1812 		lighten1	= tint_color(no_tint, B_LIGHTEN_1_TINT);
1813 		darken2		= tint_color(no_tint, (B_NO_TINT + B_DARKEN_1_TINT) / 2.0);
1814 		darken3		= tint_color(no_tint, B_DARKEN_1_TINT);
1815 		darkenmax	= tint_color(no_tint, B_DARKEN_3_TINT);
1816 	}
1817 
1818 	// blank background for shadow
1819 	// ToDo: this also draws over the hash marks (though it's not *that* noticeable)
1820 	view->SetHighColor(no_tint);
1821 	view->StrokeLine(BPoint(frame.left, frame.top),
1822 					 BPoint(frame.left, frame.top));
1823 
1824 	BRect barFrame = BarFrame();
1825 	if (barFrame.right >= frame.right) {
1826 		// leave out barFrame from shadow background clearing
1827 		view->StrokeLine(BPoint(frame.right, frame.top),
1828 						 BPoint(frame.right, barFrame.top - 1.0f));
1829 		view->StrokeLine(BPoint(frame.right, barFrame.bottom + 1.0f),
1830 						 BPoint(frame.right, frame.bottom));
1831 	} else {
1832 		view->StrokeLine(BPoint(frame.right, frame.top),
1833 						 BPoint(frame.right, frame.bottom));
1834 	}
1835 
1836 	view->StrokeLine(BPoint(frame.left, frame.bottom),
1837 					 BPoint(frame.right - 1.0f, frame.bottom));
1838 	view->StrokeLine(BPoint(frame.left, frame.bottom - 1.0f),
1839 					 BPoint(frame.left, frame.bottom - 1.0f));
1840 	view->StrokeLine(BPoint(frame.right - 1.0f, frame.top),
1841 					 BPoint(frame.right - 1.0f, frame.top));
1842 
1843 	// Outline (top, left)
1844 	view->SetHighColor(darken3);
1845 	view->StrokeLine(BPoint(frame.left, frame.bottom - 2.0f),
1846 					 BPoint(frame.left, frame.top + 1.0f));
1847 	view->StrokeLine(BPoint(frame.left + 1.0f, frame.top),
1848 					 BPoint(frame.right - 2.0f, frame.top));
1849 
1850 	// Shadow
1851 	view->SetHighColor(0, 0, 0, IsEnabled() ? 100 : 50);
1852 	view->SetDrawingMode(B_OP_ALPHA);
1853 	view->StrokeLine(BPoint(frame.right, frame.top + 2.0f),
1854 					 BPoint(frame.right, frame.bottom - 1.0f));
1855 	view->StrokeLine(BPoint(frame.left + 2.0f, frame.bottom),
1856 					 BPoint(frame.right - 1.0f, frame.bottom));
1857 
1858 	view->SetDrawingMode(B_OP_COPY);
1859 	view->SetHighColor(darken3);
1860 	view->StrokeLine(BPoint(frame.right - 1.0f, frame.bottom - 1.0f),
1861 					 BPoint(frame.right - 1.0f, frame.bottom - 1.0f));
1862 
1863 
1864 	// First bevel
1865 	frame.InsetBy(1.0f, 1.0f);
1866 
1867 	view->SetHighColor(darkenmax);
1868 	view->StrokeLine(BPoint(frame.left, frame.bottom),
1869 					 BPoint(frame.right - 1.0f, frame.bottom));
1870 	view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f),
1871 					 BPoint(frame.right, frame.top));
1872 
1873 	view->SetHighColor(lighten2);
1874 	view->StrokeLine(BPoint(frame.left, frame.top),
1875 					 BPoint(frame.left, frame.bottom - 1.0f));
1876 	view->StrokeLine(BPoint(frame.left + 1.0f, frame.top),
1877 					 BPoint(frame.right - 1.0f, frame.top));
1878 
1879 	frame.InsetBy(1.0f, 1.0f);
1880 
1881 	view->FillRect(BRect(frame.left, frame.top, frame.right - 1.0f, frame.bottom - 1.0f));
1882 
1883 	// Second bevel and center dots
1884 	view->SetHighColor(darken2);
1885 	view->StrokeLine(BPoint(frame.left, frame.bottom),
1886 					 BPoint(frame.right, frame.bottom));
1887 	view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f),
1888 					 BPoint(frame.right, frame.top));
1889 
1890 	if (Orientation() == B_HORIZONTAL) {
1891 		view->StrokeLine(BPoint(frame.left + 6.0f, frame.top + 2.0f),
1892 						 BPoint(frame.left + 6.0f, frame.top + 2.0f));
1893 		view->StrokeLine(BPoint(frame.left + 6.0f, frame.top + 4.0f),
1894 						 BPoint(frame.left + 6.0f, frame.top + 4.0f));
1895 		view->StrokeLine(BPoint(frame.left + 6.0f, frame.top + 6.0f),
1896 						 BPoint(frame.left + 6.0f, frame.top + 6.0f));
1897 	} else {
1898 		view->StrokeLine(BPoint(frame.left + 2.0f, frame.top + 6.0f),
1899 						 BPoint(frame.left + 2.0f, frame.top + 6.0f));
1900 		view->StrokeLine(BPoint(frame.left + 4.0f, frame.top + 6.0f),
1901 						 BPoint(frame.left + 4.0f, frame.top + 6.0f));
1902 		view->StrokeLine(BPoint(frame.left + 6.0f, frame.top + 6.0f),
1903 						 BPoint(frame.left + 6.0f, frame.top + 6.0f));
1904 	}
1905 
1906 	frame.InsetBy(1.0f, 1.0f);
1907 
1908 	// Third bevel
1909 	view->SetHighColor(lighten1);
1910 	view->StrokeLine(BPoint(frame.left, frame.bottom),
1911 					 BPoint(frame.right, frame.bottom));
1912 	view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f),
1913 					 BPoint(frame.right, frame.top));
1914 }
1915 
1916 
1917 void
1918 BSlider::_DrawTriangleThumb()
1919 {
1920 	BRect frame = ThumbFrame();
1921 	BView *view = OffscreenView();
1922 
1923 	if (be_control_look != NULL) {
1924 		rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
1925 		uint32 flags = be_control_look->Flags(this);
1926 		be_control_look->DrawSliderTriangle(view, frame, frame, base, flags,
1927 			fOrientation);
1928 		return;
1929 	}
1930 
1931 	rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR);
1932 	rgb_color lightenmax;
1933 	rgb_color lighten1;
1934 	rgb_color darken2;
1935 	rgb_color darken3;
1936 	rgb_color darkenmax;
1937 
1938 	if (IsEnabled()) {
1939 		lightenmax	= tint_color(no_tint, B_LIGHTEN_MAX_TINT);
1940 		lighten1	= no_tint;
1941 		darken2		= tint_color(no_tint, B_DARKEN_2_TINT);
1942 		darken3		= tint_color(no_tint, B_DARKEN_3_TINT);
1943 		darkenmax	= tint_color(no_tint, B_DARKEN_MAX_TINT);
1944 	} else {
1945 		lightenmax	= tint_color(no_tint, B_LIGHTEN_2_TINT);
1946 		lighten1	= tint_color(no_tint, B_LIGHTEN_1_TINT);
1947 		darken2		= tint_color(no_tint, (B_NO_TINT + B_DARKEN_1_TINT) / 2);
1948 		darken3		= tint_color(no_tint, B_DARKEN_1_TINT);
1949 		darkenmax	= tint_color(no_tint, B_DARKEN_3_TINT);
1950 	}
1951 
1952 	if (Orientation() == B_HORIZONTAL) {
1953 		view->SetHighColor(lighten1);
1954 		view->FillTriangle(
1955 			BPoint(frame.left + 1, frame.bottom - 3),
1956 			BPoint((frame.left + frame.right) / 2, frame.top + 1),
1957 			BPoint(frame.right - 1, frame.bottom - 3));
1958 
1959 		view->SetHighColor(no_tint);
1960 		view->StrokeLine(BPoint(frame.right - 2, frame.bottom - 3),
1961 			BPoint(frame.left + 3, frame.bottom - 3));
1962 
1963 		view->SetHighColor(darkenmax);
1964 		view->StrokeLine(BPoint(frame.left, frame.bottom - 1),
1965 			BPoint(frame.right, frame.bottom - 1));
1966 		view->StrokeLine(BPoint(frame.right, frame.bottom - 2),
1967 			BPoint((frame.left + frame.right) / 2, frame.top));
1968 
1969 		view->SetHighColor(darken2);
1970 		view->StrokeLine(BPoint(frame.right - 1, frame.bottom - 2),
1971 			BPoint(frame.left + 1, frame.bottom - 2));
1972 		view->SetHighColor(darken3);
1973 		view->StrokeLine(BPoint(frame.left, frame.bottom - 2),
1974 			BPoint((frame.left + frame.right) / 2 - 1, frame.top + 1));
1975 
1976 		view->SetHighColor(lightenmax);
1977 		view->StrokeLine(BPoint(frame.left + 2, frame.bottom - 3),
1978 			BPoint((frame.left + frame.right) / 2, frame.top + 1));
1979 
1980 		// Shadow
1981 		view->SetHighColor(0, 0, 0, IsEnabled() ? 80 : 40);
1982 		view->SetDrawingMode(B_OP_ALPHA);
1983 		view->StrokeLine(BPoint(frame.left + 1, frame.bottom),
1984 			BPoint(frame.right, frame.bottom));
1985 	} else {
1986 		view->SetHighColor(lighten1);
1987 		view->FillTriangle(
1988 			BPoint(frame.left, (frame.top + frame.bottom) / 2),
1989 			BPoint(frame.right - 1, frame.top + 1),
1990 			BPoint(frame.right - 1, frame.bottom - 1));
1991 
1992 		view->SetHighColor(darkenmax);
1993 		view->StrokeLine(BPoint(frame.right - 1, frame.top),
1994 			BPoint(frame.right - 1, frame.bottom));
1995 		view->StrokeLine(BPoint(frame.right - 1, frame.bottom),
1996 			BPoint(frame.right - 2, frame.bottom));
1997 
1998 		view->SetHighColor(darken2);
1999 		view->StrokeLine(BPoint(frame.right - 2, frame.top + 2),
2000 			BPoint(frame.right - 2, frame.bottom - 1));
2001 		view->StrokeLine(
2002 			BPoint(frame.left, (frame.top + frame.bottom) / 2),
2003 			BPoint(frame.right - 2, frame.top));
2004 		view->SetHighColor(darken3);
2005 		view->StrokeLine(
2006 			BPoint(frame.left + 1, (frame.top + frame.bottom) / 2 + 1),
2007 			BPoint(frame.right - 3, frame.bottom - 1));
2008 
2009 		view->SetHighColor(lightenmax);
2010 		view->StrokeLine(
2011 			BPoint(frame.left + 1, (frame.top + frame.bottom) / 2),
2012 			BPoint(frame.right - 2, frame.top + 1));
2013 
2014 		// Shadow
2015 		view->SetHighColor(0, 0, 0, IsEnabled() ? 80 : 40);
2016 		view->SetDrawingMode(B_OP_ALPHA);
2017 		view->StrokeLine(BPoint(frame.right, frame.top + 1),
2018 			BPoint(frame.right, frame.bottom));
2019 	}
2020 
2021 	view->SetDrawingMode(B_OP_COPY);
2022 }
2023 
2024 
2025 BPoint
2026 BSlider::_Location() const
2027 {
2028 	return fLocation;
2029 }
2030 
2031 
2032 void
2033 BSlider::_SetLocationForValue(int32 value)
2034 {
2035 	BPoint loc;
2036 	float range = (float)(fMaxValue - fMinValue);
2037 	if (range == 0)
2038 		range = 1;
2039 
2040 	float pos = (float)(value - fMinValue) / range *
2041 		(_MaxPosition() - _MinPosition());
2042 
2043 	if (fOrientation == B_HORIZONTAL) {
2044 		loc.x = ceil(_MinPosition() + pos);
2045 		loc.y = 0;
2046 	} else {
2047 		loc.x = 0;
2048 		loc.y = floor(_MaxPosition() - pos);
2049 	}
2050 	fLocation = loc;
2051 }
2052 
2053 
2054 float
2055 BSlider::_MinPosition() const
2056 {
2057 	if (fOrientation == B_HORIZONTAL)
2058 		return BarFrame().left + 1.0f;
2059 
2060 	return BarFrame().bottom - 1.0f;
2061 }
2062 
2063 
2064 float
2065 BSlider::_MaxPosition() const
2066 {
2067 	if (fOrientation == B_HORIZONTAL)
2068 		return BarFrame().right - 1.0f;
2069 
2070 	return BarFrame().top + 1.0f;
2071 }
2072 
2073 
2074 BSize
2075 BSlider::_ValidateMinSize()
2076 {
2077 	if (fMinSize.width >= 0) {
2078 		// the preferred size is up to date
2079 		return fMinSize;
2080 	}
2081 
2082 	font_height fontHeight;
2083 	GetFontHeight(&fontHeight);
2084 
2085 	float width = 0.0;
2086 	float height = 0.0;
2087 
2088 	if (fMaxUpdateTextWidth < 0.0)
2089 		fMaxUpdateTextWidth = MaxUpdateTextWidth();
2090 
2091 	if (Orientation() == B_HORIZONTAL) {
2092 		height = 12.0 + fBarThickness;
2093 		int32 rows = 0;
2094 
2095 		float labelWidth = 0;
2096 		int32 labelRows = 0;
2097 		float labelSpacing = StringWidth("M") * 2;
2098 		if (Label()) {
2099 			labelWidth = StringWidth(Label());
2100 			labelRows = 1;
2101 		}
2102 		if (fMaxUpdateTextWidth > 0.0) {
2103 			if (labelWidth > 0)
2104 				labelWidth += labelSpacing;
2105 			labelWidth += fMaxUpdateTextWidth;
2106 			labelRows = 1;
2107 		}
2108 		rows += labelRows;
2109 
2110 		if (MinLimitLabel())
2111 			width = StringWidth(MinLimitLabel());
2112 		if (MaxLimitLabel()) {
2113 			// some space between the labels
2114 			if (MinLimitLabel())
2115 				width += labelSpacing;
2116 
2117 			width += StringWidth(MaxLimitLabel());
2118 		}
2119 
2120 		if (labelWidth > width)
2121 			width = labelWidth;
2122 		if (width < 32.0)
2123 			width = 32.0;
2124 
2125 		if (MinLimitLabel() || MaxLimitLabel())
2126 			rows++;
2127 
2128 		height += rows * (ceilf(fontHeight.ascent)
2129 			+ ceilf(fontHeight.descent) + 4.0);
2130 	} else {
2131 		// B_VERTICAL
2132 		width = 12.0 + fBarThickness;
2133 		height = 32.0;
2134 
2135 		float lineHeightNoLeading = ceilf(fontHeight.ascent)
2136 			+ ceilf(fontHeight.descent);
2137 		float lineHeight = lineHeightNoLeading + ceilf(fontHeight.leading);
2138 
2139 		// find largest label
2140 		float labelWidth = 0;
2141 		if (Label()) {
2142 			labelWidth = StringWidth(Label());
2143 			height += lineHeightNoLeading;
2144 		}
2145 		if (MaxLimitLabel()) {
2146 			labelWidth = max_c(labelWidth, StringWidth(MaxLimitLabel()));
2147 			height += Label() ? lineHeight : lineHeightNoLeading;
2148 		}
2149 		if (MinLimitLabel()) {
2150 			labelWidth = max_c(labelWidth, StringWidth(MinLimitLabel()));
2151 			height += lineHeightNoLeading;
2152 		}
2153 		if (fMaxUpdateTextWidth > 0.0) {
2154 			labelWidth = max_c(labelWidth, fMaxUpdateTextWidth);
2155 			height += MinLimitLabel() ? lineHeight : lineHeightNoLeading;
2156 		}
2157 
2158 		width = max_c(labelWidth, width);
2159 	}
2160 
2161 	fMinSize.width = width;
2162 	fMinSize.height = height;
2163 
2164 	ResetLayoutInvalidation();
2165 
2166 	return fMinSize;
2167 }
2168 
2169 
2170 // #pragma mark - FBC padding
2171 
2172 void BSlider::_ReservedSlider6() {}
2173 void BSlider::_ReservedSlider7() {}
2174 void BSlider::_ReservedSlider8() {}
2175 void BSlider::_ReservedSlider9() {}
2176 void BSlider::_ReservedSlider10() {}
2177 void BSlider::_ReservedSlider11() {}
2178 void BSlider::_ReservedSlider12() {}
2179 
2180 
2181 BSlider &
2182 BSlider::operator=(const BSlider &)
2183 {
2184 	return *this;
2185 }
2186 
2187 
2188 //	#pragma mark - BeOS compatibility
2189 
2190 
2191 #if __GNUC__ < 3
2192 
2193 extern "C" void
2194 GetLimits__7BSliderPlT1(BSlider* slider, int32* minimum, int32* maximum)
2195 {
2196 	slider->GetLimits(minimum, maximum);
2197 }
2198 
2199 
2200 extern "C" void
2201 _ReservedSlider4__7BSlider(BSlider *slider, int32 minimum, int32 maximum)
2202 {
2203 	slider->BSlider::SetLimits(minimum, maximum);
2204 }
2205 
2206 extern "C" float
2207 _ReservedSlider5__7BSlider(BSlider *slider)
2208 {
2209 	return slider->BSlider::MaxUpdateTextWidth();
2210 }
2211 
2212 
2213 extern "C" void
2214 _ReservedSlider1__7BSlider(BSlider* slider, orientation _orientation)
2215 {
2216 	slider->BSlider::SetOrientation(_orientation);
2217 }
2218 
2219 
2220 extern "C" void
2221 _ReservedSlider2__7BSlider(BSlider* slider, float thickness)
2222 {
2223 	slider->BSlider::SetBarThickness(thickness);
2224 }
2225 
2226 
2227 extern "C" void
2228 _ReservedSlider3__7BSlider(BSlider* slider, const BFont* font,
2229 	uint32 properties)
2230 {
2231 	slider->BSlider::SetFont(font, properties);
2232 }
2233 
2234 #endif	// __GNUC__ < 3
2235