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