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