1 /* 2 * Copyright 2001-2006, Haiku Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Marc Flerackers (mflerackers@androme.be) 7 * Axel Dörfler, axeld@pinc-software.de 8 */ 9 10 /** BColorControl displays a palette of selectable colors. */ 11 12 13 #include <stdio.h> 14 #include <stdlib.h> 15 16 #include <ColorControl.h> 17 #include <Bitmap.h> 18 #include <TextControl.h> 19 #include <Region.h> 20 #include <Screen.h> 21 #include <Window.h> 22 23 24 static const uint32 kMsgColorEntered = 'ccol'; 25 static const uint32 kMinCellSize = 6; 26 27 28 BColorControl::BColorControl(BPoint leftTop, color_control_layout layout, 29 float cellSize, const char *name, BMessage *message, 30 bool bufferedDrawing) 31 : BControl(BRect(leftTop, leftTop), name, NULL, message, 32 B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_NAVIGABLE) 33 { 34 _InitData(layout, cellSize, bufferedDrawing, NULL); 35 } 36 37 38 BColorControl::BColorControl(BMessage* archive) 39 : BControl(archive) 40 { 41 int32 layout; 42 float cellSize; 43 bool useOffscreen; 44 45 archive->FindInt32("_layout", &layout); 46 archive->FindFloat("_csize", &cellSize); 47 archive->FindBool("_use_off", &useOffscreen); 48 49 _InitData((color_control_layout)layout, cellSize, useOffscreen, archive); 50 } 51 52 53 BColorControl::~BColorControl() 54 { 55 } 56 57 58 void 59 BColorControl::_InitData(color_control_layout layout, float size, 60 bool useOffscreen, BMessage* archive) 61 { 62 fColumns = layout; 63 fRows = 256 / fColumns; 64 fCellSize = max_c(kMinCellSize, size); 65 fFocusedComponent = 0; 66 67 if (archive) { 68 int32 value = 0; 69 archive->FindInt32("_val", &value); 70 71 SetValue(value); 72 73 fRedText = (BTextControl*)FindView("_red"); 74 fGreenText = (BTextControl*)FindView("_green"); 75 fBlueText = (BTextControl*)FindView("_blue"); 76 } else { 77 BRect rect(0.0f, 0.0f, 70.0f, 15.0f); 78 float labelWidth = StringWidth("Green:") + 5; 79 rect.right = labelWidth + StringWidth("999") + 20; 80 81 // red 82 83 fRedText = new BTextControl(rect, "_red", "Red:", "0", 84 new BMessage(kMsgColorEntered), B_FOLLOW_LEFT | B_FOLLOW_TOP, 85 B_WILL_DRAW | B_NAVIGABLE); 86 fRedText->SetDivider(labelWidth); 87 88 float offset = fRedText->Bounds().Height() + 2; 89 90 for (int32 i = 0; i < 256; i++) 91 fRedText->TextView()->DisallowChar(i); 92 for (int32 i = '0'; i <= '9'; i++) 93 fRedText->TextView()->AllowChar(i); 94 fRedText->TextView()->SetMaxBytes(3); 95 96 // green 97 98 rect.OffsetBy(0.0f, offset); 99 fGreenText = new BTextControl(rect, "_green", "Green:", "0", 100 new BMessage(kMsgColorEntered), B_FOLLOW_LEFT | B_FOLLOW_TOP, 101 B_WILL_DRAW | B_NAVIGABLE); 102 fGreenText->SetDivider(labelWidth); 103 104 for (int32 i = 0; i < 256; i++) 105 fGreenText->TextView()->DisallowChar(i); 106 for (int32 i = '0'; i <= '9'; i++) 107 fGreenText->TextView()->AllowChar(i); 108 fGreenText->TextView()->SetMaxBytes(3); 109 110 // blue 111 112 rect.OffsetBy(0.0f, offset); 113 fBlueText = new BTextControl(rect, "_blue", "Blue:", "0", 114 new BMessage(kMsgColorEntered), B_FOLLOW_LEFT | B_FOLLOW_TOP, 115 B_WILL_DRAW | B_NAVIGABLE); 116 fBlueText->SetDivider(labelWidth); 117 118 for (int32 i = 0; i < 256; i++) 119 fBlueText->TextView()->DisallowChar(i); 120 for (int32 i = '0'; i <= '9'; i++) 121 fBlueText->TextView()->AllowChar(i); 122 fBlueText->TextView()->SetMaxBytes(3); 123 124 _LayoutView(); 125 126 AddChild(fRedText); 127 AddChild(fGreenText); 128 AddChild(fBlueText); 129 } 130 131 if (useOffscreen) { 132 BRect bounds(Bounds()); 133 bounds.right = floorf(fBlueText->Frame().left) - 1; 134 135 fBitmap = new BBitmap(bounds, /*BScreen(Window()).ColorSpace()*/B_RGB32, true, false); 136 fOffscreenView = new BView(bounds, "off_view", 0, 0); 137 138 fBitmap->Lock(); 139 fBitmap->AddChild(fOffscreenView); 140 fBitmap->Unlock(); 141 } else { 142 fBitmap = NULL; 143 fOffscreenView = NULL; 144 } 145 } 146 147 148 void 149 BColorControl::_LayoutView() 150 { 151 BRect rect(0.0f, 0.0f, ceil(fColumns * fCellSize), ceil(fRows * fCellSize + 2)); 152 153 if (rect.Height() < fBlueText->Frame().bottom) { 154 // adjust the height to fit 155 rect.bottom = fBlueText->Frame().bottom; 156 } 157 158 ResizeTo(rect.Width() + fRedText->Bounds().Width(), rect.Height()); 159 160 float offset = floor(rect.bottom / 4); 161 float y = offset; 162 if (offset < fRedText->Bounds().Height() + 2) { 163 offset = fRedText->Bounds().Height() + 2; 164 y = 0; 165 } 166 167 fRedText->MoveTo(rect.right + 1, y); 168 169 y += offset; 170 fGreenText->MoveTo(rect.right + 1, y); 171 172 y += offset; 173 fBlueText->MoveTo(rect.right + 1, y); 174 } 175 176 177 BArchivable * 178 BColorControl::Instantiate(BMessage *archive) 179 { 180 if ( validate_instantiation(archive, "BColorControl")) 181 return new BColorControl(archive); 182 183 return NULL; 184 } 185 186 187 status_t 188 BColorControl::Archive(BMessage *archive, bool deep) const 189 { 190 status_t status = BControl::Archive(archive, deep); 191 192 if (status == B_OK) 193 status = archive->AddInt32("_layout", Layout()); 194 if (status == B_OK) 195 status = archive->AddFloat("_csize", fCellSize); 196 if (status == B_OK) 197 status = archive->AddBool("_use_off", fOffscreenView != NULL); 198 199 return status; 200 } 201 202 203 void 204 BColorControl::SetValue(int32 value) 205 { 206 if (Value() == value) 207 return; 208 209 rgb_color c1 = ValueAsColor(); 210 rgb_color c2; 211 c2.red = (value & 0xFF000000) >> 24; 212 c2.green = (value & 0x00FF0000) >> 16; 213 c2.blue = (value & 0x0000FF00) >> 8; 214 char string[4]; 215 216 if (c1.red != c2.red) { 217 sprintf(string, "%d", c2.red); 218 fRedText->SetText(string); 219 } 220 221 if (c1.green != c2.green) { 222 sprintf(string, "%d", c2.green); 223 fGreenText->SetText(string); 224 } 225 226 if (c1.blue != c2.blue) { 227 sprintf(string, "%d", c2.blue); 228 fBlueText->SetText(string); 229 } 230 231 BControl::SetValueNoUpdate(value); 232 233 // TODO: This causes lot of flickering 234 Invalidate(); 235 236 if (LockLooper()) { 237 Window()->UpdateIfNeeded(); 238 UnlockLooper(); 239 } 240 } 241 242 243 rgb_color 244 BColorControl::ValueAsColor() 245 { 246 int32 value = Value(); 247 rgb_color color; 248 249 color.red = (value & 0xFF000000) >> 24; 250 color.green = (value & 0x00FF0000) >> 16; 251 color.blue = (value & 0x0000FF00) >> 8; 252 color.alpha = 255; 253 254 return color; 255 } 256 257 258 void 259 BColorControl::SetEnabled(bool enabled) 260 { 261 BControl::SetEnabled(enabled); 262 263 fRedText->SetEnabled(enabled); 264 fGreenText->SetEnabled(enabled); 265 fBlueText->SetEnabled(enabled); 266 } 267 268 269 void 270 BColorControl::AttachedToWindow() 271 { 272 if (Parent()) 273 SetViewColor(Parent()->ViewColor()); 274 else 275 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 276 277 BControl::AttachedToWindow(); 278 279 fRedText->SetTarget(this); 280 fGreenText->SetTarget(this); 281 fBlueText->SetTarget(this); 282 } 283 284 285 void 286 BColorControl::MessageReceived(BMessage *message) 287 { 288 switch (message->what) { 289 case kMsgColorEntered: 290 { 291 rgb_color color; 292 color.red = strtol(fRedText->Text(), NULL, 10); 293 color.green = strtol(fGreenText->Text(), NULL, 10); 294 color.blue = strtol(fBlueText->Text(), NULL, 10); 295 color.alpha = 255; 296 297 SetValue(color); 298 Invoke(); 299 break; 300 } 301 default: 302 BControl::MessageReceived(message); 303 } 304 } 305 306 307 void 308 BColorControl::Draw(BRect updateRect) 309 { 310 if (fBitmap) { 311 if (!fBitmap->Lock()) 312 return; 313 314 if (fOffscreenView->Bounds().Intersects(updateRect)) { 315 _UpdateOffscreen(updateRect); 316 BRegion region(updateRect); 317 ConstrainClippingRegion(®ion); 318 DrawBitmap(fBitmap, B_ORIGIN); 319 ConstrainClippingRegion(NULL); 320 } 321 322 fBitmap->Unlock(); 323 } else 324 _DrawColorArea(this, updateRect); 325 } 326 327 328 void 329 BColorControl::_DrawColorArea(BView* target, BRect update) 330 { 331 BRegion region(update); 332 target->ConstrainClippingRegion(®ion); 333 334 BRect rect(0.0f, 0.0f, ceil(fColumns * fCellSize), Bounds().bottom); 335 336 rgb_color noTint = ui_color(B_PANEL_BACKGROUND_COLOR), 337 lightenmax = tint_color(noTint, B_LIGHTEN_MAX_TINT), 338 darken1 = tint_color(noTint, B_DARKEN_1_TINT), 339 darken4 = tint_color(noTint, B_DARKEN_4_TINT); 340 341 // First bevel 342 target->SetHighColor(darken1); 343 target->StrokeLine(rect.LeftBottom(), rect.LeftTop()); 344 target->StrokeLine(rect.LeftTop(), rect.RightTop()); 345 target->SetHighColor(lightenmax); 346 target->StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), rect.RightBottom()); 347 target->StrokeLine(rect.RightBottom(), BPoint(rect.right, rect.top + 1.0f)); 348 349 rect.InsetBy(1.0f, 1.0f); 350 351 // Second bevel 352 target->SetHighColor(darken4); 353 target->StrokeLine(rect.LeftBottom(), rect.LeftTop()); 354 target->StrokeLine(rect.LeftTop(), rect.RightTop()); 355 target->SetHighColor(noTint); 356 target->StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), rect.RightBottom()); 357 target->StrokeLine(rect.RightBottom(), BPoint(rect.right, rect.top + 1.0f)); 358 359 // Ramps 360 rgb_color white = {255, 255, 255, 255}; 361 rgb_color red = {255, 0, 0, 255}; 362 rgb_color green = {0, 255, 0, 255}; 363 rgb_color blue = {0, 0, 255, 255}; 364 365 rect.InsetBy(1.0f, 1.0f); 366 367 BRect rampRect(rect); 368 float rampSize = rampRect.Height() / 4.0; 369 370 rampRect.bottom = rampRect.top + rampSize; 371 372 _ColorRamp(rampRect, target, white, 0, false); 373 374 rampRect.OffsetBy(0, rampSize); 375 _ColorRamp(rampRect, target, red, 0, false); 376 377 rampRect.OffsetBy(0,rampSize); 378 _ColorRamp(rampRect, target, green, 0, false); 379 380 rampRect.OffsetBy(0, rampSize); 381 _ColorRamp(rampRect, target, blue, 0, false); 382 383 // Selectors 384 rgb_color color = ValueAsColor(); 385 float x, y = rampSize * 1.5; 386 387 target->SetDrawingMode(B_OP_OVER); 388 target->SetPenSize(2.0f); 389 390 x = rect.left + color.red * (rect.Width() - 7) / 255; 391 target->SetHighColor(255, 255, 255); 392 target->StrokeEllipse(BRect(x, y, x + 4.0f, y + 4.0f)); 393 394 y += rampSize; 395 396 x = rect.left + color.green * (rect.Width() - 7) / 255; 397 target->SetHighColor(255, 255, 255); 398 target->StrokeEllipse(BRect(x, y, x + 4.0f, y + 4.0f)); 399 400 y += rampSize; 401 402 x = rect.left + color.blue * (rect.Width() - 7) / 255; 403 target->SetHighColor(255, 255, 255); 404 target->StrokeEllipse(BRect(x, y, x + 4.0f, y + 4.0f)); 405 406 target->SetPenSize(1.0f); 407 target->SetDrawingMode(B_OP_COPY); 408 409 target->ConstrainClippingRegion(NULL); 410 } 411 412 413 void 414 BColorControl::_ColorRamp(BRect rect, BView* target, 415 rgb_color baseColor, int16 flag, bool focused) 416 { 417 float width = rect.Width()+1; 418 rgb_color color; 419 color.alpha = 255; 420 421 target->BeginLineArray((int32)width); 422 423 for (float i = 0; i <= width; i++) { 424 color.red = (uint8)(i * baseColor.red / width); 425 color.green = (uint8)(i * baseColor.green / width); 426 color.blue = (uint8)(i * baseColor.blue / width); 427 428 target->AddLine(BPoint(rect.left + i, rect.top), 429 BPoint(rect.left + i, rect.bottom), color); 430 } 431 432 target->EndLineArray(); 433 } 434 435 436 void 437 BColorControl::_UpdateOffscreen(BRect update) 438 { 439 if (fBitmap->Lock()) { 440 update = update & fOffscreenView->Bounds(); 441 fOffscreenView->FillRect(update); 442 _DrawColorArea(fOffscreenView, update); 443 fOffscreenView->Sync(); 444 fBitmap->Unlock(); 445 } 446 } 447 448 449 void 450 BColorControl::SetCellSize(float cellSide) 451 { 452 fCellSize = max_c(kMinCellSize, cellSide); 453 454 ResizeToPreferred(); 455 } 456 457 458 float 459 BColorControl::CellSize() const 460 { 461 return fCellSize; 462 } 463 464 465 void 466 BColorControl::SetLayout(color_control_layout layout) 467 { 468 switch (layout) { 469 case B_CELLS_4x64: 470 fColumns = 4; 471 fRows = 64; 472 break; 473 case B_CELLS_8x32: 474 fColumns = 8; 475 fRows = 32; 476 break; 477 case B_CELLS_16x16: 478 fColumns = 16; 479 fRows = 16; 480 break; 481 case B_CELLS_32x8: 482 fColumns = 32; 483 fRows = 8; 484 break; 485 case B_CELLS_64x4: 486 fColumns = 64; 487 fRows = 4; 488 break; 489 } 490 491 ResizeToPreferred(); 492 Invalidate(); 493 } 494 495 496 color_control_layout 497 BColorControl::Layout() const 498 { 499 if (fColumns == 4 && fRows == 64) 500 return B_CELLS_4x64; 501 if (fColumns == 8 && fRows == 32) 502 return B_CELLS_8x32; 503 if (fColumns == 16 && fRows == 16) 504 return B_CELLS_16x16; 505 if (fColumns == 32 && fRows == 8) 506 return B_CELLS_32x8; 507 if (fColumns == 64 && fRows == 4) 508 return B_CELLS_64x4; 509 510 return B_CELLS_32x8; 511 } 512 513 514 void 515 BColorControl::WindowActivated(bool state) 516 { 517 BControl::WindowActivated(state); 518 } 519 520 521 void 522 BColorControl::KeyDown(const char* bytes, int32 numBytes) 523 { 524 // TODO: make this keyboard navigable! 525 BControl::KeyDown(bytes, numBytes); 526 } 527 528 529 void 530 BColorControl::MouseUp(BPoint point) 531 { 532 fFocusedComponent = 0; 533 SetTracking(false); 534 } 535 536 537 void 538 BColorControl::MouseDown(BPoint point) 539 { 540 BRect rect(0.0f, 0.0f, fColumns * fCellSize, Bounds().bottom); 541 if (!rect.Contains(point)) 542 return; 543 544 rgb_color color = ValueAsColor(); 545 546 float rampsize = rect.bottom / 4; 547 548 uint8 shade = (unsigned char)max_c(0, 549 min_c((point.x - 2) * 255 / (rect.Width() - 4.0f), 255)); 550 551 if (point.y - 2 < rampsize) { 552 color.red = color.green = color.blue = shade; 553 fFocusedComponent = 1; 554 } else if (point.y - 2 < (rampsize * 2)) { 555 color.red = shade; 556 fFocusedComponent = 2; 557 } else if (point.y - 2 < (rampsize * 3)) { 558 color.green = shade; 559 fFocusedComponent = 3; 560 } else { 561 color.blue = shade; 562 fFocusedComponent = 4; 563 } 564 565 SetValue(color); 566 Invoke(); 567 568 SetTracking(true); 569 MakeFocus(); 570 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS); 571 } 572 573 574 void 575 BColorControl::MouseMoved(BPoint point, uint32 transit, 576 const BMessage *message) 577 { 578 if (fFocusedComponent == 0 || !IsTracking()) 579 return; 580 581 rgb_color color = ValueAsColor(); 582 583 BRect rect(0.0f, 0.0f, fColumns * fCellSize, Bounds().bottom); 584 585 uint8 shade = (unsigned char)max_c(0, 586 min_c((point.x - 2) * 255 / (rect.Width() - 4.0f), 255)); 587 588 switch (fFocusedComponent) { 589 case 1: 590 color.red = color.green = color.blue = shade; 591 break; 592 case 2: 593 color.red = shade; 594 break; 595 case 3: 596 color.green = shade; 597 break; 598 case 4: 599 color.blue = shade; 600 break; 601 default: 602 break; 603 } 604 605 SetValue(color); 606 Invoke(); 607 } 608 609 610 void 611 BColorControl::DetachedFromWindow() 612 { 613 BControl::DetachedFromWindow(); 614 } 615 616 617 void 618 BColorControl::GetPreferredSize(float *_width, float *_height) 619 { 620 if (_width) 621 *_width = fColumns * fCellSize + 4.0f + fRedText->Bounds().Width(); 622 623 if (_height) 624 *_height = fBlueText->Frame().bottom; 625 } 626 627 628 void 629 BColorControl::ResizeToPreferred() 630 { 631 BControl::ResizeToPreferred(); 632 633 _LayoutView(); 634 } 635 636 637 status_t 638 BColorControl::Invoke(BMessage *msg) 639 { 640 return BControl::Invoke(msg); 641 } 642 643 644 void 645 BColorControl::FrameMoved(BPoint new_position) 646 { 647 BControl::FrameMoved(new_position); 648 } 649 650 651 void 652 BColorControl::FrameResized(float new_width, float new_height) 653 { 654 BControl::FrameResized(new_width, new_height); 655 } 656 657 658 BHandler * 659 BColorControl::ResolveSpecifier(BMessage *msg, int32 index, 660 BMessage *specifier, int32 form, const char *property) 661 { 662 return BControl::ResolveSpecifier(msg, index, specifier, form, property); 663 } 664 665 666 status_t 667 BColorControl::GetSupportedSuites(BMessage *data) 668 { 669 return BControl::GetSupportedSuites(data); 670 } 671 672 673 void 674 BColorControl::MakeFocus(bool state) 675 { 676 BControl::MakeFocus(state); 677 } 678 679 680 void 681 BColorControl::AllAttached() 682 { 683 BControl::AllAttached(); 684 } 685 686 687 void 688 BColorControl::AllDetached() 689 { 690 BControl::AllDetached(); 691 } 692 693 694 status_t 695 BColorControl::Perform(perform_code d, void *arg) 696 { 697 return BControl::Perform(d, arg); 698 } 699 700 701 void BColorControl::_ReservedColorControl1() {} 702 void BColorControl::_ReservedColorControl2() {} 703 void BColorControl::_ReservedColorControl3() {} 704 void BColorControl::_ReservedColorControl4() {} 705 706 707 BColorControl & 708 BColorControl::operator=(const BColorControl &) 709 { 710 return *this; 711 } 712