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