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