1/*
2 * Copyright 2006-2011, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Stephan A��mus <superstippi@gmx.de>
7 */
8
9#include "StyleView.h"
10
11#include <new>
12
13#include <Catalog.h>
14#include <GridLayout.h>
15#include <GroupLayout.h>
16#include <Locale.h>
17#include <Menu.h>
18#include <MenuBar.h>
19#include <MenuField.h>
20#include <MenuItem.h>
21#include <Message.h>
22#include <PopUpMenu.h>
23#include <SpaceLayoutItem.h>
24#include <Window.h>
25
26#include "CommandStack.h"
27#include "CurrentColor.h"
28#include "GradientTransformable.h"
29#include "GradientControl.h"
30#include "SetColorCommand.h"
31#include "SetGradientCommand.h"
32#include "Style.h"
33
34
35#undef B_TRANSLATION_CONTEXT
36#define B_TRANSLATION_CONTEXT "Icon-O-Matic-StyleTypes"
37
38
39using std::nothrow;
40
41enum {
42	MSG_SET_COLOR			= 'stcl',
43
44	MSG_SET_STYLE_TYPE		= 'stst',
45	MSG_SET_GRADIENT_TYPE	= 'stgt',
46};
47
48enum {
49	STYLE_TYPE_COLOR		= 0,
50	STYLE_TYPE_GRADIENT,
51};
52
53
54StyleView::StyleView(BRect frame)
55	:
56	BView("style view", 0),
57	fCommandStack(NULL),
58	fCurrentColor(NULL),
59	fStyle(NULL),
60	fGradient(NULL),
61	fIgnoreCurrentColorNotifications(false),
62	fIgnoreControlGradientNotifications(false),
63	fPreviousBounds(frame.OffsetToCopy(B_ORIGIN))
64{
65	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
66
67	// style type
68	BMenu* menu = new BPopUpMenu(B_TRANSLATE("<unavailable>"));
69	BMessage* message = new BMessage(MSG_SET_STYLE_TYPE);
70	message->AddInt32("type", STYLE_TYPE_COLOR);
71	menu->AddItem(new BMenuItem(B_TRANSLATE("Color"), message));
72	message = new BMessage(MSG_SET_STYLE_TYPE);
73	message->AddInt32("type", STYLE_TYPE_GRADIENT);
74	menu->AddItem(new BMenuItem(B_TRANSLATE("Gradient"), message));
75
76	BGridLayout* layout = new BGridLayout(5, 5);
77	SetLayout(layout);
78
79	fStyleType = new BMenuField(B_TRANSLATE("Style type"), menu);
80
81	// gradient type
82	menu = new BPopUpMenu(B_TRANSLATE("<unavailable>"));
83	message = new BMessage(MSG_SET_GRADIENT_TYPE);
84	message->AddInt32("type", GRADIENT_LINEAR);
85	menu->AddItem(new BMenuItem(B_TRANSLATE("Linear"), message));
86	message = new BMessage(MSG_SET_GRADIENT_TYPE);
87	message->AddInt32("type", GRADIENT_CIRCULAR);
88	menu->AddItem(new BMenuItem(B_TRANSLATE("Radial"), message));
89	message = new BMessage(MSG_SET_GRADIENT_TYPE);
90	message->AddInt32("type", GRADIENT_DIAMOND);
91	menu->AddItem(new BMenuItem(B_TRANSLATE("Diamond"), message));
92	message = new BMessage(MSG_SET_GRADIENT_TYPE);
93	message->AddInt32("type", GRADIENT_CONIC);
94	menu->AddItem(new BMenuItem(B_TRANSLATE("Conic"), message));
95
96	fGradientType = new BMenuField(B_TRANSLATE("Gradient type"), menu);
97	fGradientControl = new GradientControl(new BMessage(MSG_SET_COLOR), this);
98
99	layout->AddItem(BSpaceLayoutItem::CreateVerticalStrut(3), 0, 0, 4);
100	layout->AddItem(BSpaceLayoutItem::CreateHorizontalStrut(3), 0, 1, 1, 3);
101
102	layout->AddItem(fStyleType->CreateLabelLayoutItem(), 1, 1);
103	layout->AddItem(fStyleType->CreateMenuBarLayoutItem(), 2, 1);
104
105	layout->AddItem(fGradientType->CreateLabelLayoutItem(), 1, 2);
106	layout->AddItem(fGradientType->CreateMenuBarLayoutItem(), 2, 2);
107
108	layout->AddView(fGradientControl, 1, 3, 2);
109
110	layout->AddItem(BSpaceLayoutItem::CreateHorizontalStrut(3), 3, 1, 1, 3);
111	layout->AddItem(BSpaceLayoutItem::CreateVerticalStrut(3), 0, 4, 4);
112
113	fStyleType->SetEnabled(false);
114	fGradientType->SetEnabled(false);
115	fGradientControl->SetEnabled(false);
116	fGradientControl->Gradient()->AddObserver(this);
117}
118
119
120StyleView::~StyleView()
121{
122	SetStyle(NULL);
123	SetCurrentColor(NULL);
124	fGradientControl->Gradient()->RemoveObserver(this);
125
126	if (fGradient.IsSet()) {
127		fGradient->RemoveObserver(this);
128	}
129}
130
131
132void
133StyleView::AttachedToWindow()
134{
135	fStyleType->Menu()->SetTargetForItems(this);
136	fGradientType->Menu()->SetTargetForItems(this);
137}
138
139
140void
141StyleView::FrameResized(float width, float height)
142{
143	fPreviousBounds = Bounds();
144}
145
146
147void
148StyleView::MessageReceived(BMessage* message)
149{
150	switch (message->what) {
151		case MSG_SET_STYLE_TYPE:
152		{
153			int32 type;
154			if (message->FindInt32("type", &type) == B_OK)
155				_SetStyleType(type);
156			break;
157		}
158		case MSG_SET_GRADIENT_TYPE:
159		{
160			int32 type;
161			if (message->FindInt32("type", &type) == B_OK)
162				_SetGradientType(type);
163			break;
164		}
165		case MSG_SET_COLOR:
166		case MSG_GRADIENT_CONTROL_FOCUS_CHANGED:
167			_TransferGradientStopColor();
168			break;
169
170		default:
171			BView::MessageReceived(message);
172			break;
173	}
174}
175
176
177BSize
178StyleView::MinSize()
179{
180	BSize minSize = BView::MinSize();
181	minSize.width = fGradientControl->MinSize().width + 10;
182	return minSize;
183}
184
185
186// #pragma mark -
187
188
189void
190StyleView::ObjectChanged(const Observable* object)
191{
192	if (!fStyle)
193		return;
194
195	Gradient* controlGradient = fGradientControl->Gradient();
196
197	// NOTE: it is important to compare the gradients
198	// before assignment, or we will get into an endless loop
199	if (object == controlGradient) {
200		if (!fGradient.IsSet())
201			return;
202		if (fIgnoreControlGradientNotifications)
203			return;
204		fIgnoreControlGradientNotifications = true;
205
206		if (!fGradient->ColorStepsAreEqual(*controlGradient)) {
207			// Make sure we never apply the transformation from the control
208			// gradient to the style gradient. Setting this here would cause to
209			// re-enter ObjectChanged(), which is prevented to cause harm via
210			// fIgnoreControlGradientNotifications.
211			controlGradient->SetTransform(*fGradient);
212			if (fCommandStack) {
213				fCommandStack->Perform(
214					new (nothrow) SetGradientCommand(
215						fStyle, controlGradient));
216			} else {
217				*fGradient = *controlGradient;
218			}
219			// transfer the current gradient color to the current color
220			_TransferGradientStopColor();
221		}
222
223		fIgnoreControlGradientNotifications = false;
224	} else if (object == fGradient) {
225		if (!fGradient->ColorStepsAreEqual(*controlGradient)) {
226			fGradientControl->SetGradient(fGradient);
227			_MarkType(fGradientType->Menu(), fGradient->Type());
228			// transfer the current gradient color to the current color
229			_TransferGradientStopColor();
230		}
231	} else if (object == fStyle) {
232		// maybe the gradient was added or removed
233		// or the color changed
234		_SetGradient(fStyle->Gradient(), false, true);
235		if (fCurrentColor && !fStyle->Gradient())
236			fCurrentColor->SetColor(fStyle->Color());
237	} else if (object == fCurrentColor) {
238		// NOTE: because of imprecisions in converting
239		// RGB<->HSV, the current color can be different
240		// even if we just set it to the current
241		// gradient stop color, that's why we ignore
242		// notifications caused by this situation
243		if (!fIgnoreCurrentColorNotifications)
244			_AdoptCurrentColor(fCurrentColor->Color());
245	}
246}
247
248
249// #pragma mark -
250
251
252void
253StyleView::SetStyle(Style* style)
254{
255	if (fStyle == style)
256		return;
257
258	if (fStyle) {
259		fStyle->RemoveObserver(this);
260		fStyle->ReleaseReference();
261	}
262
263	fStyle = style;
264
265	Gradient* gradient = NULL;
266	if (fStyle) {
267		fStyle->AcquireReference();
268		fStyle->AddObserver(this);
269		gradient = fStyle->Gradient();
270
271		if (fCurrentColor && !gradient)
272			fCurrentColor->SetColor(fStyle->Color());
273
274		fStyleType->SetEnabled(true);
275	} else
276		fStyleType->SetEnabled(false);
277
278	_SetGradient(gradient, true);
279}
280
281
282void
283StyleView::SetCommandStack(CommandStack* stack)
284{
285	fCommandStack = stack;
286}
287
288
289void
290StyleView::SetCurrentColor(CurrentColor* color)
291{
292	if (fCurrentColor == color)
293		return;
294
295	if (fCurrentColor)
296		fCurrentColor->RemoveObserver(this);
297
298	fCurrentColor = color;
299
300	if (fCurrentColor)
301		fCurrentColor->AddObserver(this);
302}
303
304
305// #pragma mark -
306
307
308void
309StyleView::_SetGradient(Gradient* gradient, bool forceControlUpdate,
310	bool sendMessage)
311{
312	if (!forceControlUpdate && gradient == fGradient)
313		return;
314
315	if (fGradient.IsSet())
316		fGradient->RemoveObserver(this);
317
318	fGradient.SetTo(gradient);
319
320	if (fGradient.IsSet()) {
321		fGradient->AddObserver(this);
322	}
323
324	if (fGradient.IsSet()) {
325		fGradientControl->SetEnabled(true);
326		fGradientControl->SetGradient(fGradient);
327		fGradientType->SetEnabled(true);
328		_MarkType(fStyleType->Menu(), STYLE_TYPE_GRADIENT);
329		_MarkType(fGradientType->Menu(), fGradient->Type());
330	} else {
331		fGradientControl->SetEnabled(false);
332		fGradientType->SetEnabled(false);
333		_MarkType(fStyleType->Menu(), STYLE_TYPE_COLOR);
334		_MarkType(fGradientType->Menu(), -1);
335	}
336
337	if (sendMessage) {
338		BMessage message(MSG_STYLE_TYPE_CHANGED);
339		message.AddPointer("style", fStyle);
340		Window()->PostMessage(&message);
341	}
342}
343
344
345void
346StyleView::_MarkType(BMenu* menu, int32 type) const
347{
348	for (int32 i = 0; BMenuItem* item = menu->ItemAt(i); i++) {
349		BMessage* message = item->Message();
350		int32 t;
351		if (message->FindInt32("type", &t) == B_OK && t == type) {
352			if (!item->IsMarked())
353				item->SetMarked(true);
354			return;
355		}
356	}
357}
358
359
360void
361StyleView::_SetStyleType(int32 type)
362{
363	if (!fStyle)
364		return;
365
366	if (type == STYLE_TYPE_COLOR) {
367		if (fCommandStack) {
368			fCommandStack->Perform(
369				new (nothrow) SetGradientCommand(fStyle, NULL));
370		} else {
371			fStyle->SetGradient(NULL);
372		}
373	} else if (type == STYLE_TYPE_GRADIENT) {
374		if (fCommandStack) {
375			Gradient gradient(true);
376			gradient.AddColor(fStyle->Color(), 0);
377			gradient.AddColor(fStyle->Color(), 1);
378			fCommandStack->Perform(
379				new (nothrow) SetGradientCommand(fStyle, &gradient));
380		} else {
381			fStyle->SetGradient(fGradientControl->Gradient());
382		}
383	}
384}
385
386
387void
388StyleView::_SetGradientType(int32 type)
389{
390	fGradientControl->Gradient()->SetType((gradients_type)type);
391}
392
393
394void
395StyleView::_AdoptCurrentColor(rgb_color color)
396{
397	if (!fStyle)
398		return;
399
400	if (fGradient.IsSet()) {
401		// set the focused gradient color stop
402		if (fGradientControl->IsFocus()) {
403			fGradientControl->SetCurrentStop(color);
404		}
405	} else {
406		if (fCommandStack) {
407			fCommandStack->Perform(
408				new (nothrow) SetColorCommand(fStyle, color));
409		} else {
410			fStyle->SetColor(color);
411		}
412	}
413}
414
415
416void
417StyleView::_TransferGradientStopColor()
418{
419	if (fCurrentColor && fGradientControl->IsFocus()) {
420		rgb_color color;
421		if (fGradientControl->GetCurrentStop(&color)) {
422			fIgnoreCurrentColorNotifications = true;
423			fCurrentColor->SetColor(color);
424			fIgnoreCurrentColorNotifications = false;
425		}
426	}
427}
428