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
35BSlider::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
66BSlider::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
96BSlider::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
126BSlider::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
204BSlider::~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
216void
217BSlider::_InitBarColor()
218{
219	SetBarColor(be_control_look->SliderBarColor(
220		ui_color(B_PANEL_BACKGROUND_COLOR)));
221	UseFillColor(false, NULL);
222}
223
224
225void
226BSlider::_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
244BArchivable*
245BSlider::Instantiate(BMessage* archive)
246{
247	if (validate_instantiation(archive, "BSlider"))
248		return new BSlider(archive);
249
250	return NULL;
251}
252
253
254status_t
255BSlider::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
305status_t
306BSlider::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
373void
374BSlider::WindowActivated(bool state)
375{
376	BControl::WindowActivated(state);
377}
378
379
380void
381BSlider::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
428void
429BSlider::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
441void
442BSlider::AllDetached()
443{
444	BControl::AllDetached();
445}
446
447
448void
449BSlider::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
463void
464BSlider::MessageReceived(BMessage* message)
465{
466	BControl::MessageReceived(message);
467}
468
469
470void
471BSlider::FrameMoved(BPoint new_position)
472{
473	BControl::FrameMoved(new_position);
474}
475
476
477void
478BSlider::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
503void
504BSlider::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
548void
549BSlider::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*/
567bool
568BSlider::_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
594void
595BSlider::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
640void
641BSlider::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
653void
654BSlider::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
669void
670BSlider::Pulse()
671{
672	BControl::Pulse();
673}
674
675
676void
677BSlider::SetLabel(const char* label)
678{
679	BControl::SetLabel(label);
680}
681
682
683void
684BSlider::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
704const char*
705BSlider::MinLimitLabel() const
706{
707	return fMinLimitLabel;
708}
709
710
711const char*
712BSlider::MaxLimitLabel() const
713{
714	return fMaxLimitLabel;
715}
716
717
718void
719BSlider::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
765int32
766BSlider::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
792void
793BSlider::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
804float
805BSlider::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
815void
816BSlider::SetEnabled(bool on)
817{
818	BControl::SetEnabled(on);
819}
820
821
822void
823BSlider::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
836void
837BSlider::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
871void
872BSlider::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
899void
900BSlider::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
914void
915BSlider::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
930void
931BSlider::DrawThumb()
932{
933	if (Style() == B_BLOCK_THUMB)
934		_DrawBlockThumb();
935	else
936		_DrawTriangleThumb();
937}
938
939
940void
941BSlider::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
968void
969BSlider::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
1045const char*
1046BSlider::UpdateText() const
1047{
1048	return NULL;
1049}
1050
1051
1052void
1053BSlider::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
1099BRect
1100BSlider::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
1149BRect
1150BSlider::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
1166BRect
1167BSlider::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
1214void
1215BSlider::SetFlags(uint32 flags)
1216{
1217	BControl::SetFlags(flags);
1218}
1219
1220
1221void
1222BSlider::SetResizingMode(uint32 mode)
1223{
1224	BControl::SetResizingMode(mode);
1225}
1226
1227
1228void
1229BSlider::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
1257void
1258BSlider::ResizeToPreferred()
1259{
1260	BControl::ResizeToPreferred();
1261}
1262
1263
1264status_t
1265BSlider::Invoke(BMessage* message)
1266{
1267	return BControl::Invoke(message);
1268}
1269
1270
1271BHandler*
1272BSlider::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
1280status_t
1281BSlider::GetSupportedSuites(BMessage* message)
1282{
1283	return BControl::GetSupportedSuites(message);
1284}
1285
1286
1287void
1288BSlider::SetModificationMessage(BMessage* message)
1289{
1290	delete fModificationMessage;
1291	fModificationMessage = message;
1292}
1293
1294
1295BMessage*
1296BSlider::ModificationMessage() const
1297{
1298	return fModificationMessage;
1299}
1300
1301
1302void
1303BSlider::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
1314int32
1315BSlider::SnoozeAmount() const
1316{
1317	return fSnoozeAmount;
1318}
1319
1320
1321void
1322BSlider::SetKeyIncrementValue(int32 incrementValue)
1323{
1324	fKeyIncrementValue = incrementValue;
1325}
1326
1327
1328int32
1329BSlider::KeyIncrementValue() const
1330{
1331	return fKeyIncrementValue;
1332}
1333
1334
1335void
1336BSlider::SetHashMarkCount(int32 hashMarkCount)
1337{
1338	fHashMarkCount = hashMarkCount;
1339	Invalidate();
1340}
1341
1342
1343int32
1344BSlider::HashMarkCount() const
1345{
1346	return fHashMarkCount;
1347}
1348
1349
1350void
1351BSlider::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
1360hash_mark_location
1361BSlider::HashMarks() const
1362{
1363	return fHashMarks;
1364}
1365
1366
1367void
1368BSlider::SetStyle(thumb_style style)
1369{
1370	fStyle = style;
1371	InvalidateLayout();
1372	Invalidate();
1373}
1374
1375
1376thumb_style
1377BSlider::Style() const
1378{
1379	return fStyle;
1380}
1381
1382
1383void
1384BSlider::SetBarColor(rgb_color barColor)
1385{
1386	fBarColor = barColor;
1387	Invalidate(BarFrame());
1388}
1389
1390
1391rgb_color
1392BSlider::BarColor() const
1393{
1394	return fBarColor;
1395}
1396
1397
1398void
1399BSlider::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
1410bool
1411BSlider::FillColor(rgb_color* barColor) const
1412{
1413	if (barColor && fUseFillColor)
1414		*barColor = fFillColor;
1415
1416	return fUseFillColor;
1417}
1418
1419
1420BView*
1421BSlider::OffscreenView() const
1422{
1423#if USE_OFF_SCREEN_VIEW
1424	return fOffScreenView;
1425#else
1426	return (BView*)this;
1427#endif
1428}
1429
1430
1431orientation
1432BSlider::Orientation() const
1433{
1434	return fOrientation;
1435}
1436
1437
1438void
1439BSlider::SetOrientation(orientation posture)
1440{
1441	if (fOrientation == posture)
1442		return;
1443
1444	fOrientation = posture;
1445	InvalidateLayout();
1446	Invalidate();
1447}
1448
1449
1450float
1451BSlider::BarThickness() const
1452{
1453	return fBarThickness;
1454}
1455
1456
1457void
1458BSlider::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
1485void
1486BSlider::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
1503void
1504BSlider::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
1520float
1521BSlider::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
1540BSize
1541BSlider::MinSize()
1542{
1543	return BLayoutUtils::ComposeSize(ExplicitMinSize(), _ValidateMinSize());
1544}
1545
1546
1547BSize
1548BSlider::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
1560BSize
1561BSlider::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
1573status_t
1574BSlider::SetIcon(const BBitmap* icon, uint32 flags)
1575{
1576	return BControl::SetIcon(icon, flags);
1577}
1578
1579
1580void
1581BSlider::LayoutInvalidated(bool descendants)
1582{
1583	// invalidate cached preferred size
1584	fMinSize.Set(-1, -1);
1585}
1586
1587
1588// #pragma mark - private
1589
1590
1591void
1592BSlider::_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
1604void
1605BSlider::_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
1616BPoint
1617BSlider::_Location() const
1618{
1619	return fLocation;
1620}
1621
1622
1623void
1624BSlider::_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
1645float
1646BSlider::_MinPosition() const
1647{
1648	if (fOrientation == B_HORIZONTAL)
1649		return BarFrame().left + 1.0f;
1650
1651	return BarFrame().bottom - 1.0f;
1652}
1653
1654
1655float
1656BSlider::_MaxPosition() const
1657{
1658	if (fOrientation == B_HORIZONTAL)
1659		return BarFrame().right - 1.0f;
1660
1661	return BarFrame().top + 1.0f;
1662}
1663
1664
1665BSize
1666BSlider::_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
1766void BSlider::_ReservedSlider6() {}
1767void BSlider::_ReservedSlider7() {}
1768void BSlider::_ReservedSlider8() {}
1769void BSlider::_ReservedSlider9() {}
1770void BSlider::_ReservedSlider10() {}
1771void BSlider::_ReservedSlider11() {}
1772void BSlider::_ReservedSlider12() {}
1773
1774
1775BSlider&
1776BSlider::operator=(const BSlider&)
1777{
1778	return *this;
1779}
1780
1781
1782//	#pragma mark - BeOS compatibility
1783
1784
1785#if __GNUC__ < 3
1786
1787extern "C" void
1788GetLimits__7BSliderPlT1(BSlider* slider, int32* minimum, int32* maximum)
1789{
1790	slider->GetLimits(minimum, maximum);
1791}
1792
1793
1794extern "C" void
1795_ReservedSlider4__7BSlider(BSlider* slider, int32 minimum, int32 maximum)
1796{
1797	slider->BSlider::SetLimits(minimum, maximum);
1798}
1799
1800extern "C" float
1801_ReservedSlider5__7BSlider(BSlider* slider)
1802{
1803	return slider->BSlider::MaxUpdateTextWidth();
1804}
1805
1806
1807extern "C" void
1808_ReservedSlider1__7BSlider(BSlider* slider, orientation _orientation)
1809{
1810	slider->BSlider::SetOrientation(_orientation);
1811}
1812
1813
1814extern "C" void
1815_ReservedSlider2__7BSlider(BSlider* slider, float thickness)
1816{
1817	slider->BSlider::SetBarThickness(thickness);
1818}
1819
1820
1821extern "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
1832extern "C" void
1833B_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