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