xref: /haiku/src/kits/interface/Slider.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
1 /*
2  * Copyright 2001-2016 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Stephan Aßmus <superstippi@gmx.de>
7  *		Axel Dörfler, axeld@pinc-software.de
8  *		Marc Flerackers (mflerackers@androme.be)
9  */
10 
11 
12 #include <Slider.h>
13 
14 #include <algorithm>
15 
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 
20 #include <Bitmap.h>
21 #include <ControlLook.h>
22 #include <Errors.h>
23 #include <LayoutUtils.h>
24 #include <Message.h>
25 #include <Region.h>
26 #include <String.h>
27 #include <Window.h>
28 
29 #include <binary_compatibility/Interface.h>
30 
31 
32 #define USE_OFF_SCREEN_VIEW 0
33 
34 
35 BSlider::BSlider(
36 	BRect frame, const char* name, const char* label, BMessage* message,
37 	int32 minValue, int32 maxValue, thumb_style thumbType, uint32 resizingMode,
38 	uint32 flags)
39 	:
40 	BControl(frame, name, label, message, resizingMode, flags),
41 	fModificationMessage(NULL),
42 	fSnoozeAmount(20000),
43 
44 	fMinLimitLabel(NULL),
45 	fMaxLimitLabel(NULL),
46 
47 	fMinValue(minValue),
48 	fMaxValue(maxValue),
49 	fKeyIncrementValue(1),
50 
51 	fHashMarkCount(0),
52 	fHashMarks(B_HASH_MARKS_NONE),
53 
54 	fStyle(thumbType),
55 
56 	fOrientation(B_HORIZONTAL),
57 	fBarThickness(6.0)
58 {
59 	_InitBarColor();
60 
61 	_InitObject();
62 	SetValue(0);
63 }
64 
65 
66 BSlider::BSlider(BRect frame, const char* name, const char* label,
67 	BMessage* message, int32 minValue, int32 maxValue, orientation posture,
68 	thumb_style thumbType, uint32 resizingMode, uint32 flags)
69 	:
70 	BControl(frame, name, label, message, resizingMode, flags),
71 	fModificationMessage(NULL),
72 	fSnoozeAmount(20000),
73 
74 	fMinLimitLabel(NULL),
75 	fMaxLimitLabel(NULL),
76 
77 	fMinValue(minValue),
78 	fMaxValue(maxValue),
79 	fKeyIncrementValue(1),
80 
81 	fHashMarkCount(0),
82 	fHashMarks(B_HASH_MARKS_NONE),
83 
84 	fStyle(thumbType),
85 
86 	fOrientation(posture),
87 	fBarThickness(6.0)
88 {
89 	_InitBarColor();
90 
91 	_InitObject();
92 	SetValue(0);
93 }
94 
95 
96 BSlider::BSlider(const char* name, const char* label, BMessage* message,
97 	int32 minValue, int32 maxValue, orientation posture, thumb_style thumbType,
98 	uint32 flags)
99 	:
100 	BControl(name, label, message, flags),
101 	fModificationMessage(NULL),
102 	fSnoozeAmount(20000),
103 
104 	fMinLimitLabel(NULL),
105 	fMaxLimitLabel(NULL),
106 
107 	fMinValue(minValue),
108 	fMaxValue(maxValue),
109 	fKeyIncrementValue(1),
110 
111 	fHashMarkCount(0),
112 	fHashMarks(B_HASH_MARKS_NONE),
113 
114 	fStyle(thumbType),
115 
116 	fOrientation(posture),
117 	fBarThickness(6.0)
118 {
119 	_InitBarColor();
120 
121 	_InitObject();
122 	SetValue(0);
123 }
124 
125 
126 BSlider::BSlider(BMessage* archive)
127 	:
128 	BControl(archive)
129 {
130 	fModificationMessage = NULL;
131 
132 	if (archive->HasMessage("_mod_msg")) {
133 		BMessage* message = new BMessage;
134 
135 		archive->FindMessage("_mod_msg", message);
136 
137 		SetModificationMessage(message);
138 	}
139 
140 	if (archive->FindInt32("_sdelay", &fSnoozeAmount) != B_OK)
141 		SetSnoozeAmount(20000);
142 
143 	rgb_color color;
144 	if (archive->FindInt32("_fcolor", (int32*)&color) == B_OK)
145 		UseFillColor(true, &color);
146 	else
147 		UseFillColor(false);
148 
149 	int32 orient;
150 	if (archive->FindInt32("_orient", &orient) == B_OK)
151 		fOrientation = (orientation)orient;
152 	else
153 		fOrientation = B_HORIZONTAL;
154 
155 	fMinLimitLabel = NULL;
156 	fMaxLimitLabel = NULL;
157 
158 	const char* minlbl = NULL;
159 	const char* maxlbl = NULL;
160 
161 	archive->FindString("_minlbl", &minlbl);
162 	archive->FindString("_maxlbl", &maxlbl);
163 
164 	SetLimitLabels(minlbl, maxlbl);
165 
166 	if (archive->FindInt32("_min", &fMinValue) != B_OK)
167 		fMinValue = 0;
168 
169 	if (archive->FindInt32("_max", &fMaxValue) != B_OK)
170 		fMaxValue = 100;
171 
172 	if (archive->FindInt32("_incrementvalue", &fKeyIncrementValue) != B_OK)
173 		fKeyIncrementValue = 1;
174 
175 	if (archive->FindInt32("_hashcount", &fHashMarkCount) != B_OK)
176 		fHashMarkCount = 11;
177 
178 	int16 hashloc;
179 	if (archive->FindInt16("_hashloc", &hashloc) == B_OK)
180 		fHashMarks = (hash_mark_location)hashloc;
181 	else
182 		fHashMarks = B_HASH_MARKS_NONE;
183 
184 	int16 sstyle;
185 	if (archive->FindInt16("_sstyle", &sstyle) == B_OK)
186 		fStyle = (thumb_style)sstyle;
187 	else
188 		fStyle = B_BLOCK_THUMB;
189 
190 	if (archive->FindInt32("_bcolor", (int32*)&color) != B_OK)
191 		color = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_4_TINT);
192 	SetBarColor(color);
193 
194 	float bthickness;
195 	if (archive->FindFloat("_bthickness", &bthickness) == B_OK)
196 		fBarThickness = bthickness;
197 	else
198 		fBarThickness = 6.0f;
199 
200 	_InitObject();
201 }
202 
203 
204 BSlider::~BSlider()
205 {
206 #if USE_OFF_SCREEN_VIEW
207 	delete fOffScreenBits;
208 #endif
209 
210 	delete fModificationMessage;
211 	free(fMinLimitLabel);
212 	free(fMaxLimitLabel);
213 }
214 
215 
216 void
217 BSlider::_InitBarColor()
218 {
219 	SetBarColor(be_control_look->SliderBarColor(
220 		ui_color(B_PANEL_BACKGROUND_COLOR)));
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 
265 	if (ret == B_OK)
266 		ret = archive->AddInt32("_bcolor", (const uint32&)fBarColor);
267 
268 	if (FillColor(NULL) && ret == B_OK)
269 		ret = archive->AddInt32("_fcolor", (const uint32&)fFillColor);
270 
271 	if (ret == B_OK && fMinLimitLabel != NULL)
272 		ret = archive->AddString("_minlbl", fMinLimitLabel);
273 
274 	if (ret == B_OK && fMaxLimitLabel != NULL)
275 		ret = archive->AddString("_maxlbl", fMaxLimitLabel);
276 
277 	if (ret == B_OK)
278 		ret = archive->AddInt32("_min", fMinValue);
279 
280 	if (ret == B_OK)
281 		ret = archive->AddInt32("_max", fMaxValue);
282 
283 	if (ret == B_OK)
284 		ret = archive->AddInt32("_incrementvalue", fKeyIncrementValue);
285 
286 	if (ret == B_OK)
287 		ret = archive->AddInt32("_hashcount", fHashMarkCount);
288 
289 	if (ret == B_OK)
290 		ret = archive->AddInt16("_hashloc", fHashMarks);
291 
292 	if (ret == B_OK)
293 		ret = archive->AddInt16("_sstyle", fStyle);
294 
295 	if (ret == B_OK)
296 		ret = archive->AddInt32("_orient", fOrientation);
297 
298 	if (ret == B_OK)
299 		ret = archive->AddFloat("_bthickness", fBarThickness);
300 
301 	return ret;
302 }
303 
304 
305 status_t
306 BSlider::Perform(perform_code code, void* _data)
307 {
308 	switch (code) {
309 		case PERFORM_CODE_MIN_SIZE:
310 			((perform_data_min_size*)_data)->return_value = BSlider::MinSize();
311 			return B_OK;
312 
313 		case PERFORM_CODE_MAX_SIZE:
314 			((perform_data_max_size*)_data)->return_value = BSlider::MaxSize();
315 			return B_OK;
316 
317 		case PERFORM_CODE_PREFERRED_SIZE:
318 			((perform_data_preferred_size*)_data)->return_value
319 				= BSlider::PreferredSize();
320 			return B_OK;
321 
322 		case PERFORM_CODE_LAYOUT_ALIGNMENT:
323 			((perform_data_layout_alignment*)_data)->return_value
324 				= BSlider::LayoutAlignment();
325 			return B_OK;
326 
327 		case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
328 			((perform_data_has_height_for_width*)_data)->return_value
329 				= BSlider::HasHeightForWidth();
330 			return B_OK;
331 
332 		case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
333 		{
334 			perform_data_get_height_for_width* data
335 				= (perform_data_get_height_for_width*)_data;
336 			BSlider::GetHeightForWidth(data->width, &data->min, &data->max,
337 				&data->preferred);
338 			return B_OK;
339 		}
340 
341 		case PERFORM_CODE_SET_LAYOUT:
342 		{
343 			perform_data_set_layout* data = (perform_data_set_layout*)_data;
344 			BSlider::SetLayout(data->layout);
345 			return B_OK;
346 		}
347 
348 		case PERFORM_CODE_LAYOUT_INVALIDATED:
349 		{
350 			perform_data_layout_invalidated* data
351 				= (perform_data_layout_invalidated*)_data;
352 			BSlider::LayoutInvalidated(data->descendants);
353 			return B_OK;
354 		}
355 
356 		case PERFORM_CODE_DO_LAYOUT:
357 		{
358 			BSlider::DoLayout();
359 			return B_OK;
360 		}
361 
362 		case PERFORM_CODE_SET_ICON:
363 		{
364 			perform_data_set_icon* data = (perform_data_set_icon*)_data;
365 			return BSlider::SetIcon(data->icon, data->flags);
366 		}
367 	}
368 
369 	return BControl::Perform(code, _data);
370 }
371 
372 
373 void
374 BSlider::WindowActivated(bool state)
375 {
376 	BControl::WindowActivated(state);
377 }
378 
379 
380 void
381 BSlider::AttachedToWindow()
382 {
383 	ResizeToPreferred();
384 
385 #if USE_OFF_SCREEN_VIEW
386 	BRect bounds(Bounds());
387 
388 	if (!fOffScreenView) {
389 		fOffScreenView = new BView(bounds, "", B_FOLLOW_ALL, B_WILL_DRAW);
390 
391 		BFont font;
392 		GetFont(&font);
393 		fOffScreenView->SetFont(&font);
394 	}
395 
396 	if (!fOffScreenBits) {
397 		fOffScreenBits = new BBitmap(bounds, B_RGBA32, true, false);
398 
399 		if (fOffScreenBits && fOffScreenView)
400 			fOffScreenBits->AddChild(fOffScreenView);
401 
402 	} else if (fOffScreenView)
403 		fOffScreenBits->AddChild(fOffScreenView);
404 #endif // USE_OFF_SCREEN_VIEW
405 
406 	BControl::AttachedToWindow();
407 
408 	BView* view = OffscreenView();
409 	if (view != NULL && view->LockLooper()) {
410 		view->SetViewColor(B_TRANSPARENT_COLOR);
411 		if (LowUIColor() != B_NO_COLOR)
412 			view->SetLowUIColor(LowUIColor());
413 		else
414 			view->SetLowColor(LowColor());
415 
416 		view->UnlockLooper();
417 	}
418 
419 	int32 value = Value();
420 	SetValue(value);
421 		// makes sure the value is within valid bounds
422 	_SetLocationForValue(Value());
423 		// makes sure the location is correct
424 	UpdateTextChanged();
425 }
426 
427 
428 void
429 BSlider::AllAttached()
430 {
431 	BControl::AllAttached();
432 
433 	// When using a layout we may not have a parent, so we need to employ the
434 	// standard system colors manually. Due to how layouts work, this must
435 	// happen here, rather than in AttachedToWindow().
436 	if (Parent() == NULL)
437 		SetLowUIColor(B_PANEL_BACKGROUND_COLOR);
438 }
439 
440 
441 void
442 BSlider::AllDetached()
443 {
444 	BControl::AllDetached();
445 }
446 
447 
448 void
449 BSlider::DetachedFromWindow()
450 {
451 	BControl::DetachedFromWindow();
452 
453 #if USE_OFF_SCREEN_VIEW
454 	if (fOffScreenBits) {
455 		delete fOffScreenBits;
456 		fOffScreenBits = NULL;
457 		fOffScreenView = NULL;
458 	}
459 #endif
460 }
461 
462 
463 void
464 BSlider::MessageReceived(BMessage* message)
465 {
466 	BControl::MessageReceived(message);
467 }
468 
469 
470 void
471 BSlider::FrameMoved(BPoint new_position)
472 {
473 	BControl::FrameMoved(new_position);
474 }
475 
476 
477 void
478 BSlider::FrameResized(float w,float h)
479 {
480 	BControl::FrameResized(w, h);
481 
482 	BRect bounds(Bounds());
483 
484 	if (bounds.right <= 0.0f || bounds.bottom <= 0.0f)
485 		return;
486 
487 #if USE_OFF_SCREEN_VIEW
488 	if (fOffScreenBits) {
489 		fOffScreenBits->RemoveChild(fOffScreenView);
490 		delete fOffScreenBits;
491 
492 		fOffScreenView->ResizeTo(bounds.Width(), bounds.Height());
493 
494 		fOffScreenBits = new BBitmap(Bounds(), B_RGBA32, true, false);
495 		fOffScreenBits->AddChild(fOffScreenView);
496 	}
497 #endif
498 
499 	Invalidate();
500 }
501 
502 
503 void
504 BSlider::KeyDown(const char* bytes, int32 numBytes)
505 {
506 	if (!IsEnabled() || IsHidden())
507 		return;
508 
509 	int32 newValue = Value();
510 
511 	switch (bytes[0]) {
512 		case B_LEFT_ARROW:
513 		case B_DOWN_ARROW:
514 			newValue -= KeyIncrementValue();
515 			break;
516 
517 		case B_RIGHT_ARROW:
518 		case B_UP_ARROW:
519 			newValue += KeyIncrementValue();
520 			break;
521 
522 		case B_HOME:
523 			newValue = fMinValue;
524 			break;
525 
526 		case B_END:
527 			newValue = fMaxValue;
528 			break;
529 
530 		default:
531 			BControl::KeyDown(bytes, numBytes);
532 			return;
533 	}
534 
535 	if (newValue < fMinValue)
536 		newValue = fMinValue;
537 
538 	if (newValue > fMaxValue)
539 		newValue = fMaxValue;
540 
541 	if (newValue != Value()) {
542 		fInitialLocation = _Location();
543 		SetValue(newValue);
544 		InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED);
545 	}
546 }
547 
548 void
549 BSlider::KeyUp(const char* bytes, int32 numBytes)
550 {
551 	if (fInitialLocation != _Location()) {
552 		// The last KeyDown event triggered the modification message or no
553 		// notification at all, we may also have sent the modification message
554 		// continually while the user kept pressing the key. In either case,
555 		// finish with the final message to make the behavior consistent with
556 		// changing the value by mouse.
557 		Invoke();
558 	}
559 }
560 
561 
562 /*!
563 	Makes sure the \a point is within valid bounds.
564 	Returns \c true if the relevant coordinate (depending on the orientation
565 	of the slider) differs from \a comparePoint.
566 */
567 bool
568 BSlider::_ConstrainPoint(BPoint& point, BPoint comparePoint) const
569 {
570 	if (fOrientation == B_HORIZONTAL) {
571 		if (point.x != comparePoint.x) {
572 			if (point.x < _MinPosition())
573 				point.x = _MinPosition();
574 			else if (point.x > _MaxPosition())
575 				point.x = _MaxPosition();
576 
577 			return true;
578 		}
579 	} else {
580 		if (point.y != comparePoint.y) {
581 			if (point.y > _MinPosition())
582 				point.y = _MinPosition();
583 			else if (point.y < _MaxPosition())
584 				point.y = _MaxPosition();
585 
586 			return true;
587 		}
588 	}
589 
590 	return false;
591 }
592 
593 
594 void
595 BSlider::MouseDown(BPoint point)
596 {
597 	if (!IsEnabled())
598 		return;
599 
600 	if (BarFrame().Contains(point) || ThumbFrame().Contains(point))
601 		fInitialLocation = _Location();
602 
603 	uint32 buttons;
604 	GetMouse(&point, &buttons, true);
605 
606 	_ConstrainPoint(point, fInitialLocation);
607 	SetValue(ValueForPoint(point));
608 
609 	if (_Location() != fInitialLocation)
610 		InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED);
611 
612 	if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) {
613 		SetTracking(true);
614 		SetMouseEventMask(B_POINTER_EVENTS,
615 			B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY);
616 	} else {
617 		// synchronous mouse tracking
618 		BPoint prevPoint;
619 
620 		while (buttons) {
621 			prevPoint = point;
622 
623 			snooze(SnoozeAmount());
624 			GetMouse(&point, &buttons, true);
625 
626 			if (_ConstrainPoint(point, prevPoint)) {
627 				int32 value = ValueForPoint(point);
628 				if (value != Value()) {
629 					SetValue(value);
630 					InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED);
631 				}
632 			}
633 		}
634 		if (_Location() != fInitialLocation)
635 			Invoke();
636 	}
637 }
638 
639 
640 void
641 BSlider::MouseUp(BPoint point)
642 {
643 	if (IsTracking()) {
644 		if (_Location() != fInitialLocation)
645 			Invoke();
646 
647 		SetTracking(false);
648 	} else
649 		BControl::MouseUp(point);
650 }
651 
652 
653 void
654 BSlider::MouseMoved(BPoint point, uint32 transit, const BMessage* message)
655 {
656 	if (IsTracking()) {
657 		if (_ConstrainPoint(point, _Location())) {
658 			int32 value = ValueForPoint(point);
659 			if (value != Value()) {
660 				SetValue(value);
661 				InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED);
662 			}
663 		}
664 	} else
665 		BControl::MouseMoved(point, transit, message);
666 }
667 
668 
669 void
670 BSlider::Pulse()
671 {
672 	BControl::Pulse();
673 }
674 
675 
676 void
677 BSlider::SetLabel(const char* label)
678 {
679 	BControl::SetLabel(label);
680 }
681 
682 
683 void
684 BSlider::SetLimitLabels(const char* minLabel, const char* maxLabel)
685 {
686 	free(fMinLimitLabel);
687 	fMinLimitLabel = minLabel ? strdup(minLabel) : NULL;
688 
689 	free(fMaxLimitLabel);
690 	fMaxLimitLabel = maxLabel ? strdup(maxLabel) : NULL;
691 
692 	InvalidateLayout();
693 
694 	// TODO: This is for backwards compatibility and should
695 	// probably be removed when breaking binary compatiblity.
696 	// Applications like our own Mouse rely on this behavior.
697 	if ((Flags() & B_SUPPORTS_LAYOUT) == 0)
698 		ResizeToPreferred();
699 
700 	Invalidate();
701 }
702 
703 
704 const char*
705 BSlider::MinLimitLabel() const
706 {
707 	return fMinLimitLabel;
708 }
709 
710 
711 const char*
712 BSlider::MaxLimitLabel() const
713 {
714 	return fMaxLimitLabel;
715 }
716 
717 
718 void
719 BSlider::SetValue(int32 value)
720 {
721 	if (value < fMinValue)
722 		value = fMinValue;
723 
724 	if (value > fMaxValue)
725 		value = fMaxValue;
726 
727 	if (value == Value())
728 		return;
729 
730 	_SetLocationForValue(value);
731 
732 	BRect oldThumbFrame = ThumbFrame();
733 
734 	// While it would be enough to do this dependent on fUseFillColor,
735 	// that doesn't work out if DrawBar() has been overridden by a sub class
736 	if (fOrientation == B_HORIZONTAL)
737 		oldThumbFrame.top = BarFrame().top;
738 	else
739 		oldThumbFrame.left = BarFrame().left;
740 
741 	BControl::SetValueNoUpdate(value);
742 	BRect invalid = oldThumbFrame | ThumbFrame();
743 
744 	if (Style() == B_TRIANGLE_THUMB) {
745 		// 1) We need to take care of pixels touched because of anti-aliasing.
746 		// 2) We need to update the region with the focus mark as well. (A
747 		// method BSlider::FocusMarkFrame() would be nice as well.)
748 		if (fOrientation == B_HORIZONTAL) {
749 			if (IsFocus())
750 				invalid.bottom += 2;
751 			invalid.InsetBy(-1, 0);
752 		} else {
753 			if (IsFocus())
754 				invalid.left -= 2;
755 			invalid.InsetBy(0, -1);
756 		}
757 	}
758 
759 	Invalidate(invalid);
760 
761 	UpdateTextChanged();
762 }
763 
764 
765 int32
766 BSlider::ValueForPoint(BPoint location) const
767 {
768 	float min;
769 	float max;
770 	float position;
771 	if (fOrientation == B_HORIZONTAL) {
772 		min = _MinPosition();
773 		max = _MaxPosition();
774 		position = location.x;
775 	} else {
776 		max = _MinPosition();
777 		min = _MaxPosition();
778 		position = min + (max - location.y);
779 	}
780 
781 	if (position < min)
782 		position = min;
783 
784 	if (position > max)
785 		position = max;
786 
787 	return (int32)roundf(((position - min) * (fMaxValue - fMinValue)
788 		/ (max - min)) + fMinValue);
789 }
790 
791 
792 void
793 BSlider::SetPosition(float position)
794 {
795 	if (position <= 0.0f)
796 		SetValue(fMinValue);
797 	else if (position >= 1.0f)
798 		SetValue(fMaxValue);
799 	else
800 		SetValue((int32)(position * (fMaxValue - fMinValue) + fMinValue));
801 }
802 
803 
804 float
805 BSlider::Position() const
806 {
807 	float range = (float)(fMaxValue - fMinValue);
808 	if (range == 0.0f)
809 		range = 1.0f;
810 
811 	return (float)(Value() - fMinValue) / range;
812 }
813 
814 
815 void
816 BSlider::SetEnabled(bool on)
817 {
818 	BControl::SetEnabled(on);
819 }
820 
821 
822 void
823 BSlider::GetLimits(int32* minimum, int32* maximum) const
824 {
825 	if (minimum != NULL)
826 		*minimum = fMinValue;
827 
828 	if (maximum != NULL)
829 		*maximum = fMaxValue;
830 }
831 
832 
833 // #pragma mark - drawing
834 
835 
836 void
837 BSlider::Draw(BRect updateRect)
838 {
839 	// clear out background
840 	BRegion background(updateRect);
841 	background.Exclude(BarFrame());
842 	bool drawBackground = true;
843 	if (Parent() != NULL && (Parent()->Flags() & B_DRAW_ON_CHILDREN) != 0) {
844 		// This view is embedded somewhere, most likely the Tracker Desktop
845 		// shelf.
846 		drawBackground = false;
847 	}
848 
849 #if USE_OFF_SCREEN_VIEW
850 	if (!fOffScreenBits)
851 		return;
852 
853 	if (fOffScreenBits->Lock()) {
854 		fOffScreenView->SetViewColor(ViewColor());
855 		fOffScreenView->SetLowColor(LowColor());
856 #endif
857 
858 		if (drawBackground && background.Frame().IsValid())
859 			OffscreenView()->FillRegion(&background, B_SOLID_LOW);
860 
861 #if USE_OFF_SCREEN_VIEW
862 		fOffScreenView->Sync();
863 		fOffScreenBits->Unlock();
864 	}
865 #endif
866 
867 	DrawSlider();
868 }
869 
870 
871 void
872 BSlider::DrawSlider()
873 {
874 	if (LockLooper()) {
875 #if USE_OFF_SCREEN_VIEW
876 		if (fOffScreenBits == NULL)
877 			return;
878 
879 		if (fOffScreenBits->Lock()) {
880 #endif
881 			DrawBar();
882 			DrawHashMarks();
883 			DrawThumb();
884 			DrawFocusMark();
885 			DrawText();
886 
887 #if USE_OFF_SCREEN_VIEW
888 			fOffScreenView->Sync();
889 			fOffScreenBits->Unlock();
890 
891 			DrawBitmap(fOffScreenBits, B_ORIGIN);
892 		}
893 #endif
894 		UnlockLooper();
895 	}
896 }
897 
898 
899 void
900 BSlider::DrawBar()
901 {
902 	BRect frame = BarFrame();
903 	BView* view = OffscreenView();
904 
905 	uint32 flags = be_control_look->Flags(this);
906 	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
907 	rgb_color rightFillColor = fBarColor;
908 	rgb_color leftFillColor = fUseFillColor ? fFillColor : fBarColor;
909 	be_control_look->DrawSliderBar(view, frame, frame, base, leftFillColor,
910 		rightFillColor, Position(), flags, fOrientation);
911 }
912 
913 
914 void
915 BSlider::DrawHashMarks()
916 {
917 	if (fHashMarks == B_HASH_MARKS_NONE)
918 		return;
919 
920 	BRect frame = HashMarksFrame();
921 	BView* view = OffscreenView();
922 
923 	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
924 	uint32 flags = be_control_look->Flags(this);
925 	be_control_look->DrawSliderHashMarks(view, frame, frame, base,
926 		fHashMarkCount, fHashMarks, flags, fOrientation);
927 }
928 
929 
930 void
931 BSlider::DrawThumb()
932 {
933 	if (Style() == B_BLOCK_THUMB)
934 		_DrawBlockThumb();
935 	else
936 		_DrawTriangleThumb();
937 }
938 
939 
940 void
941 BSlider::DrawFocusMark()
942 {
943 	if (!IsFocus())
944 		return;
945 
946 	OffscreenView()->SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
947 
948 	BRect frame = ThumbFrame();
949 
950 	if (fStyle == B_BLOCK_THUMB) {
951 		frame.left += 2.0f;
952 		frame.top += 2.0f;
953 		frame.right -= 3.0f;
954 		frame.bottom -= 3.0f;
955 		OffscreenView()->StrokeRect(frame);
956 	} else {
957 		if (fOrientation == B_HORIZONTAL) {
958 			OffscreenView()->StrokeLine(BPoint(frame.left, frame.bottom + 2.0f),
959 				BPoint(frame.right, frame.bottom + 2.0f));
960 		} else {
961 			OffscreenView()->StrokeLine(BPoint(frame.left - 2.0f, frame.top),
962 				BPoint(frame.left - 2.0f, frame.bottom));
963 		}
964 	}
965 }
966 
967 
968 void
969 BSlider::DrawText()
970 {
971 	BRect bounds(Bounds());
972 	BView* view = OffscreenView();
973 
974 	rgb_color base = LowColor();
975 	uint32 flags = be_control_look->Flags(this);
976 
977 	// erase the is control flag before drawing the label so that the label
978 	// will get drawn using B_PANEL_TEXT_COLOR
979 	flags &= ~BControlLook::B_IS_CONTROL;
980 
981 	font_height fontHeight;
982 	GetFontHeight(&fontHeight);
983 	if (Orientation() == B_HORIZONTAL) {
984 		if (Label() != NULL) {
985 			be_control_look->DrawLabel(view, Label(), base, flags,
986 				BPoint(0.0f, ceilf(fontHeight.ascent)));
987 		}
988 
989 		// the update text is updated in SetValue() only
990 		if (fUpdateText != NULL) {
991 			be_control_look->DrawLabel(view, fUpdateText, base, flags,
992 				BPoint(bounds.right - StringWidth(fUpdateText),
993 					ceilf(fontHeight.ascent)));
994 		}
995 
996 		if (fMinLimitLabel != NULL) {
997 			be_control_look->DrawLabel(view, fMinLimitLabel, base, flags,
998 				BPoint(0.0f, bounds.bottom - fontHeight.descent));
999 		}
1000 
1001 		if (fMaxLimitLabel != NULL) {
1002 			be_control_look->DrawLabel(view, fMaxLimitLabel, base, flags,
1003 				BPoint(bounds.right - StringWidth(fMaxLimitLabel),
1004 					bounds.bottom - fontHeight.descent));
1005 		}
1006 	} else {
1007 		float lineHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent)
1008 			+ ceilf(fontHeight.leading);
1009 		float baseLine = ceilf(fontHeight.ascent);
1010 
1011 		if (Label() != NULL) {
1012 			be_control_look->DrawLabel(view, Label(), base, flags,
1013 				BPoint((bounds.Width() - StringWidth(Label())) / 2.0,
1014 					baseLine));
1015 			baseLine += lineHeight;
1016 		}
1017 
1018 		if (fMaxLimitLabel != NULL) {
1019 			be_control_look->DrawLabel(view, fMaxLimitLabel, base, flags,
1020 				BPoint((bounds.Width() - StringWidth(fMaxLimitLabel)) / 2.0,
1021 					baseLine));
1022 		}
1023 
1024 		baseLine = bounds.bottom - ceilf(fontHeight.descent);
1025 
1026 		if (fMinLimitLabel != NULL) {
1027 			be_control_look->DrawLabel(view, fMinLimitLabel, base, flags,
1028 				BPoint((bounds.Width() - StringWidth(fMinLimitLabel)) / 2.0,
1029 					baseLine));
1030 				baseLine -= lineHeight;
1031 		}
1032 
1033 		if (fUpdateText != NULL) {
1034 			be_control_look->DrawLabel(view, fUpdateText, base, flags,
1035 				BPoint((bounds.Width() - StringWidth(fUpdateText)) / 2.0,
1036 					baseLine));
1037 		}
1038 	}
1039 }
1040 
1041 
1042 // #pragma mark -
1043 
1044 
1045 const char*
1046 BSlider::UpdateText() const
1047 {
1048 	return NULL;
1049 }
1050 
1051 
1052 void
1053 BSlider::UpdateTextChanged()
1054 {
1055 	// update text label
1056 	float oldWidth = 0.0;
1057 	if (fUpdateText != NULL)
1058 		oldWidth = StringWidth(fUpdateText);
1059 
1060 	const char* oldUpdateText = fUpdateText;
1061 	fUpdateText = UpdateText();
1062 	bool updateTextOnOff = (fUpdateText == NULL && oldUpdateText != NULL)
1063 		|| (fUpdateText != NULL && oldUpdateText == NULL);
1064 
1065 	float newWidth = 0.0;
1066 	if (fUpdateText != NULL)
1067 		newWidth = StringWidth(fUpdateText);
1068 
1069 	float width = ceilf(std::max(newWidth, oldWidth)) + 2.0f;
1070 	if (width != 0) {
1071 		font_height fontHeight;
1072 		GetFontHeight(&fontHeight);
1073 
1074 		float height = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent);
1075 		float lineHeight = height + ceilf(fontHeight.leading);
1076 		BRect invalid(Bounds());
1077 		if (fOrientation == B_HORIZONTAL)
1078 			invalid = BRect(invalid.right - width, 0, invalid.right, height);
1079 		else {
1080 			if (!updateTextOnOff) {
1081 				invalid.left = (invalid.left + invalid.right - width) / 2;
1082 				invalid.right = invalid.left + width;
1083 				if (fMinLimitLabel != NULL)
1084 					invalid.bottom -= lineHeight;
1085 
1086 				invalid.top = invalid.bottom - height;
1087 			}
1088 		}
1089 		Invalidate(invalid);
1090 	}
1091 
1092 	float oldMaxUpdateTextWidth = fMaxUpdateTextWidth;
1093 	fMaxUpdateTextWidth = MaxUpdateTextWidth();
1094 	if (oldMaxUpdateTextWidth != fMaxUpdateTextWidth)
1095 		InvalidateLayout();
1096 }
1097 
1098 
1099 BRect
1100 BSlider::BarFrame() const
1101 {
1102 	BRect frame(Bounds());
1103 
1104 	font_height fontHeight;
1105 	GetFontHeight(&fontHeight);
1106 
1107 	float textHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent);
1108 	float leading = ceilf(fontHeight.leading);
1109 
1110 	float thumbInset;
1111 	if (fStyle == B_BLOCK_THUMB)
1112 		thumbInset = 8.0;
1113 	else
1114 		thumbInset = 7.0;
1115 
1116 	if (Orientation() == B_HORIZONTAL) {
1117 		frame.left = thumbInset;
1118 		frame.top = 6.0 + (Label() || fUpdateText ? textHeight + 4.0 : 0.0);
1119 		frame.right -= thumbInset;
1120 		frame.bottom = frame.top + fBarThickness;
1121 	} else {
1122 		frame.left = floorf((frame.Width() - fBarThickness) / 2.0);
1123 		frame.top = thumbInset;
1124 		if (Label() != NULL)
1125 			frame.top += textHeight;
1126 
1127 		if (fMaxLimitLabel != NULL) {
1128 			frame.top += textHeight;
1129 			if (Label())
1130 				frame.top += leading;
1131 		}
1132 
1133 		frame.right = frame.left + fBarThickness;
1134 		frame.bottom = frame.bottom - thumbInset;
1135 		if (fMinLimitLabel != NULL)
1136 			frame.bottom -= textHeight;
1137 
1138 		if (fUpdateText != NULL) {
1139 			frame.bottom -= textHeight;
1140 			if (fMinLimitLabel != NULL)
1141 				frame.bottom -= leading;
1142 		}
1143 	}
1144 
1145 	return frame;
1146 }
1147 
1148 
1149 BRect
1150 BSlider::HashMarksFrame() const
1151 {
1152 	BRect frame(BarFrame());
1153 
1154 	if (fOrientation == B_HORIZONTAL) {
1155 		frame.top -= 6.0;
1156 		frame.bottom += 6.0;
1157 	} else {
1158 		frame.left -= 6.0;
1159 		frame.right += 6.0;
1160 	}
1161 
1162 	return frame;
1163 }
1164 
1165 
1166 BRect
1167 BSlider::ThumbFrame() const
1168 {
1169 	// TODO: The slider looks really ugly and broken when it is too little.
1170 	// I would suggest using BarFrame() here to get the top and bottom coords
1171 	// and spread them further apart for the thumb
1172 
1173 	BRect frame = Bounds();
1174 
1175 	font_height fontHeight;
1176 	GetFontHeight(&fontHeight);
1177 
1178 	float textHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent);
1179 
1180 	if (fStyle == B_BLOCK_THUMB) {
1181 		if (Orientation() == B_HORIZONTAL) {
1182 			frame.left = floorf(Position() * (_MaxPosition()
1183 				- _MinPosition()) + _MinPosition()) - 8;
1184 			frame.top = 2 + (Label() || fUpdateText ? textHeight + 4 : 0);
1185 			frame.right = frame.left + 17;
1186 			frame.bottom = frame.top + fBarThickness + 7;
1187 		} else {
1188 			frame.left = floor((frame.Width() - fBarThickness) / 2) - 4;
1189 			frame.top = floorf(Position() * (_MaxPosition()
1190 				- _MinPosition()) + _MinPosition()) - 8;
1191 			frame.right = frame.left + fBarThickness + 7;
1192 			frame.bottom = frame.top + 17;
1193 		}
1194 	} else {
1195 		if (Orientation() == B_HORIZONTAL) {
1196 			frame.left = floorf(Position() * (_MaxPosition()
1197 				- _MinPosition()) + _MinPosition()) - 6;
1198 			frame.right = frame.left + 12;
1199 			frame.top = 3 + fBarThickness + (Label() ? textHeight + 4 : 0);
1200 			frame.bottom = frame.top + 8;
1201 		} else {
1202 			frame.left = floorf((frame.Width() + fBarThickness) / 2) - 3;
1203 			frame.top = floorf(Position() * (_MaxPosition()
1204 				- _MinPosition())) + _MinPosition() - 6;
1205 			frame.right = frame.left + 8;
1206 			frame.bottom = frame.top + 12;
1207 		}
1208 	}
1209 
1210 	return frame;
1211 }
1212 
1213 
1214 void
1215 BSlider::SetFlags(uint32 flags)
1216 {
1217 	BControl::SetFlags(flags);
1218 }
1219 
1220 
1221 void
1222 BSlider::SetResizingMode(uint32 mode)
1223 {
1224 	BControl::SetResizingMode(mode);
1225 }
1226 
1227 
1228 void
1229 BSlider::GetPreferredSize(float* _width, float* _height)
1230 {
1231 	BSize preferredSize = PreferredSize();
1232 
1233 	if (Orientation() == B_HORIZONTAL) {
1234 		if (_width != NULL) {
1235 			// NOTE: For compatibility reasons, a horizontal BSlider
1236 			// never shrinks horizontally. This only affects applications
1237 			// which do not use the new layout system.
1238 			*_width = std::max(Bounds().Width(), preferredSize.width);
1239 		}
1240 
1241 		if (_height != NULL)
1242 			*_height = preferredSize.height;
1243 	} else {
1244 		if (_width != NULL)
1245 			*_width = preferredSize.width;
1246 
1247 		if (_height != NULL) {
1248 			// NOTE: Similarly, a vertical BSlider never shrinks
1249 			// vertically. This only affects applications which do not
1250 			// use the new layout system.
1251 			*_height = std::max(Bounds().Height(), preferredSize.height);
1252 		}
1253 	}
1254 }
1255 
1256 
1257 void
1258 BSlider::ResizeToPreferred()
1259 {
1260 	BControl::ResizeToPreferred();
1261 }
1262 
1263 
1264 status_t
1265 BSlider::Invoke(BMessage* message)
1266 {
1267 	return BControl::Invoke(message);
1268 }
1269 
1270 
1271 BHandler*
1272 BSlider::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier,
1273 	int32 command, const char* property)
1274 {
1275 	return BControl::ResolveSpecifier(message, index, specifier, command,
1276 		property);
1277 }
1278 
1279 
1280 status_t
1281 BSlider::GetSupportedSuites(BMessage* message)
1282 {
1283 	return BControl::GetSupportedSuites(message);
1284 }
1285 
1286 
1287 void
1288 BSlider::SetModificationMessage(BMessage* message)
1289 {
1290 	delete fModificationMessage;
1291 	fModificationMessage = message;
1292 }
1293 
1294 
1295 BMessage*
1296 BSlider::ModificationMessage() const
1297 {
1298 	return fModificationMessage;
1299 }
1300 
1301 
1302 void
1303 BSlider::SetSnoozeAmount(int32 snoozeTime)
1304 {
1305 	if (snoozeTime < 10000)
1306 		snoozeTime = 10000;
1307 	else if (snoozeTime > 1000000)
1308 		snoozeTime = 1000000;
1309 
1310 	fSnoozeAmount = snoozeTime;
1311 }
1312 
1313 
1314 int32
1315 BSlider::SnoozeAmount() const
1316 {
1317 	return fSnoozeAmount;
1318 }
1319 
1320 
1321 void
1322 BSlider::SetKeyIncrementValue(int32 incrementValue)
1323 {
1324 	fKeyIncrementValue = incrementValue;
1325 }
1326 
1327 
1328 int32
1329 BSlider::KeyIncrementValue() const
1330 {
1331 	return fKeyIncrementValue;
1332 }
1333 
1334 
1335 void
1336 BSlider::SetHashMarkCount(int32 hashMarkCount)
1337 {
1338 	fHashMarkCount = hashMarkCount;
1339 	Invalidate();
1340 }
1341 
1342 
1343 int32
1344 BSlider::HashMarkCount() const
1345 {
1346 	return fHashMarkCount;
1347 }
1348 
1349 
1350 void
1351 BSlider::SetHashMarks(hash_mark_location where)
1352 {
1353 	fHashMarks = where;
1354 // TODO: enable if the hashmark look is influencing the control size!
1355 //	InvalidateLayout();
1356 	Invalidate();
1357 }
1358 
1359 
1360 hash_mark_location
1361 BSlider::HashMarks() const
1362 {
1363 	return fHashMarks;
1364 }
1365 
1366 
1367 void
1368 BSlider::SetStyle(thumb_style style)
1369 {
1370 	fStyle = style;
1371 	InvalidateLayout();
1372 	Invalidate();
1373 }
1374 
1375 
1376 thumb_style
1377 BSlider::Style() const
1378 {
1379 	return fStyle;
1380 }
1381 
1382 
1383 void
1384 BSlider::SetBarColor(rgb_color barColor)
1385 {
1386 	fBarColor = barColor;
1387 	Invalidate(BarFrame());
1388 }
1389 
1390 
1391 rgb_color
1392 BSlider::BarColor() const
1393 {
1394 	return fBarColor;
1395 }
1396 
1397 
1398 void
1399 BSlider::UseFillColor(bool useFill, const rgb_color* barColor)
1400 {
1401 	fUseFillColor = useFill;
1402 
1403 	if (useFill && barColor)
1404 		fFillColor = *barColor;
1405 
1406 	Invalidate(BarFrame());
1407 }
1408 
1409 
1410 bool
1411 BSlider::FillColor(rgb_color* barColor) const
1412 {
1413 	if (barColor && fUseFillColor)
1414 		*barColor = fFillColor;
1415 
1416 	return fUseFillColor;
1417 }
1418 
1419 
1420 BView*
1421 BSlider::OffscreenView() const
1422 {
1423 #if USE_OFF_SCREEN_VIEW
1424 	return fOffScreenView;
1425 #else
1426 	return (BView*)this;
1427 #endif
1428 }
1429 
1430 
1431 orientation
1432 BSlider::Orientation() const
1433 {
1434 	return fOrientation;
1435 }
1436 
1437 
1438 void
1439 BSlider::SetOrientation(orientation posture)
1440 {
1441 	if (fOrientation == posture)
1442 		return;
1443 
1444 	fOrientation = posture;
1445 	InvalidateLayout();
1446 	Invalidate();
1447 }
1448 
1449 
1450 float
1451 BSlider::BarThickness() const
1452 {
1453 	return fBarThickness;
1454 }
1455 
1456 
1457 void
1458 BSlider::SetBarThickness(float thickness)
1459 {
1460 	if (thickness < 1.0)
1461 		thickness = 1.0;
1462 	else
1463 		thickness = roundf(thickness);
1464 
1465 	if (thickness != fBarThickness) {
1466 		// calculate invalid barframe and extend by hashmark size
1467 		float hInset = 0.0;
1468 		float vInset = 0.0;
1469 		if (fOrientation == B_HORIZONTAL)
1470 			vInset = -6.0;
1471 		else
1472 			hInset = -6.0;
1473 		BRect invalid = BarFrame().InsetByCopy(hInset, vInset) | ThumbFrame();
1474 
1475 		fBarThickness = thickness;
1476 
1477 		invalid = invalid | BarFrame().InsetByCopy(hInset, vInset)
1478 			| ThumbFrame();
1479 		Invalidate(invalid);
1480 		InvalidateLayout();
1481 	}
1482 }
1483 
1484 
1485 void
1486 BSlider::SetFont(const BFont* font, uint32 properties)
1487 {
1488 	BControl::SetFont(font, properties);
1489 
1490 #if USE_OFF_SCREEN_VIEW
1491 	if (fOffScreenView && fOffScreenBits) {
1492 		if (fOffScreenBits->Lock()) {
1493 			fOffScreenView->SetFont(font, properties);
1494 			fOffScreenBits->Unlock();
1495 		}
1496 	}
1497 #endif
1498 
1499 	InvalidateLayout();
1500 }
1501 
1502 
1503 void
1504 BSlider::SetLimits(int32 minimum, int32 maximum)
1505 {
1506 	if (minimum <= maximum) {
1507 		fMinValue = minimum;
1508 		fMaxValue = maximum;
1509 
1510 		int32 value = Value();
1511 		value = std::max(minimum, value);
1512 		value = std::min(maximum, value);
1513 
1514 		if (value != Value())
1515 			SetValue(value);
1516 	}
1517 }
1518 
1519 
1520 float
1521 BSlider::MaxUpdateTextWidth()
1522 {
1523 	// very simplistic implementation that assumes the string will be widest
1524 	// at the maximum value
1525 	int32 value = Value();
1526 	SetValueNoUpdate(fMaxValue);
1527 	float width = StringWidth(UpdateText());
1528 	SetValueNoUpdate(value);
1529 	// in case the derived class uses a fixed buffer, the contents
1530 	// should be reset for the old value
1531 	UpdateText();
1532 
1533 	return width;
1534 }
1535 
1536 
1537 // #pragma mark - layout related
1538 
1539 
1540 BSize
1541 BSlider::MinSize()
1542 {
1543 	return BLayoutUtils::ComposeSize(ExplicitMinSize(), _ValidateMinSize());
1544 }
1545 
1546 
1547 BSize
1548 BSlider::MaxSize()
1549 {
1550 	BSize maxSize = _ValidateMinSize();
1551 	if (fOrientation == B_HORIZONTAL)
1552 		maxSize.width = B_SIZE_UNLIMITED;
1553 	else
1554 		maxSize.height = B_SIZE_UNLIMITED;
1555 
1556 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), maxSize);
1557 }
1558 
1559 
1560 BSize
1561 BSlider::PreferredSize()
1562 {
1563 	BSize preferredSize = _ValidateMinSize();
1564 	if (fOrientation == B_HORIZONTAL)
1565 		preferredSize.width = std::max(100.0f, preferredSize.width);
1566 	else
1567 		preferredSize.height = std::max(100.0f, preferredSize.height);
1568 
1569 	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), preferredSize);
1570 }
1571 
1572 
1573 status_t
1574 BSlider::SetIcon(const BBitmap* icon, uint32 flags)
1575 {
1576 	return BControl::SetIcon(icon, flags);
1577 }
1578 
1579 
1580 void
1581 BSlider::LayoutInvalidated(bool descendants)
1582 {
1583 	// invalidate cached preferred size
1584 	fMinSize.Set(-1, -1);
1585 }
1586 
1587 
1588 // #pragma mark - private
1589 
1590 
1591 void
1592 BSlider::_DrawBlockThumb()
1593 {
1594 	BRect frame = ThumbFrame();
1595 	BView* view = OffscreenView();
1596 
1597 	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
1598 	uint32 flags = be_control_look->Flags(this);
1599 	be_control_look->DrawSliderThumb(view, frame, frame, base, flags,
1600 		fOrientation);
1601 }
1602 
1603 
1604 void
1605 BSlider::_DrawTriangleThumb()
1606 {
1607 	BRect frame = ThumbFrame();
1608 	BView* view = OffscreenView();
1609 	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
1610 	uint32 flags = be_control_look->Flags(this);
1611 	be_control_look->DrawSliderTriangle(view, frame, frame, base, flags,
1612 		fOrientation);
1613 }
1614 
1615 
1616 BPoint
1617 BSlider::_Location() const
1618 {
1619 	return fLocation;
1620 }
1621 
1622 
1623 void
1624 BSlider::_SetLocationForValue(int32 value)
1625 {
1626 	BPoint loc;
1627 	float range = (float)(fMaxValue - fMinValue);
1628 	if (range == 0)
1629 		range = 1;
1630 
1631 	float pos = (float)(value - fMinValue) / range *
1632 		(_MaxPosition() - _MinPosition());
1633 
1634 	if (fOrientation == B_HORIZONTAL) {
1635 		loc.x = ceil(_MinPosition() + pos);
1636 		loc.y = 0;
1637 	} else {
1638 		loc.x = 0;
1639 		loc.y = floor(_MaxPosition() - pos);
1640 	}
1641 	fLocation = loc;
1642 }
1643 
1644 
1645 float
1646 BSlider::_MinPosition() const
1647 {
1648 	if (fOrientation == B_HORIZONTAL)
1649 		return BarFrame().left + 1.0f;
1650 
1651 	return BarFrame().bottom - 1.0f;
1652 }
1653 
1654 
1655 float
1656 BSlider::_MaxPosition() const
1657 {
1658 	if (fOrientation == B_HORIZONTAL)
1659 		return BarFrame().right - 1.0f;
1660 
1661 	return BarFrame().top + 1.0f;
1662 }
1663 
1664 
1665 BSize
1666 BSlider::_ValidateMinSize()
1667 {
1668 	if (fMinSize.width >= 0) {
1669 		// the preferred size is up to date
1670 		return fMinSize;
1671 	}
1672 
1673 	font_height fontHeight;
1674 	GetFontHeight(&fontHeight);
1675 
1676 	float width = 0.0f;
1677 	float height = 0.0f;
1678 
1679 	if (fMaxUpdateTextWidth < 0.0f)
1680 		fMaxUpdateTextWidth = MaxUpdateTextWidth();
1681 
1682 	if (Orientation() == B_HORIZONTAL) {
1683 		height = 12.0f + fBarThickness;
1684 		int32 rows = 0;
1685 
1686 		float labelWidth = 0;
1687 		int32 labelRows = 0;
1688 		float labelSpacing = StringWidth("M") * 2;
1689 		if (Label() != NULL) {
1690 			labelWidth = StringWidth(Label());
1691 			labelRows = 1;
1692 		}
1693 		if (fMaxUpdateTextWidth > 0.0f) {
1694 			if (labelWidth > 0)
1695 				labelWidth += labelSpacing;
1696 
1697 			labelWidth += fMaxUpdateTextWidth;
1698 			labelRows = 1;
1699 		}
1700 		rows += labelRows;
1701 
1702 		if (MinLimitLabel() != NULL)
1703 			width = StringWidth(MinLimitLabel());
1704 
1705 		if (MaxLimitLabel() != NULL) {
1706 			// some space between the labels
1707 			if (MinLimitLabel() != NULL)
1708 				width += labelSpacing;
1709 
1710 			width += StringWidth(MaxLimitLabel());
1711 		}
1712 
1713 		if (labelWidth > width)
1714 			width = labelWidth;
1715 
1716 		if (width < 32.0f)
1717 			width = 32.0f;
1718 
1719 		if (MinLimitLabel() || MaxLimitLabel())
1720 			rows++;
1721 
1722 		height += rows * (ceilf(fontHeight.ascent)
1723 			+ ceilf(fontHeight.descent) + 4.0);
1724 	} else {
1725 		// B_VERTICAL
1726 		width = 12.0f + fBarThickness;
1727 		height = 32.0f;
1728 
1729 		float lineHeightNoLeading = ceilf(fontHeight.ascent)
1730 			+ ceilf(fontHeight.descent);
1731 		float lineHeight = lineHeightNoLeading + ceilf(fontHeight.leading);
1732 
1733 		// find largest label
1734 		float labelWidth = 0;
1735 		if (Label() != NULL) {
1736 			labelWidth = StringWidth(Label());
1737 			height += lineHeightNoLeading;
1738 		}
1739 		if (MaxLimitLabel() != NULL) {
1740 			labelWidth = std::max(labelWidth, StringWidth(MaxLimitLabel()));
1741 			height += Label() ? lineHeight : lineHeightNoLeading;
1742 		}
1743 		if (MinLimitLabel() != NULL) {
1744 			labelWidth = std::max(labelWidth, StringWidth(MinLimitLabel()));
1745 			height += lineHeightNoLeading;
1746 		}
1747 		if (fMaxUpdateTextWidth > 0.0f) {
1748 			labelWidth = std::max(labelWidth, fMaxUpdateTextWidth);
1749 			height += MinLimitLabel() ? lineHeight : lineHeightNoLeading;
1750 		}
1751 
1752 		width = std::max(labelWidth, width);
1753 	}
1754 
1755 	fMinSize.width = width;
1756 	fMinSize.height = height;
1757 
1758 	ResetLayoutInvalidation();
1759 
1760 	return fMinSize;
1761 }
1762 
1763 
1764 // #pragma mark - FBC padding
1765 
1766 void BSlider::_ReservedSlider6() {}
1767 void BSlider::_ReservedSlider7() {}
1768 void BSlider::_ReservedSlider8() {}
1769 void BSlider::_ReservedSlider9() {}
1770 void BSlider::_ReservedSlider10() {}
1771 void BSlider::_ReservedSlider11() {}
1772 void BSlider::_ReservedSlider12() {}
1773 
1774 
1775 BSlider&
1776 BSlider::operator=(const BSlider&)
1777 {
1778 	return *this;
1779 }
1780 
1781 
1782 //	#pragma mark - BeOS compatibility
1783 
1784 
1785 #if __GNUC__ < 3
1786 
1787 extern "C" void
1788 GetLimits__7BSliderPlT1(BSlider* slider, int32* minimum, int32* maximum)
1789 {
1790 	slider->GetLimits(minimum, maximum);
1791 }
1792 
1793 
1794 extern "C" void
1795 _ReservedSlider4__7BSlider(BSlider* slider, int32 minimum, int32 maximum)
1796 {
1797 	slider->BSlider::SetLimits(minimum, maximum);
1798 }
1799 
1800 extern "C" float
1801 _ReservedSlider5__7BSlider(BSlider* slider)
1802 {
1803 	return slider->BSlider::MaxUpdateTextWidth();
1804 }
1805 
1806 
1807 extern "C" void
1808 _ReservedSlider1__7BSlider(BSlider* slider, orientation _orientation)
1809 {
1810 	slider->BSlider::SetOrientation(_orientation);
1811 }
1812 
1813 
1814 extern "C" void
1815 _ReservedSlider2__7BSlider(BSlider* slider, float thickness)
1816 {
1817 	slider->BSlider::SetBarThickness(thickness);
1818 }
1819 
1820 
1821 extern "C" void
1822 _ReservedSlider3__7BSlider(BSlider* slider, const BFont* font,
1823 	uint32 properties)
1824 {
1825 	slider->BSlider::SetFont(font, properties);
1826 }
1827 
1828 
1829 #endif	// __GNUC__ < 3
1830 
1831 
1832 extern "C" void
1833 B_IF_GCC_2(InvalidateLayout__7BSliderb, _ZN7BSlider16InvalidateLayoutEb)(
1834 	BView* view, bool descendants)
1835 {
1836 	perform_data_layout_invalidated data;
1837 	data.descendants = descendants;
1838 
1839 	view->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data);
1840 }
1841