xref: /haiku/src/apps/icon-o-matic/gui/StyleView.cpp (revision 2263aa8f9c06d596d9415a9185ae7fbfc810c5ff)
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 
39 using std::nothrow;
40 
41 enum {
42 	MSG_SET_COLOR			= 'stcl',
43 
44 	MSG_SET_STYLE_TYPE		= 'stst',
45 	MSG_SET_GRADIENT_TYPE	= 'stgt',
46 };
47 
48 enum {
49 	STYLE_TYPE_COLOR		= 0,
50 	STYLE_TYPE_GRADIENT,
51 };
52 
53 
StyleView(BRect frame)54 StyleView::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 
~StyleView()120 StyleView::~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 
132 void
AttachedToWindow()133 StyleView::AttachedToWindow()
134 {
135 	fStyleType->Menu()->SetTargetForItems(this);
136 	fGradientType->Menu()->SetTargetForItems(this);
137 }
138 
139 
140 void
FrameResized(float width,float height)141 StyleView::FrameResized(float width, float height)
142 {
143 	fPreviousBounds = Bounds();
144 }
145 
146 
147 void
MessageReceived(BMessage * message)148 StyleView::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 
177 BSize
MinSize()178 StyleView::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 
189 void
ObjectChanged(const Observable * object)190 StyleView::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 
252 void
SetStyle(Style * style)253 StyleView::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 
282 void
SetCommandStack(CommandStack * stack)283 StyleView::SetCommandStack(CommandStack* stack)
284 {
285 	fCommandStack = stack;
286 }
287 
288 
289 void
SetCurrentColor(CurrentColor * color)290 StyleView::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 
308 void
_SetGradient(Gradient * gradient,bool forceControlUpdate,bool sendMessage)309 StyleView::_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 
345 void
_MarkType(BMenu * menu,int32 type) const346 StyleView::_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 
360 void
_SetStyleType(int32 type)361 StyleView::_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 
387 void
_SetGradientType(int32 type)388 StyleView::_SetGradientType(int32 type)
389 {
390 	fGradientControl->Gradient()->SetType((gradients_type)type);
391 }
392 
393 
394 void
_AdoptCurrentColor(rgb_color color)395 StyleView::_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 
416 void
_TransferGradientStopColor()417 StyleView::_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