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 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 SetViewColor(ui_color(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 120 StyleView::~StyleView() 121 { 122 SetStyle(NULL); 123 SetCurrentColor(NULL); 124 fGradientControl->Gradient()->RemoveObserver(this); 125 } 126 127 128 void 129 StyleView::AttachedToWindow() 130 { 131 fStyleType->Menu()->SetTargetForItems(this); 132 fGradientType->Menu()->SetTargetForItems(this); 133 } 134 135 136 void 137 StyleView::FrameResized(float width, float height) 138 { 139 fPreviousBounds = Bounds(); 140 } 141 142 143 void 144 StyleView::MessageReceived(BMessage* message) 145 { 146 switch (message->what) { 147 case MSG_SET_STYLE_TYPE: 148 { 149 int32 type; 150 if (message->FindInt32("type", &type) == B_OK) 151 _SetStyleType(type); 152 break; 153 } 154 case MSG_SET_GRADIENT_TYPE: 155 { 156 int32 type; 157 if (message->FindInt32("type", &type) == B_OK) 158 _SetGradientType(type); 159 break; 160 } 161 case MSG_SET_COLOR: 162 case MSG_GRADIENT_CONTROL_FOCUS_CHANGED: 163 _TransferGradientStopColor(); 164 break; 165 166 default: 167 BView::MessageReceived(message); 168 break; 169 } 170 } 171 172 173 BSize 174 StyleView::MinSize() 175 { 176 BSize minSize = BView::MinSize(); 177 minSize.width = fGradientControl->MinSize().width + 10; 178 return minSize; 179 } 180 181 182 // #pragma mark - 183 184 185 void 186 StyleView::ObjectChanged(const Observable* object) 187 { 188 if (!fStyle) 189 return; 190 191 Gradient* controlGradient = fGradientControl->Gradient(); 192 193 // NOTE: it is important to compare the gradients 194 // before assignment, or we will get into an endless loop 195 if (object == controlGradient) { 196 if (!fGradient) 197 return; 198 if (fIgnoreControlGradientNotifications) 199 return; 200 fIgnoreControlGradientNotifications = true; 201 202 if (!fGradient->ColorStepsAreEqual(*controlGradient)) { 203 // Make sure we never apply the transformation from the control 204 // gradient to the style gradient. Setting this here would cause to 205 // re-enter ObjectChanged(), which is prevented to cause harm via 206 // fIgnoreControlGradientNotifications. 207 controlGradient->SetTransform(*fGradient); 208 if (fCommandStack) { 209 fCommandStack->Perform( 210 new (nothrow) SetGradientCommand( 211 fStyle, controlGradient)); 212 } else { 213 *fGradient = *controlGradient; 214 } 215 // transfer the current gradient color to the current color 216 _TransferGradientStopColor(); 217 } 218 219 fIgnoreControlGradientNotifications = false; 220 } else if (object == fGradient) { 221 if (!fGradient->ColorStepsAreEqual(*controlGradient)) { 222 fGradientControl->SetGradient(fGradient); 223 _MarkType(fGradientType->Menu(), fGradient->Type()); 224 // transfer the current gradient color to the current color 225 _TransferGradientStopColor(); 226 } 227 } else if (object == fStyle) { 228 // maybe the gradient was added or removed 229 // or the color changed 230 _SetGradient(fStyle->Gradient(), false, true); 231 if (fCurrentColor && !fStyle->Gradient()) 232 fCurrentColor->SetColor(fStyle->Color()); 233 } else if (object == fCurrentColor) { 234 // NOTE: because of imprecisions in converting 235 // RGB<->HSV, the current color can be different 236 // even if we just set it to the current 237 // gradient stop color, that's why we ignore 238 // notifications caused by this situation 239 if (!fIgnoreCurrentColorNotifications) 240 _AdoptCurrentColor(fCurrentColor->Color()); 241 } 242 } 243 244 245 // #pragma mark - 246 247 248 void 249 StyleView::SetStyle(Style* style) 250 { 251 if (fStyle == style) 252 return; 253 254 if (fStyle) { 255 fStyle->RemoveObserver(this); 256 fStyle->Release(); 257 } 258 259 fStyle = style; 260 261 Gradient* gradient = NULL; 262 if (fStyle) { 263 fStyle->Acquire(); 264 fStyle->AddObserver(this); 265 gradient = fStyle->Gradient(); 266 267 if (fCurrentColor && !gradient) 268 fCurrentColor->SetColor(fStyle->Color()); 269 270 fStyleType->SetEnabled(true); 271 } else 272 fStyleType->SetEnabled(false); 273 274 _SetGradient(gradient, true); 275 } 276 277 278 void 279 StyleView::SetCommandStack(CommandStack* stack) 280 { 281 fCommandStack = stack; 282 } 283 284 285 void 286 StyleView::SetCurrentColor(CurrentColor* color) 287 { 288 if (fCurrentColor == color) 289 return; 290 291 if (fCurrentColor) 292 fCurrentColor->RemoveObserver(this); 293 294 fCurrentColor = color; 295 296 if (fCurrentColor) 297 fCurrentColor->AddObserver(this); 298 } 299 300 301 // #pragma mark - 302 303 304 void 305 StyleView::_SetGradient(Gradient* gradient, bool forceControlUpdate, 306 bool sendMessage) 307 { 308 if (!forceControlUpdate && fGradient == gradient) 309 return; 310 311 if (fGradient) 312 fGradient->RemoveObserver(this); 313 314 fGradient = gradient; 315 316 if (fGradient) 317 fGradient->AddObserver(this); 318 319 if (fGradient) { 320 fGradientControl->SetEnabled(true); 321 fGradientControl->SetGradient(fGradient); 322 fGradientType->SetEnabled(true); 323 _MarkType(fStyleType->Menu(), STYLE_TYPE_GRADIENT); 324 _MarkType(fGradientType->Menu(), fGradient->Type()); 325 } else { 326 fGradientControl->SetEnabled(false); 327 fGradientType->SetEnabled(false); 328 _MarkType(fStyleType->Menu(), STYLE_TYPE_COLOR); 329 _MarkType(fGradientType->Menu(), -1); 330 } 331 332 if (sendMessage) { 333 BMessage message(MSG_STYLE_TYPE_CHANGED); 334 message.AddPointer("style", fStyle); 335 Window()->PostMessage(&message); 336 } 337 } 338 339 340 void 341 StyleView::_MarkType(BMenu* menu, int32 type) const 342 { 343 for (int32 i = 0; BMenuItem* item = menu->ItemAt(i); i++) { 344 BMessage* message = item->Message(); 345 int32 t; 346 if (message->FindInt32("type", &t) == B_OK && t == type) { 347 if (!item->IsMarked()) 348 item->SetMarked(true); 349 return; 350 } 351 } 352 } 353 354 355 void 356 StyleView::_SetStyleType(int32 type) 357 { 358 if (!fStyle) 359 return; 360 361 if (type == STYLE_TYPE_COLOR) { 362 if (fCommandStack) { 363 fCommandStack->Perform( 364 new (nothrow) SetGradientCommand(fStyle, NULL)); 365 } else { 366 fStyle->SetGradient(NULL); 367 } 368 } else if (type == STYLE_TYPE_GRADIENT) { 369 if (fCommandStack) { 370 Gradient gradient(true); 371 gradient.AddColor(fStyle->Color(), 0); 372 gradient.AddColor(fStyle->Color(), 1); 373 fCommandStack->Perform( 374 new (nothrow) SetGradientCommand(fStyle, &gradient)); 375 } else { 376 fStyle->SetGradient(fGradientControl->Gradient()); 377 } 378 } 379 } 380 381 382 void 383 StyleView::_SetGradientType(int32 type) 384 { 385 fGradientControl->Gradient()->SetType((gradients_type)type); 386 } 387 388 389 void 390 StyleView::_AdoptCurrentColor(rgb_color color) 391 { 392 if (!fStyle) 393 return; 394 395 if (fGradient) { 396 // set the focused gradient color stop 397 if (fGradientControl->IsFocus()) { 398 fGradientControl->SetCurrentStop(color); 399 } 400 } else { 401 if (fCommandStack) { 402 fCommandStack->Perform( 403 new (nothrow) SetColorCommand(fStyle, color)); 404 } else { 405 fStyle->SetColor(color); 406 } 407 } 408 } 409 410 411 void 412 StyleView::_TransferGradientStopColor() 413 { 414 if (fCurrentColor && fGradientControl->IsFocus()) { 415 rgb_color color; 416 if (fGradientControl->GetCurrentStop(&color)) { 417 fIgnoreCurrentColorNotifications = true; 418 fCurrentColor->SetColor(color); 419 fIgnoreCurrentColorNotifications = false; 420 } 421 } 422 } 423