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