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
BChannelSlider(BRect area,const char * name,const char * label,BMessage * model,int32 channels,uint32 resizeMode,uint32 flags)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
BChannelSlider(BRect area,const char * name,const char * label,BMessage * model,orientation orientation,int32 channels,uint32 resizeMode,uint32 flags)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
BChannelSlider(const char * name,const char * label,BMessage * model,orientation orientation,int32 channels,uint32 flags)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
BChannelSlider(BMessage * archive)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
~BChannelSlider()118 BChannelSlider::~BChannelSlider()
119 {
120 delete fBacking;
121 delete fLeftKnob;
122 delete fMidKnob;
123 delete fRightKnob;
124 delete[] fInitialValues;
125 }
126
127
128 BArchivable*
Instantiate(BMessage * archive)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
Archive(BMessage * into,bool deep) const139 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
AttachedToWindow()150 BChannelSlider::AttachedToWindow()
151 {
152 AdoptParentColors();
153 BChannelControl::AttachedToWindow();
154 }
155
156
157 void
AllAttached()158 BChannelSlider::AllAttached()
159 {
160 BChannelControl::AllAttached();
161 }
162
163
164 void
DetachedFromWindow()165 BChannelSlider::DetachedFromWindow()
166 {
167 BChannelControl::DetachedFromWindow();
168 }
169
170
171 void
AllDetached()172 BChannelSlider::AllDetached()
173 {
174 BChannelControl::AllDetached();
175 }
176
177
178 void
MessageReceived(BMessage * message)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
Draw(BRect updateRect)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
MouseDown(BPoint where)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
MouseUp(BPoint where)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
MouseMoved(BPoint where,uint32 code,const BMessage * message)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
WindowActivated(bool state)397 BChannelSlider::WindowActivated(bool state)
398 {
399 BChannelControl::WindowActivated(state);
400 }
401
402
403 void
KeyDown(const char * bytes,int32 numBytes)404 BChannelSlider::KeyDown(const char* bytes, int32 numBytes)
405 {
406 BControl::KeyDown(bytes, numBytes);
407 }
408
409
410 void
KeyUp(const char * bytes,int32 numBytes)411 BChannelSlider::KeyUp(const char* bytes, int32 numBytes)
412 {
413 BView::KeyUp(bytes, numBytes);
414 }
415
416
417 void
FrameResized(float newWidth,float newHeight)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
SetFont(const BFont * font,uint32 mask)430 BChannelSlider::SetFont(const BFont* font, uint32 mask)
431 {
432 BChannelControl::SetFont(font, mask);
433 }
434
435
436 void
MakeFocus(bool focusState)437 BChannelSlider::MakeFocus(bool focusState)
438 {
439 if (focusState && !IsFocus())
440 fFocusChannel = -1;
441 BChannelControl::MakeFocus(focusState);
442 }
443
444
445 void
GetPreferredSize(float * width,float * height)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*
ResolveSpecifier(BMessage * message,int32 index,BMessage * specifier,int32 form,const char * property)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
GetSupportedSuites(BMessage * data)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
SetEnabled(bool on)504 BChannelSlider::SetEnabled(bool on)
505 {
506 BChannelControl::SetEnabled(on);
507 }
508
509
510 orientation
Orientation() const511 BChannelSlider::Orientation() const
512 {
513 return fIsVertical ? B_VERTICAL : B_HORIZONTAL;
514 }
515
516
517 void
SetOrientation(orientation orientation)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
MaxChannelCount() const530 BChannelSlider::MaxChannelCount() const
531 {
532 return 32;
533 }
534
535
536 bool
SupportsIndividualLimits() const537 BChannelSlider::SupportsIndividualLimits() const
538 {
539 return false;
540 }
541
542
543 void
DrawChannel(BView * into,int32 channel,BRect area,bool pressed)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
DrawGroove(BView * into,int32 channel,BPoint leftTop,BPoint bottomRight)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
DrawThumb(BView * into,int32 channel,BPoint where,bool pressed)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*
ThumbFor(int32 channel,bool pressed)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
ThumbFrameFor(int32 channel)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
ThumbDeltaFor(int32 channel)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
ThumbRangeFor(int32 channel)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
_InitData()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
_FinishChange(bool update)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
_UpdateFontDimens()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
_DrawThumbs()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
_DrawGrooveFrame(BView * into,const BRect & area)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
_MouseMovedCommon(BPoint point,BPoint point2)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
_Reserved_BChannelSlider_0(void *,...)896 void BChannelSlider::_Reserved_BChannelSlider_0(void*, ...) {}
_Reserved_BChannelSlider_1(void *,...)897 void BChannelSlider::_Reserved_BChannelSlider_1(void*, ...) {}
_Reserved_BChannelSlider_2(void *,...)898 void BChannelSlider::_Reserved_BChannelSlider_2(void*, ...) {}
_Reserved_BChannelSlider_3(void *,...)899 void BChannelSlider::_Reserved_BChannelSlider_3(void*, ...) {}
_Reserved_BChannelSlider_4(void *,...)900 void BChannelSlider::_Reserved_BChannelSlider_4(void*, ...) {}
_Reserved_BChannelSlider_5(void *,...)901 void BChannelSlider::_Reserved_BChannelSlider_5(void*, ...) {}
_Reserved_BChannelSlider_6(void *,...)902 void BChannelSlider::_Reserved_BChannelSlider_6(void*, ...) {}
_Reserved_BChannelSlider_7(void *,...)903 void BChannelSlider::_Reserved_BChannelSlider_7(void*, ...) {}
904