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