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