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