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