xref: /haiku/src/kits/interface/Slider.cpp (revision 0e50eab75e25d0d82090e22dbff766dfaa6f5e86)
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 	switch (bytes[0]) {
408 		case B_LEFT_ARROW:
409 		case B_DOWN_ARROW: {
410 			SetValue(Value() - KeyIncrementValue());
411 			Invoke();
412 			break;
413 		}
414 		case B_RIGHT_ARROW:
415 		case B_UP_ARROW: {
416 			SetValue(Value() + KeyIncrementValue());
417 			Invoke();
418 			break;
419 		}
420 		default:
421 			BControl::KeyDown(bytes, numBytes);
422 	}
423 }
424 
425 
426 bool
427 BSlider::_ConstrainPoint(BPoint point, BPoint comparePoint) const
428 {
429 	if (fOrientation == B_HORIZONTAL) {
430 		if (point.x != comparePoint.x) {
431 			if (point.x < _MinPosition())
432 				point.x = _MinPosition();
433 			else if (point.x > _MaxPosition())
434 				point.x = _MaxPosition();
435 
436 			return true;
437 		}
438 	} else {
439 		if (point.y != comparePoint.y) {
440 			if (point.y > _MinPosition())
441 				point.y = _MinPosition();
442 			else if (point.y < _MaxPosition())
443 				point.y = _MaxPosition();
444 
445 			return true;
446 		}
447 	}
448 
449 	return false;
450 }
451 
452 
453 void
454 BSlider::MouseDown(BPoint point)
455 {
456 	if (!IsEnabled())
457 		return;
458 
459 	if (BarFrame().Contains(point) || ThumbFrame().Contains(point))
460 		fInitialLocation = _Location();
461 
462 	uint32 buttons;
463 	GetMouse(&point, &buttons, true);
464 
465 	_ConstrainPoint(point, fInitialLocation);
466 	SetValue(ValueForPoint(point));
467 
468 	InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED);
469 
470 	if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) {
471 		SetTracking(true);
472 		SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY);
473 	} else {
474 		// synchronous mouse tracking
475 		BPoint prevPoint;
476 
477 		while (buttons) {
478 			prevPoint = point;
479 
480 			snooze(SnoozeAmount());
481 			GetMouse(&point, &buttons, true);
482 
483 			if (_ConstrainPoint(point, prevPoint)) {
484 				SetValue(ValueForPoint(point));
485 				InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED);
486 			}
487 		}
488 		if (_Location() != fInitialLocation)
489 			Invoke();
490 	}
491 }
492 
493 
494 void
495 BSlider::MouseUp(BPoint point)
496 {
497 	if (IsTracking()) {
498 		if (_Location() != fInitialLocation)
499 			Invoke();
500 
501 		SetTracking(false);
502 	} else
503 		BControl::MouseUp(point);
504 }
505 
506 
507 void
508 BSlider::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
509 {
510 	if (IsTracking()) {
511 		if (_ConstrainPoint(point, _Location())) {
512 			SetValue(ValueForPoint(point));
513 			InvokeNotify(ModificationMessage(), B_CONTROL_MODIFIED);
514 		}
515 	} else
516 		BControl::MouseMoved(point, transit, message);
517 }
518 
519 
520 void
521 BSlider::Pulse()
522 {
523 	BControl::Pulse();
524 }
525 
526 
527 void
528 BSlider::SetLabel(const char *label)
529 {
530 	BControl::SetLabel(label);
531 }
532 
533 
534 void
535 BSlider::SetLimitLabels(const char *minLabel, const char *maxLabel)
536 {
537 	if (minLabel) {
538 		free(fMinLimitLabel);
539 		fMinLimitLabel = strdup(minLabel);
540 	}
541 
542 	if (maxLabel) {
543 		free(fMaxLimitLabel);
544 		fMaxLimitLabel = strdup(maxLabel);
545 	}
546 
547 	// TODO: Auto resizing?!? I would not want this as an app programmer!
548 	ResizeToPreferred();
549 	Invalidate();
550 }
551 
552 
553 const char*
554 BSlider::MinLimitLabel() const
555 {
556 	return fMinLimitLabel;
557 }
558 
559 
560 const char*
561 BSlider::MaxLimitLabel() const
562 {
563 	return fMaxLimitLabel;
564 }
565 
566 
567 void
568 BSlider::SetValue(int32 value)
569 {
570 	if (value < fMinValue)
571 		value = fMinValue;
572 	if (value > fMaxValue)
573 		value = fMaxValue;
574 
575 	if (value != Value()) {
576 		BPoint loc;
577 		float range = (float)(fMaxValue - fMinValue);
578 		if (range == 0)
579 			range = 1;
580 
581 		float pos = (float)(value - fMinValue) / range *
582 			_MaxPosition() - _MinPosition();
583 
584 		if (fOrientation == B_HORIZONTAL) {
585 			loc.x = ceil(_MinPosition() + pos);
586 			loc.y = 0;
587 		} else {
588 			loc.x = 0;
589 			loc.y = floor(_MaxPosition() - pos);
590 		}
591 
592 		BRect oldThumbFrame = ThumbFrame();
593 
594 		if (IsFocus() && Style() == B_TRIANGLE_THUMB) {
595 			// we need to update the region with the focus mark as well
596 			// (a method BSlider::FocusMarkFrame() would be nice as well)
597 			if (fOrientation == B_HORIZONTAL)
598 				oldThumbFrame.bottom += 2;
599 			else
600 				oldThumbFrame.left -= 2;
601 		}
602 
603 		// While it would be enough to do this dependent on fUseFillColor,
604 		// that doesn't work out if DrawBar() has been overridden by a sub class
605 		if (fOrientation == B_HORIZONTAL)
606 			oldThumbFrame.top = BarFrame().top;
607 		else
608 			oldThumbFrame.right = BarFrame().right;
609 
610 		_SetLocation(loc);
611 
612 		BControl::SetValueNoUpdate(value);
613 		Invalidate(oldThumbFrame | ThumbFrame());
614 
615 		// update text label
616 
617 		float oldWidth = 0.0f, width = 0.0f;
618 		if (fUpdateText != NULL)
619 			oldWidth = StringWidth(fUpdateText);
620 
621 		fUpdateText = UpdateText();
622 		if (fUpdateText != NULL)
623 			width = StringWidth(fUpdateText);
624 
625 		width = ceilf(max_c(width, oldWidth)) + 2.0f;
626 		if (width != 0) {
627 			font_height fontHeight;
628 			GetFontHeight(&fontHeight);
629 
630 			BRect rect(-width, 0, 0, ceilf(fontHeight.ascent + fontHeight.descent));
631 			rect.OffsetBy(Bounds().Width(), 0);
632 			Invalidate(rect);
633 		}
634 	}
635 }
636 
637 
638 int32
639 BSlider::ValueForPoint(BPoint location) const
640 {
641 	float position = fOrientation == B_HORIZONTAL ? location.x : location.y;
642 	float min = _MinPosition();
643 	float max = _MaxPosition();
644 
645 	if (position < min)
646 		position = min;
647 	if (position > max)
648 		position = max;
649 
650 	return (int32)roundf(((position - min) * (fMaxValue - fMinValue) / (max - min)) + fMinValue);
651 }
652 
653 
654 void
655 BSlider::SetPosition(float position)
656 {
657 	if (position <= 0.0f)
658 		BControl::SetValue(fMinValue);
659 	else if (position >= 1.0f)
660 		BControl::SetValue(fMaxValue);
661 	else
662 		BControl::SetValue((int32)(position * (fMaxValue - fMinValue) + fMinValue));
663 }
664 
665 
666 float
667 BSlider::Position() const
668 {
669 	float range = (float)(fMaxValue - fMinValue);
670 	if (range == 0.0f)
671 		range = 1.0f;
672 
673 	return (float)(Value() - fMinValue) / range;
674 }
675 
676 
677 void
678 BSlider::SetEnabled(bool on)
679 {
680 	BControl::SetEnabled(on);
681 }
682 
683 
684 void
685 BSlider::GetLimits(int32 *minimum, int32 *maximum)
686 {
687 	*minimum = fMinValue;
688 	*maximum = fMaxValue;
689 }
690 
691 
692 void
693 BSlider::Draw(BRect updateRect)
694 {
695 	// clear out background
696 	BRegion background(updateRect);
697 	background.Exclude(BarFrame());
698 
699 	// ToDo: the triangle thumb doesn't delete its background, so we still have to do it
700 	// Note, this also creates a different behaviour for subclasses, depending on the
701 	// thumb style - if possible this should be avoided.
702 	if (Style() == B_BLOCK_THUMB)
703 		background.Exclude(ThumbFrame());
704 
705 #if USE_OFF_SCREEN_VIEW
706 	if (!fOffScreenBits)
707 		return;
708 
709 	if (fOffScreenBits->Lock()) {
710 #endif
711 
712 		if (background.Frame().IsValid())
713 			OffscreenView()->FillRegion(&background, B_SOLID_LOW);
714 
715 #if USE_OFF_SCREEN_VIEW
716 		fOffScreenView->Sync();
717 		fOffScreenBits->Unlock();
718 	}
719 #endif
720 
721 	DrawSlider();
722 }
723 
724 
725 void
726 BSlider::DrawSlider()
727 {
728 	if (LockLooper()) {
729 #if USE_OFF_SCREEN_VIEW
730 		if (!fOffScreenBits)
731 			return;
732 		if (fOffScreenBits->Lock()) {
733 #endif
734 			DrawBar();
735 			DrawHashMarks();
736 			DrawThumb();
737 			DrawFocusMark();
738 			DrawText();
739 
740 #if USE_OFF_SCREEN_VIEW
741 			fOffScreenView->Sync();
742 			fOffScreenBits->Unlock();
743 
744 			DrawBitmap(fOffScreenBits, B_ORIGIN);
745 		}
746 #endif
747 		UnlockLooper();
748 	}
749 }
750 
751 
752 void
753 BSlider::DrawBar()
754 {
755 	BRect frame = BarFrame();
756 	BView *view = OffscreenView();
757 
758 	rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR);
759 	rgb_color lightenmax;
760 	rgb_color darken1;
761 	rgb_color darken2;
762 	rgb_color darkenmax;
763 
764 	rgb_color barColor;
765 	rgb_color fillColor;
766 
767 	if (IsEnabled()) {
768 		lightenmax	= tint_color(no_tint, B_LIGHTEN_MAX_TINT);
769 		darken1		= tint_color(no_tint, B_DARKEN_1_TINT);
770 		darken2		= tint_color(no_tint, B_DARKEN_2_TINT);
771 		darkenmax	= tint_color(no_tint, B_DARKEN_MAX_TINT);
772 		barColor	= fBarColor;
773 		fillColor	= fFillColor;
774 	} else {
775 		lightenmax	= tint_color(no_tint, B_LIGHTEN_MAX_TINT);
776 		darken1		= no_tint;
777 		darken2		= tint_color(no_tint, B_DARKEN_1_TINT);
778 		darkenmax	= tint_color(no_tint, B_DARKEN_3_TINT);
779 
780 		barColor.red	= (fBarColor.red + no_tint.red) / 2;
781 		barColor.green	= (fBarColor.green + no_tint.green) / 2;
782 		barColor.blue	= (fBarColor.blue + no_tint.blue) / 2;
783 
784 		fillColor.red	= (fFillColor.red + no_tint.red) / 2;
785 		fillColor.green	= (fFillColor.green + no_tint.green) / 2;
786 		fillColor.blue	= (fFillColor.blue + no_tint.blue) / 2;
787 	}
788 
789 	// exclude the block thumb from the bar filling
790 
791 	BRect lowerFrame = frame.InsetByCopy(1, 1);
792 	lowerFrame.top++;
793 	lowerFrame.left++;
794 	BRect upperFrame = lowerFrame;
795 	BRect thumbFrame;
796 
797 	if (Style() == B_BLOCK_THUMB) {
798 		thumbFrame = ThumbFrame();
799 
800 		if (fOrientation == B_HORIZONTAL) {
801 			lowerFrame.right = thumbFrame.left;
802 			upperFrame.left = thumbFrame.right;
803 		} else {
804 			lowerFrame.top = thumbFrame.bottom;
805 			upperFrame.bottom = thumbFrame.top;
806 		}
807 	} else if (fUseFillColor) {
808 		if (fOrientation == B_HORIZONTAL) {
809 			lowerFrame.right = floor(lowerFrame.left - 1 + Position()
810 				* (lowerFrame.Width() + 1));
811 			upperFrame.left = lowerFrame.right;
812 		} else {
813 			lowerFrame.top = floor(lowerFrame.bottom + 1 - Position()
814 				* (lowerFrame.Height() + 1));
815 			upperFrame.bottom = lowerFrame.top;
816 		}
817 	}
818 
819 	view->SetHighColor(barColor);
820 	view->FillRect(upperFrame);
821 
822 	if (Style() == B_BLOCK_THUMB || fUseFillColor) {
823 		if (fUseFillColor)
824 			view->SetHighColor(fillColor);
825 		view->FillRect(lowerFrame);
826 	}
827 
828 	if (Style() == B_BLOCK_THUMB) {
829 		// We don't want to stroke the lines over the thumb
830 
831 		PushState();
832 
833 		BRegion region;
834 		GetClippingRegion(&region);
835 		region.Exclude(thumbFrame);
836 		ConstrainClippingRegion(&region);
837 	}
838 
839 	view->SetHighColor(darken1);
840 	view->StrokeLine(BPoint(frame.left, frame.top),
841 					 BPoint(frame.left + 1.0f, frame.top));
842 	view->StrokeLine(BPoint(frame.left, frame.bottom),
843 					 BPoint(frame.left + 1.0f, frame.bottom));
844 	view->StrokeLine(BPoint(frame.right - 1.0f, frame.top),
845 					 BPoint(frame.right, frame.top));
846 
847 	view->SetHighColor(darken2);
848 	view->StrokeLine(BPoint(frame.left + 1.0f, frame.top),
849 					 BPoint(frame.right - 1.0f, frame.top));
850 	view->StrokeLine(BPoint(frame.left, frame.bottom - 1.0f),
851 					 BPoint(frame.left, frame.top + 1.0f));
852 
853 	view->SetHighColor(lightenmax);
854 	view->StrokeLine(BPoint(frame.left + 1.0f, frame.bottom),
855 					 BPoint(frame.right, frame.bottom));
856 	view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f),
857 					 BPoint(frame.right, frame.top + 1.0f));
858 
859 	frame.InsetBy(1.0f, 1.0f);
860 
861 	view->SetHighColor(darkenmax);
862 	view->StrokeLine(BPoint(frame.left, frame.bottom),
863 					 BPoint(frame.left, frame.top));
864 	view->StrokeLine(BPoint(frame.left + 1.0f, frame.top),
865 					 BPoint(frame.right, frame.top));
866 
867 	if (Style() == B_BLOCK_THUMB)
868 		PopState();
869 }
870 
871 
872 void
873 BSlider::DrawHashMarks()
874 {
875 	BRect frame = HashMarksFrame();
876 	BView *view = OffscreenView();
877 
878 	rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR);
879 	rgb_color lightenmax;
880 	rgb_color darken2;
881 
882 	if (IsEnabled()) {
883 		lightenmax	= tint_color(no_tint, B_LIGHTEN_MAX_TINT);
884 		darken2		= tint_color(no_tint, B_DARKEN_2_TINT);
885 	} else {
886 		lightenmax	= tint_color(no_tint, B_LIGHTEN_2_TINT);
887 		darken2		= tint_color(no_tint, B_DARKEN_1_TINT);
888 	}
889 
890 	float pos = _MinPosition();
891 	float factor = 0.0f;
892 	if (fHashMarkCount > 1)
893 		factor = (_MaxPosition() - pos) / (fHashMarkCount - 1);
894 
895 	if (fHashMarks & B_HASH_MARKS_TOP && fHashMarkCount > 0) {
896 
897 		view->BeginLineArray(fHashMarkCount * 2);
898 
899 		if (fOrientation == B_HORIZONTAL) {
900 			for (int32 i = 0; i < fHashMarkCount; i++) {
901 				view->AddLine(BPoint(pos, frame.top),
902 							  BPoint(pos, frame.top + 5), darken2);
903 				view->AddLine(BPoint(pos + 1, frame.top),
904 							  BPoint(pos + 1, frame.top + 5), lightenmax);
905 
906 				pos += factor;
907 			}
908 		} else {
909 			for (int32 i = 0; i < fHashMarkCount; i++) {
910 				view->AddLine(BPoint(frame.left, pos),
911 							  BPoint(frame.left + 5, pos), darken2);
912 				view->AddLine(BPoint(frame.left, pos + 1),
913 							  BPoint(frame.left + 5, pos + 1), lightenmax);
914 
915 				pos += factor;
916 			}
917 		}
918 
919 		view->EndLineArray();
920 	}
921 
922 	pos = _MinPosition();
923 
924 	if (fHashMarks & B_HASH_MARKS_BOTTOM && fHashMarkCount > 0) {
925 		view->BeginLineArray(fHashMarkCount * 2);
926 
927 		if (fOrientation == B_HORIZONTAL) {
928 			for (int32 i = 0; i < fHashMarkCount; i++) {
929 				view->AddLine(BPoint(pos, frame.bottom - 5),
930 							  BPoint(pos, frame.bottom), darken2);
931 				view->AddLine(BPoint(pos + 1, frame.bottom - 5),
932 							  BPoint(pos + 1, frame.bottom), lightenmax);
933 
934 				pos += factor;
935 			}
936 		} else {
937 			for (int32 i = 0; i < fHashMarkCount; i++) {
938 				view->AddLine(BPoint(frame.right - 5, pos),
939 							  BPoint(frame.right, pos), darken2);
940 				view->AddLine(BPoint(frame.right - 5, pos + 1),
941 							  BPoint(frame.right, pos + 1), lightenmax);
942 
943 				pos += factor;
944 			}
945 		}
946 
947 		view->EndLineArray();
948 	}
949 }
950 
951 
952 void
953 BSlider::DrawThumb()
954 {
955 	if (Style() == B_BLOCK_THUMB)
956 		_DrawBlockThumb();
957 	else
958 		_DrawTriangleThumb();
959 }
960 
961 
962 void
963 BSlider::DrawFocusMark()
964 {
965 	if (!IsFocus())
966 		return;
967 
968 	OffscreenView()->SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
969 
970 	BRect frame = ThumbFrame();
971 
972 	if (fStyle == B_BLOCK_THUMB) {
973 		frame.left += 2.0f;
974 		frame.top += 2.0f;
975 		frame.right -= 3.0f;
976 		frame.bottom -= 3.0f;
977 		OffscreenView()->StrokeRect(frame);
978 	} else {
979 		if (fOrientation == B_HORIZONTAL) {
980 			OffscreenView()->StrokeLine(BPoint(frame.left, frame.bottom + 2.0f),
981 				BPoint(frame.right, frame.bottom + 2.0f));
982 		} else {
983 			OffscreenView()->StrokeLine(BPoint(frame.left - 2.0f, frame.top),
984 				BPoint(frame.left - 2.0f, frame.bottom));
985 		}
986 	}
987 }
988 
989 
990 void
991 BSlider::DrawText()
992 {
993 	BRect bounds(Bounds());
994 	BView *view = OffscreenView();
995 
996 	if (IsEnabled()) {
997 		view->SetHighColor(0, 0, 0);
998 	} else {
999 		view->SetHighColor(tint_color(LowColor(), B_DISABLED_LABEL_TINT));
1000 	}
1001 
1002 	font_height fontHeight;
1003 	GetFontHeight(&fontHeight);
1004 
1005 	if (Orientation() == B_HORIZONTAL) {
1006 		if (Label())
1007 			view->DrawString(Label(), BPoint(2.0f, ceilf(fontHeight.ascent)));
1008 
1009 		// the update text is updated in SetValue() only
1010 		if (fUpdateText != NULL) {
1011 			view->DrawString(fUpdateText, BPoint(bounds.right - StringWidth(fUpdateText)
1012 				- 2.0f, ceilf(fontHeight.ascent)));
1013 		}
1014 
1015 		if (fMinLimitLabel)
1016 			view->DrawString(fMinLimitLabel, BPoint(2.0f, bounds.bottom - 4.0f));
1017 
1018 		if (fMaxLimitLabel) {
1019 			view->DrawString(fMaxLimitLabel, BPoint(bounds.right
1020 				- StringWidth(fMaxLimitLabel) - 2.0f, bounds.bottom - 4.0f));
1021 		}
1022 	} else {
1023 		if (Label()) {
1024 			view->DrawString(Label(), BPoint(bounds.Width() / 2.0f -
1025 											 StringWidth(Label()) / 2.0f,
1026 											 fontHeight.ascent));
1027 		}
1028 
1029 		if (fUpdateText != NULL) {
1030 			view->DrawString(fUpdateText, BPoint(bounds.Width() / 2.0f -
1031 				StringWidth(fUpdateText) / 2.0f, bounds.bottom - fontHeight.descent - 4.0f));
1032 		}
1033 
1034 		if (fMaxLimitLabel) {
1035 			view->DrawString(fMaxLimitLabel, BPoint(bounds.Width() / 2.0f -
1036 				StringWidth(fMaxLimitLabel) / 2.0f, fontHeight.ascent + (Label()
1037 					? ceilf(fontHeight.ascent + fontHeight.descent + fontHeight.leading + 2.0f)
1038 					: 0.0f)));
1039 		}
1040 
1041 		if (fMinLimitLabel) {
1042 			view->DrawString(fMinLimitLabel, BPoint(bounds.Width() / 2.0f
1043 				- StringWidth(fMinLimitLabel) / 2.0f, bounds.bottom - 2.0f));
1044 		}
1045 	}
1046 }
1047 
1048 
1049 char*
1050 BSlider::UpdateText() const
1051 {
1052 	return NULL;
1053 }
1054 
1055 
1056 BRect
1057 BSlider::BarFrame() const
1058 {
1059 	BRect frame(Bounds());
1060 
1061 	font_height fontHeight;
1062 	GetFontHeight(&fontHeight);
1063 
1064 	float textHeight = (float)ceil(fontHeight.ascent + fontHeight.descent);
1065 
1066 	if (fStyle == B_BLOCK_THUMB) {
1067 		if (Orientation() == B_HORIZONTAL) {
1068 			frame.left = 8.0f;
1069 			frame.top = 6.0f + (Label() ? textHeight + 4.0f : 0.0f);
1070 			frame.right -= 8.0f;
1071 			frame.bottom = frame.top + fBarThickness;
1072 		} else {
1073 			frame.left = floor((frame.Width() - fBarThickness) / 2.0f);
1074 			frame.top = 12.0f + (Label() ? textHeight : 0.0f) +
1075 				(fMaxLimitLabel ? textHeight : 0.0f);
1076 			frame.right = frame.left + fBarThickness;
1077 			frame.bottom = frame.bottom - 8.0f -
1078 				(fMinLimitLabel ? textHeight + 4 : 0.0f);
1079 		}
1080 	} else {
1081 		if (Orientation() == B_HORIZONTAL) {
1082 			frame.left = 7.0f;
1083 			frame.top = 6.0f + (Label() ? textHeight + 4.0f : 0.0f);
1084 			frame.right -= 7.0f;
1085 			frame.bottom = frame.top + fBarThickness;
1086 		} else {
1087 			frame.left = floor((frame.Width() - fBarThickness) / 2.0f);
1088 			frame.top = 11.0f + (Label() ? textHeight : 0.0f) +
1089 				(fMaxLimitLabel ? textHeight : 0.0f);
1090 			frame.right = frame.left + fBarThickness;
1091 			frame.bottom = frame.bottom - 7.0f -
1092 				(fMinLimitLabel ? textHeight + 4 : 0.0f);
1093 		}
1094 	}
1095 
1096 	return frame;
1097 }
1098 
1099 
1100 BRect
1101 BSlider::HashMarksFrame() const
1102 {
1103 	BRect frame(BarFrame());
1104 
1105 	if (fOrientation == B_HORIZONTAL) {
1106 		frame.top -= 6.0f;
1107 		frame.bottom += 6.0f;
1108 	} else {
1109 		frame.left -= 6.0f;
1110 		frame.right += 6.0f;
1111 	}
1112 
1113 	return frame;
1114 }
1115 
1116 
1117 BRect
1118 BSlider::ThumbFrame() const
1119 {
1120 	// TODO: The slider looks really ugly and broken when it is too little.
1121 	// I would suggest using BarFrame() here to get the top and bottom coords
1122 	// and spread them further apart for the thumb
1123 
1124 	BRect frame = Bounds();
1125 	font_height fheight;
1126 
1127 	GetFontHeight(&fheight);
1128 
1129 	float textHeight = (float)ceil(fheight.ascent + fheight.descent);
1130 
1131 	if (fStyle == B_BLOCK_THUMB) {
1132 		if (Orientation() == B_HORIZONTAL) {
1133 			frame.left = (float)floor(Position() * (_MaxPosition() - _MinPosition()) +
1134 				_MinPosition()) - 8.0f;
1135 			frame.top = 2.0f + (Label() ? textHeight + 4.0f : 0.0f);
1136 			frame.right = frame.left + 17.0f;
1137 			frame.bottom = frame.top + fBarThickness + 7.0f;
1138 		} else {
1139 			frame.left = floor((frame.Width() - fBarThickness) / 2.0f) - 4;
1140 			frame.top = (float)floor(Position() * (_MaxPosition() - _MinPosition()) +
1141 				_MinPosition()) - 8.0f;
1142 			frame.right = frame.left + fBarThickness + 7.0f;
1143 			frame.bottom = frame.top + 17;
1144 		}
1145 	} else {
1146 		if (Orientation() == B_HORIZONTAL) {
1147 			frame.left = (float)floor(Position() * (_MaxPosition() - _MinPosition()) +
1148 				_MinPosition()) - 6;
1149 			frame.right = frame.left + 12.0f;
1150 			frame.bottom = frame.bottom - 2.0f -
1151 				(MinLimitLabel() || MaxLimitLabel() ? textHeight + 4.0f : 0.0f);
1152 			frame.top = frame.bottom - 8.0f;
1153 		} else {
1154 			frame.left = floor((frame.Width() - fBarThickness) / 2.0f) - 3;
1155 			frame.top = (float)floor(Position() * (_MaxPosition() - _MinPosition())) +
1156 				_MinPosition() - 6.0f;
1157 			frame.right = frame.left + 7;
1158 			frame.bottom = frame.top + 12;
1159 		}
1160 	}
1161 
1162 	return frame;
1163 }
1164 
1165 
1166 void
1167 BSlider::SetFlags(uint32 flags)
1168 {
1169 	BControl::SetFlags(flags);
1170 }
1171 
1172 
1173 void
1174 BSlider::SetResizingMode(uint32 mode)
1175 {
1176 	BControl::SetResizingMode(mode);
1177 }
1178 
1179 
1180 void
1181 BSlider::GetPreferredSize(float* _width, float* _height)
1182 {
1183 	font_height fontHeight;
1184 	GetFontHeight(&fontHeight);
1185 
1186 	float width, height;
1187 	int32 rows = 0;
1188 
1189 	if (Orientation() == B_HORIZONTAL) {
1190 		width = Frame().Width();
1191 		height = 12.0f + fBarThickness;
1192 
1193 		float labelWidth = 0;
1194 		if (Label()) {
1195 			labelWidth = StringWidth(Label());
1196 			rows++;
1197 		}
1198 
1199 		float minWidth = 0;
1200 		if (MinLimitLabel())
1201 			minWidth = StringWidth(MinLimitLabel());
1202 		if (MaxLimitLabel()) {
1203 			// some space between the labels
1204 			if (MinLimitLabel())
1205 				minWidth += 8.0f;
1206 
1207 			minWidth += StringWidth(MaxLimitLabel());
1208 		}
1209 
1210 		if (minWidth > width)
1211 			width = minWidth;
1212 		if (labelWidth > width)
1213 			width = labelWidth;
1214 		if (width < 32.0f)
1215 			width = 32.0f;
1216 
1217 		if (MinLimitLabel() || MaxLimitLabel())
1218 			rows++;
1219 
1220 		height += rows * ((float)ceil(fontHeight.ascent + fontHeight.descent) + 4.0f);
1221 	} else {
1222 		// B_VERTICAL
1223 		width = 12.0f + fBarThickness;
1224 		height = Frame().Height();
1225 
1226 		// find largest label
1227 
1228 		float minWidth = 0;
1229 		if (Label()) {
1230 			minWidth = StringWidth(Label());
1231 			rows++;
1232 		}
1233 		if (MinLimitLabel()) {
1234 			float width = StringWidth(MinLimitLabel());
1235 			if (width > minWidth)
1236 				minWidth = width;
1237 			rows++;
1238 		}
1239 		if (MaxLimitLabel()) {
1240 			float width = StringWidth(MaxLimitLabel());
1241 			if (width > minWidth)
1242 				minWidth = width;
1243 			rows++;
1244 		}
1245 
1246 		if (minWidth > width)
1247 			width = minWidth;
1248 
1249 		float minHeight = 32.0f + rows
1250 			* ((float)ceil(fontHeight.ascent + fontHeight.descent) + 4.0f);
1251 
1252 		if (Label() && MaxLimitLabel())
1253 			minHeight -= 4.0f;
1254 
1255 		if (minHeight > height)
1256 			height = minHeight;
1257 	}
1258 
1259 	if (_width)
1260 		*_width = width;
1261 	if (_height)
1262 		*_height = height;
1263 }
1264 
1265 
1266 void
1267 BSlider::ResizeToPreferred()
1268 {
1269 	BControl::ResizeToPreferred();
1270 }
1271 
1272 
1273 status_t
1274 BSlider::Invoke(BMessage *msg)
1275 {
1276 	return BControl::Invoke(msg);
1277 }
1278 
1279 
1280 BHandler*
1281 BSlider::ResolveSpecifier(BMessage *message, int32 index,
1282 						  BMessage *specifier, int32 command,
1283 						  const char *property)
1284 {
1285 	return BControl::ResolveSpecifier(message, index, specifier,
1286 									  command, property);
1287 }
1288 
1289 
1290 status_t
1291 BSlider::GetSupportedSuites(BMessage *message)
1292 {
1293 	return BControl::GetSupportedSuites(message);
1294 }
1295 
1296 
1297 void
1298 BSlider::SetModificationMessage(BMessage *message)
1299 {
1300 	if (fModificationMessage)
1301 		delete fModificationMessage;
1302 
1303 	fModificationMessage = message;
1304 }
1305 
1306 
1307 BMessage*
1308 BSlider::ModificationMessage() const
1309 {
1310 	return fModificationMessage;
1311 }
1312 
1313 
1314 void
1315 BSlider::SetSnoozeAmount(int32 snoozeTime)
1316 {
1317 	if (snoozeTime < 10000)
1318 		snoozeTime = 10000;
1319 	else if (snoozeTime > 1000000)
1320 		snoozeTime = 1000000;
1321 
1322 	fSnoozeAmount = snoozeTime;
1323 }
1324 
1325 
1326 int32
1327 BSlider::SnoozeAmount() const
1328 {
1329 	return fSnoozeAmount;
1330 }
1331 
1332 
1333 void
1334 BSlider::SetKeyIncrementValue(int32 increment_value)
1335 {
1336 	fKeyIncrementValue = increment_value;
1337 }
1338 
1339 
1340 int32
1341 BSlider::KeyIncrementValue() const
1342 {
1343 	return fKeyIncrementValue;
1344 }
1345 
1346 
1347 void
1348 BSlider::SetHashMarkCount(int32 hash_mark_count)
1349 {
1350 	fHashMarkCount = hash_mark_count;
1351 	Invalidate();
1352 }
1353 
1354 
1355 int32
1356 BSlider::HashMarkCount() const
1357 {
1358 	return fHashMarkCount;
1359 }
1360 
1361 
1362 void
1363 BSlider::SetHashMarks(hash_mark_location where)
1364 {
1365 	fHashMarks = where;
1366 	Invalidate();
1367 }
1368 
1369 
1370 hash_mark_location
1371 BSlider::HashMarks() const
1372 {
1373 	return fHashMarks;
1374 }
1375 
1376 
1377 void
1378 BSlider::SetStyle(thumb_style style)
1379 {
1380 	fStyle = style;
1381 	Invalidate();
1382 }
1383 
1384 
1385 thumb_style
1386 BSlider::Style() const
1387 {
1388 	return fStyle;
1389 }
1390 
1391 
1392 void
1393 BSlider::SetBarColor(rgb_color bar_color)
1394 {
1395 	fBarColor = bar_color;
1396 	Invalidate();
1397 }
1398 
1399 
1400 rgb_color
1401 BSlider::BarColor() const
1402 {
1403 	return fBarColor;
1404 }
1405 
1406 
1407 void
1408 BSlider::UseFillColor(bool use_fill, const rgb_color *bar_color)
1409 {
1410 	fUseFillColor = use_fill;
1411 
1412 	if (use_fill && bar_color)
1413 		fFillColor = *bar_color;
1414 
1415 	Invalidate();
1416 }
1417 
1418 
1419 bool
1420 BSlider::FillColor(rgb_color *bar_color) const
1421 {
1422 	if (bar_color && fUseFillColor)
1423 		*bar_color = fFillColor;
1424 
1425 	return fUseFillColor;
1426 }
1427 
1428 
1429 BView*
1430 BSlider::OffscreenView() const
1431 {
1432 #if USE_OFF_SCREEN_VIEW
1433 	return fOffScreenView;
1434 #else
1435 	return (BView*)this;
1436 #endif
1437 }
1438 
1439 
1440 orientation
1441 BSlider::Orientation() const
1442 {
1443 	return fOrientation;
1444 }
1445 
1446 
1447 void
1448 BSlider::SetOrientation(orientation posture)
1449 {
1450 	fOrientation = posture;
1451 	Invalidate();
1452 }
1453 
1454 
1455 float
1456 BSlider::BarThickness() const
1457 {
1458 	return fBarThickness;
1459 }
1460 
1461 
1462 void
1463 BSlider::SetBarThickness(float thickness)
1464 {
1465 	if (thickness >= 1.0f)
1466 		fBarThickness = thickness;
1467 }
1468 
1469 
1470 void
1471 BSlider::SetFont(const BFont *font, uint32 properties)
1472 {
1473 	BControl::SetFont(font, properties);
1474 
1475 #if USE_OFF_SCREEN_VIEW
1476 	if (fOffScreenView && fOffScreenBits) {
1477 		if (fOffScreenBits->Lock()) {
1478 			fOffScreenView->SetFont(font, properties);
1479 			fOffScreenBits->Unlock();
1480 		}
1481 	}
1482 #endif
1483 }
1484 
1485 
1486 void
1487 BSlider::SetLimits(int32 minimum, int32 maximum)
1488 {
1489 	if (minimum <= maximum) {
1490 		fMinValue = minimum;
1491 		fMaxValue = maximum;
1492 
1493 		int32 value = Value();
1494 		value = max_c(minimum, value);
1495 		value = min_c(maximum, value);
1496 
1497 		if (value != Value()) {
1498 			SetValue(value);
1499 		}
1500 	}
1501 }
1502 
1503 
1504 void
1505 BSlider::_DrawBlockThumb()
1506 {
1507 	BRect frame = ThumbFrame();
1508 	BView *view = OffscreenView();
1509 
1510 	rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR);
1511 	rgb_color lighten2;
1512 	rgb_color lighten1;
1513 	rgb_color darken2;
1514 	rgb_color darken3;
1515 	rgb_color darkenmax;
1516 
1517 	if (IsEnabled()) {
1518 		lighten2	= tint_color(no_tint, B_LIGHTEN_2_TINT);
1519 		lighten1	= no_tint;
1520 		darken2		= tint_color(no_tint, B_DARKEN_2_TINT);
1521 		darken3		= tint_color(no_tint, B_DARKEN_3_TINT);
1522 		darkenmax	= tint_color(no_tint, B_DARKEN_MAX_TINT);
1523 	} else {
1524 		lighten2	= tint_color(no_tint, B_LIGHTEN_2_TINT);
1525 		lighten1	= tint_color(no_tint, B_LIGHTEN_1_TINT);
1526 		darken2		= tint_color(no_tint, (B_NO_TINT + B_DARKEN_1_TINT) / 2.0);
1527 		darken3		= tint_color(no_tint, B_DARKEN_1_TINT);
1528 		darkenmax	= tint_color(no_tint, B_DARKEN_3_TINT);
1529 	}
1530 
1531 	// blank background for shadow
1532 	// ToDo: this also draws over the hash marks (though it's not *that* noticeable)
1533 	view->SetHighColor(no_tint);
1534 	view->StrokeLine(BPoint(frame.left, frame.top),
1535 					 BPoint(frame.left, frame.top));
1536 
1537 	BRect barFrame = BarFrame();
1538 	if (barFrame.right >= frame.right) {
1539 		// leave out barFrame from shadow background clearing
1540 		view->StrokeLine(BPoint(frame.right, frame.top),
1541 						 BPoint(frame.right, barFrame.top - 1.0f));
1542 		view->StrokeLine(BPoint(frame.right, barFrame.bottom + 1.0f),
1543 						 BPoint(frame.right, frame.bottom));
1544 	} else {
1545 		view->StrokeLine(BPoint(frame.right, frame.top),
1546 						 BPoint(frame.right, frame.bottom));
1547 	}
1548 
1549 	view->StrokeLine(BPoint(frame.left, frame.bottom),
1550 					 BPoint(frame.right - 1.0f, frame.bottom));
1551 	view->StrokeLine(BPoint(frame.left, frame.bottom - 1.0f),
1552 					 BPoint(frame.left, frame.bottom - 1.0f));
1553 	view->StrokeLine(BPoint(frame.right - 1.0f, frame.top),
1554 					 BPoint(frame.right - 1.0f, frame.top));
1555 
1556 	// Outline (top, left)
1557 	view->SetHighColor(darken3);
1558 	view->StrokeLine(BPoint(frame.left, frame.bottom - 2.0f),
1559 					 BPoint(frame.left, frame.top + 1.0f));
1560 	view->StrokeLine(BPoint(frame.left + 1.0f, frame.top),
1561 					 BPoint(frame.right - 2.0f, frame.top));
1562 
1563 	// Shadow
1564 	view->SetHighColor(0, 0, 0, IsEnabled() ? 100 : 50);
1565 	view->SetDrawingMode(B_OP_ALPHA);
1566 	view->StrokeLine(BPoint(frame.right, frame.top + 2.0f),
1567 					 BPoint(frame.right, frame.bottom - 1.0f));
1568 	view->StrokeLine(BPoint(frame.left + 2.0f, frame.bottom),
1569 					 BPoint(frame.right - 1.0f, frame.bottom));
1570 
1571 	view->SetDrawingMode(B_OP_COPY);
1572 	view->SetHighColor(darken3);
1573 	view->StrokeLine(BPoint(frame.right - 1.0f, frame.bottom - 1.0f),
1574 					 BPoint(frame.right - 1.0f, frame.bottom - 1.0f));
1575 
1576 
1577 	// First bevel
1578 	frame.InsetBy(1.0f, 1.0f);
1579 
1580 	view->SetHighColor(darkenmax);
1581 	view->StrokeLine(BPoint(frame.left, frame.bottom),
1582 					 BPoint(frame.right - 1.0f, frame.bottom));
1583 	view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f),
1584 					 BPoint(frame.right, frame.top));
1585 
1586 	view->SetHighColor(lighten2);
1587 	view->StrokeLine(BPoint(frame.left, frame.top),
1588 					 BPoint(frame.left, frame.bottom - 1.0f));
1589 	view->StrokeLine(BPoint(frame.left + 1.0f, frame.top),
1590 					 BPoint(frame.right - 1.0f, frame.top));
1591 
1592 	frame.InsetBy(1.0f, 1.0f);
1593 
1594 	view->FillRect(BRect(frame.left, frame.top, frame.right - 1.0f, frame.bottom - 1.0f));
1595 
1596 	// Second bevel and center dots
1597 	view->SetHighColor(darken2);
1598 	view->StrokeLine(BPoint(frame.left, frame.bottom),
1599 					 BPoint(frame.right, frame.bottom));
1600 	view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f),
1601 					 BPoint(frame.right, frame.top));
1602 
1603 	if (Orientation() == B_HORIZONTAL) {
1604 		view->StrokeLine(BPoint(frame.left + 6.0f, frame.top + 2.0f),
1605 						 BPoint(frame.left + 6.0f, frame.top + 2.0f));
1606 		view->StrokeLine(BPoint(frame.left + 6.0f, frame.top + 4.0f),
1607 						 BPoint(frame.left + 6.0f, frame.top + 4.0f));
1608 		view->StrokeLine(BPoint(frame.left + 6.0f, frame.top + 6.0f),
1609 						 BPoint(frame.left + 6.0f, frame.top + 6.0f));
1610 	} else {
1611 		view->StrokeLine(BPoint(frame.left + 2.0f, frame.top + 6.0f),
1612 						 BPoint(frame.left + 2.0f, frame.top + 6.0f));
1613 		view->StrokeLine(BPoint(frame.left + 4.0f, frame.top + 6.0f),
1614 						 BPoint(frame.left + 4.0f, frame.top + 6.0f));
1615 		view->StrokeLine(BPoint(frame.left + 6.0f, frame.top + 6.0f),
1616 						 BPoint(frame.left + 6.0f, frame.top + 6.0f));
1617 	}
1618 
1619 	frame.InsetBy(1.0f, 1.0f);
1620 
1621 	// Third bevel
1622 	view->SetHighColor(lighten1);
1623 	view->StrokeLine(BPoint(frame.left, frame.bottom),
1624 					 BPoint(frame.right, frame.bottom));
1625 	view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0f),
1626 					 BPoint(frame.right, frame.top));
1627 }
1628 
1629 
1630 void
1631 BSlider::_DrawTriangleThumb()
1632 {
1633 	BRect frame = ThumbFrame();
1634 	BView *view = OffscreenView();
1635 
1636 	rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR);
1637 	rgb_color lightenmax;
1638 	rgb_color lighten1;
1639 	rgb_color darken2;
1640 	rgb_color darken3;
1641 	rgb_color darkenmax;
1642 
1643 	if (IsEnabled()) {
1644 		lightenmax	= tint_color(no_tint, B_LIGHTEN_MAX_TINT);
1645 		lighten1	= no_tint;
1646 		darken2		= tint_color(no_tint, B_DARKEN_2_TINT);
1647 		darken3		= tint_color(no_tint, B_DARKEN_3_TINT);
1648 		darkenmax	= tint_color(no_tint, B_DARKEN_MAX_TINT);
1649 	} else {
1650 		lightenmax	= tint_color(no_tint, B_LIGHTEN_2_TINT);
1651 		lighten1	= tint_color(no_tint, B_LIGHTEN_1_TINT);
1652 		darken2		= tint_color(no_tint, (B_NO_TINT + B_DARKEN_1_TINT) / 2.0);
1653 		darken3		= tint_color(no_tint, B_DARKEN_1_TINT);
1654 		darkenmax	= tint_color(no_tint, B_DARKEN_3_TINT);
1655 	}
1656 
1657 	view->SetDrawingMode(B_OP_OVER);
1658 
1659 	if (Orientation() == B_HORIZONTAL) {
1660 		view->SetHighColor(lighten1);
1661 		view->FillTriangle(BPoint(frame.left + 1.0, frame.bottom - 2.0),
1662 						   BPoint(frame.left + 6.0, frame.top + 1.0),
1663 						   BPoint(frame.right - 1.0, frame.bottom - 2.0));
1664 
1665 		view->SetHighColor(no_tint);
1666 		view->StrokeLine(BPoint(frame.right - 2.0, frame.bottom - 2.0),
1667 						 BPoint(frame.left + 3.0, frame.bottom - 2.0));
1668 
1669 		view->SetHighColor(darkenmax);
1670 		view->StrokeLine(BPoint(frame.left, frame.bottom),
1671 						 BPoint(frame.right, frame.bottom));
1672 		view->StrokeLine(BPoint(frame.right, frame.bottom - 1.0),
1673 						 BPoint(frame.left + 6.0, frame.top + 1.0));
1674 
1675 		view->SetHighColor(darken2);
1676 		view->StrokeLine(BPoint(frame.right - 1.0, frame.bottom - 1.0),
1677 						 BPoint(frame.left + 1.0, frame.bottom - 1.0));
1678 		view->SetHighColor(darken3);
1679 		view->StrokeLine(BPoint(frame.left, frame.bottom - 1.0),
1680 						 BPoint(frame.left + 5.0, frame.top + 2.0));
1681 
1682 		view->SetHighColor(lightenmax);
1683 		view->StrokeLine(BPoint(frame.left + 2.0, frame.bottom - 2.0),
1684 						 BPoint(frame.left + 6.0, frame.top + 2.0));
1685 	} else {
1686 		view->SetHighColor(lighten1);
1687 		view->FillTriangle(BPoint(frame.left + 1.0f, frame.top),
1688 			BPoint(frame.left + 7.0f, frame.top + 6.0f),
1689 			BPoint(frame.left + 1.0f, frame.bottom));
1690 
1691 		view->SetHighColor(darkenmax);
1692 		view->StrokeLine(BPoint(frame.left, frame.top + 1),
1693 			BPoint(frame.left, frame.bottom));
1694 		view->StrokeLine(BPoint(frame.left + 1.0f, frame.bottom),
1695 			BPoint(frame.left + 7.0f, frame.top + 6.0f));
1696 
1697 		view->SetHighColor(darken2);
1698 		view->StrokeLine(BPoint(frame.left, frame.top),
1699 			BPoint(frame.left, frame.bottom - 1));
1700 		view->StrokeLine(BPoint(frame.left + 1.0f, frame.top),
1701 			BPoint(frame.left + 6.0f, frame.top + 5.0f));
1702 
1703 		view->SetHighColor(no_tint);
1704 		view->StrokeLine(BPoint(frame.left + 1.0f, frame.top + 2.0f),
1705 			BPoint(frame.left + 1.0f, frame.bottom - 1.0f));
1706 		view->StrokeLine(BPoint(frame.left + 2.0f, frame.bottom - 2.0f),
1707 			BPoint(frame.left + 6.0f, frame.top + 6.0f));
1708 	}
1709 
1710 	view->SetDrawingMode(B_OP_COPY);
1711 }
1712 
1713 
1714 BPoint
1715 BSlider::_Location() const
1716 {
1717 	return fLocation;
1718 }
1719 
1720 
1721 void
1722 BSlider::_SetLocation(BPoint p)
1723 {
1724 	fLocation = p;
1725 }
1726 
1727 
1728 float
1729 BSlider::_MinPosition() const
1730 {
1731 	if (fOrientation == B_HORIZONTAL)
1732 		return BarFrame().left + 1.0f;
1733 
1734 	return BarFrame().bottom - 1.0f;
1735 }
1736 
1737 
1738 float
1739 BSlider::_MaxPosition() const
1740 {
1741 	if (fOrientation == B_HORIZONTAL)
1742 		return BarFrame().right - 1.0f;
1743 
1744 	return BarFrame().top + 1.0f;
1745 }
1746 
1747 
1748 void BSlider::_ReservedSlider5() {}
1749 void BSlider::_ReservedSlider6() {}
1750 void BSlider::_ReservedSlider7() {}
1751 void BSlider::_ReservedSlider8() {}
1752 void BSlider::_ReservedSlider9() {}
1753 void BSlider::_ReservedSlider10() {}
1754 void BSlider::_ReservedSlider11() {}
1755 void BSlider::_ReservedSlider12() {}
1756 
1757 
1758 BSlider &
1759 BSlider::operator=(const BSlider &)
1760 {
1761 	return *this;
1762 }
1763 
1764 
1765 //	#pragma mark - R4.5 compatibility
1766 
1767 
1768 #if __GNUC__ < 3
1769 
1770 extern "C"
1771 void _ReservedSlider4__7BSlider(BSlider *slider, int32 minimum, int32 maximum)
1772 {
1773 	slider->BSlider::SetLimits(minimum, maximum);
1774 }
1775 
1776 extern "C" void _ReservedSlider1__7BSlider() {}
1777 extern "C" void _ReservedSlider2__7BSlider() {}
1778 extern "C" void _ReservedSlider3__7BSlider() {}
1779 
1780 #endif	// __GNUC__ < 3
1781