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