xref: /haiku/src/kits/interface/Slider.cpp (revision 42df4f9689991c413d701d07577a5b2986b63778)
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 
BSlider(BRect frame,const char * name,const char * label,BMessage * message,int32 minValue,int32 maxValue,thumb_style thumbType,uint32 resizingMode,uint32 flags)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 
BSlider(BRect frame,const char * name,const char * label,BMessage * message,int32 minValue,int32 maxValue,orientation posture,thumb_style thumbType,uint32 resizingMode,uint32 flags)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 
BSlider(const char * name,const char * label,BMessage * message,int32 minValue,int32 maxValue,orientation posture,thumb_style thumbType,uint32 flags)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 
BSlider(BMessage * archive)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 
~BSlider()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
_InitBarColor()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
_InitObject()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*
Instantiate(BMessage * archive)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
Archive(BMessage * archive,bool deep) const255 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
Perform(perform_code code,void * _data)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
WindowActivated(bool state)374 BSlider::WindowActivated(bool state)
375 {
376 	BControl::WindowActivated(state);
377 }
378 
379 
380 void
AttachedToWindow()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
AllAttached()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 		SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
438 }
439 
440 
441 void
AllDetached()442 BSlider::AllDetached()
443 {
444 	BControl::AllDetached();
445 }
446 
447 
448 void
DetachedFromWindow()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
MessageReceived(BMessage * message)464 BSlider::MessageReceived(BMessage* message)
465 {
466 	BControl::MessageReceived(message);
467 }
468 
469 
470 void
FrameMoved(BPoint new_position)471 BSlider::FrameMoved(BPoint new_position)
472 {
473 	BControl::FrameMoved(new_position);
474 }
475 
476 
477 void
FrameResized(float w,float h)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
KeyDown(const char * bytes,int32 numBytes)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
KeyUp(const char * bytes,int32 numBytes)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
_ConstrainPoint(BPoint & point,BPoint comparePoint) const568 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
MouseDown(BPoint point)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
MouseUp(BPoint point)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
MouseMoved(BPoint point,uint32 transit,const BMessage * message)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
Pulse()670 BSlider::Pulse()
671 {
672 	BControl::Pulse();
673 }
674 
675 
676 void
SetLabel(const char * label)677 BSlider::SetLabel(const char* label)
678 {
679 	BControl::SetLabel(label);
680 }
681 
682 
683 void
SetLimitLabels(const char * minLabel,const char * maxLabel)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*
MinLimitLabel() const705 BSlider::MinLimitLabel() const
706 {
707 	return fMinLimitLabel;
708 }
709 
710 
711 const char*
MaxLimitLabel() const712 BSlider::MaxLimitLabel() const
713 {
714 	return fMaxLimitLabel;
715 }
716 
717 
718 void
SetValue(int32 value)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
ValueForPoint(BPoint location) const766 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
SetPosition(float position)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
Position() const805 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
SetEnabled(bool on)816 BSlider::SetEnabled(bool on)
817 {
818 	BControl::SetEnabled(on);
819 }
820 
821 
822 void
GetLimits(int32 * minimum,int32 * maximum) const823 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
Draw(BRect updateRect)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
DrawSlider()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
DrawBar()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
DrawHashMarks()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
DrawThumb()931 BSlider::DrawThumb()
932 {
933 	if (Style() == B_BLOCK_THUMB)
934 		_DrawBlockThumb();
935 	else
936 		_DrawTriangleThumb();
937 }
938 
939 
940 void
DrawFocusMark()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
DrawText()969 BSlider::DrawText()
970 {
971 	BRect bounds(Bounds());
972 	BView* view = OffscreenView();
973 
974 	rgb_color base = ViewColor();
975 	uint32 flags = be_control_look->Flags(this);
976 
977 	font_height fontHeight;
978 	GetFontHeight(&fontHeight);
979 	if (Orientation() == B_HORIZONTAL) {
980 		if (Label() != NULL) {
981 			be_control_look->DrawLabel(view, Label(), base, flags,
982 				BPoint(0.0f, ceilf(fontHeight.ascent)));
983 		}
984 
985 		// the update text is updated in SetValue() only
986 		if (fUpdateText != NULL) {
987 			be_control_look->DrawLabel(view, fUpdateText, base, flags,
988 				BPoint(bounds.right - StringWidth(fUpdateText),
989 					ceilf(fontHeight.ascent)));
990 		}
991 
992 		if (fMinLimitLabel != NULL) {
993 			be_control_look->DrawLabel(view, fMinLimitLabel, base, flags,
994 				BPoint(0.0f, bounds.bottom - fontHeight.descent));
995 		}
996 
997 		if (fMaxLimitLabel != NULL) {
998 			be_control_look->DrawLabel(view, fMaxLimitLabel, base, flags,
999 				BPoint(bounds.right - StringWidth(fMaxLimitLabel),
1000 					bounds.bottom - fontHeight.descent));
1001 		}
1002 	} else {
1003 		float lineHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent)
1004 			+ ceilf(fontHeight.leading);
1005 		float baseLine = ceilf(fontHeight.ascent);
1006 
1007 		if (Label() != NULL) {
1008 			be_control_look->DrawLabel(view, Label(), base, flags,
1009 				BPoint((bounds.Width() - StringWidth(Label())) / 2.0,
1010 					baseLine));
1011 			baseLine += lineHeight;
1012 		}
1013 
1014 		if (fMaxLimitLabel != NULL) {
1015 			be_control_look->DrawLabel(view, fMaxLimitLabel, base, flags,
1016 				BPoint((bounds.Width() - StringWidth(fMaxLimitLabel)) / 2.0,
1017 					baseLine));
1018 		}
1019 
1020 		baseLine = bounds.bottom - ceilf(fontHeight.descent);
1021 
1022 		if (fMinLimitLabel != NULL) {
1023 			be_control_look->DrawLabel(view, fMinLimitLabel, base, flags,
1024 				BPoint((bounds.Width() - StringWidth(fMinLimitLabel)) / 2.0,
1025 					baseLine));
1026 				baseLine -= lineHeight;
1027 		}
1028 
1029 		if (fUpdateText != NULL) {
1030 			be_control_look->DrawLabel(view, fUpdateText, base, flags,
1031 				BPoint((bounds.Width() - StringWidth(fUpdateText)) / 2.0,
1032 					baseLine));
1033 		}
1034 	}
1035 }
1036 
1037 
1038 // #pragma mark -
1039 
1040 
1041 const char*
UpdateText() const1042 BSlider::UpdateText() const
1043 {
1044 	return NULL;
1045 }
1046 
1047 
1048 void
UpdateTextChanged()1049 BSlider::UpdateTextChanged()
1050 {
1051 	// update text label
1052 	float oldWidth = 0.0;
1053 	if (fUpdateText != NULL)
1054 		oldWidth = StringWidth(fUpdateText);
1055 
1056 	const char* oldUpdateText = fUpdateText;
1057 	fUpdateText = UpdateText();
1058 	bool updateTextOnOff = (fUpdateText == NULL && oldUpdateText != NULL)
1059 		|| (fUpdateText != NULL && oldUpdateText == NULL);
1060 
1061 	float newWidth = 0.0;
1062 	if (fUpdateText != NULL)
1063 		newWidth = StringWidth(fUpdateText);
1064 
1065 	float width = ceilf(std::max(newWidth, oldWidth)) + 2.0f;
1066 	if (width != 0) {
1067 		font_height fontHeight;
1068 		GetFontHeight(&fontHeight);
1069 
1070 		float height = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent);
1071 		float lineHeight = height + ceilf(fontHeight.leading);
1072 		BRect invalid(Bounds());
1073 		if (fOrientation == B_HORIZONTAL)
1074 			invalid = BRect(invalid.right - width, 0, invalid.right, height);
1075 		else {
1076 			if (!updateTextOnOff) {
1077 				invalid.left = (invalid.left + invalid.right - width) / 2;
1078 				invalid.right = invalid.left + width;
1079 				if (fMinLimitLabel != NULL)
1080 					invalid.bottom -= lineHeight;
1081 
1082 				invalid.top = invalid.bottom - height;
1083 			}
1084 		}
1085 		Invalidate(invalid);
1086 	}
1087 
1088 	float oldMaxUpdateTextWidth = fMaxUpdateTextWidth;
1089 	fMaxUpdateTextWidth = MaxUpdateTextWidth();
1090 	if (oldMaxUpdateTextWidth != fMaxUpdateTextWidth)
1091 		InvalidateLayout();
1092 }
1093 
1094 
1095 BRect
BarFrame() const1096 BSlider::BarFrame() const
1097 {
1098 	BRect frame(Bounds());
1099 
1100 	font_height fontHeight;
1101 	GetFontHeight(&fontHeight);
1102 
1103 	float textHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent);
1104 	float leading = ceilf(fontHeight.leading);
1105 
1106 	float thumbInset;
1107 	if (fStyle == B_BLOCK_THUMB)
1108 		thumbInset = 8.0;
1109 	else
1110 		thumbInset = 7.0;
1111 
1112 	if (Orientation() == B_HORIZONTAL) {
1113 		frame.left = thumbInset;
1114 		frame.top = 6.0 + (Label() || fUpdateText ? textHeight + 4.0 : 0.0);
1115 		frame.right -= thumbInset;
1116 		frame.bottom = frame.top + fBarThickness;
1117 	} else {
1118 		frame.left = floorf((frame.Width() - fBarThickness) / 2.0);
1119 		frame.top = thumbInset;
1120 		if (Label() != NULL)
1121 			frame.top += textHeight;
1122 
1123 		if (fMaxLimitLabel != NULL) {
1124 			frame.top += textHeight;
1125 			if (Label())
1126 				frame.top += leading;
1127 		}
1128 
1129 		frame.right = frame.left + fBarThickness;
1130 		frame.bottom = frame.bottom - thumbInset;
1131 		if (fMinLimitLabel != NULL)
1132 			frame.bottom -= textHeight;
1133 
1134 		if (fUpdateText != NULL) {
1135 			frame.bottom -= textHeight;
1136 			if (fMinLimitLabel != NULL)
1137 				frame.bottom -= leading;
1138 		}
1139 	}
1140 
1141 	return frame;
1142 }
1143 
1144 
1145 BRect
HashMarksFrame() const1146 BSlider::HashMarksFrame() const
1147 {
1148 	BRect frame(BarFrame());
1149 
1150 	if (fOrientation == B_HORIZONTAL) {
1151 		frame.top -= 6.0;
1152 		frame.bottom += 6.0;
1153 	} else {
1154 		frame.left -= 6.0;
1155 		frame.right += 6.0;
1156 	}
1157 
1158 	return frame;
1159 }
1160 
1161 
1162 BRect
ThumbFrame() const1163 BSlider::ThumbFrame() const
1164 {
1165 	// TODO: The slider looks really ugly and broken when it is too little.
1166 	// I would suggest using BarFrame() here to get the top and bottom coords
1167 	// and spread them further apart for the thumb
1168 
1169 	BRect frame = Bounds();
1170 
1171 	font_height fontHeight;
1172 	GetFontHeight(&fontHeight);
1173 
1174 	float textHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent);
1175 
1176 	if (fStyle == B_BLOCK_THUMB) {
1177 		if (Orientation() == B_HORIZONTAL) {
1178 			frame.left = floorf(Position() * (_MaxPosition()
1179 				- _MinPosition()) + _MinPosition()) - 8;
1180 			frame.top = 2 + (Label() || fUpdateText ? textHeight + 4 : 0);
1181 			frame.right = frame.left + 17;
1182 			frame.bottom = frame.top + fBarThickness + 7;
1183 		} else {
1184 			frame.left = floor((frame.Width() - fBarThickness) / 2) - 4;
1185 			frame.top = floorf(Position() * (_MaxPosition()
1186 				- _MinPosition()) + _MinPosition()) - 8;
1187 			frame.right = frame.left + fBarThickness + 7;
1188 			frame.bottom = frame.top + 17;
1189 		}
1190 	} else {
1191 		if (Orientation() == B_HORIZONTAL) {
1192 			frame.left = floorf(Position() * (_MaxPosition()
1193 				- _MinPosition()) + _MinPosition()) - 6;
1194 			frame.right = frame.left + 12;
1195 			frame.top = 3 + fBarThickness + (Label() ? textHeight + 4 : 0);
1196 			frame.bottom = frame.top + 8;
1197 		} else {
1198 			frame.left = floorf((frame.Width() + fBarThickness) / 2) - 3;
1199 			frame.top = floorf(Position() * (_MaxPosition()
1200 				- _MinPosition())) + _MinPosition() - 6;
1201 			frame.right = frame.left + 8;
1202 			frame.bottom = frame.top + 12;
1203 		}
1204 	}
1205 
1206 	return frame;
1207 }
1208 
1209 
1210 void
SetFlags(uint32 flags)1211 BSlider::SetFlags(uint32 flags)
1212 {
1213 	BControl::SetFlags(flags);
1214 }
1215 
1216 
1217 void
SetResizingMode(uint32 mode)1218 BSlider::SetResizingMode(uint32 mode)
1219 {
1220 	BControl::SetResizingMode(mode);
1221 }
1222 
1223 
1224 void
GetPreferredSize(float * _width,float * _height)1225 BSlider::GetPreferredSize(float* _width, float* _height)
1226 {
1227 	BSize preferredSize = PreferredSize();
1228 
1229 	if (Orientation() == B_HORIZONTAL) {
1230 		if (_width != NULL) {
1231 			// NOTE: For compatibility reasons, a horizontal BSlider
1232 			// never shrinks horizontally. This only affects applications
1233 			// which do not use the new layout system.
1234 			*_width = std::max(Bounds().Width(), preferredSize.width);
1235 		}
1236 
1237 		if (_height != NULL)
1238 			*_height = preferredSize.height;
1239 	} else {
1240 		if (_width != NULL)
1241 			*_width = preferredSize.width;
1242 
1243 		if (_height != NULL) {
1244 			// NOTE: Similarly, a vertical BSlider never shrinks
1245 			// vertically. This only affects applications which do not
1246 			// use the new layout system.
1247 			*_height = std::max(Bounds().Height(), preferredSize.height);
1248 		}
1249 	}
1250 }
1251 
1252 
1253 void
ResizeToPreferred()1254 BSlider::ResizeToPreferred()
1255 {
1256 	BControl::ResizeToPreferred();
1257 }
1258 
1259 
1260 status_t
Invoke(BMessage * message)1261 BSlider::Invoke(BMessage* message)
1262 {
1263 	return BControl::Invoke(message);
1264 }
1265 
1266 
1267 BHandler*
ResolveSpecifier(BMessage * message,int32 index,BMessage * specifier,int32 command,const char * property)1268 BSlider::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier,
1269 	int32 command, const char* property)
1270 {
1271 	return BControl::ResolveSpecifier(message, index, specifier, command,
1272 		property);
1273 }
1274 
1275 
1276 status_t
GetSupportedSuites(BMessage * message)1277 BSlider::GetSupportedSuites(BMessage* message)
1278 {
1279 	return BControl::GetSupportedSuites(message);
1280 }
1281 
1282 
1283 void
SetModificationMessage(BMessage * message)1284 BSlider::SetModificationMessage(BMessage* message)
1285 {
1286 	delete fModificationMessage;
1287 	fModificationMessage = message;
1288 }
1289 
1290 
1291 BMessage*
ModificationMessage() const1292 BSlider::ModificationMessage() const
1293 {
1294 	return fModificationMessage;
1295 }
1296 
1297 
1298 void
SetSnoozeAmount(int32 snoozeTime)1299 BSlider::SetSnoozeAmount(int32 snoozeTime)
1300 {
1301 	if (snoozeTime < 10000)
1302 		snoozeTime = 10000;
1303 	else if (snoozeTime > 1000000)
1304 		snoozeTime = 1000000;
1305 
1306 	fSnoozeAmount = snoozeTime;
1307 }
1308 
1309 
1310 int32
SnoozeAmount() const1311 BSlider::SnoozeAmount() const
1312 {
1313 	return fSnoozeAmount;
1314 }
1315 
1316 
1317 void
SetKeyIncrementValue(int32 incrementValue)1318 BSlider::SetKeyIncrementValue(int32 incrementValue)
1319 {
1320 	fKeyIncrementValue = incrementValue;
1321 }
1322 
1323 
1324 int32
KeyIncrementValue() const1325 BSlider::KeyIncrementValue() const
1326 {
1327 	return fKeyIncrementValue;
1328 }
1329 
1330 
1331 void
SetHashMarkCount(int32 hashMarkCount)1332 BSlider::SetHashMarkCount(int32 hashMarkCount)
1333 {
1334 	fHashMarkCount = hashMarkCount;
1335 	Invalidate();
1336 }
1337 
1338 
1339 int32
HashMarkCount() const1340 BSlider::HashMarkCount() const
1341 {
1342 	return fHashMarkCount;
1343 }
1344 
1345 
1346 void
SetHashMarks(hash_mark_location where)1347 BSlider::SetHashMarks(hash_mark_location where)
1348 {
1349 	fHashMarks = where;
1350 // TODO: enable if the hashmark look is influencing the control size!
1351 //	InvalidateLayout();
1352 	Invalidate();
1353 }
1354 
1355 
1356 hash_mark_location
HashMarks() const1357 BSlider::HashMarks() const
1358 {
1359 	return fHashMarks;
1360 }
1361 
1362 
1363 void
SetStyle(thumb_style style)1364 BSlider::SetStyle(thumb_style style)
1365 {
1366 	fStyle = style;
1367 	InvalidateLayout();
1368 	Invalidate();
1369 }
1370 
1371 
1372 thumb_style
Style() const1373 BSlider::Style() const
1374 {
1375 	return fStyle;
1376 }
1377 
1378 
1379 void
SetBarColor(rgb_color barColor)1380 BSlider::SetBarColor(rgb_color barColor)
1381 {
1382 	fBarColor = barColor;
1383 	Invalidate(BarFrame());
1384 }
1385 
1386 
1387 rgb_color
BarColor() const1388 BSlider::BarColor() const
1389 {
1390 	return fBarColor;
1391 }
1392 
1393 
1394 void
UseFillColor(bool useFill,const rgb_color * barColor)1395 BSlider::UseFillColor(bool useFill, const rgb_color* barColor)
1396 {
1397 	fUseFillColor = useFill;
1398 
1399 	if (useFill && barColor)
1400 		fFillColor = *barColor;
1401 
1402 	Invalidate(BarFrame());
1403 }
1404 
1405 
1406 bool
FillColor(rgb_color * barColor) const1407 BSlider::FillColor(rgb_color* barColor) const
1408 {
1409 	if (barColor && fUseFillColor)
1410 		*barColor = fFillColor;
1411 
1412 	return fUseFillColor;
1413 }
1414 
1415 
1416 BView*
OffscreenView() const1417 BSlider::OffscreenView() const
1418 {
1419 #if USE_OFF_SCREEN_VIEW
1420 	return fOffScreenView;
1421 #else
1422 	return (BView*)this;
1423 #endif
1424 }
1425 
1426 
1427 orientation
Orientation() const1428 BSlider::Orientation() const
1429 {
1430 	return fOrientation;
1431 }
1432 
1433 
1434 void
SetOrientation(orientation posture)1435 BSlider::SetOrientation(orientation posture)
1436 {
1437 	if (fOrientation == posture)
1438 		return;
1439 
1440 	fOrientation = posture;
1441 	InvalidateLayout();
1442 	Invalidate();
1443 }
1444 
1445 
1446 float
BarThickness() const1447 BSlider::BarThickness() const
1448 {
1449 	return fBarThickness;
1450 }
1451 
1452 
1453 void
SetBarThickness(float thickness)1454 BSlider::SetBarThickness(float thickness)
1455 {
1456 	if (thickness < 1.0)
1457 		thickness = 1.0;
1458 	else
1459 		thickness = roundf(thickness);
1460 
1461 	if (thickness != fBarThickness) {
1462 		// calculate invalid barframe and extend by hashmark size
1463 		float hInset = 0.0;
1464 		float vInset = 0.0;
1465 		if (fOrientation == B_HORIZONTAL)
1466 			vInset = -6.0;
1467 		else
1468 			hInset = -6.0;
1469 		BRect invalid = BarFrame().InsetByCopy(hInset, vInset) | ThumbFrame();
1470 
1471 		fBarThickness = thickness;
1472 
1473 		invalid = invalid | BarFrame().InsetByCopy(hInset, vInset)
1474 			| ThumbFrame();
1475 		Invalidate(invalid);
1476 		InvalidateLayout();
1477 	}
1478 }
1479 
1480 
1481 void
SetFont(const BFont * font,uint32 properties)1482 BSlider::SetFont(const BFont* font, uint32 properties)
1483 {
1484 	BControl::SetFont(font, properties);
1485 
1486 #if USE_OFF_SCREEN_VIEW
1487 	if (fOffScreenView && fOffScreenBits) {
1488 		if (fOffScreenBits->Lock()) {
1489 			fOffScreenView->SetFont(font, properties);
1490 			fOffScreenBits->Unlock();
1491 		}
1492 	}
1493 #endif
1494 
1495 	InvalidateLayout();
1496 }
1497 
1498 
1499 void
SetLimits(int32 minimum,int32 maximum)1500 BSlider::SetLimits(int32 minimum, int32 maximum)
1501 {
1502 	if (minimum <= maximum) {
1503 		fMinValue = minimum;
1504 		fMaxValue = maximum;
1505 
1506 		int32 value = Value();
1507 		value = std::max(minimum, value);
1508 		value = std::min(maximum, value);
1509 
1510 		if (value != Value())
1511 			SetValue(value);
1512 	}
1513 }
1514 
1515 
1516 float
MaxUpdateTextWidth()1517 BSlider::MaxUpdateTextWidth()
1518 {
1519 	// very simplistic implementation that assumes the string will be widest
1520 	// at the maximum value
1521 	int32 value = Value();
1522 	SetValueNoUpdate(fMaxValue);
1523 	float width = StringWidth(UpdateText());
1524 	SetValueNoUpdate(value);
1525 	// in case the derived class uses a fixed buffer, the contents
1526 	// should be reset for the old value
1527 	UpdateText();
1528 
1529 	return width;
1530 }
1531 
1532 
1533 // #pragma mark - layout related
1534 
1535 
1536 BSize
MinSize()1537 BSlider::MinSize()
1538 {
1539 	return BLayoutUtils::ComposeSize(ExplicitMinSize(), _ValidateMinSize());
1540 }
1541 
1542 
1543 BSize
MaxSize()1544 BSlider::MaxSize()
1545 {
1546 	BSize maxSize = _ValidateMinSize();
1547 	if (fOrientation == B_HORIZONTAL)
1548 		maxSize.width = B_SIZE_UNLIMITED;
1549 	else
1550 		maxSize.height = B_SIZE_UNLIMITED;
1551 
1552 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), maxSize);
1553 }
1554 
1555 
1556 BSize
PreferredSize()1557 BSlider::PreferredSize()
1558 {
1559 	BSize preferredSize = _ValidateMinSize();
1560 	if (fOrientation == B_HORIZONTAL)
1561 		preferredSize.width = std::max(100.0f, preferredSize.width);
1562 	else
1563 		preferredSize.height = std::max(100.0f, preferredSize.height);
1564 
1565 	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), preferredSize);
1566 }
1567 
1568 
1569 status_t
SetIcon(const BBitmap * icon,uint32 flags)1570 BSlider::SetIcon(const BBitmap* icon, uint32 flags)
1571 {
1572 	return BControl::SetIcon(icon, flags);
1573 }
1574 
1575 
1576 void
LayoutInvalidated(bool descendants)1577 BSlider::LayoutInvalidated(bool descendants)
1578 {
1579 	// invalidate cached preferred size
1580 	fMinSize.Set(-1, -1);
1581 }
1582 
1583 
1584 // #pragma mark - private
1585 
1586 
1587 void
_DrawBlockThumb()1588 BSlider::_DrawBlockThumb()
1589 {
1590 	BRect frame = ThumbFrame();
1591 	BView* view = OffscreenView();
1592 
1593 	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
1594 	uint32 flags = be_control_look->Flags(this);
1595 	be_control_look->DrawSliderThumb(view, frame, frame, base, flags,
1596 		fOrientation);
1597 }
1598 
1599 
1600 void
_DrawTriangleThumb()1601 BSlider::_DrawTriangleThumb()
1602 {
1603 	BRect frame = ThumbFrame();
1604 	BView* view = OffscreenView();
1605 	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
1606 	uint32 flags = be_control_look->Flags(this);
1607 	be_control_look->DrawSliderTriangle(view, frame, frame, base, flags,
1608 		fOrientation);
1609 }
1610 
1611 
1612 BPoint
_Location() const1613 BSlider::_Location() const
1614 {
1615 	return fLocation;
1616 }
1617 
1618 
1619 void
_SetLocationForValue(int32 value)1620 BSlider::_SetLocationForValue(int32 value)
1621 {
1622 	BPoint loc;
1623 	float range = (float)(fMaxValue - fMinValue);
1624 	if (range == 0)
1625 		range = 1;
1626 
1627 	float pos = (float)(value - fMinValue) / range *
1628 		(_MaxPosition() - _MinPosition());
1629 
1630 	if (fOrientation == B_HORIZONTAL) {
1631 		loc.x = ceil(_MinPosition() + pos);
1632 		loc.y = 0;
1633 	} else {
1634 		loc.x = 0;
1635 		loc.y = floor(_MaxPosition() - pos);
1636 	}
1637 	fLocation = loc;
1638 }
1639 
1640 
1641 float
_MinPosition() const1642 BSlider::_MinPosition() const
1643 {
1644 	if (fOrientation == B_HORIZONTAL)
1645 		return BarFrame().left + 1.0f;
1646 
1647 	return BarFrame().bottom - 1.0f;
1648 }
1649 
1650 
1651 float
_MaxPosition() const1652 BSlider::_MaxPosition() const
1653 {
1654 	if (fOrientation == B_HORIZONTAL)
1655 		return BarFrame().right - 1.0f;
1656 
1657 	return BarFrame().top + 1.0f;
1658 }
1659 
1660 
1661 BSize
_ValidateMinSize()1662 BSlider::_ValidateMinSize()
1663 {
1664 	if (fMinSize.width >= 0) {
1665 		// the preferred size is up to date
1666 		return fMinSize;
1667 	}
1668 
1669 	font_height fontHeight;
1670 	GetFontHeight(&fontHeight);
1671 
1672 	float width = 0.0f;
1673 	float height = 0.0f;
1674 
1675 	if (fMaxUpdateTextWidth < 0.0f)
1676 		fMaxUpdateTextWidth = MaxUpdateTextWidth();
1677 
1678 	if (Orientation() == B_HORIZONTAL) {
1679 		height = 12.0f + fBarThickness;
1680 		int32 rows = 0;
1681 
1682 		float labelWidth = 0;
1683 		int32 labelRows = 0;
1684 		float labelSpacing = StringWidth("M") * 2;
1685 		if (Label() != NULL) {
1686 			labelWidth = StringWidth(Label());
1687 			labelRows = 1;
1688 		}
1689 		if (fMaxUpdateTextWidth > 0.0f) {
1690 			if (labelWidth > 0)
1691 				labelWidth += labelSpacing;
1692 
1693 			labelWidth += fMaxUpdateTextWidth;
1694 			labelRows = 1;
1695 		}
1696 		rows += labelRows;
1697 
1698 		if (MinLimitLabel() != NULL)
1699 			width = StringWidth(MinLimitLabel());
1700 
1701 		if (MaxLimitLabel() != NULL) {
1702 			// some space between the labels
1703 			if (MinLimitLabel() != NULL)
1704 				width += labelSpacing;
1705 
1706 			width += StringWidth(MaxLimitLabel());
1707 		}
1708 
1709 		if (labelWidth > width)
1710 			width = labelWidth;
1711 
1712 		if (width < 32.0f)
1713 			width = 32.0f;
1714 
1715 		if (MinLimitLabel() || MaxLimitLabel())
1716 			rows++;
1717 
1718 		height += rows * (ceilf(fontHeight.ascent)
1719 			+ ceilf(fontHeight.descent) + 4.0);
1720 	} else {
1721 		// B_VERTICAL
1722 		width = 12.0f + fBarThickness;
1723 		height = 32.0f;
1724 
1725 		float lineHeightNoLeading = ceilf(fontHeight.ascent)
1726 			+ ceilf(fontHeight.descent);
1727 		float lineHeight = lineHeightNoLeading + ceilf(fontHeight.leading);
1728 
1729 		// find largest label
1730 		float labelWidth = 0;
1731 		if (Label() != NULL) {
1732 			labelWidth = StringWidth(Label());
1733 			height += lineHeightNoLeading;
1734 		}
1735 		if (MaxLimitLabel() != NULL) {
1736 			labelWidth = std::max(labelWidth, StringWidth(MaxLimitLabel()));
1737 			height += Label() ? lineHeight : lineHeightNoLeading;
1738 		}
1739 		if (MinLimitLabel() != NULL) {
1740 			labelWidth = std::max(labelWidth, StringWidth(MinLimitLabel()));
1741 			height += lineHeightNoLeading;
1742 		}
1743 		if (fMaxUpdateTextWidth > 0.0f) {
1744 			labelWidth = std::max(labelWidth, fMaxUpdateTextWidth);
1745 			height += MinLimitLabel() ? lineHeight : lineHeightNoLeading;
1746 		}
1747 
1748 		width = std::max(labelWidth, width);
1749 	}
1750 
1751 	fMinSize.width = width;
1752 	fMinSize.height = height;
1753 
1754 	ResetLayoutInvalidation();
1755 
1756 	return fMinSize;
1757 }
1758 
1759 
1760 // #pragma mark - FBC padding
1761 
_ReservedSlider6()1762 void BSlider::_ReservedSlider6() {}
_ReservedSlider7()1763 void BSlider::_ReservedSlider7() {}
_ReservedSlider8()1764 void BSlider::_ReservedSlider8() {}
_ReservedSlider9()1765 void BSlider::_ReservedSlider9() {}
_ReservedSlider10()1766 void BSlider::_ReservedSlider10() {}
_ReservedSlider11()1767 void BSlider::_ReservedSlider11() {}
_ReservedSlider12()1768 void BSlider::_ReservedSlider12() {}
1769 
1770 
1771 BSlider&
operator =(const BSlider &)1772 BSlider::operator=(const BSlider&)
1773 {
1774 	return *this;
1775 }
1776 
1777 
1778 //	#pragma mark - BeOS compatibility
1779 
1780 
1781 #if __GNUC__ < 3
1782 
1783 extern "C" void
GetLimits__7BSliderPlT1(BSlider * slider,int32 * minimum,int32 * maximum)1784 GetLimits__7BSliderPlT1(BSlider* slider, int32* minimum, int32* maximum)
1785 {
1786 	slider->GetLimits(minimum, maximum);
1787 }
1788 
1789 
1790 extern "C" void
_ReservedSlider4__7BSlider(BSlider * slider,int32 minimum,int32 maximum)1791 _ReservedSlider4__7BSlider(BSlider* slider, int32 minimum, int32 maximum)
1792 {
1793 	slider->BSlider::SetLimits(minimum, maximum);
1794 }
1795 
1796 extern "C" float
_ReservedSlider5__7BSlider(BSlider * slider)1797 _ReservedSlider5__7BSlider(BSlider* slider)
1798 {
1799 	return slider->BSlider::MaxUpdateTextWidth();
1800 }
1801 
1802 
1803 extern "C" void
_ReservedSlider1__7BSlider(BSlider * slider,orientation _orientation)1804 _ReservedSlider1__7BSlider(BSlider* slider, orientation _orientation)
1805 {
1806 	slider->BSlider::SetOrientation(_orientation);
1807 }
1808 
1809 
1810 extern "C" void
_ReservedSlider2__7BSlider(BSlider * slider,float thickness)1811 _ReservedSlider2__7BSlider(BSlider* slider, float thickness)
1812 {
1813 	slider->BSlider::SetBarThickness(thickness);
1814 }
1815 
1816 
1817 extern "C" void
_ReservedSlider3__7BSlider(BSlider * slider,const BFont * font,uint32 properties)1818 _ReservedSlider3__7BSlider(BSlider* slider, const BFont* font,
1819 	uint32 properties)
1820 {
1821 	slider->BSlider::SetFont(font, properties);
1822 }
1823 
1824 
1825 #endif	// __GNUC__ < 3
1826 
1827 
1828 extern "C" void
B_IF_GCC_2(InvalidateLayout__7BSliderb,_ZN7BSlider16InvalidateLayoutEb)1829 B_IF_GCC_2(InvalidateLayout__7BSliderb, _ZN7BSlider16InvalidateLayoutEb)(
1830 	BView* view, bool descendants)
1831 {
1832 	perform_data_layout_invalidated data;
1833 	data.descendants = descendants;
1834 
1835 	view->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data);
1836 }
1837