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