1/* 2 * Copyright 2001 Werner Freytag - please read to the LICENSE file 3 * 4 * Copyright 2002-2015, Stephan A��mus <superstippi@gmx.de> 5 * All rights reserved. 6 * 7 */ 8 9#include "ColorSlider.h" 10 11#include <stdio.h> 12 13#include <Bitmap.h> 14#include <ControlLook.h> 15#include <LayoutUtils.h> 16#include <OS.h> 17#include <Window.h> 18#include <math.h> 19 20#include "support_ui.h" 21 22#include "rgb_hsv.h" 23 24 25#define round(x) (int)(x+.5) 26 27enum { 28 MSG_UPDATE = 'Updt', 29}; 30 31#define MAX_X 255 32#define MAX_Y 255 33 34 35ColorSlider::ColorSlider(SelectedColorMode mode, 36 float value1, float value2, orientation dir, border_style border) 37 : BControl("ColorSlider", "", new BMessage(MSG_COLOR_SLIDER), 38 B_WILL_DRAW | B_FRAME_EVENTS) 39{ 40 _Init(mode, value1, value2, dir, border); 41 FrameResized(Bounds().Width(), Bounds().Height()); 42} 43 44 45ColorSlider::ColorSlider(BPoint offsetPoint, SelectedColorMode mode, 46 float value1, float value2, orientation dir, border_style border) 47 : BControl(BRect(0.0, 0.0, 35.0, 265.0).OffsetToCopy(offsetPoint), 48 "ColorSlider", "", new BMessage(MSG_COLOR_SLIDER), 49 B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_FRAME_EVENTS) 50{ 51 _Init(mode, value1, value2, dir, border); 52 FrameResized(Bounds().Width(), Bounds().Height()); 53} 54 55 56ColorSlider::~ColorSlider() 57{ 58 delete fBitmap; 59} 60 61 62BSize 63ColorSlider::MinSize() 64{ 65 BSize minSize; 66 if (fOrientation == B_VERTICAL) 67 minSize = BSize(36, 10 + MAX_Y / 17); 68 else 69 minSize = BSize(10 + MAX_X / 17, 10); 70 return BLayoutUtils::ComposeSize(ExplicitMinSize(), minSize); 71} 72 73 74BSize 75ColorSlider::PreferredSize() 76{ 77 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), MinSize()); 78} 79 80 81BSize 82ColorSlider::MaxSize() 83{ 84 BSize maxSize; 85 if (fOrientation == B_VERTICAL) 86 maxSize = BSize(36, 10 + MAX_Y); 87 else 88 maxSize = BSize(10 + MAX_X, 18); 89 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), maxSize); 90} 91 92 93void 94ColorSlider::AttachedToWindow() 95{ 96} 97 98 99status_t 100ColorSlider::Invoke(BMessage* message) 101{ 102 if (message == NULL) 103 message = Message(); 104 105 if (message == NULL) 106 return BControl::Invoke(message); 107 108 message->RemoveName("value"); 109 message->RemoveName("begin"); 110 111 switch (fMode) { 112 case R_SELECTED: 113 case G_SELECTED: 114 case B_SELECTED: 115 message->AddFloat("value", 1.0 - (float)Value() / 255); 116 break; 117 118 case H_SELECTED: 119 message->AddFloat("value", (1.0 - (float)Value() / 255) * 6); 120 break; 121 122 case S_SELECTED: 123 case V_SELECTED: 124 message->AddFloat("value", 1.0 - (float)Value() / 255); 125 break; 126 } 127 128 // some other parts of WonderBrush rely on this. 129 // if the flag is present, it triggers generating an undo action 130 // fMouseDown is not set yet the first message is sent 131 if (!fMouseDown) 132 message->AddBool("begin", true); 133 134 return BControl::Invoke(message); 135} 136 137 138void 139ColorSlider::Draw(BRect updateRect) 140{ 141 if (fBitmapDirty && fBitmap != NULL) { 142 _FillBitmap(fBitmap, fMode, fFixedValue1, fFixedValue2, fOrientation); 143 fBitmapDirty = false; 144 } 145 146 BRect bounds = _BitmapRect(); 147 148 // Frame 149 if (fBorderStyle == B_FANCY_BORDER) { 150 bounds.InsetBy(-2, -2); 151 rgb_color color = LowColor(); 152 be_control_look->DrawTextControlBorder(this, bounds, updateRect, 153 color); 154 } 155 156 // Color slider fill 157 if (fBitmap != NULL) 158 DrawBitmap(fBitmap, bounds.LeftTop()); 159 else { 160 SetHighColor(255, 0, 0); 161 FillRect(bounds); 162 } 163 164 // Marker background 165 if (fOrientation == B_VERTICAL) { 166 bounds.InsetBy(-2, -2); 167 BRect r = Bounds(); 168 FillRect(BRect(r.left, r.top, bounds.left - 1, r.bottom), 169 B_SOLID_LOW); 170 FillRect(BRect(bounds.right + 1, r.top, r.right, r.bottom), 171 B_SOLID_LOW); 172 FillRect( 173 BRect(bounds.left, r.top, bounds.right, bounds.top - 1), 174 B_SOLID_LOW); 175 FillRect( 176 BRect(bounds.left, bounds.bottom + 1, bounds.right, r.bottom), 177 B_SOLID_LOW); 178 } 179 180 // marker 181 if (fOrientation == B_VERTICAL) { 182 // draw the triangle markers 183 SetHighColor(0, 0, 0); 184 BRect r = Bounds(); 185 float offset = Value() * (r.Height() - 10) / 255.0; 186 _DrawTriangle( 187 BPoint(r.left, offset), 188 BPoint(r.left + 5.0, offset + 5.0), 189 BPoint(r.left, offset + 10.0)); 190 191 _DrawTriangle( 192 BPoint(r.right, offset), 193 BPoint(r.right - 5.0, offset + 5.0), 194 BPoint(r.right, offset + 10.0)); 195 } else { 196 BRect r = bounds; 197 float x = bounds.left - 2 + (255 - Value()) * bounds.Width() / 255.0; 198 if (x > r.left) { 199 SetHighColor(255, 255, 255); 200 StrokeLine(BPoint(x, bounds.top), 201 BPoint(x, bounds.bottom)); 202 } 203 if (x + 1 > r.left) { 204 SetHighColor(0, 0, 0); 205 StrokeLine(BPoint(x + 1, bounds.top), 206 BPoint(x + 1, bounds.bottom)); 207 } 208 if (x + 3 < r.right) { 209 SetHighColor(0, 0, 0); 210 StrokeLine(BPoint(x + 3, bounds.top), 211 BPoint(x + 3, bounds.bottom)); 212 } 213 if (x + 4 < r.right) { 214 SetHighColor(255, 255, 255); 215 StrokeLine(BPoint(x + 4, bounds.top), 216 BPoint(x + 4, bounds.bottom)); 217 } 218 } 219} 220 221 222void 223ColorSlider::FrameResized(float width, float height) 224{ 225 BRect r = _BitmapRect(); 226 _AllocBitmap(r.IntegerWidth() + 1, r.IntegerHeight() + 1); 227 Invalidate(); 228} 229 230 231void 232ColorSlider::MouseDown(BPoint where) 233{ 234 SetMouseEventMask(B_POINTER_EVENTS, 235 B_SUSPEND_VIEW_FOCUS | B_LOCK_WINDOW_FOCUS); 236 _TrackMouse(where); 237 fMouseDown = true; 238} 239 240 241void 242ColorSlider::MouseUp(BPoint where) 243{ 244 fMouseDown = false; 245} 246 247 248void 249ColorSlider::MouseMoved( BPoint where, uint32 code, 250 const BMessage* dragMessage) 251{ 252 if (dragMessage != NULL || !fMouseDown) 253 return; 254 255 _TrackMouse(where); 256} 257 258 259void 260ColorSlider::SetValue(int32 value) 261{ 262 value = max_c(min_c(value, 255), 0); 263 if (value != Value()) { 264 BControl::SetValue(value); 265 Invalidate(); 266 } 267} 268 269 270void 271ColorSlider::SetModeAndValues(SelectedColorMode mode, 272 float value1, float value2) 273{ 274 float R = 0; 275 float G = 0; 276 float B = 0; 277 float h = 0; 278 float s = 0; 279 float v = 0; 280 281 switch (fMode) { 282 case R_SELECTED: 283 R = 255 - Value(); 284 G = round(fFixedValue1 * 255.0); 285 B = round(fFixedValue2 * 255.0); 286 break; 287 288 case G_SELECTED: 289 R = round(fFixedValue1 * 255.0); 290 G = 255 - Value(); 291 B = round(fFixedValue2 * 255.0); 292 break; 293 294 case B_SELECTED: 295 R = round(fFixedValue1 * 255.0); 296 G = round(fFixedValue2 * 255.0); 297 B = 255 - Value(); 298 break; 299 300 case H_SELECTED: 301 h = (1.0 - (float)Value()/255.0)*6.0; 302 s = fFixedValue1; 303 v = fFixedValue2; 304 break; 305 306 case S_SELECTED: 307 h = fFixedValue1; 308 s = 1.0 - (float)Value()/255.0; 309 v = fFixedValue2; 310 break; 311 312 case V_SELECTED: 313 h = fFixedValue1; 314 s = fFixedValue2; 315 v = 1.0 - (float)Value()/255.0; 316 break; 317 } 318 319 if ((fMode & (H_SELECTED | S_SELECTED | V_SELECTED)) != 0) { 320 HSV_to_RGB(h, s, v, R, G, B); 321 R *= 255.0; 322 G *= 255.0; 323 B *= 255.0; 324 } 325 326 rgb_color color = { (uint8)round(R), (uint8)round(G), (uint8)round(B), 327 255 }; 328 329 fMode = mode; 330 SetOtherValues(value1, value2); 331 332 SetMarkerToColor(color); 333 _Update(); 334} 335 336 337void 338ColorSlider::SetOtherValues(float value1, float value2) 339{ 340 fFixedValue1 = value1; 341 fFixedValue2 = value2; 342 343 if (fMode != H_SELECTED) 344 _Update(); 345} 346 347 348void 349ColorSlider::GetOtherValues(float* value1, float* value2) const 350{ 351 if (value1 != NULL) 352 *value1 = fFixedValue1; 353 354 if (value2 != NULL) 355 *value2 = fFixedValue2; 356} 357 358 359void 360ColorSlider::SetMarkerToColor(rgb_color color) 361{ 362 float h = 0; 363 float s = 0; 364 float v = 0; 365 if ((fMode & (H_SELECTED | S_SELECTED | V_SELECTED)) != 0) { 366 RGB_to_HSV( 367 (float)color.red / 255.0, 368 (float)color.green / 255.0, 369 (float)color.blue / 255.0, 370 h, s, v 371 ); 372 } 373 374 switch (fMode) { 375 case R_SELECTED: 376 SetValue(255 - color.red); 377 break; 378 379 case G_SELECTED: 380 SetValue(255 - color.green); 381 break; 382 383 case B_SELECTED: 384 SetValue(255 - color.blue); 385 break; 386 387 case H_SELECTED: 388 SetValue(255.0 - round(h / 6.0 * 255.0)); 389 break; 390 391 case S_SELECTED: 392 SetValue(255.0 - round(s * 255.0)); 393 break; 394 395 case V_SELECTED: 396 SetValue(255.0 - round(v * 255.0)); 397 break; 398 } 399} 400 401 402// #pragma mark - private 403 404 405void 406ColorSlider::_Init(SelectedColorMode mode, float value1, float value2, 407 orientation dir, border_style border) 408{ 409 fMode = mode; 410 fFixedValue1 = value1; 411 fFixedValue2 = value2; 412 fMouseDown = false; 413 fBitmap = NULL; 414 fBitmapDirty = true; 415 fOrientation = dir; 416 fBorderStyle = border; 417 418 SetViewColor(B_TRANSPARENT_COLOR); 419 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 420} 421 422 423void 424ColorSlider::_AllocBitmap(int32 width, int32 height) 425{ 426 if (width < 2 || height < 2) 427 return; 428 429 delete fBitmap; 430 fBitmap = new BBitmap(BRect(0, 0, width - 1, height - 1), 0, B_RGB32); 431 432 fBitmapDirty = true; 433} 434 435 436void 437ColorSlider::_Update() 438{ 439 fBitmapDirty = true; 440 Invalidate(); 441} 442 443 444BRect 445ColorSlider::_BitmapRect() const 446{ 447 BRect r = Bounds(); 448 if (fOrientation == B_VERTICAL) 449 r.InsetBy(7, 3); 450 if (fBorderStyle == B_FANCY_BORDER) 451 r.InsetBy(2, 2); 452 return r; 453} 454 455 456void 457ColorSlider::_FillBitmap(BBitmap* bitmap, SelectedColorMode mode, 458 float fixedValue1, float fixedValue2, orientation orient) const 459{ 460 int32 width = bitmap->Bounds().IntegerWidth(); 461 int32 height = bitmap->Bounds().IntegerHeight(); 462 uint32 bpr = bitmap->BytesPerRow(); 463 464 uint8* bits = (uint8*)bitmap->Bits(); 465 466 float r = 0; 467 float g = 0; 468 float b = 0; 469 float h; 470 float s; 471 float v; 472 473 switch (mode) { 474 case R_SELECTED: 475 g = round(fixedValue1 * 255); 476 b = round(fixedValue2 * 255); 477 if (orient == B_VERTICAL) { 478 for (int y = 0; y <= height; y++) { 479 r = 255 - y * 255 / height; 480 _DrawColorLineY(bits, width, r, g, b); 481 bits += bpr; 482 } 483 } else { 484 for (int x = 0; x <= width; x++) { 485 r = x * 255 / width; 486 _DrawColorLineX(bits, height, bpr, r, g, b); 487 bits += 4; 488 } 489 } 490 break; 491 492 case G_SELECTED: 493 r = round(fixedValue1 * 255); 494 b = round(fixedValue2 * 255); 495 if (orient == B_VERTICAL) { 496 for (int y = 0; y <= height; y++) { 497 g = 255 - y * 255 / height; 498 _DrawColorLineY(bits, width, r, g, b); 499 bits += bpr; 500 } 501 } else { 502 for (int x = 0; x <= width; x++) { 503 g = 255 - x * 255 / width; 504 _DrawColorLineX(bits, height, bpr, r, g, b); 505 bits += 4; 506 } 507 } 508 break; 509 510 case B_SELECTED: 511 r = round(fixedValue1 * 255); 512 g = round(fixedValue2 * 255); 513 if (orient == B_VERTICAL) { 514 for (int y = 0; y <= height; y++) { 515 b = 255 - y * 255 / height; 516 _DrawColorLineY(bits, width, r, g, b); 517 bits += bpr; 518 } 519 } else { 520 for (int x = 0; x <= width; x++) { 521 b = x * 255 / width; 522 _DrawColorLineX(bits, height, bpr, r, g, b); 523 bits += 4; 524 } 525 } 526 527 case H_SELECTED: 528 s = 1.0;//fixedValue1; 529 v = 1.0;//fixedValue2; 530 if (orient == B_VERTICAL) { 531 for (int y = 0; y <= height; y++) { 532 HSV_to_RGB(6.0 - (float)y * 6.0 / height, s, v, r, g, b); 533 _DrawColorLineY(bits, width, r * 255, g * 255, b * 255); 534 bits += bpr; 535 } 536 } else { 537 for (int x = 0; x <= width; x++) { 538 HSV_to_RGB((float)x * 6.0 / width, s, v, r, g, b); 539 _DrawColorLineX(bits, height, bpr, 540 r * 255, g * 255, b * 255); 541 bits += 4; 542 } 543 } 544 break; 545 546 case S_SELECTED: 547 h = fixedValue1; 548 v = 1.0;//fixedValue2; 549 if (orient == B_VERTICAL) { 550 for (int y = 0; y <= height; y++) { 551 HSV_to_RGB(h, 1.0 - (float)y / height, v, r, g, b); 552 _DrawColorLineY(bits, width, r * 255, g * 255, b * 255); 553 bits += bpr; 554 } 555 } else { 556 for (int x = 0; x <= width; x++) { 557 HSV_to_RGB(h, 1.0 - (float)x / width, v, r, g, b); 558 _DrawColorLineX(bits, height, bpr, 559 r * 255, g * 255, b * 255); 560 bits += 4; 561 } 562 } 563 break; 564 565 case V_SELECTED: 566 h = fixedValue1; 567 s = 1.0;//fixedValue2; 568 if (orient == B_VERTICAL) { 569 for (int y = 0; y <= height; y++) { 570 HSV_to_RGB(h, s, 1.0 - (float)y / height, r, g, b); 571 _DrawColorLineY(bits, width, r * 255, g * 255, b * 255); 572 bits += bpr; 573 } 574 } else { 575 for (int x = 0; x <= width; x++) { 576 HSV_to_RGB(h, s, (float)x / width, r, g, b); 577 _DrawColorLineX(bits, height, bpr, 578 r * 255, g * 255, b * 255); 579 bits += 4; 580 } 581 } 582 break; 583 } 584} 585 586 587void 588ColorSlider::_DrawColorLineY(uint8* bits, int width, int r, int g, int b) 589{ 590 for (int x = 0; x <= width; x++) { 591 bits[0] = b; 592 bits[1] = g; 593 bits[2] = r; 594 bits[3] = 255; 595 bits += 4; 596 } 597} 598 599 600void 601ColorSlider::_DrawColorLineX(uint8* bits, int height, int bpr, 602 int r, int g, int b) 603{ 604 for (int y = 0; y <= height; y++) { 605 bits[0] = b; 606 bits[1] = g; 607 bits[2] = r; 608 bits[3] = 255; 609 bits += bpr; 610 } 611} 612 613 614void 615ColorSlider::_DrawTriangle(BPoint point1, BPoint point2, BPoint point3) 616{ 617 StrokeLine(point1, point2); 618 StrokeLine(point3); 619 StrokeLine(point1); 620} 621 622 623void 624ColorSlider::_TrackMouse(BPoint where) 625{ 626 BRect b(_BitmapRect()); 627 628 if (fOrientation == B_VERTICAL) 629 SetValue((int)(((where.y - b.top) / b.Height()) * 255.0)); 630 else 631 SetValue(255 - (int)(((where.x - b.left) / b.Width()) * 255.0)); 632 633 Invoke(); 634} 635