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