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 <stdio.h> 10#include <stdlib.h> 11#include <string.h> 12 13#include <Application.h> 14#include <Bitmap.h> 15#include <Beep.h> 16#include <ControlLook.h> 17#include <GroupLayout.h> 18#include <LayoutBuilder.h> 19#include <Message.h> 20#include <MessageRunner.h> 21#include <RadioButton.h> 22#include <StringView.h> 23#include <TextControl.h> 24#include <Window.h> 25 26#include "ColorField.h" 27#include "ColorPreview.h" 28#include "ColorSlider.h" 29#include "rgb_hsv.h" 30 31#include "ColorPickerView.h" 32 33 34#define round(x) (int)(x+.5) 35#define hexdec(str, offset) (int)(((str[offset]<60?str[offset]-48:(str[offset]|32)-87)<<4)|(str[offset+1]<60?str[offset+1]-48:(str[offset+1]|32)-87)) 36 37 38ColorPickerView::ColorPickerView(const char* name, rgb_color color, 39 SelectedColorMode mode) 40 : 41 BView(name, 0), 42 h(0.0), 43 s(1.0), 44 v(1.0), 45 r((float)color.red / 255.0), 46 g((float)color.green / 255.0), 47 b((float)color.blue / 255.0), 48 fRequiresUpdate(false) 49{ 50 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 51 52 RGB_to_HSV(r, g, b, h, s, v); 53 54 SetColorMode(mode, false); 55} 56 57 58ColorPickerView::~ColorPickerView() 59{ 60} 61 62 63void 64ColorPickerView::AttachedToWindow() 65{ 66 rgb_color color = { (uint8)(r * 255), (uint8)(g * 255), (uint8)(b * 255), 67 255 }; 68 BView::AttachedToWindow(); 69 70 fColorField = new ColorField(fSelectedColorMode, *p); 71 fColorField->SetMarkerToColor(color); 72 fColorField->SetTarget(this); 73 74 fColorSlider = new ColorSlider(fSelectedColorMode, *p1, *p2); 75 fColorSlider->SetMarkerToColor(color); 76 fColorSlider->SetTarget(this); 77 78 fColorPreview = new ColorPreview(color); 79 fColorPreview->SetTarget(this); 80 81 fColorField->SetExplicitMinSize(BSize(256, 256)); 82 fColorField->SetExplicitMaxSize( 83 BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED)); 84 fColorSlider->SetExplicitMaxSize( 85 BSize(B_SIZE_UNSET, B_SIZE_UNLIMITED)); 86 fColorPreview->SetExplicitMinSize(BSize(B_SIZE_UNSET, 70)); 87 88 const char* title[] = { "H", "S", "V", "R", "G", "B" }; 89 90 int32 selectedRadioButton = _NumForMode(fSelectedColorMode); 91 92 for (int i = 0; i < 6; i++) { 93 fRadioButton[i] = new BRadioButton(NULL, title[i], 94 new BMessage(MSG_RADIOBUTTON + i)); 95 fRadioButton[i]->SetTarget(this); 96 97 if (i == selectedRadioButton) 98 fRadioButton[i]->SetValue(1); 99 100 fTextControl[i] = new BTextControl(NULL, NULL, NULL, 101 new BMessage(MSG_TEXTCONTROL + i)); 102 103 fTextControl[i]->TextView()->SetMaxBytes(3); 104 for (int j = 32; j < 255; ++j) { 105 if (j < '0' || j > '9') 106 fTextControl[i]->TextView()->DisallowChar(j); 107 } 108 } 109 110 fHexTextControl = new BTextControl(NULL, "#", NULL, 111 new BMessage(MSG_HEXTEXTCONTROL)); 112 113 fHexTextControl->TextView()->SetMaxBytes(6); 114 for (int j = 32; j < 255; ++j) { 115 if (!((j >= '0' && j <= '9') || (j >= 'a' && j <= 'f') 116 || (j >= 'A' && j <= 'F'))) { 117 fHexTextControl->TextView()->DisallowChar(j); 118 } 119 } 120 121 const float inset = be_control_look->DefaultLabelSpacing(); 122 BSize separatorSize(B_SIZE_UNSET, inset / 2); 123 BAlignment separatorAlignment(B_ALIGN_LEFT, B_ALIGN_TOP); 124 125 SetLayout(new BGroupLayout(B_HORIZONTAL)); 126 BLayoutBuilder::Group<>(this, B_HORIZONTAL) 127 .AddGroup(B_HORIZONTAL, 0.0f) 128 .Add(fColorField) 129 .SetInsets(3, 3, 0, 3) 130 .End() 131 .Add(fColorSlider) 132 .AddGroup(B_VERTICAL) 133 .Add(fColorPreview) 134 .AddGrid(inset / 2, inset / 2) 135 .Add(fRadioButton[0], 0, 0) 136 .Add(fTextControl[0], 1, 0) 137 .Add(new BStringView(NULL, "��"), 2, 0) 138 139 .Add(fRadioButton[1], 0, 1) 140 .Add(fTextControl[1], 1, 1) 141 .Add(new BStringView(NULL, "%"), 2, 1) 142 143 .Add(fRadioButton[2], 0, 2) 144 .Add(fTextControl[2], 1, 2) 145 .Add(new BStringView(NULL, "%"), 2, 2) 146 147 .Add(new BSpaceLayoutItem(separatorSize, separatorSize, 148 separatorSize, separatorAlignment), 149 0, 3, 3) 150 151 .Add(fRadioButton[3], 0, 4) 152 .Add(fTextControl[3], 1, 4) 153 154 .Add(fRadioButton[4], 0, 5) 155 .Add(fTextControl[4], 1, 5) 156 157 .Add(fRadioButton[5], 0, 6) 158 .Add(fTextControl[5], 1, 6) 159 160 .Add(new BSpaceLayoutItem(separatorSize, separatorSize, 161 separatorSize, separatorAlignment), 162 0, 7, 3) 163 164 .AddGroup(B_HORIZONTAL, 0.0f, 0, 8, 2) 165 .Add(fHexTextControl->CreateLabelLayoutItem()) 166 .Add(fHexTextControl->CreateTextViewLayoutItem()) 167 .End() 168 .End() 169 .SetInsets(0, 3, 3, 3) 170 .End() 171 .SetInsets(inset, inset, inset, inset) 172 ; 173 174 // After the views are attached, configure their target 175 for (int i = 0; i < 6; i++) { 176 fRadioButton[i]->SetTarget(this); 177 fTextControl[i]->SetTarget(this); 178 } 179 fHexTextControl->SetTarget(this); 180 181 _UpdateTextControls(); 182} 183 184 185void 186ColorPickerView::MessageReceived(BMessage *message) 187{ 188 switch (message->what) { 189 case MSG_UPDATE_COLOR_PICKER_VIEW: 190 if (fRequiresUpdate) 191 _UpdateTextControls(); 192 break; 193 194 case MSG_COLOR_FIELD: 195 { 196 float value1, value2; 197 value1 = message->FindFloat("value"); 198 value2 = message->FindFloat("value", 1); 199 _UpdateColor(-1, value1, value2); 200 fRequiresUpdate = true; 201 break; 202 } 203 204 case MSG_COLOR_SLIDER: 205 { 206 float value; 207 message->FindFloat("value", &value); 208 _UpdateColor(value, -1, -1); 209 fRequiresUpdate = true; 210 break; 211 } 212 213 case MSG_COLOR_PREVIEW: 214 { 215 rgb_color* color; 216 ssize_t numBytes; 217 if (message->FindData("color", B_RGB_COLOR_TYPE, 218 (const void**)&color, &numBytes) == B_OK) { 219 color->alpha = 255; 220 SetColor(*color); 221 } 222 break; 223 } 224 225 case MSG_RADIOBUTTON: 226 SetColorMode(H_SELECTED); 227 break; 228 229 case MSG_RADIOBUTTON + 1: 230 SetColorMode(S_SELECTED); 231 break; 232 233 case MSG_RADIOBUTTON + 2: 234 SetColorMode(V_SELECTED); 235 break; 236 237 case MSG_RADIOBUTTON + 3: 238 SetColorMode(R_SELECTED); 239 break; 240 241 case MSG_RADIOBUTTON + 4: 242 SetColorMode(G_SELECTED); 243 break; 244 245 case MSG_RADIOBUTTON + 5: 246 SetColorMode(B_SELECTED); 247 break; 248 249 case MSG_TEXTCONTROL: 250 case MSG_TEXTCONTROL + 1: 251 case MSG_TEXTCONTROL + 2: 252 case MSG_TEXTCONTROL + 3: 253 case MSG_TEXTCONTROL + 4: 254 case MSG_TEXTCONTROL + 5: 255 { 256 int nr = message->what - MSG_TEXTCONTROL; 257 int value = atoi(fTextControl[nr]->Text()); 258 259 switch (nr) { 260 case 0: { 261 value %= 360; 262 h = (float)value / 60; 263 } break; 264 265 case 1: { 266 value = min_c(value, 100); 267 s = (float)value / 100; 268 } break; 269 270 case 2: { 271 value = min_c(value, 100); 272 v = (float)value / 100; 273 } break; 274 275 case 3: { 276 value = min_c(value, 255); 277 r = (float)value / 255; 278 } break; 279 280 case 4: { 281 value = min_c(value, 255); 282 g = (float)value / 255; 283 } break; 284 285 case 5: { 286 value = min_c(value, 255); 287 b = (float)value / 255; 288 } break; 289 } 290 291 if (nr < 3) { // hsv-mode 292 HSV_to_RGB(h, s, v, r, g, b); 293 } 294 295 rgb_color color = { (uint8)round(r * 255), (uint8)round(g * 255), 296 (uint8)round(b * 255), 255 }; 297 298 SetColor(color); 299 break; 300 } 301 302 case MSG_HEXTEXTCONTROL: 303 { 304 BString string = _HexTextControlString(); 305 if (string.Length() == 6) { 306 rgb_color color = { 307 (uint8)hexdec(string, 0), 308 (uint8)hexdec(string, 2), 309 (uint8)hexdec(string, 4), 255 }; 310 SetColor(color); 311 } 312 break; 313 } 314 315 default: 316 BView::MessageReceived(message); 317 } 318} 319 320 321void 322ColorPickerView::SetColorMode(SelectedColorMode mode, bool update) 323{ 324 fSelectedColorMode = mode; 325 switch (mode) { 326 case R_SELECTED: 327 p = &r; 328 p1 = &g; 329 p2 = &b; 330 break; 331 332 case G_SELECTED: 333 p = &g; 334 p1 = &r; 335 p2 = &b; 336 break; 337 338 case B_SELECTED: 339 p = &b; 340 p1 = &r; 341 p2 = &g; 342 break; 343 344 case H_SELECTED: 345 p = &h; 346 p1 = &s; 347 p2 = &v; 348 break; 349 350 case S_SELECTED: 351 p = &s; 352 p1 = &h; 353 p2 = &v; 354 break; 355 356 case V_SELECTED: 357 p = &v; 358 p1 = &h; 359 p2 = &s; 360 break; 361 } 362 363 if (!update) 364 return; 365 366 fColorSlider->SetModeAndValues(fSelectedColorMode, *p1, *p2); 367 fColorField->SetModeAndValue(fSelectedColorMode, *p); 368 369} 370 371// SetColor 372void 373ColorPickerView::SetColor(rgb_color color) 374{ 375 r = (float)color.red / 255; 376 g = (float)color.green / 255; 377 b = (float)color.blue / 255; 378 RGB_to_HSV(r, g, b, h, s, v); 379 380 fColorSlider->SetModeAndValues(fSelectedColorMode, *p1, *p2); 381 fColorSlider->SetMarkerToColor(color); 382 383 fColorField->SetModeAndValue(fSelectedColorMode, *p); 384 fColorField->SetMarkerToColor(color); 385 386 fColorPreview->SetColor(color); 387 388 _UpdateTextControls(); 389} 390 391 392rgb_color 393ColorPickerView::Color() 394{ 395 if (fSelectedColorMode & (R_SELECTED | G_SELECTED | B_SELECTED)) 396 RGB_to_HSV(r, g, b, h, s, v); 397 else 398 HSV_to_RGB(h, s, v, r, g, b); 399 400 rgb_color color; 401 color.red = (uint8)round(r * 255.0); 402 color.green = (uint8)round(g * 255.0); 403 color.blue = (uint8)round(b * 255.0); 404 color.alpha = 255; 405 406 return color; 407} 408 409 410int32 411ColorPickerView::_NumForMode(SelectedColorMode mode) const 412{ 413 int32 num = -1; 414 switch (mode) { 415 case H_SELECTED: 416 num = 0; 417 break; 418 case S_SELECTED: 419 num = 1; 420 break; 421 case V_SELECTED: 422 num = 2; 423 break; 424 case R_SELECTED: 425 num = 3; 426 break; 427 case G_SELECTED: 428 num = 4; 429 break; 430 case B_SELECTED: 431 num = 5; 432 break; 433 } 434 return num; 435} 436 437 438void 439ColorPickerView::_UpdateColor(float value, float value1, float value2) 440{ 441 if (value != -1) { 442 fColorField->SetFixedValue(value); 443 *p = value; 444 } else if (value1 != -1 && value2 != -1) { 445 fColorSlider->SetOtherValues(value1, value2); 446 *p1 = value1; *p2 = value2; 447 } 448 449 if (fSelectedColorMode & (R_SELECTED|G_SELECTED|B_SELECTED)) 450 RGB_to_HSV(r, g, b, h, s, v); 451 else 452 HSV_to_RGB(h, s, v, r, g, b); 453 454 rgb_color color = { (uint8)(r * 255), (uint8)(g * 255), 455 (uint8)(b * 255), 255 }; 456 fColorPreview->SetColor(color); 457} 458 459 460void 461ColorPickerView::_UpdateTextControls() 462{ 463 bool updateRequired = false; 464 updateRequired |= _SetTextControlValue(0, round(h * 60)); 465 updateRequired |= _SetTextControlValue(1, round(s * 100)); 466 updateRequired |= _SetTextControlValue(2, round(v * 100)); 467 updateRequired |= _SetTextControlValue(3, round(r * 255)); 468 updateRequired |= _SetTextControlValue(4, round(g * 255)); 469 updateRequired |= _SetTextControlValue(5, round(b * 255)); 470 471 BString hexString; 472 hexString.SetToFormat("%.6X", 473 (round(r * 255) << 16) | (round(g * 255) << 8) | round(b * 255)); 474 updateRequired |= _SetHexTextControlString(hexString); 475 476 fRequiresUpdate = updateRequired; 477 if (fRequiresUpdate) { 478 // Couldn't set all values. Try again later. 479 BMessage message(MSG_UPDATE_COLOR_PICKER_VIEW); 480 BMessageRunner::StartSending(this, &message, 500000, 1); 481 } 482} 483 484 485int 486ColorPickerView::_TextControlValue(int32 index) 487{ 488 return atoi(fTextControl[index]->Text()); 489} 490 491 492// Returns whether the value needs to be set later, since it is currently 493// being edited by the user. 494bool 495ColorPickerView::_SetTextControlValue(int32 index, int value) 496{ 497 BString text; 498 text << value; 499 return _SetText(fTextControl[index], text); 500} 501 502 503BString 504ColorPickerView::_HexTextControlString() const 505{ 506 return fHexTextControl->TextView()->Text(); 507} 508 509 510// Returns whether the value needs to be set later, since it is currently 511// being edited by the user. 512bool 513ColorPickerView::_SetHexTextControlString(const BString& text) 514{ 515 return _SetText(fHexTextControl, text); 516} 517 518 519// Returns whether the value needs to be set later, since it is currently 520// being edited by the user. 521bool 522ColorPickerView::_SetText(BTextControl* control, const BString& text) 523{ 524 if (text == control->Text()) 525 return false; 526 527 // This textview needs updating, but don't screw with user while she is 528 // typing. 529 if (control->TextView()->IsFocus()) 530 return true; 531 532 control->SetText(text.String()); 533 return false; 534} 535 536