xref: /haiku/src/apps/icon-o-matic/gui/StyleView.cpp (revision 1deede7388b04dbeec5af85cae7164735ea9e70d)
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 
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 
120 StyleView::~StyleView()
121 {
122 	SetStyle(NULL);
123 	SetCurrentColor(NULL);
124 	fGradientControl->Gradient()->RemoveObserver(this);
125 }
126 
127 
128 void
129 StyleView::AttachedToWindow()
130 {
131 	fStyleType->Menu()->SetTargetForItems(this);
132 	fGradientType->Menu()->SetTargetForItems(this);
133 }
134 
135 
136 void
137 StyleView::FrameResized(float width, float height)
138 {
139 	fPreviousBounds = Bounds();
140 }
141 
142 
143 void
144 StyleView::MessageReceived(BMessage* message)
145 {
146 	switch (message->what) {
147 		case MSG_SET_STYLE_TYPE:
148 		{
149 			int32 type;
150 			if (message->FindInt32("type", &type) == B_OK)
151 				_SetStyleType(type);
152 			break;
153 		}
154 		case MSG_SET_GRADIENT_TYPE:
155 		{
156 			int32 type;
157 			if (message->FindInt32("type", &type) == B_OK)
158 				_SetGradientType(type);
159 			break;
160 		}
161 		case MSG_SET_COLOR:
162 		case MSG_GRADIENT_CONTROL_FOCUS_CHANGED:
163 			_TransferGradientStopColor();
164 			break;
165 
166 		default:
167 			BView::MessageReceived(message);
168 			break;
169 	}
170 }
171 
172 
173 BSize
174 StyleView::MinSize()
175 {
176 	BSize minSize = BView::MinSize();
177 	minSize.width = fGradientControl->MinSize().width + 10;
178 	return minSize;
179 }
180 
181 
182 // #pragma mark -
183 
184 
185 void
186 StyleView::ObjectChanged(const Observable* object)
187 {
188 	if (!fStyle)
189 		return;
190 
191 	Gradient* controlGradient = fGradientControl->Gradient();
192 
193 	// NOTE: it is important to compare the gradients
194 	// before assignment, or we will get into an endless loop
195 	if (object == controlGradient) {
196 		if (!fGradient)
197 			return;
198 		if (fIgnoreControlGradientNotifications)
199 			return;
200 		fIgnoreControlGradientNotifications = true;
201 
202 		if (!fGradient->ColorStepsAreEqual(*controlGradient)) {
203 			// Make sure we never apply the transformation from the control
204 			// gradient to the style gradient. Setting this here would cause to
205 			// re-enter ObjectChanged(), which is prevented to cause harm via
206 			// fIgnoreControlGradientNotifications.
207 			controlGradient->SetTransform(*fGradient);
208 			if (fCommandStack) {
209 				fCommandStack->Perform(
210 					new (nothrow) SetGradientCommand(
211 						fStyle, controlGradient));
212 			} else {
213 				*fGradient = *controlGradient;
214 			}
215 			// transfer the current gradient color to the current color
216 			_TransferGradientStopColor();
217 		}
218 
219 		fIgnoreControlGradientNotifications = false;
220 	} else if (object == fGradient) {
221 		if (!fGradient->ColorStepsAreEqual(*controlGradient)) {
222 			fGradientControl->SetGradient(fGradient);
223 			_MarkType(fGradientType->Menu(), fGradient->Type());
224 			// transfer the current gradient color to the current color
225 			_TransferGradientStopColor();
226 		}
227 	} else if (object == fStyle) {
228 		// maybe the gradient was added or removed
229 		// or the color changed
230 		_SetGradient(fStyle->Gradient(), false, true);
231 		if (fCurrentColor && !fStyle->Gradient())
232 			fCurrentColor->SetColor(fStyle->Color());
233 	} else if (object == fCurrentColor) {
234 		// NOTE: because of imprecisions in converting
235 		// RGB<->HSV, the current color can be different
236 		// even if we just set it to the current
237 		// gradient stop color, that's why we ignore
238 		// notifications caused by this situation
239 		if (!fIgnoreCurrentColorNotifications)
240 			_AdoptCurrentColor(fCurrentColor->Color());
241 	}
242 }
243 
244 
245 // #pragma mark -
246 
247 
248 void
249 StyleView::SetStyle(Style* style)
250 {
251 	if (fStyle == style)
252 		return;
253 
254 	if (fStyle) {
255 		fStyle->RemoveObserver(this);
256 		fStyle->ReleaseReference();
257 	}
258 
259 	fStyle = style;
260 
261 	Gradient* gradient = NULL;
262 	if (fStyle) {
263 		fStyle->AcquireReference();
264 		fStyle->AddObserver(this);
265 		gradient = fStyle->Gradient();
266 
267 		if (fCurrentColor && !gradient)
268 			fCurrentColor->SetColor(fStyle->Color());
269 
270 		fStyleType->SetEnabled(true);
271 	} else
272 		fStyleType->SetEnabled(false);
273 
274 	_SetGradient(gradient, true);
275 }
276 
277 
278 void
279 StyleView::SetCommandStack(CommandStack* stack)
280 {
281 	fCommandStack = stack;
282 }
283 
284 
285 void
286 StyleView::SetCurrentColor(CurrentColor* color)
287 {
288 	if (fCurrentColor == color)
289 		return;
290 
291 	if (fCurrentColor)
292 		fCurrentColor->RemoveObserver(this);
293 
294 	fCurrentColor = color;
295 
296 	if (fCurrentColor)
297 		fCurrentColor->AddObserver(this);
298 }
299 
300 
301 // #pragma mark -
302 
303 
304 void
305 StyleView::_SetGradient(Gradient* gradient, bool forceControlUpdate,
306 	bool sendMessage)
307 {
308 	if (!forceControlUpdate && fGradient == gradient)
309 		return;
310 
311 	Gradient* oldGradient = fGradient;
312 	if (oldGradient != NULL)
313 		oldGradient->RemoveObserver(this);
314 
315 	fGradient = gradient;
316 
317 	if (fGradient) {
318 		fGradient->AcquireReference();
319 		fGradient->AddObserver(this);
320 	}
321 
322 	if (fGradient) {
323 		fGradientControl->SetEnabled(true);
324 		fGradientControl->SetGradient(fGradient);
325 		fGradientType->SetEnabled(true);
326 		_MarkType(fStyleType->Menu(), STYLE_TYPE_GRADIENT);
327 		_MarkType(fGradientType->Menu(), fGradient->Type());
328 	} else {
329 		fGradientControl->SetEnabled(false);
330 		fGradientType->SetEnabled(false);
331 		_MarkType(fStyleType->Menu(), STYLE_TYPE_COLOR);
332 		_MarkType(fGradientType->Menu(), -1);
333 	}
334 
335 	if (oldGradient != NULL)
336 		oldGradient->ReleaseReference();
337 
338 	if (sendMessage) {
339 		BMessage message(MSG_STYLE_TYPE_CHANGED);
340 		message.AddPointer("style", fStyle);
341 		Window()->PostMessage(&message);
342 	}
343 }
344 
345 
346 void
347 StyleView::_MarkType(BMenu* menu, int32 type) const
348 {
349 	for (int32 i = 0; BMenuItem* item = menu->ItemAt(i); i++) {
350 		BMessage* message = item->Message();
351 		int32 t;
352 		if (message->FindInt32("type", &t) == B_OK && t == type) {
353 			if (!item->IsMarked())
354 				item->SetMarked(true);
355 			return;
356 		}
357 	}
358 }
359 
360 
361 void
362 StyleView::_SetStyleType(int32 type)
363 {
364 	if (!fStyle)
365 		return;
366 
367 	if (type == STYLE_TYPE_COLOR) {
368 		if (fCommandStack) {
369 			fCommandStack->Perform(
370 				new (nothrow) SetGradientCommand(fStyle, NULL));
371 		} else {
372 			fStyle->SetGradient(NULL);
373 		}
374 	} else if (type == STYLE_TYPE_GRADIENT) {
375 		if (fCommandStack) {
376 			Gradient gradient(true);
377 			gradient.AddColor(fStyle->Color(), 0);
378 			gradient.AddColor(fStyle->Color(), 1);
379 			fCommandStack->Perform(
380 				new (nothrow) SetGradientCommand(fStyle, &gradient));
381 		} else {
382 			fStyle->SetGradient(fGradientControl->Gradient());
383 		}
384 	}
385 }
386 
387 
388 void
389 StyleView::_SetGradientType(int32 type)
390 {
391 	fGradientControl->Gradient()->SetType((gradients_type)type);
392 }
393 
394 
395 void
396 StyleView::_AdoptCurrentColor(rgb_color color)
397 {
398 	if (!fStyle)
399 		return;
400 
401 	if (fGradient) {
402 		// set the focused gradient color stop
403 		if (fGradientControl->IsFocus()) {
404 			fGradientControl->SetCurrentStop(color);
405 		}
406 	} else {
407 		if (fCommandStack) {
408 			fCommandStack->Perform(
409 				new (nothrow) SetColorCommand(fStyle, color));
410 		} else {
411 			fStyle->SetColor(color);
412 		}
413 	}
414 }
415 
416 
417 void
418 StyleView::_TransferGradientStopColor()
419 {
420 	if (fCurrentColor && fGradientControl->IsFocus()) {
421 		rgb_color color;
422 		if (fGradientControl->GetCurrentStop(&color)) {
423 			fIgnoreCurrentColorNotifications = true;
424 			fCurrentColor->SetColor(color);
425 			fIgnoreCurrentColorNotifications = false;
426 		}
427 	}
428 }
429