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 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 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 133 StyleView::AttachedToWindow() 134 { 135 fStyleType->Menu()->SetTargetForItems(this); 136 fGradientType->Menu()->SetTargetForItems(this); 137 } 138 139 140 void 141 StyleView::FrameResized(float width, float height) 142 { 143 fPreviousBounds = Bounds(); 144 } 145 146 147 void 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 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 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 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 283 StyleView::SetCommandStack(CommandStack* stack) 284 { 285 fCommandStack = stack; 286 } 287 288 289 void 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 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 346 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 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 388 StyleView::_SetGradientType(int32 type) 389 { 390 fGradientControl->Gradient()->SetType((gradients_type)type); 391 } 392 393 394 void 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 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