xref: /haiku/src/kits/interface/ChannelSlider.cpp (revision 0c93c0a807b27096abbfad677436afb7d1712d4a)
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 				SetCurrentChannel(channel);
276 				break;
277 			}
278 		}
279 
280 		// Click wasn't on a slider. Bail out.
281 		if (fCurrentChannel == -1)
282 			return;
283 
284 		uint32 buttons = 0;
285 		BMessage *currentMessage = Window()->CurrentMessage();
286 		if (currentMessage != NULL)
287 			currentMessage->FindInt32("buttons", (int32 *)&buttons);
288 
289 		fAllChannels = (buttons & B_SECONDARY_MOUSE_BUTTON) == 0;
290 
291 		if (fInitialValues != NULL && fAllChannels) {
292 			delete[] fInitialValues;
293 			fInitialValues = NULL;
294 		}
295 
296 		if (fInitialValues == NULL)
297 			fInitialValues = new int32[numChannels];
298 
299 		if (fAllChannels) {
300 			for (int32 i = 0; i < numChannels; i++)
301 				fInitialValues[i] = ValueFor(i);
302 		} else
303 			fInitialValues[fCurrentChannel] = ValueFor(fCurrentChannel);
304 
305 		if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) {
306 			if (!IsTracking()) {
307 				SetTracking(true);
308 				DrawThumbs();
309 				Flush();
310 			}
311 
312 			MouseMovedCommon(where, B_ORIGIN);
313 			SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY);
314 		} else {
315 			do {
316 				snooze(30000);
317 				GetMouse(&where, &buttons);
318 				MouseMovedCommon(where, B_ORIGIN);
319 			} while (buttons != 0);
320 			FinishChange();
321 			fCurrentChannel = -1;
322 			fAllChannels = false;
323 		}
324 	}
325 }
326 
327 
328 void
329 BChannelSlider::MouseUp(BPoint where)
330 {
331 	if (IsEnabled() && IsTracking()) {
332 		FinishChange();
333 		SetTracking(false);
334 		fAllChannels = false;
335 		fCurrentChannel = -1;
336 		fMinpoint = 0;
337 	} else
338 		BControl::MouseUp(where);
339 }
340 
341 
342 void
343 BChannelSlider::MouseMoved(BPoint where, uint32 code, const BMessage *message)
344 {
345 	if (IsEnabled() && IsTracking())
346 		MouseMovedCommon(where, B_ORIGIN);
347 	else
348 		BControl::MouseMoved(where, code, message);
349 }
350 
351 
352 void
353 BChannelSlider::WindowActivated(bool state)
354 {
355 	BControl::WindowActivated(state);
356 }
357 
358 
359 void
360 BChannelSlider::KeyDown(const char *bytes, int32 numBytes)
361 {
362 	BControl::KeyDown(bytes, numBytes);
363 }
364 
365 
366 void
367 BChannelSlider::KeyUp(const char *bytes, int32 numBytes)
368 {
369 	BView::KeyUp(bytes, numBytes);
370 }
371 
372 
373 void
374 BChannelSlider::FrameResized(float newWidth, float newHeight)
375 {
376 	inherited::FrameResized(newWidth, newHeight);
377 	Invalidate(Bounds());
378 }
379 
380 
381 void
382 BChannelSlider::SetFont(const BFont *font, uint32 mask)
383 {
384 	inherited::SetFont(font, mask);
385 }
386 
387 
388 void
389 BChannelSlider::MakeFocus(bool focusState)
390 {
391 	if (focusState && !IsFocus())
392 		fFocusChannel = -1;
393 	BControl::MakeFocus(focusState);
394 }
395 
396 
397 void
398 BChannelSlider::SetEnabled(bool on)
399 {
400 	BControl::SetEnabled(on);
401 }
402 
403 
404 void
405 BChannelSlider::GetPreferredSize(float *width, float *height)
406 {
407 	if (width) {
408 		float _width = (float)ceil(StringWidth(Label()));
409 		if (Vertical()) {
410 			*width = max_c(_width, 2 + 12 * CountChannels());
411 		} else {
412 			*width = max_c(_width, 64);
413 		}
414 	}
415 	if (height) {
416 		if (Vertical())
417 			*height = 195;
418 		else
419 			*height = 71;
420 	}
421 }
422 
423 
424 BHandler *
425 BChannelSlider::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier,
426 								int32 form, const char *property)
427 {
428 	BHandler *target = this;
429 	BPropertyInfo propertyInfo(sPropertyInfo);
430 	if (propertyInfo.FindMatch(msg, index, specifier, form, property) < B_OK)
431 		target = BChannelControl::ResolveSpecifier(msg, index, specifier, form, property);
432 
433 	return target;
434 }
435 
436 
437 status_t
438 BChannelSlider::GetSupportedSuites(BMessage *data)
439 {
440 	if (data == NULL)
441 		return B_BAD_VALUE;
442 
443 	status_t err = data->AddString("suites", "suite/vnd.Be-channel-slider");
444 
445 	BPropertyInfo propInfo(sPropertyInfo);
446 	if (err == B_OK)
447 		err = data->AddFlat("messages", &propInfo);
448 
449 	if (err == B_OK)
450 		return BChannelControl::GetSupportedSuites(data);
451 	return err;
452 }
453 
454 
455 void
456 BChannelSlider::DrawChannel(BView *into, int32 channel, BRect area, bool pressed)
457 {
458 	float hCenter = area.Width() / 2;
459 	float vCenter = area.Height() / 2;
460 
461 	BPoint leftTop;
462 	BPoint bottomRight;
463 	if (Vertical()) {
464 		leftTop.Set(area.left + hCenter, area.top + vCenter);
465 		bottomRight.Set(leftTop.x, leftTop.y + ThumbRangeFor(channel));
466 	} else {
467 		leftTop.Set(area.left, area.top + vCenter);
468 		bottomRight.Set(area.left + ThumbRangeFor(channel), leftTop.y);
469 	}
470 
471 	DrawGroove(into, channel, leftTop, bottomRight);
472 
473 	BPoint thumbLocation = leftTop;
474 	if (Vertical())
475 		thumbLocation.y += ThumbDeltaFor(channel);
476 	else
477 		thumbLocation.x += ThumbDeltaFor(channel);
478 
479 	DrawThumb(into, channel, thumbLocation, pressed);
480 }
481 
482 
483 void
484 BChannelSlider::DrawGroove(BView *into, int32 channel, BPoint topLeft, BPoint bottomRight)
485 {
486 	ASSERT(into != NULL);
487 	BRect rect(topLeft, bottomRight);
488 
489 	DrawThumbFrame(fBackingView, rect.InsetByCopy(-2.5, -2.5));
490 
491 	rect.InsetBy(-0.5, -0.5);
492 	into->FillRect(rect, B_SOLID_HIGH);
493 }
494 
495 
496 void
497 BChannelSlider::DrawThumb(BView *into, int32 channel, BPoint where, bool pressed)
498 {
499 	ASSERT(into != NULL);
500 
501 	const BBitmap *thumb = ThumbFor(channel, pressed);
502 	if (thumb == NULL)
503 		return;
504 
505 	BRect bitmapBounds = thumb->Bounds();
506 	where.x -= bitmapBounds.right / 2;
507 	where.y -= bitmapBounds.bottom / 2;
508 
509 	into->PushState();
510 
511 	into->SetDrawingMode(B_OP_OVER);
512 	into->DrawBitmapAsync(thumb, where);
513 
514 	if (pressed) {
515 		into->SetDrawingMode(B_OP_ALPHA);
516 
517 		rgb_color color = tint_color(into->ViewColor(), B_DARKEN_4_TINT);
518 		color.alpha = 128;
519 		into->SetHighColor(color);
520 
521 		BRect destRect(where, where);
522 		destRect.right += bitmapBounds.right;
523 		destRect.bottom += bitmapBounds.bottom;
524 
525 		into->FillRect(destRect);
526 	}
527 
528 	into->PopState();
529 }
530 
531 
532 const BBitmap *
533 BChannelSlider::ThumbFor(int32 channel, bool pressed)
534 {
535 	// TODO: Finish me
536 	if (fLeftKnob == NULL) {
537 		if (Vertical()) {
538 			fLeftKnob = new BBitmap(BRect(0, 0, 11, 14), B_CMAP8);
539 			fLeftKnob->SetBits(kVerticalKnobData, sizeof(kVerticalKnobData), 0, B_CMAP8);
540 		} else {
541 			fLeftKnob = new BBitmap(BRect(0, 0, 14, 11), B_CMAP8);
542 			fLeftKnob->SetBits(kHorizontalKnobData, sizeof(kHorizontalKnobData), 0, B_CMAP8);
543 		}
544 	}
545 
546 	return fLeftKnob;
547 }
548 
549 
550 BRect
551 BChannelSlider::ThumbFrameFor(int32 channel)
552 {
553 	UpdateFontDimens();
554 
555 	BRect frame(0, 0, 0, 0);
556 	const BBitmap *thumb = ThumbFor(channel, false);
557 	if (thumb != NULL) {
558 		frame = thumb->Bounds();
559 		if (Vertical())
560 			frame.OffsetBy(channel * frame.Width(), fLineFeed + kPadding);
561 		else
562 			frame.OffsetBy(kPadding, fLineFeed + channel * frame.Height());
563 	}
564 
565 	return frame;
566 }
567 
568 
569 float
570 BChannelSlider::ThumbDeltaFor(int32 channel)
571 {
572 	float delta = 0;
573 	if (channel >= 0 && channel < MaxChannelCount()) {
574 		float range = ThumbRangeFor(channel);
575 		int32 limitRange = MaxLimitList()[channel] - MinLimitList()[channel];
576 		delta = (ValueList()[channel] - MinLimitList()[channel]) * range / limitRange;
577 
578 		if (Vertical())
579 			delta = range - delta;
580 	}
581 
582 	return delta;
583 }
584 
585 
586 float
587 BChannelSlider::ThumbRangeFor(int32 channel)
588 {
589 	UpdateFontDimens();
590 
591 	float range = 0;
592 	BRect bounds = Bounds();
593 	BRect frame = ThumbFrameFor(channel);
594 	if (Vertical())
595 		range = bounds.Height() - frame.Height() - (kPadding + fLineFeed) * 2;
596 	else
597 		range = bounds.Width() - frame.Width() - kPadding * 2;
598 
599 	return range;
600 }
601 
602 
603 void
604 BChannelSlider::InitData()
605 {
606 	UpdateFontDimens();
607 
608 	fLeftKnob = NULL;
609 	fMidKnob = NULL;
610 	fRightKnob = NULL;
611 	fBacking = NULL;
612 	fBackingView = NULL;
613 	fVertical = Bounds().Width() / Bounds().Height() < 1;
614 	fClickDelta = B_ORIGIN;
615 
616 	fCurrentChannel = -1;
617 	fAllChannels = false;
618 	fInitialValues = NULL;
619 	fMinpoint = 0;
620 	fFocusChannel = -1;
621 
622 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
623 }
624 
625 
626 void
627 BChannelSlider::FinishChange()
628 {
629 	if (fInitialValues != NULL) {
630 		bool *inMask = NULL;
631 		int32 numChannels = CountChannels();
632 		if (!fAllChannels) {
633 			inMask = new bool[CountChannels()];
634 			for (int i=0; i<numChannels; i++)
635 				inMask[i] = false;
636 			inMask[fCurrentChannel] = true;
637 		}
638 		InvokeChannel(NULL, 0, numChannels, inMask);
639 	}
640 
641 	SetTracking(false);
642 	Redraw();
643 }
644 
645 
646 void
647 BChannelSlider::UpdateFontDimens()
648 {
649 	font_height height;
650 	GetFontHeight(&height);
651 	fBaseLine = height.ascent + height.leading;
652 	fLineFeed = fBaseLine + height.descent;
653 }
654 
655 
656 void
657 BChannelSlider::DrawThumbs()
658 {
659 	if (fBacking == NULL) {
660 		// This is the idea: we build a bitmap by taking the coordinates
661 		// of the first and last thumb frames (top/left and bottom/right)
662 		BRect first = ThumbFrameFor(0);
663 		BRect last = ThumbFrameFor(CountChannels() - 1);
664 		BRect bitmapFrame(first.LeftTop(), last.RightBottom());
665 
666 		if (Vertical())
667 			bitmapFrame.top -= ThumbRangeFor(0);
668 		else
669 			bitmapFrame.right += ThumbRangeFor(0);
670 
671 		fBacking = new BBitmap(bitmapFrame.OffsetToCopy(B_ORIGIN),
672 #ifdef __HAIKU__
673 		BScreen(Window()).ColorSpace(),
674 #else
675 		B_RGB32,
676 #endif
677 		true, false);
678 		if (fBacking->Lock()) {
679 			fBackingView = new BView(bitmapFrame.OffsetToCopy(B_ORIGIN), "backing view", B_FOLLOW_NONE, B_WILL_DRAW);
680 			fBacking->AddChild(fBackingView);
681 			fBackingView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
682 			fBackingView->SetLowColor(fBackingView->ViewColor());
683 			fBacking->Unlock();
684 		}
685 	}
686 
687 	BPoint drawHere;
688 	drawHere.x = (Bounds().Width() - fBacking->Bounds().Width()) / 2;
689 	drawHere.y = (Bounds().Height() - fBacking->Bounds().Height() + fLineFeed) / 2;
690 
691 	if (fBacking->Lock()) {
692 		// Clear the view's background
693 		fBackingView->FillRect(fBackingView->Bounds(), B_SOLID_LOW);
694 
695 		BRect channelArea;
696 		int32 channelCount = CountChannels();
697 		for (int32 channel = 0; channel < channelCount; channel++) {
698 			channelArea = ThumbFrameFor(channel);
699 			// TODO: This is (apparently) needed because ThumbFrameFor() doesn't
700 			// take into account that the view we draw on is attached to an offscreen
701 			// bitmap. Still this doesn't make much sense:
702 			// could be that I'm simply missing something.
703 			if (Vertical())
704 				channelArea.OffsetBy(0, -channelArea.top);
705 			else
706 				channelArea.OffsetBy(0, -channelArea.Height());
707 
708 			bool pressed = fMinpoint != 0 && (channel == fCurrentChannel || fAllChannels);
709 			DrawChannel(fBackingView, channel, channelArea, pressed);
710 		}
711 
712 #if 1
713 		// This part draws the current value over the thumb.
714 		// TODO: make it nicer. Simplify the code.
715 		if (fCurrentChannel != -1 && fMinpoint != 0) {
716 			char valueString[32];
717 			snprintf(valueString, 32, "%ld", ValueFor(fCurrentChannel));
718 			float width = fBackingView->StringWidth(valueString);
719 			BRect valueRect(0, 0, width, 10);
720 			rgb_color oldColor = fBackingView->HighColor();
721 			if (Vertical())
722 				valueRect.OffsetTo((ThumbFrameFor(fCurrentChannel).Width() - width) / 2 + fCurrentChannel * ThumbFrameFor(fCurrentChannel).Width(),
723 						ThumbDeltaFor(fCurrentChannel));
724 			else
725 				valueRect.OffsetTo(ThumbDeltaFor(fCurrentChannel), ThumbFrameFor(fCurrentChannel).top - 10);
726 			fBackingView->SetHighColor(255, 255, 172);
727 			fBackingView->FillRect(valueRect);
728 			fBackingView->SetHighColor(0, 0, 0);
729 			valueRect.OffsetBy(1 , 9);
730 			fBackingView->DrawString(valueString, valueRect.LeftTop());
731 			fBackingView->SetHighColor(oldColor);
732 		}
733 #endif
734 		fBackingView->Sync();
735 		fBacking->Unlock();
736 	}
737 
738 	DrawBitmapAsync(fBacking, drawHere);
739 
740 #if 0
741 	// this part draws the value at the bottom of the sliders.
742 	if (fCurrentChannel != -1 && fMinpoint != 0) {
743 		char valueString[32];
744 		snprintf(valueString, 32, "%ld", ValueFor(fCurrentChannel));
745 		BPoint stringPoint = drawHere;
746 		float stringWidth = StringWidth(valueString);
747 		stringPoint.x += (fBacking->Bounds().Width() - stringWidth) / 2;
748 		stringPoint.y += fBacking->Bounds().Height() + fBaseLine;
749 		BRect stringRect(stringPoint, stringPoint);
750 		stringRect.left -= 10;
751 		stringRect.right += StringWidth("100");
752 		stringRect.top -= fLineFeed;
753 
754 		SetHighColor(ViewColor());
755 		FillRect(stringRect);
756 
757 		SetHighColor(0, 0, 0);
758 		DrawString(valueString, stringPoint);
759 	}
760 #endif
761 
762 	// fClickDelta is used in MouseMoved()
763 	fClickDelta = drawHere;
764 
765 	// TODO: See above
766 	if (Vertical())
767 		fClickDelta.y -= ThumbFrameFor(0).top;
768 	else
769 		fClickDelta.y -= ThumbFrameFor(0).Height();
770 }
771 
772 
773 void
774 BChannelSlider::DrawThumbFrame(BView *into, const BRect &area)
775 {
776 	rgb_color oldColor = into->HighColor();
777 
778 	into->SetHighColor(255, 255, 255);
779 	into->StrokeRect(area);
780 	into->SetHighColor(tint_color(into->ViewColor(), B_DARKEN_1_TINT));
781 	into->StrokeLine(area.LeftTop(), BPoint(area.right, area.top));
782 	into->StrokeLine(area.LeftTop(), BPoint(area.left, area.bottom - 1));
783 	into->SetHighColor(tint_color(into->ViewColor(), B_DARKEN_2_TINT));
784 	into->StrokeLine(BPoint(area.left + 1, area.top + 1), BPoint(area.right - 1, area.top + 1));
785 	into->StrokeLine(BPoint(area.left + 1, area.top + 1), BPoint(area.left + 1, area.bottom - 2));
786 
787 	into->SetHighColor(oldColor);
788 }
789 
790 
791 bool
792 BChannelSlider::Vertical() const
793 {
794 	return fVertical;
795 }
796 
797 
798 void
799 BChannelSlider::Redraw()
800 {
801 	Invalidate(Bounds());
802 	Flush();
803 }
804 
805 
806 void
807 BChannelSlider::MouseMovedCommon(BPoint point, BPoint point2)
808 {
809 	float floatValue = 0;
810 	int32 limitRange = MaxLimitList()[fCurrentChannel] - MinLimitList()[fCurrentChannel];
811 	float range = ThumbRangeFor(fCurrentChannel);
812 	if (Vertical())
813 		floatValue = range - (point.y - fMinpoint);
814 	else
815 		floatValue = range + (point.x - fMinpoint);
816 
817 	int32 value = (int32)(floatValue / range * limitRange) + MinLimitList()[fCurrentChannel];
818 	if (fAllChannels)
819 		SetAllValue(value);
820 	else
821 		SetValueFor(fCurrentChannel, value);
822 
823 	DrawThumbs();
824 }
825 
826 
827 void BChannelSlider::_Reserved_BChannelSlider_0(void *, ...) {}
828 void BChannelSlider::_Reserved_BChannelSlider_1(void *, ...) {}
829 void BChannelSlider::_Reserved_BChannelSlider_2(void *, ...) {}
830 void BChannelSlider::_Reserved_BChannelSlider_3(void *, ...) {}
831 void BChannelSlider::_Reserved_BChannelSlider_4(void *, ...) {}
832 void BChannelSlider::_Reserved_BChannelSlider_5(void *, ...) {}
833 void BChannelSlider::_Reserved_BChannelSlider_6(void *, ...) {}
834 void BChannelSlider::_Reserved_BChannelSlider_7(void *, ...) {}
835