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