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 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->ReleaseReference(); 257 } 258 259 fStyle = style; 260 261 Gradient* gradient = NULL; 262 if (fStyle) { 263 fStyle->AcquireReference(); 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 Gradient* oldGradient = fGradient; 312 if (oldGradient != NULL) 313 oldGradient->RemoveObserver(this); 314 315 fGradient = gradient; 316 317 if (fGradient) { 318 fGradient->AcquireReference(); 319 fGradient->AddObserver(this); 320 } 321 322 if (fGradient) { 323 fGradientControl->SetEnabled(true); 324 fGradientControl->SetGradient(fGradient); 325 fGradientType->SetEnabled(true); 326 _MarkType(fStyleType->Menu(), STYLE_TYPE_GRADIENT); 327 _MarkType(fGradientType->Menu(), fGradient->Type()); 328 } else { 329 fGradientControl->SetEnabled(false); 330 fGradientType->SetEnabled(false); 331 _MarkType(fStyleType->Menu(), STYLE_TYPE_COLOR); 332 _MarkType(fGradientType->Menu(), -1); 333 } 334 335 if (oldGradient != NULL) 336 oldGradient->ReleaseReference(); 337 338 if (sendMessage) { 339 BMessage message(MSG_STYLE_TYPE_CHANGED); 340 message.AddPointer("style", fStyle); 341 Window()->PostMessage(&message); 342 } 343 } 344 345 346 void 347 StyleView::_MarkType(BMenu* menu, int32 type) const 348 { 349 for (int32 i = 0; BMenuItem* item = menu->ItemAt(i); i++) { 350 BMessage* message = item->Message(); 351 int32 t; 352 if (message->FindInt32("type", &t) == B_OK && t == type) { 353 if (!item->IsMarked()) 354 item->SetMarked(true); 355 return; 356 } 357 } 358 } 359 360 361 void 362 StyleView::_SetStyleType(int32 type) 363 { 364 if (!fStyle) 365 return; 366 367 if (type == STYLE_TYPE_COLOR) { 368 if (fCommandStack) { 369 fCommandStack->Perform( 370 new (nothrow) SetGradientCommand(fStyle, NULL)); 371 } else { 372 fStyle->SetGradient(NULL); 373 } 374 } else if (type == STYLE_TYPE_GRADIENT) { 375 if (fCommandStack) { 376 Gradient gradient(true); 377 gradient.AddColor(fStyle->Color(), 0); 378 gradient.AddColor(fStyle->Color(), 1); 379 fCommandStack->Perform( 380 new (nothrow) SetGradientCommand(fStyle, &gradient)); 381 } else { 382 fStyle->SetGradient(fGradientControl->Gradient()); 383 } 384 } 385 } 386 387 388 void 389 StyleView::_SetGradientType(int32 type) 390 { 391 fGradientControl->Gradient()->SetType((gradients_type)type); 392 } 393 394 395 void 396 StyleView::_AdoptCurrentColor(rgb_color color) 397 { 398 if (!fStyle) 399 return; 400 401 if (fGradient) { 402 // set the focused gradient color stop 403 if (fGradientControl->IsFocus()) { 404 fGradientControl->SetCurrentStop(color); 405 } 406 } else { 407 if (fCommandStack) { 408 fCommandStack->Perform( 409 new (nothrow) SetColorCommand(fStyle, color)); 410 } else { 411 fStyle->SetColor(color); 412 } 413 } 414 } 415 416 417 void 418 StyleView::_TransferGradientStopColor() 419 { 420 if (fCurrentColor && fGradientControl->IsFocus()) { 421 rgb_color color; 422 if (fGradientControl->GetCurrentStop(&color)) { 423 fIgnoreCurrentColorNotifications = true; 424 fCurrentColor->SetColor(color); 425 fIgnoreCurrentColorNotifications = false; 426 } 427 } 428 } 429