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