xref: /haiku/src/apps/icon-o-matic/generic/gui/panel/color_picker/ColorPickerView.cpp (revision 1011a95a7c535af3d8db639f395476fbbbbd49b8)
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