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::SetLayout(BLayout* layout) 205 { 206 // We need to implement this method, since we have another SetLayout() 207 // method and C++ has this special method hiding "feature". 208 BControl::SetLayout(layout); 209 } 210 211 212 void 213 BColorControl::SetValue(int32 value) 214 { 215 if (Value() == value) 216 return; 217 218 rgb_color c1 = ValueAsColor(); 219 rgb_color c2; 220 c2.red = (value & 0xFF000000) >> 24; 221 c2.green = (value & 0x00FF0000) >> 16; 222 c2.blue = (value & 0x0000FF00) >> 8; 223 char string[4]; 224 225 if (c1.red != c2.red) { 226 sprintf(string, "%d", c2.red); 227 fRedText->SetText(string); 228 } 229 230 if (c1.green != c2.green) { 231 sprintf(string, "%d", c2.green); 232 fGreenText->SetText(string); 233 } 234 235 if (c1.blue != c2.blue) { 236 sprintf(string, "%d", c2.blue); 237 fBlueText->SetText(string); 238 } 239 240 BControl::SetValueNoUpdate(value); 241 242 // TODO: This causes lot of flickering 243 Invalidate(); 244 245 if (LockLooper()) { 246 Window()->UpdateIfNeeded(); 247 UnlockLooper(); 248 } 249 } 250 251 252 rgb_color 253 BColorControl::ValueAsColor() 254 { 255 int32 value = Value(); 256 rgb_color color; 257 258 color.red = (value & 0xFF000000) >> 24; 259 color.green = (value & 0x00FF0000) >> 16; 260 color.blue = (value & 0x0000FF00) >> 8; 261 color.alpha = 255; 262 263 return color; 264 } 265 266 267 void 268 BColorControl::SetEnabled(bool enabled) 269 { 270 BControl::SetEnabled(enabled); 271 272 fRedText->SetEnabled(enabled); 273 fGreenText->SetEnabled(enabled); 274 fBlueText->SetEnabled(enabled); 275 } 276 277 278 void 279 BColorControl::AttachedToWindow() 280 { 281 if (Parent()) 282 SetViewColor(Parent()->ViewColor()); 283 else 284 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 285 286 BControl::AttachedToWindow(); 287 288 fRedText->SetTarget(this); 289 fGreenText->SetTarget(this); 290 fBlueText->SetTarget(this); 291 } 292 293 294 void 295 BColorControl::MessageReceived(BMessage *message) 296 { 297 switch (message->what) { 298 case kMsgColorEntered: 299 { 300 rgb_color color; 301 color.red = strtol(fRedText->Text(), NULL, 10); 302 color.green = strtol(fGreenText->Text(), NULL, 10); 303 color.blue = strtol(fBlueText->Text(), NULL, 10); 304 color.alpha = 255; 305 306 SetValue(color); 307 Invoke(); 308 break; 309 } 310 default: 311 BControl::MessageReceived(message); 312 } 313 } 314 315 316 void 317 BColorControl::Draw(BRect updateRect) 318 { 319 if (fBitmap) { 320 if (!fBitmap->Lock()) 321 return; 322 323 if (fOffscreenView->Bounds().Intersects(updateRect)) { 324 _UpdateOffscreen(updateRect); 325 BRegion region(updateRect); 326 ConstrainClippingRegion(®ion); 327 DrawBitmap(fBitmap, B_ORIGIN); 328 ConstrainClippingRegion(NULL); 329 } 330 331 fBitmap->Unlock(); 332 } else 333 _DrawColorArea(this, updateRect); 334 } 335 336 337 void 338 BColorControl::_DrawColorArea(BView* target, BRect update) 339 { 340 BRegion region(update); 341 target->ConstrainClippingRegion(®ion); 342 343 BRect rect(0.0f, 0.0f, ceil(fColumns * fCellSize), Bounds().bottom); 344 345 rgb_color noTint = ui_color(B_PANEL_BACKGROUND_COLOR), 346 lightenmax = tint_color(noTint, B_LIGHTEN_MAX_TINT), 347 darken1 = tint_color(noTint, B_DARKEN_1_TINT), 348 darken4 = tint_color(noTint, B_DARKEN_4_TINT); 349 350 // First bevel 351 target->SetHighColor(darken1); 352 target->StrokeLine(rect.LeftBottom(), rect.LeftTop()); 353 target->StrokeLine(rect.LeftTop(), rect.RightTop()); 354 target->SetHighColor(lightenmax); 355 target->StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), rect.RightBottom()); 356 target->StrokeLine(rect.RightBottom(), BPoint(rect.right, rect.top + 1.0f)); 357 358 rect.InsetBy(1.0f, 1.0f); 359 360 // Second bevel 361 target->SetHighColor(darken4); 362 target->StrokeLine(rect.LeftBottom(), rect.LeftTop()); 363 target->StrokeLine(rect.LeftTop(), rect.RightTop()); 364 target->SetHighColor(noTint); 365 target->StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), rect.RightBottom()); 366 target->StrokeLine(rect.RightBottom(), BPoint(rect.right, rect.top + 1.0f)); 367 368 // Ramps 369 rgb_color white = {255, 255, 255, 255}; 370 rgb_color red = {255, 0, 0, 255}; 371 rgb_color green = {0, 255, 0, 255}; 372 rgb_color blue = {0, 0, 255, 255}; 373 374 rect.InsetBy(1.0f, 1.0f); 375 376 BRect rampRect(rect); 377 float rampSize = rampRect.Height() / 4.0; 378 379 rampRect.bottom = rampRect.top + rampSize; 380 381 _ColorRamp(rampRect, target, white, 0, false); 382 383 rampRect.OffsetBy(0, rampSize); 384 _ColorRamp(rampRect, target, red, 0, false); 385 386 rampRect.OffsetBy(0,rampSize); 387 _ColorRamp(rampRect, target, green, 0, false); 388 389 rampRect.OffsetBy(0, rampSize); 390 _ColorRamp(rampRect, target, blue, 0, false); 391 392 // Selectors 393 rgb_color color = ValueAsColor(); 394 float x, y = rampSize * 1.5; 395 396 target->SetDrawingMode(B_OP_OVER); 397 target->SetPenSize(2.0f); 398 399 x = rect.left + color.red * (rect.Width() - 7) / 255; 400 target->SetHighColor(255, 255, 255); 401 target->StrokeEllipse(BRect(x, y, x + 4.0f, y + 4.0f)); 402 403 y += rampSize; 404 405 x = rect.left + color.green * (rect.Width() - 7) / 255; 406 target->SetHighColor(255, 255, 255); 407 target->StrokeEllipse(BRect(x, y, x + 4.0f, y + 4.0f)); 408 409 y += rampSize; 410 411 x = rect.left + color.blue * (rect.Width() - 7) / 255; 412 target->SetHighColor(255, 255, 255); 413 target->StrokeEllipse(BRect(x, y, x + 4.0f, y + 4.0f)); 414 415 target->SetPenSize(1.0f); 416 target->SetDrawingMode(B_OP_COPY); 417 418 target->ConstrainClippingRegion(NULL); 419 } 420 421 422 void 423 BColorControl::_ColorRamp(BRect rect, BView* target, 424 rgb_color baseColor, int16 flag, bool focused) 425 { 426 float width = rect.Width()+1; 427 rgb_color color; 428 color.alpha = 255; 429 430 target->BeginLineArray((int32)width); 431 432 for (float i = 0; i <= width; i++) { 433 color.red = (uint8)(i * baseColor.red / width); 434 color.green = (uint8)(i * baseColor.green / width); 435 color.blue = (uint8)(i * baseColor.blue / width); 436 437 target->AddLine(BPoint(rect.left + i, rect.top), 438 BPoint(rect.left + i, rect.bottom), color); 439 } 440 441 target->EndLineArray(); 442 } 443 444 445 void 446 BColorControl::_UpdateOffscreen(BRect update) 447 { 448 if (fBitmap->Lock()) { 449 update = update & fOffscreenView->Bounds(); 450 fOffscreenView->FillRect(update); 451 _DrawColorArea(fOffscreenView, update); 452 fOffscreenView->Sync(); 453 fBitmap->Unlock(); 454 } 455 } 456 457 458 void 459 BColorControl::SetCellSize(float cellSide) 460 { 461 fCellSize = max_c(kMinCellSize, cellSide); 462 463 ResizeToPreferred(); 464 } 465 466 467 float 468 BColorControl::CellSize() const 469 { 470 return fCellSize; 471 } 472 473 474 void 475 BColorControl::SetLayout(color_control_layout layout) 476 { 477 switch (layout) { 478 case B_CELLS_4x64: 479 fColumns = 4; 480 fRows = 64; 481 break; 482 case B_CELLS_8x32: 483 fColumns = 8; 484 fRows = 32; 485 break; 486 case B_CELLS_16x16: 487 fColumns = 16; 488 fRows = 16; 489 break; 490 case B_CELLS_32x8: 491 fColumns = 32; 492 fRows = 8; 493 break; 494 case B_CELLS_64x4: 495 fColumns = 64; 496 fRows = 4; 497 break; 498 } 499 500 ResizeToPreferred(); 501 Invalidate(); 502 } 503 504 505 color_control_layout 506 BColorControl::Layout() const 507 { 508 if (fColumns == 4 && fRows == 64) 509 return B_CELLS_4x64; 510 if (fColumns == 8 && fRows == 32) 511 return B_CELLS_8x32; 512 if (fColumns == 16 && fRows == 16) 513 return B_CELLS_16x16; 514 if (fColumns == 32 && fRows == 8) 515 return B_CELLS_32x8; 516 if (fColumns == 64 && fRows == 4) 517 return B_CELLS_64x4; 518 519 return B_CELLS_32x8; 520 } 521 522 523 void 524 BColorControl::WindowActivated(bool state) 525 { 526 BControl::WindowActivated(state); 527 } 528 529 530 void 531 BColorControl::KeyDown(const char* bytes, int32 numBytes) 532 { 533 // TODO: make this keyboard navigable! 534 BControl::KeyDown(bytes, numBytes); 535 } 536 537 538 void 539 BColorControl::MouseUp(BPoint point) 540 { 541 fFocusedComponent = 0; 542 SetTracking(false); 543 } 544 545 546 void 547 BColorControl::MouseDown(BPoint point) 548 { 549 BRect rect(0.0f, 0.0f, fColumns * fCellSize, Bounds().bottom); 550 if (!rect.Contains(point)) 551 return; 552 553 rgb_color color = ValueAsColor(); 554 555 float rampsize = rect.bottom / 4; 556 557 uint8 shade = (unsigned char)max_c(0, 558 min_c((point.x - 2) * 255 / (rect.Width() - 4.0f), 255)); 559 560 if (point.y - 2 < rampsize) { 561 color.red = color.green = color.blue = shade; 562 fFocusedComponent = 1; 563 } else if (point.y - 2 < (rampsize * 2)) { 564 color.red = shade; 565 fFocusedComponent = 2; 566 } else if (point.y - 2 < (rampsize * 3)) { 567 color.green = shade; 568 fFocusedComponent = 3; 569 } else { 570 color.blue = shade; 571 fFocusedComponent = 4; 572 } 573 574 SetValue(color); 575 Invoke(); 576 577 SetTracking(true); 578 MakeFocus(); 579 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS); 580 } 581 582 583 void 584 BColorControl::MouseMoved(BPoint point, uint32 transit, 585 const BMessage *message) 586 { 587 if (fFocusedComponent == 0 || !IsTracking()) 588 return; 589 590 rgb_color color = ValueAsColor(); 591 592 BRect rect(0.0f, 0.0f, fColumns * fCellSize, Bounds().bottom); 593 594 uint8 shade = (unsigned char)max_c(0, 595 min_c((point.x - 2) * 255 / (rect.Width() - 4.0f), 255)); 596 597 switch (fFocusedComponent) { 598 case 1: 599 color.red = color.green = color.blue = shade; 600 break; 601 case 2: 602 color.red = shade; 603 break; 604 case 3: 605 color.green = shade; 606 break; 607 case 4: 608 color.blue = shade; 609 break; 610 default: 611 break; 612 } 613 614 SetValue(color); 615 Invoke(); 616 } 617 618 619 void 620 BColorControl::DetachedFromWindow() 621 { 622 BControl::DetachedFromWindow(); 623 } 624 625 626 void 627 BColorControl::GetPreferredSize(float *_width, float *_height) 628 { 629 if (_width) 630 *_width = fColumns * fCellSize + 4.0f + fRedText->Bounds().Width(); 631 632 if (_height) 633 *_height = fBlueText->Frame().bottom; 634 } 635 636 637 void 638 BColorControl::ResizeToPreferred() 639 { 640 BControl::ResizeToPreferred(); 641 642 _LayoutView(); 643 } 644 645 646 status_t 647 BColorControl::Invoke(BMessage *msg) 648 { 649 return BControl::Invoke(msg); 650 } 651 652 653 void 654 BColorControl::FrameMoved(BPoint new_position) 655 { 656 BControl::FrameMoved(new_position); 657 } 658 659 660 void 661 BColorControl::FrameResized(float new_width, float new_height) 662 { 663 BControl::FrameResized(new_width, new_height); 664 } 665 666 667 BHandler * 668 BColorControl::ResolveSpecifier(BMessage *msg, int32 index, 669 BMessage *specifier, int32 form, const char *property) 670 { 671 return BControl::ResolveSpecifier(msg, index, specifier, form, property); 672 } 673 674 675 status_t 676 BColorControl::GetSupportedSuites(BMessage *data) 677 { 678 return BControl::GetSupportedSuites(data); 679 } 680 681 682 void 683 BColorControl::MakeFocus(bool state) 684 { 685 BControl::MakeFocus(state); 686 } 687 688 689 void 690 BColorControl::AllAttached() 691 { 692 BControl::AllAttached(); 693 } 694 695 696 void 697 BColorControl::AllDetached() 698 { 699 BControl::AllDetached(); 700 } 701 702 703 status_t 704 BColorControl::Perform(perform_code d, void *arg) 705 { 706 return BControl::Perform(d, arg); 707 } 708 709 710 void BColorControl::_ReservedColorControl1() {} 711 void BColorControl::_ReservedColorControl2() {} 712 void BColorControl::_ReservedColorControl3() {} 713 void BColorControl::_ReservedColorControl4() {} 714 715 716 BColorControl & 717 BColorControl::operator=(const BColorControl &) 718 { 719 return *this; 720 } 721