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
ColorPickerView(const char * name,rgb_color color,SelectedColorMode mode)38 ColorPickerView::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
~ColorPickerView()58 ColorPickerView::~ColorPickerView()
59 {
60 }
61
62
63 void
AttachedToWindow()64 ColorPickerView::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
185 void
MessageReceived(BMessage * message)186 ColorPickerView::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
321 void
SetColorMode(SelectedColorMode mode,bool update)322 ColorPickerView::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
372 void
SetColor(rgb_color color)373 ColorPickerView::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
392 rgb_color
Color()393 ColorPickerView::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
410 int32
_NumForMode(SelectedColorMode mode) const411 ColorPickerView::_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
438 void
_UpdateColor(float value,float value1,float value2)439 ColorPickerView::_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
460 void
_UpdateTextControls()461 ColorPickerView::_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
485 int
_TextControlValue(int32 index)486 ColorPickerView::_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.
494 bool
_SetTextControlValue(int32 index,int value)495 ColorPickerView::_SetTextControlValue(int32 index, int value)
496 {
497 BString text;
498 text << value;
499 return _SetText(fTextControl[index], text);
500 }
501
502
503 BString
_HexTextControlString() const504 ColorPickerView::_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.
512 bool
_SetHexTextControlString(const BString & text)513 ColorPickerView::_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.
521 bool
_SetText(BTextControl * control,const BString & text)522 ColorPickerView::_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