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