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