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 BPoint drawHere; 819 BRect bounds(fBacking->Bounds()); 820 drawHere.x = (Bounds().Width() - bounds.Width()) / 2.0; 821 drawHere.y = (Bounds().Height() - bounds.Height()) - kPadding - fLineFeed; 822 823 if (fBacking && fBackingView) { 824 if (fBacking->Lock()) { 825 // Clear the view's background 826 fBackingView->FillRect(fBackingView->Bounds(), B_SOLID_LOW); 827 828 BRect channelArea; 829 // draw the entire control 830 for (int32 channel = 0; channel < CountChannels(); channel++) { 831 channelArea = ThumbFrameFor(channel); 832 bool pressed = IsTracking() 833 && (channel == fCurrentChannel || fAllChannels); 834 DrawChannel(fBackingView, channel, channelArea, pressed); 835 } 836 837 // draw some kind of current value tool tip 838 if (fCurrentChannel != -1 && fMinPoint != 0) { 839 char valueString[32]; 840 snprintf(valueString, 32, "%ld", ValueFor(fCurrentChannel)); 841 float stringWidth = fBackingView->StringWidth(valueString); 842 float width = max_c(10.0, stringWidth); 843 BRect valueRect(0.0, 0.0, width, 10.0); 844 845 BRect thumbFrame(ThumbFrameFor(fCurrentChannel)); 846 float thumbDelta(ThumbDeltaFor(fCurrentChannel)); 847 848 if (fIsVertical) { 849 valueRect.OffsetTo((thumbFrame.Width() - width) / 2.0 + 850 fCurrentChannel * thumbFrame.Width(), 851 thumbDelta + thumbFrame.Height() + 2.0); 852 if (valueRect.bottom > fBackingView->Frame().bottom) 853 valueRect.OffsetBy(0.0, -(thumbFrame.Height() + 12.0)); 854 } else { 855 valueRect.OffsetTo((thumbDelta - (width + 2.0)), 856 thumbFrame.top); 857 if (valueRect.left < fBackingView->Frame().left) { 858 valueRect.OffsetBy(thumbFrame.Width() + width + 2.0, 859 0.0); 860 } 861 } 862 863 rgb_color oldColor = fBackingView->HighColor(); 864 fBackingView->SetHighColor(255, 255, 172); 865 fBackingView->FillRect(valueRect); 866 fBackingView->SetHighColor(0, 0, 0); 867 fBackingView->DrawString(valueString, BPoint(valueRect.left 868 + (valueRect.Width() - stringWidth) / 2.0, valueRect.bottom 869 - 1.0)); 870 fBackingView->StrokeRect(valueRect.InsetByCopy(-0.5, -0.5)); 871 fBackingView->SetHighColor(oldColor); 872 } 873 874 fBackingView->Sync(); 875 fBacking->Unlock(); 876 } 877 } 878 879 if (fBacking) 880 DrawBitmapAsync(fBacking, drawHere); 881 882 // fClickDelta is used in MouseMoved() 883 fClickDelta = drawHere; 884 } 885 886 887 void 888 BChannelSlider::_DrawGrooveFrame(BView* into, const BRect& area) 889 { 890 if (into) { 891 rgb_color oldColor = into->HighColor(); 892 893 into->SetHighColor(255, 255, 255); 894 into->StrokeRect(area); 895 into->SetHighColor(tint_color(into->ViewColor(), B_DARKEN_1_TINT)); 896 into->StrokeLine(area.LeftTop(), BPoint(area.right, area.top)); 897 into->StrokeLine(area.LeftTop(), BPoint(area.left, area.bottom - 1)); 898 into->SetHighColor(tint_color(into->ViewColor(), B_DARKEN_2_TINT)); 899 into->StrokeLine(BPoint(area.left + 1, area.top + 1), 900 BPoint(area.right - 1, area.top + 1)); 901 into->StrokeLine(BPoint(area.left + 1, area.top + 1), 902 BPoint(area.left + 1, area.bottom - 2)); 903 904 into->SetHighColor(oldColor); 905 } 906 } 907 908 909 void 910 BChannelSlider::_MouseMovedCommon(BPoint point, BPoint point2) 911 { 912 float floatValue = 0; 913 int32 limitRange = MaxLimitList()[fCurrentChannel] - 914 MinLimitList()[fCurrentChannel]; 915 float range = ThumbRangeFor(fCurrentChannel); 916 if (fIsVertical) 917 floatValue = range - (point.y - fMinPoint); 918 else 919 floatValue = range + (point.x - fMinPoint); 920 921 int32 value = (int32)(floatValue / range * limitRange) + 922 MinLimitList()[fCurrentChannel]; 923 if (fAllChannels) 924 SetAllValue(value); 925 else 926 SetValueFor(fCurrentChannel, value); 927 928 if (ModificationMessage()) 929 _FinishChange(true); 930 931 _DrawThumbs(); 932 } 933 934 935 // #pragma mark - FBC padding 936 937 938 void BChannelSlider::_Reserved_BChannelSlider_0(void*, ...) {} 939 void BChannelSlider::_Reserved_BChannelSlider_1(void*, ...) {} 940 void BChannelSlider::_Reserved_BChannelSlider_2(void*, ...) {} 941 void BChannelSlider::_Reserved_BChannelSlider_3(void*, ...) {} 942 void BChannelSlider::_Reserved_BChannelSlider_4(void*, ...) {} 943 void BChannelSlider::_Reserved_BChannelSlider_5(void*, ...) {} 944 void BChannelSlider::_Reserved_BChannelSlider_6(void*, ...) {} 945 void BChannelSlider::_Reserved_BChannelSlider_7(void*, ...) {} 946