1/* 2 * Copyright 2001 Werner Freytag - please read to the LICENSE file 3 * 4 * Copyright 2002-2006, 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 <OS.h> 15#include <Window.h> 16#include <math.h> 17 18#include "selected_color_mode.h" 19#include "support_ui.h" 20 21#include "rgb_hsv.h" 22 23#define round(x) (int)(x+.5) 24 25enum { 26 MSG_UPDATE = 'Updt', 27}; 28 29#define MAX_X 255 30#define MAX_Y 255 31 32// constructor 33ColorSlider::ColorSlider(BPoint offset_point, 34 selected_color_mode mode, 35 float value1, float value2, orientation dir) 36 : BControl(BRect(0.0, 0.0, 35.0, 265.0).OffsetToCopy(offset_point), 37 "ColorSlider", "", new BMessage(MSG_COLOR_SLIDER), 38 B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_FRAME_EVENTS), 39 fMode(mode), 40 fFixedValue1(value1), 41 fFixedValue2(value2), 42 fMouseDown(false), 43 fBgBitmap(new BBitmap(Bounds(), B_RGB32, true)), 44 fBgView(NULL), 45 fUpdateThread(0), 46 fUpdatePort(0), 47 fOrientation(dir) 48{ 49 SetViewColor(B_TRANSPARENT_32_BIT); 50 51 if (fBgBitmap->IsValid() && fBgBitmap->Lock()) { 52 fBgView = new BView(Bounds(), "", B_FOLLOW_NONE, B_WILL_DRAW); 53 fBgBitmap->AddChild(fBgView); 54/* if (fOrientation == B_VERTICAL) 55 fBgView->SetOrigin(8.0, 2.0); 56 else 57 fBgView->SetOrigin(2.0, 2.0);*/ 58 fBgBitmap->Unlock(); 59 } else { 60 delete fBgBitmap; 61 fBgBitmap = NULL; 62 fBgView = this; 63 } 64 65 fUpdatePort = create_port(100, "color slider update port"); 66 67 fUpdateThread = spawn_thread(ColorSlider::_UpdateThread, "color slider update thread", 10, this); 68 resume_thread( fUpdateThread ); 69 70 Update(2); 71} 72 73// destructor 74ColorSlider::~ColorSlider() 75{ 76 if (fUpdatePort) 77 delete_port(fUpdatePort); 78 if (fUpdateThread) 79 kill_thread(fUpdateThread); 80 81 delete fBgBitmap; 82} 83 84#if LIB_LAYOUT 85// layoutprefs 86minimax 87ColorSlider::layoutprefs() 88{ 89 if (fOrientation == B_VERTICAL) { 90 mpm.mini.x = 36; 91 mpm.maxi.x = 36; 92 mpm.mini.y = 10 + MAX_Y / 17; 93 mpm.maxi.y = 10 + MAX_Y; 94 } else { 95 mpm.mini.x = 10 + MAX_X / 17; 96 mpm.maxi.x = 10 + MAX_X; 97 mpm.mini.y = 10.0; 98 mpm.maxi.y = 12.0; 99 } 100 mpm.weight = 1.0; 101 102 return mpm; 103} 104 105// layout 106BRect 107ColorSlider::layout(BRect frame) 108{ 109 MoveTo(frame.LeftTop()); 110 ResizeTo(frame.Width(), frame.Height()); 111// Update(2); 112 return Frame(); 113} 114#endif // LIB_LAYOUT 115 116// AttachedToWindow 117void 118ColorSlider::AttachedToWindow() 119{ 120 BControl::AttachedToWindow(); 121 122 SetViewColor(B_TRANSPARENT_32_BIT); 123 124 if (fBgBitmap && fBgBitmap->Lock()) { 125 fBgView->SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 126 fBgView->FillRect(Bounds()); 127 fBgBitmap->Unlock(); 128 } else { 129 SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 130 } 131 132 Update(2); 133} 134 135 136// Invoke 137status_t 138ColorSlider::Invoke(BMessage *msg) 139{ 140 if (!msg) msg = Message(); 141 142 msg->RemoveName("value"); 143 msg->RemoveName("begin"); 144 145 switch (fMode) { 146 147 case R_SELECTED: 148 case G_SELECTED: 149 case B_SELECTED: { 150 msg->AddFloat("value", 1.0 - (float)Value() / 255); 151 } break; 152 153 case H_SELECTED: { 154 msg->AddFloat("value", (1.0 - (float)Value() / 255) * 6); 155 } break; 156 157 case S_SELECTED: 158 case V_SELECTED: { 159 msg->AddFloat("value", 1.0 - (float)Value() / 255); 160 } break; 161 162 } 163 164 // some other parts of WonderBrush rely on this. 165 // if the flag is present, it triggers generating an undo action 166 // fMouseDown is not set yet the first message is sent 167 if (!fMouseDown) 168 msg->AddBool("begin", true); 169 170 return BControl::Invoke(msg); 171} 172 173// Draw 174void 175ColorSlider::Draw(BRect updateRect) 176{ 177 Update(0); 178} 179 180// FrameResized 181void 182ColorSlider::FrameResized(float width, float height) 183{ 184 if (fBgBitmap) { 185 fBgBitmap->Lock(); 186 delete fBgBitmap; 187 } 188 fBgBitmap = new BBitmap(Bounds(), B_RGB32, true); 189 if (fBgBitmap->IsValid() && fBgBitmap->Lock()) { 190 fBgView = new BView(Bounds(), "", B_FOLLOW_NONE, B_WILL_DRAW); 191 fBgBitmap->AddChild(fBgView); 192/* if (fOrientation == B_VERTICAL) 193 fBgView->SetOrigin(8.0, 2.0); 194 else 195 fBgView->SetOrigin(2.0, 2.0);*/ 196 fBgBitmap->Unlock(); 197 } else { 198 delete fBgBitmap; 199 fBgBitmap = NULL; 200 fBgView = this; 201 } 202 Update(2); 203} 204 205// MouseDown 206void 207ColorSlider::MouseDown(BPoint where) 208{ 209 Window()->Activate(); 210 211 SetMouseEventMask(B_POINTER_EVENTS, B_SUSPEND_VIEW_FOCUS | B_LOCK_WINDOW_FOCUS); 212 _TrackMouse(where); 213 fMouseDown = true; 214} 215 216// MouseUp 217void 218ColorSlider::MouseUp(BPoint where) 219{ 220 fMouseDown = false; 221} 222 223// MouseMoved 224void 225ColorSlider::MouseMoved( BPoint where, uint32 code, const BMessage* dragMessage) 226{ 227 if (dragMessage || !fMouseDown) 228 return; 229 230 _TrackMouse(where); 231} 232 233// SetValue 234void 235ColorSlider::SetValue(int32 value) 236{ 237 value = max_c(min_c(value, 255), 0); 238 if (value != Value()) { 239 BControl::SetValue(value); 240 241 Update(1); 242 } 243} 244 245// Update 246void 247ColorSlider::Update(int depth) 248{ 249 // depth: 0 = onscreen only, 1 = bitmap 1, 2 = bitmap 0 250 if (depth == 2) { 251 write_port(fUpdatePort, MSG_UPDATE, NULL, 0); 252 return; 253 } 254 255 if (!Parent()) 256 return; 257 258 fBgBitmap->Lock(); 259 260// BRect r(fBgView->Bounds()); 261 BRect r(Bounds()); 262 BRect bounds(r); 263 if (fOrientation == B_VERTICAL) { 264// r.OffsetBy(-8.0, -2.0); 265 r.InsetBy(6.0, 3.0); 266// bounds.Set(-8.0, -2.0, r.right - 8.0, r.bottom - 2.0); 267// bounds.OffsetBy(8.0, 2.0); 268 } else { 269// r.OffsetBy(-2.0, -2.0); 270// bounds.Set(-2.0, -2.0, r.right - 2.0, r.bottom - 2.0); 271// bounds.OffsetBy(2.0, 2.0); 272 } 273 274 fBgBitmap->Unlock(); 275 276 rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR); 277 rgb_color shadow = tint_color(background, B_DARKEN_1_TINT); 278 rgb_color darkShadow = tint_color(background, B_DARKEN_3_TINT); 279 rgb_color light = tint_color(background, B_LIGHTEN_MAX_TINT); 280 281 if (depth >= 1) { 282 283 fBgBitmap->Lock(); 284 285 286 // frame 287 stroke_frame(fBgView, r, shadow, shadow, light, light); 288 r.InsetBy(1.0, 1.0); 289 stroke_frame(fBgView, r, darkShadow, darkShadow, background, background); 290 291 if (fOrientation == B_VERTICAL) { 292 // clear area left and right from slider 293 fBgView->SetHighColor( background ); 294 fBgView->FillRect( BRect(bounds.left, bounds.top, bounds.left + 5.0, bounds.bottom) ); 295 fBgView->FillRect( BRect(bounds.right - 5.0, bounds.top, bounds.right, bounds.bottom) ); 296 } 297/* 298 // marker 299 if (fOrientation == B_VERTICAL) { 300 // clear area left and right from slider 301 fBgView->SetHighColor( background ); 302 fBgView->FillRect( BRect(bounds.left, bounds.top, bounds.left + 5.0, bounds.bottom) ); 303 fBgView->FillRect( BRect(bounds.right - 5.0, bounds.top, bounds.right, bounds.bottom) ); 304 // draw the triangle markers 305 fBgView->SetHighColor( 0, 0, 0 ); 306 float value = Value(); 307 fBgView->StrokeLine( BPoint(bounds.left, value - 2.0), BPoint(bounds.left + 5.0, value + 3.0)); 308 fBgView->StrokeLine( BPoint(bounds.left, value + 8.0)); 309 fBgView->StrokeLine( BPoint(bounds.left, value - 2.0)); 310 311 fBgView->StrokeLine( BPoint(bounds.right, value - 2.0), BPoint(bounds.right - 5.0, value + 3.0)); 312 fBgView->StrokeLine( BPoint(bounds.right, value + 8.0)); 313 fBgView->StrokeLine( BPoint(bounds.right, value - 2.0)); 314 } else { 315 r.InsetBy(1.0, 1.0); 316 float value = (Value() / 255.0) * (bounds.Width() - 4.0); 317 if (value - 2 > r.left) { 318 fBgView->SetHighColor( 255, 255, 255 ); 319 fBgView->StrokeLine( BPoint(value - 2, bounds.top + 2.0), 320 BPoint(value - 2, bounds.bottom - 2.0)); 321 } 322 if (value - 1 > r.left) { 323 fBgView->SetHighColor( 0, 0, 0 ); 324 fBgView->StrokeLine( BPoint(value - 1, bounds.top + 2.0), 325 BPoint(value - 1, bounds.bottom - 2.0)); 326 } 327 if (value + 1 < r.right) { 328 fBgView->SetHighColor( 0, 0, 0 ); 329 fBgView->StrokeLine( BPoint(value + 1, bounds.top + 2.0), 330 BPoint(value + 1, bounds.bottom - 2.0)); 331 } 332 if (value + 2 < r.right) { 333 fBgView->SetHighColor( 255, 255, 255 ); 334 fBgView->StrokeLine( BPoint(value + 2, bounds.top + 2.0), 335 BPoint(value + 2, bounds.bottom - 2.0)); 336 } 337 }*/ 338 339 fBgView->Sync(); 340 341 fBgBitmap->Unlock(); 342 } else 343 r.InsetBy(1.0, 1.0); 344 345 DrawBitmap(fBgBitmap, BPoint(0.0, 0.0)); 346 347 // marker 348 if (fOrientation == B_VERTICAL) { 349 // draw the triangle markers 350 SetHighColor( 0, 0, 0 ); 351 float value = Value(); 352 StrokeLine( BPoint(bounds.left, value), 353 BPoint(bounds.left + 5.0, value + 5.0)); 354 StrokeLine( BPoint(bounds.left, value + 10.0)); 355 StrokeLine( BPoint(bounds.left, value)); 356 357 StrokeLine( BPoint(bounds.right, value), 358 BPoint(bounds.right - 5.0, value + 5.0)); 359 StrokeLine( BPoint(bounds.right, value + 10.0)); 360 StrokeLine( BPoint(bounds.right, value)); 361 } else { 362 r.InsetBy(1.0, 1.0); 363 float value = (Value() / 255.0) * (bounds.Width() - 4.0); 364 if (value > r.left) { 365 SetHighColor( 255, 255, 255 ); 366 StrokeLine( BPoint(value, bounds.top + 2.0), 367 BPoint(value, bounds.bottom - 2.0)); 368 } 369 if (value + 1 > r.left) { 370 SetHighColor( 0, 0, 0 ); 371 StrokeLine( BPoint(value + 1, bounds.top + 2.0), 372 BPoint(value + 1, bounds.bottom - 2.0)); 373 } 374 if (value + 3 < r.right) { 375 SetHighColor( 0, 0, 0 ); 376 StrokeLine( BPoint(value + 3, bounds.top + 2.0), 377 BPoint(value + 3, bounds.bottom - 2.0)); 378 } 379 if (value + 4 < r.right) { 380 SetHighColor( 255, 255, 255 ); 381 StrokeLine( BPoint(value + 4, bounds.top + 2.0), 382 BPoint(value + 4, bounds.bottom - 2.0)); 383 } 384 } 385 SetOrigin(0.0, 0.0); 386} 387 388// SetModeAndValues 389void 390ColorSlider::SetModeAndValues(selected_color_mode mode, 391 float value1, float value2) 392{ 393 float R(0), G(0), B(0); 394 float h(0), s(0), v(0); 395 396 fBgBitmap->Lock(); 397 398 switch (fMode) { 399 400 case R_SELECTED: { 401 R = 255 - Value(); 402 G = round(fFixedValue1 * 255.0); 403 B = round(fFixedValue2 * 255.0); 404 }; break; 405 406 case G_SELECTED: { 407 R = round(fFixedValue1 * 255.0); 408 G = 255 - Value(); 409 B = round(fFixedValue2 * 255.0); 410 }; break; 411 412 case B_SELECTED: { 413 R = round(fFixedValue1 * 255.0); 414 G = round(fFixedValue2 * 255.0); 415 B = 255 - Value(); 416 }; break; 417 418 case H_SELECTED: { 419 h = (1.0 - (float)Value()/255.0)*6.0; 420 s = fFixedValue1; 421 v = fFixedValue2; 422 }; break; 423 424 case S_SELECTED: { 425 h = fFixedValue1; 426 s = 1.0 - (float)Value()/255.0; 427 v = fFixedValue2; 428 }; break; 429 430 case V_SELECTED: { 431 h = fFixedValue1; 432 s = fFixedValue2; 433 v = 1.0 - (float)Value()/255.0; 434 }; break; 435 } 436 437 if (fMode & (H_SELECTED|S_SELECTED|V_SELECTED) ) { 438 HSV_to_RGB(h, s, v, R, G, B); 439 R*=255.0; G*=255.0; B*=255.0; 440 } 441 442 rgb_color color = { round(R), round(G), round(B), 255 }; 443 444 fMode = mode; 445 SetOtherValues(value1, value2); 446 fBgBitmap->Unlock(); 447 448 SetMarkerToColor( color ); 449 Update(2); 450} 451 452// SetOtherValues 453void 454ColorSlider::SetOtherValues(float value1, float value2) 455{ 456 fFixedValue1 = value1; 457 fFixedValue2 = value2; 458 459 if (fMode != H_SELECTED) { 460 Update(2); 461 } 462} 463 464// GetOtherValues 465void 466ColorSlider::GetOtherValues(float* value1, float* value2) const 467{ 468 if (value1 && value2) { 469 *value1 = fFixedValue1; 470 *value2 = fFixedValue2; 471 } 472} 473 474// SetMarkerToColor 475void 476ColorSlider::SetMarkerToColor(rgb_color color) 477{ 478 float h = 0.0f; 479 float s = 0.0f; 480 float v = 0.0f; 481 if ((fMode & (H_SELECTED | S_SELECTED | V_SELECTED)) != 0) { 482 RGB_to_HSV((float)color.red / 255.0f, (float)color.green / 255.0f, 483 (float)color.blue / 255.0f, h, s, v); 484 } 485 486 switch (fMode) { 487 case R_SELECTED: 488 SetValue(255 - color.red); 489 break; 490 491 case G_SELECTED: 492 SetValue(255 - color.green); 493 break; 494 495 case B_SELECTED: 496 SetValue(255 - color.blue); 497 break; 498 499 case H_SELECTED: 500 SetValue(255.0 - round(h / 6.0 * 255.0)); 501 break; 502 503 case S_SELECTED: 504 SetValue(255.0 - round(s * 255.0)); 505 break; 506 507 case V_SELECTED: 508 SetValue(255.0 - round(v * 255.0)); 509 break; 510 } 511} 512 513// _UpdateThread 514status_t 515ColorSlider::_UpdateThread(void* data) 516{ 517 // initializing 518 ColorSlider* colorSlider = (ColorSlider*)data; 519 520 bool looperLocked = colorSlider->LockLooper(); 521 522 port_id port = colorSlider->fUpdatePort; 523 orientation orient = colorSlider->fOrientation; 524 525 if (looperLocked) 526 colorSlider->UnlockLooper(); 527 528 float h, s, v, r, g, b; 529 int R, G, B; 530 531 // drawing 532 533 int32 msg_code; 534 char msg_buffer; 535 536 while (true) { 537 538 port_info info; 539 540 do { 541 542 read_port(port, &msg_code, &msg_buffer, sizeof(msg_buffer)); 543 get_port_info(port, &info); 544 545 } while (info.queue_count); 546 547 if (colorSlider->LockLooper()) { 548 549 uint colormode = colorSlider->fMode; 550 float fixedvalue1 = colorSlider->fFixedValue1; 551 float fixedvalue2 = colorSlider->fFixedValue2; 552 553 BBitmap* bitmap = colorSlider->fBgBitmap; 554 BView* view = colorSlider->fBgView; 555 556 bitmap->Lock(); 557 558 colorSlider->UnlockLooper(); 559 560 view->BeginLineArray(256); 561 562 switch (colormode) { 563 564 case R_SELECTED: { 565 G = round(fixedvalue1 * 255); 566 B = round(fixedvalue2 * 255); 567 for (int R = 0; R < 256; ++R) { 568 _DrawColorLineY( view, R, R, G, B ); 569 } 570 }; break; 571 572 case G_SELECTED: { 573 R = round(fixedvalue1 * 255); 574 B = round(fixedvalue2 * 255); 575 for (int G = 0; G < 256; ++G) { 576 _DrawColorLineY( view, G, R, G, B ); 577 } 578 }; break; 579 580 case B_SELECTED: { 581 R = round(fixedvalue1 * 255); 582 G = round(fixedvalue2 * 255); 583 for (int B = 0; B < 256; ++B) { 584 _DrawColorLineY( view, B, R, G, B ); 585 } 586 }; break; 587 588 case H_SELECTED: { 589 s = 1.0;//fixedvalue1; 590 v = 1.0;//fixedvalue2; 591 if (orient == B_VERTICAL) { 592 for (int y = 0; y < 256; ++y) { 593 HSV_to_RGB( (float)y*6.0/255.0, s, v, r, g, b ); 594 _DrawColorLineY( view, y, r*255, g*255, b*255 ); 595 } 596 } else { 597 for (int x = 0; x < 256; ++x) { 598 HSV_to_RGB( (float)x*6.0/255.0, s, v, r, g, b ); 599 _DrawColorLineX( view, x, r*255, g*255, b*255 ); 600 } 601 } 602 }; break; 603 604 case S_SELECTED: { 605 h = fixedvalue1; 606 v = 1.0;//fixedvalue2; 607 for (int y = 0; y < 256; ++y) { 608 HSV_to_RGB( h, (float)y/255, v, r, g, b ); 609 _DrawColorLineY( view, y, r*255, g*255, b*255 ); 610 } 611 }; break; 612 613 case V_SELECTED: { 614 h = fixedvalue1; 615 s = 1.0;//fixedvalue2; 616 for (int y = 0; y < 256; ++y) { 617 HSV_to_RGB( h, s, (float)y/255, r, g, b ); 618 _DrawColorLineY( view, y, r*255, g*255, b*255 ); 619 } 620 }; break; 621 } 622 623 view->EndLineArray(); 624 view->Sync(); 625 bitmap->Unlock(); 626 627 if (colorSlider->LockLooper()) { 628 colorSlider->Update(1); 629 colorSlider->UnlockLooper(); 630 } 631 } 632 } 633 return B_OK; 634} 635 636// _DrawColorLineY 637void 638ColorSlider::_DrawColorLineY(BView *view, float y, 639 int r, int g, int b) 640{ 641 rgb_color color = { r, g, b, 255 }; 642 y = 255.0 - y; 643 view->AddLine( BPoint(8.0, y + 5.0), BPoint(27.0, y + 5.0), color ); 644} 645 646// _DrawColorLineX 647void 648ColorSlider::_DrawColorLineX(BView *view, float x, 649 int r, int g, int b) 650{ 651 rgb_color color = { r, g, b, 255 }; 652 BRect bounds(view->Bounds()); 653 x = (255.0 - x) * (bounds.Width() - 2.0) / 255.0 + 2.0; 654 view->AddLine( BPoint(x, bounds.top + 2.0), BPoint(x, bounds.bottom - 2.0), color ); 655} 656 657// _TrackMouse 658void 659ColorSlider::_TrackMouse(BPoint where) 660{ 661 if (fOrientation == B_VERTICAL) { 662 SetValue((int)where.y - 2); 663 } else { 664 BRect b(Bounds()); 665 SetValue((int)(((where.x - 2.0) / b.Width()) * 255.0)); 666 } 667 Invoke(); 668} 669