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