xref: /haiku/src/apps/icon-o-matic/gui/StyleView.cpp (revision 83b1a68c52ba3e0e8796282759f694b7fdddf06d)
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 	SetViewColor(ui_color(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->Release();
257 	}
258 
259 	fStyle = style;
260 
261 	Gradient* gradient = NULL;
262 	if (fStyle) {
263 		fStyle->Acquire();
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 	if (fGradient)
312 		fGradient->RemoveObserver(this);
313 
314 	fGradient = gradient;
315 
316 	if (fGradient)
317 		fGradient->AddObserver(this);
318 
319 	if (fGradient) {
320 		fGradientControl->SetEnabled(true);
321 		fGradientControl->SetGradient(fGradient);
322 		fGradientType->SetEnabled(true);
323 		_MarkType(fStyleType->Menu(), STYLE_TYPE_GRADIENT);
324 		_MarkType(fGradientType->Menu(), fGradient->Type());
325 	} else {
326 		fGradientControl->SetEnabled(false);
327 		fGradientType->SetEnabled(false);
328 		_MarkType(fStyleType->Menu(), STYLE_TYPE_COLOR);
329 		_MarkType(fGradientType->Menu(), -1);
330 	}
331 
332 	if (sendMessage) {
333 		BMessage message(MSG_STYLE_TYPE_CHANGED);
334 		message.AddPointer("style", fStyle);
335 		Window()->PostMessage(&message);
336 	}
337 }
338 
339 
340 void
341 StyleView::_MarkType(BMenu* menu, int32 type) const
342 {
343 	for (int32 i = 0; BMenuItem* item = menu->ItemAt(i); i++) {
344 		BMessage* message = item->Message();
345 		int32 t;
346 		if (message->FindInt32("type", &t) == B_OK && t == type) {
347 			if (!item->IsMarked())
348 				item->SetMarked(true);
349 			return;
350 		}
351 	}
352 }
353 
354 
355 void
356 StyleView::_SetStyleType(int32 type)
357 {
358 	if (!fStyle)
359 		return;
360 
361 	if (type == STYLE_TYPE_COLOR) {
362 		if (fCommandStack) {
363 			fCommandStack->Perform(
364 				new (nothrow) SetGradientCommand(fStyle, NULL));
365 		} else {
366 			fStyle->SetGradient(NULL);
367 		}
368 	} else if (type == STYLE_TYPE_GRADIENT) {
369 		if (fCommandStack) {
370 			Gradient gradient(true);
371 			gradient.AddColor(fStyle->Color(), 0);
372 			gradient.AddColor(fStyle->Color(), 1);
373 			fCommandStack->Perform(
374 				new (nothrow) SetGradientCommand(fStyle, &gradient));
375 		} else {
376 			fStyle->SetGradient(fGradientControl->Gradient());
377 		}
378 	}
379 }
380 
381 
382 void
383 StyleView::_SetGradientType(int32 type)
384 {
385 	fGradientControl->Gradient()->SetType((gradients_type)type);
386 }
387 
388 
389 void
390 StyleView::_AdoptCurrentColor(rgb_color color)
391 {
392 	if (!fStyle)
393 		return;
394 
395 	if (fGradient) {
396 		// set the focused gradient color stop
397 		if (fGradientControl->IsFocus()) {
398 			fGradientControl->SetCurrentStop(color);
399 		}
400 	} else {
401 		if (fCommandStack) {
402 			fCommandStack->Perform(
403 				new (nothrow) SetColorCommand(fStyle, color));
404 		} else {
405 			fStyle->SetColor(color);
406 		}
407 	}
408 }
409 
410 
411 void
412 StyleView::_TransferGradientStopColor()
413 {
414 	if (fCurrentColor && fGradientControl->IsFocus()) {
415 		rgb_color color;
416 		if (fGradientControl->GetCurrentStop(&color)) {
417 			fIgnoreCurrentColorNotifications = true;
418 			fCurrentColor->SetColor(color);
419 			fIgnoreCurrentColorNotifications = false;
420 		}
421 	}
422 }
423