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