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