xref: /haiku/src/kits/interface/ChannelSlider.cpp (revision 4f00613311d0bd6b70fa82ce19931c41f071ea4e)
1 /*
2  * Copyright 2005, Haiku Inc. All Rights Reserved.
3  * Author: Stefano Ceccherini (burton666@libero.it)
4  * Distributed under the terms of the MIT License.
5  */
6 
7 #include <Bitmap.h>
8 #include <ChannelSlider.h>
9 #include <Debug.h>
10 #include <PropertyInfo.h>
11 #include <Screen.h>
12 #include <Window.h>
13 
14 const static unsigned char
15 kVerticalKnobData[] = {
16 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
17 	0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
18 	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0xff,
19 	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0xff,
20 	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12,
21 	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12,
22 	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12,
23 	0xff, 0x00, 0x3f, 0x3f, 0xcb, 0xcb, 0xcb, 0xcb, 0x3f, 0x3f, 0x00, 0x12,
24 	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12,
25 	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12,
26 	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12,
27 	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12,
28 	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12,
29 	0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x12,
30 	0xff, 0xff, 0xff, 0xff, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0xff
31 };
32 
33 
34 const static unsigned char
35 kHorizontalKnobData[] = {
36 	0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
37 	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0xff, 0xff,
38 	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0xff, 0xff,
39 	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xcb, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12, 0xff,
40 	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xcb, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12, 0xff,
41 	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xcb, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12, 0xff,
42 	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xcb, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12, 0xff,
43 	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12, 0xff,
44 	0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x12, 0xff,
45 	0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x12, 0xff,
46 	0xff, 0xff, 0xff, 0xff, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0xff, 0xff
47 };
48 
49 
50 static property_info
51 sPropertyInfo[] = {
52 	{ "Orientation",
53 	{ B_GET_PROPERTY, B_SET_PROPERTY, 0 },
54 	{ B_DIRECT_SPECIFIER, 0 }, "" },
55 
56 	{ 0 }
57 };
58 
59 
60 const static float kPadding = 10.0;
61 
62 BChannelSlider::BChannelSlider(BRect area, const char *name, const char *label,
63 	BMessage *model, int32 channels, uint32 resizeMode, uint32 flags)
64 	: BChannelControl(area, name, label, model, channels, resizeMode, flags)
65 {
66 	InitData();
67 }
68 
69 
70 BChannelSlider::BChannelSlider(BRect area, const char *name, const char *label,
71 	BMessage *model, orientation o, int32 channels, uint32 resizeMode, uint32 flags)
72 	: BChannelControl(area, name, label, model, channels, resizeMode, flags)
73 
74 {
75 	InitData();
76 	SetOrientation(o);
77 }
78 
79 
80 BChannelSlider::BChannelSlider(BMessage *archive)
81 	: BChannelControl(archive)
82 {
83 	InitData();
84 
85 	orientation orient;
86 	if (archive->FindInt32("_orient", (int32 *)&orient) == B_OK)
87 		SetOrientation(orient);
88 }
89 
90 
91 BChannelSlider::~BChannelSlider()
92 {
93 	delete fLeftKnob;
94 	delete fMidKnob;
95 	delete fRightKnob;
96 	delete[] fInitialValues;
97 }
98 
99 
100 BArchivable *
101 BChannelSlider::Instantiate(BMessage *archive)
102 {
103 	if (validate_instantiation(archive, "BChannelSlider"))
104 		return new BChannelSlider(archive);
105 	else
106 		return NULL;
107 }
108 
109 
110 status_t
111 BChannelSlider::Archive(BMessage *into, bool deep) const
112 {
113 	status_t status = BChannelControl::Archive(into, deep);
114 	if (status == B_OK)
115 		status = into->AddInt32("_orient", (int32)Orientation());
116 
117 	return status;
118 }
119 
120 
121 orientation
122 BChannelSlider::Orientation() const
123 {
124 	return Vertical() ? B_VERTICAL : B_HORIZONTAL;
125 }
126 
127 
128 void
129 BChannelSlider::SetOrientation(orientation _orientation)
130 {
131 	bool isVertical = _orientation == B_VERTICAL;
132 	if (isVertical != Vertical()) {
133 		fVertical = isVertical;
134 		Invalidate(Bounds());
135 	}
136 }
137 
138 
139 int32
140 BChannelSlider::MaxChannelCount() const
141 {
142 	return 32;
143 }
144 
145 
146 bool
147 BChannelSlider::SupportsIndividualLimits() const
148 {
149 	return false;
150 }
151 
152 
153 void
154 BChannelSlider::AttachedToWindow()
155 {
156 	BView *parent = Parent();
157 	if (parent != NULL)
158 		SetViewColor(parent->ViewColor());
159 
160 	inherited::AttachedToWindow();
161 }
162 
163 
164 void
165 BChannelSlider::AllAttached()
166 {
167 	BControl::AllAttached();
168 }
169 
170 
171 void
172 BChannelSlider::DetachedFromWindow()
173 {
174 	inherited::DetachedFromWindow();
175 }
176 
177 
178 void
179 BChannelSlider::AllDetached()
180 {
181 	BControl::AllDetached();
182 }
183 
184 
185 void
186 BChannelSlider::MessageReceived(BMessage *message)
187 {
188 	switch (message->what) {
189 		case B_SET_PROPERTY:
190 		case B_GET_PROPERTY:
191 		{
192 			BMessage reply(B_REPLY);
193 			int32 index = 0;
194 			BMessage specifier;
195 			int32 what = 0;
196 			const char *property = NULL;
197 			bool handled = false;
198 			status_t status = message->GetCurrentSpecifier(&index, &specifier, &what, &property);
199 			BPropertyInfo propInfo(sPropertyInfo);
200 			if (status == B_OK
201 				&& propInfo.FindMatch(message, index, &specifier, what, property) >= 0) {
202 				handled = true;
203 				if (message->what == B_SET_PROPERTY) {
204 					orientation orient;
205 					if (specifier.FindInt32("data", (int32 *)&orient) == B_OK) {
206 						SetOrientation(orient);
207 						Invalidate(Bounds());
208 					}
209 				} else if (message->what == B_GET_PROPERTY)
210 					reply.AddInt32("result", (int32)Orientation());
211 				else
212 					status = B_BAD_SCRIPT_SYNTAX;
213 			}
214 
215 			if (handled) {
216 				reply.AddInt32("error", status);
217 				message->SendReply(&reply);
218 			} else
219 				inherited::MessageReceived(message);
220 			break;
221 		}
222 		default:
223 			inherited::MessageReceived(message);
224 			break;
225 	}
226 }
227 
228 
229 void
230 BChannelSlider::Draw(BRect updateRect)
231 {
232 	UpdateFontDimens();
233 	DrawThumbs();
234 
235 	BRect bounds(Bounds());
236 	float labelWidth = StringWidth(Label());
237 
238 	DrawString(Label(), BPoint((bounds.Width() - labelWidth) / 2, fBaseLine));
239 }
240 
241 
242 void
243 BChannelSlider::MouseDown(BPoint where)
244 {
245 	if (!IsEnabled())
246 		BControl::MouseDown(where);
247 	else {
248 		fCurrentChannel = -1;
249 		fMinpoint = 0;
250 
251 		// Search the channel on which the mouse was over
252 		int32 numChannels = CountChannels();
253 		for (int32 channel = 0; channel < numChannels; channel++) {
254 			BRect frame = ThumbFrameFor(channel);
255 			frame.OffsetBy(fClickDelta);
256 
257 			float range = ThumbRangeFor(channel);
258 			if (Vertical()) {
259 				fMinpoint = frame.top + frame.Height() / 2;
260 				frame.bottom += range;
261 			} else {
262 				// TODO: Fix this, the clickzone isn't perfect
263 				frame.right += range;
264 				fMinpoint = frame.Width();
265 			}
266 
267 			// Click was on a slider.
268 			if (frame.Contains(where)) {
269 				fCurrentChannel = channel;
270 				break;
271 			}
272 		}
273 
274 		// Click wasn't on a slider. Bail out.
275 		if (fCurrentChannel == -1)
276 			return;
277 
278 		uint32 buttons = 0;
279 		BMessage *currentMessage = Window()->CurrentMessage();
280 		if (currentMessage != NULL)
281 			currentMessage->FindInt32("buttons", (int32 *)&buttons);
282 
283 		fAllChannels = (buttons & B_SECONDARY_MOUSE_BUTTON) == 0;
284 
285 		if (fInitialValues != NULL && fAllChannels) {
286 			delete[] fInitialValues;
287 			fInitialValues = NULL;
288 		}
289 
290 		if (fInitialValues == NULL)
291 			fInitialValues = new int32[numChannels];
292 
293 		if (fAllChannels) {
294 			for (int32 i = 0; i < numChannels; i++)
295 				fInitialValues[i] = ValueFor(i);
296 		} else
297 			fInitialValues[fCurrentChannel] = ValueFor(fCurrentChannel);
298 
299 		if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) {
300 			if (!IsTracking()) {
301 				SetTracking(true);
302 				DrawThumbs();
303 				Flush();
304 			}
305 
306 			MouseMovedCommon(where, B_ORIGIN);
307 			SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY);
308 		} else {
309 			do {
310 				snooze(30000);
311 				GetMouse(&where, &buttons);
312 				MouseMovedCommon(where, B_ORIGIN);
313 			} while (buttons != 0);
314 			FinishChange();
315 			fCurrentChannel = -1;
316 			fAllChannels = false;
317 		}
318 	}
319 }
320 
321 
322 void
323 BChannelSlider::MouseUp(BPoint where)
324 {
325 	if (IsEnabled() && IsTracking()) {
326 		FinishChange();
327 		SetTracking(false);
328 		fAllChannels = false;
329 		fCurrentChannel = -1;
330 		fMinpoint = 0;
331 	} else
332 		BControl::MouseUp(where);
333 }
334 
335 
336 void
337 BChannelSlider::MouseMoved(BPoint where, uint32 code, const BMessage *message)
338 {
339 	if (IsEnabled() && IsTracking())
340 		MouseMovedCommon(where, B_ORIGIN);
341 	else
342 		BControl::MouseMoved(where, code, message);
343 }
344 
345 
346 void
347 BChannelSlider::WindowActivated(bool state)
348 {
349 	BControl::WindowActivated(state);
350 }
351 
352 
353 void
354 BChannelSlider::KeyDown(const char *bytes, int32 numBytes)
355 {
356 	BControl::KeyDown(bytes, numBytes);
357 }
358 
359 
360 void
361 BChannelSlider::KeyUp(const char *bytes, int32 numBytes)
362 {
363 	BView::KeyUp(bytes, numBytes);
364 }
365 
366 
367 void
368 BChannelSlider::FrameResized(float newWidth, float newHeight)
369 {
370 	inherited::FrameResized(newWidth, newHeight);
371 	Invalidate(Bounds());
372 }
373 
374 
375 void
376 BChannelSlider::SetFont(const BFont *font, uint32 mask)
377 {
378 	inherited::SetFont(font, mask);
379 }
380 
381 
382 void
383 BChannelSlider::MakeFocus(bool focusState)
384 {
385 	if (focusState && !IsFocus())
386 		fFocusChannel = -1;
387 	BControl::MakeFocus(focusState);
388 }
389 
390 
391 void
392 BChannelSlider::SetEnabled(bool on)
393 {
394 	BControl::SetEnabled(on);
395 }
396 
397 
398 void
399 BChannelSlider::GetPreferredSize(float *width, float *height)
400 {
401 	// TODO: Implement
402 }
403 
404 
405 BHandler *
406 BChannelSlider::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier,
407 								int32 form, const char *property)
408 {
409 	BHandler *target = this;
410 	BPropertyInfo propertyInfo(sPropertyInfo);
411 	if (propertyInfo.FindMatch(msg, index, specifier, form, property) < B_OK)
412 		target = BChannelControl::ResolveSpecifier(msg, index, specifier, form, property);
413 
414 	return target;
415 }
416 
417 
418 status_t
419 BChannelSlider::GetSupportedSuites(BMessage *data)
420 {
421 	if (data == NULL)
422 		return B_BAD_VALUE;
423 
424 	data->AddString("suites", "suite/vnd.Be-channel-slider");
425 
426 	BPropertyInfo propInfo(sPropertyInfo);
427 	data->AddFlat("messages", &propInfo, 1);
428 
429 	return inherited::GetSupportedSuites(data);
430 }
431 
432 
433 void
434 BChannelSlider::DrawChannel(BView *into, int32 channel, BRect area, bool pressed)
435 {
436 	float hCenter = area.Width() / 2;
437 	float vCenter = area.Height() / 2;
438 
439 	BPoint leftTop;
440 	BPoint bottomRight;
441 	if (Vertical()) {
442 		leftTop.Set(area.left + hCenter, area.top + vCenter);
443 		bottomRight.Set(leftTop.x, leftTop.y + ThumbRangeFor(channel));
444 	} else {
445 		leftTop.Set(area.left, area.top + vCenter);
446 		bottomRight.Set(area.left + ThumbRangeFor(channel), leftTop.y);
447 	}
448 
449 	DrawGroove(into, channel, leftTop, bottomRight);
450 
451 	BPoint thumbLocation = leftTop;
452 	if (Vertical())
453 		thumbLocation.y += ThumbDeltaFor(channel);
454 	else
455 		thumbLocation.x += ThumbDeltaFor(channel);
456 
457 	DrawThumb(into, channel, thumbLocation, pressed);
458 }
459 
460 
461 void
462 BChannelSlider::DrawGroove(BView *into, int32 channel, BPoint topLeft, BPoint bottomRight)
463 {
464 	ASSERT(into != NULL);
465 	BRect rect(topLeft, bottomRight);
466 
467 	DrawThumbFrame(fBackingView, rect.InsetByCopy(-2.5, -2.5));
468 
469 	rect.InsetBy(-0.5, -0.5);
470 	into->FillRect(rect, B_SOLID_HIGH);
471 }
472 
473 
474 void
475 BChannelSlider::DrawThumb(BView *into, int32 channel, BPoint where, bool pressed)
476 {
477 	ASSERT(into != NULL);
478 
479 	const BBitmap *thumb = ThumbFor(channel, pressed);
480 	if (thumb == NULL)
481 		return;
482 
483 	BRect bitmapBounds = thumb->Bounds();
484 	where.x -= bitmapBounds.right / 2;
485 	where.y -= bitmapBounds.bottom / 2;
486 
487 	into->PushState();
488 
489 	into->SetDrawingMode(B_OP_OVER);
490 	into->DrawBitmapAsync(thumb, where);
491 
492 	if (pressed) {
493 		into->SetDrawingMode(B_OP_ALPHA);
494 
495 		rgb_color color = tint_color(into->ViewColor(), B_DARKEN_4_TINT);
496 		color.alpha = 128;
497 		into->SetHighColor(color);
498 
499 		BRect destRect(where, where);
500 		destRect.right += bitmapBounds.right;
501 		destRect.bottom += bitmapBounds.bottom;
502 
503 		into->FillRect(destRect);
504 	}
505 
506 	into->PopState();
507 }
508 
509 
510 const BBitmap *
511 BChannelSlider::ThumbFor(int32 channel, bool pressed)
512 {
513 	// TODO: Finish me
514 	if (fLeftKnob == NULL) {
515 		if (Vertical()) {
516 			fLeftKnob = new BBitmap(BRect(0, 0, 11, 14), B_CMAP8);
517 			fLeftKnob->SetBits(kVerticalKnobData, sizeof(kVerticalKnobData), 0, B_CMAP8);
518 		} else {
519 			fLeftKnob = new BBitmap(BRect(0, 0, 14, 10), B_CMAP8);
520 			fLeftKnob->SetBits(kHorizontalKnobData, sizeof(kHorizontalKnobData), 0, B_CMAP8);
521 		}
522 	}
523 
524 	return fLeftKnob;
525 }
526 
527 
528 BRect
529 BChannelSlider::ThumbFrameFor(int32 channel)
530 {
531 	UpdateFontDimens();
532 
533 	BRect frame(0, 0, 0, 0);
534 	const BBitmap *thumb = ThumbFor(channel, false);
535 	if (thumb != NULL) {
536 		frame = thumb->Bounds();
537 		if (Vertical())
538 			frame.OffsetBy(channel * frame.Width(), fLineFeed + kPadding);
539 		else
540 			frame.OffsetBy(kPadding, fLineFeed + channel * frame.Height());
541 	}
542 
543 	return frame;
544 }
545 
546 
547 float
548 BChannelSlider::ThumbDeltaFor(int32 channel)
549 {
550 	float delta = 0;
551 	if (channel >= 0 && channel < MaxChannelCount()) {
552 		float range = ThumbRangeFor(channel);
553 		int32 limitRange = MaxLimitList()[channel] - MinLimitList()[channel];
554 		delta = ValueList()[channel] * range / limitRange;
555 
556 		if (Vertical())
557 			delta = range - delta;
558 	}
559 
560 	return delta;
561 }
562 
563 
564 float
565 BChannelSlider::ThumbRangeFor(int32 channel)
566 {
567 	UpdateFontDimens();
568 
569 	float range = 0;
570 	BRect bounds = Bounds();
571 	BRect frame = ThumbFrameFor(channel);
572 	if (Vertical())
573 		range = bounds.Height() - frame.Height() - (kPadding + fLineFeed) * 2;
574 	else
575 		range = bounds.Width() - frame.Width() - kPadding * 2;
576 
577 	return range;
578 }
579 
580 
581 void
582 BChannelSlider::InitData()
583 {
584 	UpdateFontDimens();
585 
586 	fLeftKnob = NULL;
587 	fMidKnob = NULL;
588 	fRightKnob = NULL;
589 	fBacking = NULL;
590 	fBackingView = NULL;
591 	fVertical = Bounds().Width() / Bounds().Height() < 1;
592 	fClickDelta = B_ORIGIN;
593 
594 	fCurrentChannel = -1;
595 	fAllChannels = false;
596 	fInitialValues = NULL;
597 	fMinpoint = 0;
598 	fFocusChannel = -1;
599 
600 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
601 }
602 
603 
604 void
605 BChannelSlider::FinishChange()
606 {
607 	if (fInitialValues != NULL) {
608 		if (fAllChannels) {
609 			// TODO: Iterate through the list of channels, and invoke only
610 			// for changed values ?
611 
612 			InvokeChannel();
613 
614 		} else {
615 			if (ValueList()[fCurrentChannel] != fInitialValues[fCurrentChannel]) {
616 				SetValueFor(fCurrentChannel, ValueList()[fCurrentChannel]);
617 				Invoke();
618 			}
619 		}
620 	}
621 
622 	SetTracking(false);
623 	Redraw();
624 }
625 
626 
627 void
628 BChannelSlider::UpdateFontDimens()
629 {
630 	font_height height;
631 	GetFontHeight(&height);
632 	fBaseLine = height.ascent + height.leading;
633 	fLineFeed = fBaseLine + height.descent;
634 }
635 
636 
637 void
638 BChannelSlider::DrawThumbs()
639 {
640 	if (fBacking == NULL) {
641 		// This is the idea: we build a bitmap by taking the coordinates
642 		// of the first and last thumb frames (top/left and bottom/right),
643 		// and by enlarging this rectangle a bit (hence the "InsetBy(-3, -3)").
644 		BRect first = ThumbFrameFor(0);
645 		BRect last = ThumbFrameFor(CountChannels() - 1);
646 		BRect bitmapFrame(first.LeftTop(), last.RightBottom());
647 
648 		if (Vertical())
649 			bitmapFrame.top -= ThumbRangeFor(0);
650 		else
651 			bitmapFrame.right += ThumbRangeFor(0);
652 
653 		bitmapFrame.InsetBy(-3, -3);
654 		fBacking = new BBitmap(bitmapFrame, BScreen(Window()).ColorSpace(), true, false);
655 		if (fBacking->Lock()) {
656 			fBackingView = new BView(bitmapFrame.OffsetToCopy(B_ORIGIN), "backing view", B_FOLLOW_NONE, B_WILL_DRAW);
657 			fBacking->AddChild(fBackingView);
658 			fBackingView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
659 			fBackingView->SetLowColor(fBackingView->ViewColor());
660 			fBacking->Unlock();
661 		}
662 	}
663 
664 	BPoint drawHere;
665 	drawHere.x = (Bounds().Width() - fBacking->Bounds().Width()) / 2;
666 	drawHere.y = (Bounds().Height() - fBacking->Bounds().Height() + fLineFeed) / 2;
667 
668 	if (fBacking->Lock()) {
669 		// Clear the view's background
670 		fBackingView->FillRect(fBackingView->Bounds(), B_SOLID_LOW);
671 
672 		BRect channelArea;
673 		int32 channelCount = CountChannels();
674 		for (int32 channel = 0; channel < channelCount; channel++) {
675 			channelArea = ThumbFrameFor(channel);
676 			// TODO: This is (apparently) needed because ThumbFrameFor() doesn't
677 			// take into account that the view we draw on is attached to an offscreen
678 			// bitmap. Still this doesn't make much sense:
679 			// could be that I'm simply missing something.
680 			if (Vertical())
681 				channelArea.OffsetBy(0, -channelArea.top);
682 			else
683 				channelArea.OffsetBy(0, -channelArea.Height());
684 
685 			bool pressed = fMinpoint != 0 && (channel == fCurrentChannel || fAllChannels);
686 			DrawChannel(fBackingView, channel, channelArea, pressed);
687 		}
688 
689 #if 1
690 		// This part draws the current value over the thumb.
691 		// TODO: make it nicer. Simplify the code.
692 		if (fCurrentChannel != -1 && fMinpoint != 0) {
693 			char valueString[32];
694 			snprintf(valueString, 32, "%ld", ValueFor(fCurrentChannel));
695 			float width = fBackingView->StringWidth(valueString);
696 			BRect valueRect(0, 0, width, 10);
697 			rgb_color oldColor = fBackingView->HighColor();
698 			if (Vertical())
699 				valueRect.OffsetTo((ThumbFrameFor(fCurrentChannel).Width() - width) / 2 + fCurrentChannel * ThumbFrameFor(fCurrentChannel).Width(),
700 						ThumbDeltaFor(fCurrentChannel));
701 			else
702 				valueRect.OffsetTo(ThumbDeltaFor(fCurrentChannel), ThumbFrameFor(fCurrentChannel).top - 10);
703 			fBackingView->SetHighColor(255, 255, 172);
704 			fBackingView->FillRect(valueRect);
705 			fBackingView->SetHighColor(0, 0, 0);
706 			valueRect.OffsetBy(1 , 9);
707 			fBackingView->DrawString(valueString, valueRect.LeftTop());
708 			fBackingView->SetHighColor(oldColor);
709 		}
710 #endif
711 		fBackingView->Sync();
712 		fBacking->Unlock();
713 	}
714 
715 	DrawBitmapAsync(fBacking, drawHere);
716 
717 #if 0
718 	// this part draws the value at the bottom of the sliders.
719 	if (fCurrentChannel != -1 && fMinpoint != 0) {
720 		char valueString[32];
721 		snprintf(valueString, 32, "%ld", ValueFor(fCurrentChannel));
722 		BPoint stringPoint = drawHere;
723 		float stringWidth = StringWidth(valueString);
724 		stringPoint.x += (fBacking->Bounds().Width() - stringWidth) / 2;
725 		stringPoint.y += fBacking->Bounds().Height() + fBaseLine;
726 		BRect stringRect(stringPoint, stringPoint);
727 		stringRect.left -= 10;
728 		stringRect.right += StringWidth("100");
729 		stringRect.top -= fLineFeed;
730 
731 		SetHighColor(ViewColor());
732 		FillRect(stringRect);
733 
734 		SetHighColor(0, 0, 0);
735 		DrawString(valueString, stringPoint);
736 	}
737 #endif
738 
739 	// fClickDelta is used in MouseMoved()
740 	fClickDelta = drawHere;
741 
742 	// TODO: See above
743 	if (Vertical())
744 		fClickDelta.y -= ThumbFrameFor(0).top;
745 	else
746 		fClickDelta.y -= ThumbFrameFor(0).Height();
747 }
748 
749 
750 void
751 BChannelSlider::DrawThumbFrame(BView *into, const BRect &area)
752 {
753 	rgb_color oldColor = into->HighColor();
754 
755 	into->SetHighColor(255, 255, 255);
756 	into->StrokeRect(area);
757 	into->SetHighColor(tint_color(into->ViewColor(), B_DARKEN_1_TINT));
758 	into->StrokeLine(area.LeftTop(), BPoint(area.right, area.top));
759 	into->StrokeLine(area.LeftTop(), BPoint(area.left, area.bottom - 1));
760 	into->SetHighColor(tint_color(into->ViewColor(), B_DARKEN_2_TINT));
761 	into->StrokeLine(BPoint(area.left + 1, area.top + 1), BPoint(area.right - 1, area.top + 1));
762 	into->StrokeLine(BPoint(area.left + 1, area.top + 1), BPoint(area.left + 1, area.bottom - 2));
763 
764 	into->SetHighColor(oldColor);
765 }
766 
767 
768 bool
769 BChannelSlider::Vertical() const
770 {
771 	return fVertical;
772 }
773 
774 
775 void
776 BChannelSlider::Redraw()
777 {
778 	Invalidate(Bounds());
779 	Flush();
780 }
781 
782 
783 void
784 BChannelSlider::MouseMovedCommon(BPoint point, BPoint point2)
785 {
786 	float floatValue = 0;
787 	int32 limitRange = MaxLimitList()[fCurrentChannel] - MinLimitList()[fCurrentChannel];
788 	float range = ThumbRangeFor(fCurrentChannel);
789 	if (Vertical())
790 		floatValue = range - (point.y - fMinpoint);
791 	else
792 		floatValue = range + (point.x - fMinpoint);
793 
794 	int32 value = (int32)(floatValue / range * limitRange);
795 	if (fAllChannels)
796 		SetAllValue(value);
797 	else
798 		SetValueFor(fCurrentChannel, value);
799 
800 	InvokeNotifyChannel(ModificationMessage());
801 	DrawThumbs();
802 }
803 
804 
805 void BChannelSlider::_Reserved_BChannelSlider_0(void *, ...) {}
806 void BChannelSlider::_Reserved_BChannelSlider_1(void *, ...) {}
807 void BChannelSlider::_Reserved_BChannelSlider_2(void *, ...) {}
808 void BChannelSlider::_Reserved_BChannelSlider_3(void *, ...) {}
809 void BChannelSlider::_Reserved_BChannelSlider_4(void *, ...) {}
810 void BChannelSlider::_Reserved_BChannelSlider_5(void *, ...) {}
811 void BChannelSlider::_Reserved_BChannelSlider_6(void *, ...) {}
812 void BChannelSlider::_Reserved_BChannelSlider_7(void *, ...) {}
813