1/*
2 * Copyright 2002-2022 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		DarkWyrm, darkwyrm@earthlink.net
7 *		John Scipione, jscipione@gmail.com
8 */
9
10
11#include "ColorPreview.h"
12
13#include <algorithm>
14
15#include <stdio.h>
16
17#include <Bitmap.h>
18#include <Message.h>
19#include <MessageRunner.h>
20#include <String.h>
21#include <View.h>
22#include <Window.h>
23
24#include "defs.h"
25
26
27
28static const int32 kMsgMessageRunner = 'MsgR';
29
30
31//	#pragma mark - ColorPreview
32
33
34ColorPreview::ColorPreview(BMessage* message, uint32 flags)
35	:
36	BControl("ColorPreview", "", message, flags | B_WILL_DRAW),
37	fColor(ui_color(B_PANEL_BACKGROUND_COLOR)),
38	fDisabledColor((rgb_color){ 128, 128, 128 }),
39	fMessageRunner(NULL),
40	fIsRectangle(true)
41{
42	SetViewColor(B_TRANSPARENT_COLOR);
43	SetLowColor(0, 0, 0);
44	SetExplicitSize(BSize(StringWidth("M") * 8, StringWidth("M") * 7));
45}
46
47
48ColorPreview::~ColorPreview(void)
49{
50}
51
52
53void
54ColorPreview::Draw(BRect updateRect)
55{
56	rgb_color color;
57	if (IsEnabled())
58		color = fColor;
59	else
60		color = fDisabledColor;
61
62	if (fIsRectangle) {
63		if (IsEnabled()) {
64			rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR);
65			rgb_color shadow = tint_color(background, B_DARKEN_1_TINT);
66			rgb_color darkShadow = tint_color(background, B_DARKEN_3_TINT);
67			rgb_color light = tint_color(background, B_LIGHTEN_MAX_TINT);
68
69			BRect bounds(Bounds());
70
71			BeginLineArray(4);
72			AddLine(BPoint(bounds.left, bounds.bottom),
73			BPoint(bounds.left, bounds.top), shadow);
74			AddLine(BPoint(bounds.left + 1.0, bounds.top),
75			BPoint(bounds.right, bounds.top), shadow);
76			AddLine(BPoint(bounds.right, bounds.top + 1.0),
77			BPoint(bounds.right, bounds.bottom), light);
78			AddLine(BPoint(bounds.right - 1.0, bounds.bottom),
79			BPoint(bounds.left + 1.0, bounds.bottom), light);
80			EndLineArray();
81			bounds.InsetBy(1.0, 1.0);
82
83			BeginLineArray(4);
84			AddLine(BPoint(bounds.left, bounds.bottom),
85			BPoint(bounds.left, bounds.top), darkShadow);
86			AddLine(BPoint(bounds.left + 1.0, bounds.top),
87			BPoint(bounds.right, bounds.top), darkShadow);
88			AddLine(BPoint(bounds.right, bounds.top + 1.0),
89			BPoint(bounds.right, bounds.bottom), background);
90			AddLine(BPoint(bounds.right - 1.0, bounds.bottom),
91			BPoint(bounds.left + 1.0, bounds.bottom), background);
92			EndLineArray();
93			bounds.InsetBy(1.0, 1.0);
94
95			SetHighColor(color);
96			FillRect(bounds);
97		} else {
98			SetHighColor(color);
99			FillRect(Bounds());
100		}
101	} else {
102		// fill background
103		SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
104		FillRect(updateRect);
105
106		SetHighColor(color);
107		FillEllipse(Bounds());
108
109		if (IsEnabled())
110			StrokeEllipse(Bounds(), B_SOLID_LOW);
111	}
112}
113
114
115void
116ColorPreview::MessageReceived(BMessage* message)
117{
118	// If we received a dropped message, see if it contains color data
119	if (message->WasDropped()) {
120		rgb_color* color;
121		ssize_t size;
122		if (message->FindData(kRGBColor, B_RGB_COLOR_TYPE,
123				(const void**)&color, &size) == B_OK) {
124			BMessage setColorMessage(SET_CURRENT_COLOR);
125			setColorMessage.AddData(kRGBColor, B_RGB_COLOR_TYPE, color,
126				sizeof(rgb_color));
127			Invoke(&setColorMessage);
128		}
129	} else if ((int32)message->what == kMsgMessageRunner) {
130		BPoint where;
131		uint32 buttons;
132		GetMouse(&where, &buttons);
133
134		_DragColor(where);
135	}
136
137	BControl::MessageReceived(message);
138}
139
140
141void
142ColorPreview::MouseDown(BPoint where)
143{
144	BWindow* window = Window();
145	if (window != NULL)
146		window->Activate();
147
148	fMessageRunner = new BMessageRunner(this, new BMessage(kMsgMessageRunner),
149		300000, 1);
150
151	SetMouseEventMask(B_POINTER_EVENTS,
152		B_SUSPEND_VIEW_FOCUS | B_LOCK_WINDOW_FOCUS);
153
154	BRect rect = Bounds().InsetByCopy(2.0f, 2.0f);
155	rect.top = roundf(rect.bottom / 2.0f + 1);
156
157	if (rect.Contains(where)) {
158		Invalidate();
159		Invoke();
160	}
161
162	BControl::MouseDown(where);
163}
164
165
166void
167ColorPreview::MouseMoved(BPoint where, uint32 transit, const BMessage* message)
168{
169	if (fMessageRunner != NULL)
170		_DragColor(where);
171
172	BControl::MouseMoved(where, transit, message);
173}
174
175
176void
177ColorPreview::MouseUp(BPoint where)
178{
179	delete fMessageRunner;
180	fMessageRunner = NULL;
181
182	BControl::MouseUp(where);
183}
184
185
186rgb_color
187ColorPreview::Color(void) const
188{
189	return fColor;
190}
191
192
193void
194ColorPreview::SetColor(rgb_color color)
195{
196	color.alpha = 255;
197
198	SetHighColor(color);
199	fColor = color;
200
201	Invalidate();
202	Invoke();
203}
204
205
206void
207ColorPreview::SetColor(uint8 red, uint8 green, uint8 blue)
208{
209	SetHighColor(red, green, blue);
210	fColor.red = red;
211	fColor.green = green;
212	fColor.blue = blue;
213	fColor.alpha = 255;
214
215	Invalidate();
216	Invoke();
217}
218
219
220void
221ColorPreview::SetMode(bool rectangle)
222{
223	fIsRectangle = rectangle;
224}
225
226
227//	#pragma mark - ColorPreview private methods
228
229
230void
231ColorPreview::_DragColor(BPoint where)
232{
233	BString hexStr;
234	hexStr.SetToFormat("#%.2X%.2X%.2X", fColor.red, fColor.green, fColor.blue);
235
236	BMessage message(B_PASTE);
237	message.AddData("text/plain", B_MIME_TYPE, hexStr.String(),
238		hexStr.Length());
239	message.AddData(kRGBColor, B_RGB_COLOR_TYPE, &fColor, sizeof(fColor));
240
241	BRect rect(0.0f, 0.0f, 20.0f, 20.0f);
242
243	BBitmap* bitmap = new BBitmap(rect, B_RGB32, true);
244	if (bitmap->Lock()) {
245		BView* view = new BView(rect, "", B_FOLLOW_NONE, B_WILL_DRAW);
246		bitmap->AddChild(view);
247
248		view->SetHighColor(B_TRANSPARENT_COLOR);
249		view->FillRect(view->Bounds());
250
251		++rect.top;
252		++rect.left;
253
254		view->SetHighColor(0, 0, 0, 100);
255		view->FillRect(rect);
256		rect.OffsetBy(-1.0f, -1.0f);
257
258		view->SetHighColor(std::min(255, (int)(1.2 * fColor.red + 40)),
259			std::min(255, (int)(1.2 * fColor.green + 40)),
260			std::min(255, (int)(1.2 * fColor.blue + 40)));
261		view->StrokeRect(rect);
262
263		++rect.left;
264		++rect.top;
265
266		view->SetHighColor((int32)(0.8 * fColor.red),
267			(int32)(0.8 * fColor.green),
268			(int32)(0.8 * fColor.blue));
269		view->StrokeRect(rect);
270
271		--rect.right;
272		--rect.bottom;
273
274		view->SetHighColor(fColor.red, fColor.green, fColor.blue);
275		view->FillRect(rect);
276		view->Sync();
277
278		bitmap->Unlock();
279	}
280
281	DragMessage(&message, bitmap, B_OP_ALPHA, BPoint(14.0f, 14.0f));
282
283	MouseUp(where);
284}
285