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