/* * Copyright 2006-2011, Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Stephan Aßmus */ #include "StyleView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "CommandStack.h" #include "CurrentColor.h" #include "GradientTransformable.h" #include "GradientControl.h" #include "SetColorCommand.h" #include "SetGradientCommand.h" #include "Style.h" #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "Icon-O-Matic-StyleTypes" using std::nothrow; enum { MSG_SET_COLOR = 'stcl', MSG_SET_STYLE_TYPE = 'stst', MSG_SET_GRADIENT_TYPE = 'stgt', }; enum { STYLE_TYPE_COLOR = 0, STYLE_TYPE_GRADIENT, }; StyleView::StyleView(BRect frame) : BView("style view", 0), fCommandStack(NULL), fCurrentColor(NULL), fStyle(NULL), fGradient(NULL), fIgnoreCurrentColorNotifications(false), fIgnoreControlGradientNotifications(false), fPreviousBounds(frame.OffsetToCopy(B_ORIGIN)) { SetViewUIColor(B_PANEL_BACKGROUND_COLOR); // style type BMenu* menu = new BPopUpMenu(B_TRANSLATE("")); BMessage* message = new BMessage(MSG_SET_STYLE_TYPE); message->AddInt32("type", STYLE_TYPE_COLOR); menu->AddItem(new BMenuItem(B_TRANSLATE("Color"), message)); message = new BMessage(MSG_SET_STYLE_TYPE); message->AddInt32("type", STYLE_TYPE_GRADIENT); menu->AddItem(new BMenuItem(B_TRANSLATE("Gradient"), message)); BGridLayout* layout = new BGridLayout(5, 5); SetLayout(layout); fStyleType = new BMenuField(B_TRANSLATE("Style type"), menu); // gradient type menu = new BPopUpMenu(B_TRANSLATE("")); message = new BMessage(MSG_SET_GRADIENT_TYPE); message->AddInt32("type", GRADIENT_LINEAR); menu->AddItem(new BMenuItem(B_TRANSLATE("Linear"), message)); message = new BMessage(MSG_SET_GRADIENT_TYPE); message->AddInt32("type", GRADIENT_CIRCULAR); menu->AddItem(new BMenuItem(B_TRANSLATE("Radial"), message)); message = new BMessage(MSG_SET_GRADIENT_TYPE); message->AddInt32("type", GRADIENT_DIAMOND); menu->AddItem(new BMenuItem(B_TRANSLATE("Diamond"), message)); message = new BMessage(MSG_SET_GRADIENT_TYPE); message->AddInt32("type", GRADIENT_CONIC); menu->AddItem(new BMenuItem(B_TRANSLATE("Conic"), message)); fGradientType = new BMenuField(B_TRANSLATE("Gradient type"), menu); fGradientControl = new GradientControl(new BMessage(MSG_SET_COLOR), this); layout->AddItem(BSpaceLayoutItem::CreateVerticalStrut(3), 0, 0, 4); layout->AddItem(BSpaceLayoutItem::CreateHorizontalStrut(3), 0, 1, 1, 3); layout->AddItem(fStyleType->CreateLabelLayoutItem(), 1, 1); layout->AddItem(fStyleType->CreateMenuBarLayoutItem(), 2, 1); layout->AddItem(fGradientType->CreateLabelLayoutItem(), 1, 2); layout->AddItem(fGradientType->CreateMenuBarLayoutItem(), 2, 2); layout->AddView(fGradientControl, 1, 3, 2); layout->AddItem(BSpaceLayoutItem::CreateHorizontalStrut(3), 3, 1, 1, 3); layout->AddItem(BSpaceLayoutItem::CreateVerticalStrut(3), 0, 4, 4); fStyleType->SetEnabled(false); fGradientType->SetEnabled(false); fGradientControl->SetEnabled(false); fGradientControl->Gradient()->AddObserver(this); } StyleView::~StyleView() { SetStyle(NULL); SetCurrentColor(NULL); fGradientControl->Gradient()->RemoveObserver(this); if (fGradient.IsSet()) { fGradient->RemoveObserver(this); } } void StyleView::AttachedToWindow() { fStyleType->Menu()->SetTargetForItems(this); fGradientType->Menu()->SetTargetForItems(this); } void StyleView::FrameResized(float width, float height) { fPreviousBounds = Bounds(); } void StyleView::MessageReceived(BMessage* message) { switch (message->what) { case MSG_SET_STYLE_TYPE: { int32 type; if (message->FindInt32("type", &type) == B_OK) _SetStyleType(type); break; } case MSG_SET_GRADIENT_TYPE: { int32 type; if (message->FindInt32("type", &type) == B_OK) _SetGradientType(type); break; } case MSG_SET_COLOR: case MSG_GRADIENT_CONTROL_FOCUS_CHANGED: _TransferGradientStopColor(); break; default: BView::MessageReceived(message); break; } } BSize StyleView::MinSize() { BSize minSize = BView::MinSize(); minSize.width = fGradientControl->MinSize().width + 10; return minSize; } // #pragma mark - void StyleView::ObjectChanged(const Observable* object) { if (!fStyle) return; Gradient* controlGradient = fGradientControl->Gradient(); // NOTE: it is important to compare the gradients // before assignment, or we will get into an endless loop if (object == controlGradient) { if (!fGradient.IsSet()) return; if (fIgnoreControlGradientNotifications) return; fIgnoreControlGradientNotifications = true; if (!fGradient->ColorStepsAreEqual(*controlGradient)) { // Make sure we never apply the transformation from the control // gradient to the style gradient. Setting this here would cause to // re-enter ObjectChanged(), which is prevented to cause harm via // fIgnoreControlGradientNotifications. controlGradient->SetTransform(*fGradient); if (fCommandStack) { fCommandStack->Perform( new (nothrow) SetGradientCommand( fStyle, controlGradient)); } else { *fGradient = *controlGradient; } // transfer the current gradient color to the current color _TransferGradientStopColor(); } fIgnoreControlGradientNotifications = false; } else if (object == fGradient) { if (!fGradient->ColorStepsAreEqual(*controlGradient)) { fGradientControl->SetGradient(fGradient); _MarkType(fGradientType->Menu(), fGradient->Type()); // transfer the current gradient color to the current color _TransferGradientStopColor(); } } else if (object == fStyle) { // maybe the gradient was added or removed // or the color changed _SetGradient(fStyle->Gradient(), false, true); if (fCurrentColor && !fStyle->Gradient()) fCurrentColor->SetColor(fStyle->Color()); } else if (object == fCurrentColor) { // NOTE: because of imprecisions in converting // RGB<->HSV, the current color can be different // even if we just set it to the current // gradient stop color, that's why we ignore // notifications caused by this situation if (!fIgnoreCurrentColorNotifications) _AdoptCurrentColor(fCurrentColor->Color()); } } // #pragma mark - void StyleView::SetStyle(Style* style) { if (fStyle == style) return; if (fStyle) { fStyle->RemoveObserver(this); fStyle->ReleaseReference(); } fStyle = style; Gradient* gradient = NULL; if (fStyle) { fStyle->AcquireReference(); fStyle->AddObserver(this); gradient = fStyle->Gradient(); if (fCurrentColor && !gradient) fCurrentColor->SetColor(fStyle->Color()); fStyleType->SetEnabled(true); } else fStyleType->SetEnabled(false); _SetGradient(gradient, true); } void StyleView::SetCommandStack(CommandStack* stack) { fCommandStack = stack; } void StyleView::SetCurrentColor(CurrentColor* color) { if (fCurrentColor == color) return; if (fCurrentColor) fCurrentColor->RemoveObserver(this); fCurrentColor = color; if (fCurrentColor) fCurrentColor->AddObserver(this); } // #pragma mark - void StyleView::_SetGradient(Gradient* gradient, bool forceControlUpdate, bool sendMessage) { if (!forceControlUpdate && gradient == fGradient) return; if (fGradient.IsSet()) fGradient->RemoveObserver(this); fGradient.SetTo(gradient); if (fGradient.IsSet()) { fGradient->AddObserver(this); } if (fGradient.IsSet()) { fGradientControl->SetEnabled(true); fGradientControl->SetGradient(fGradient); fGradientType->SetEnabled(true); _MarkType(fStyleType->Menu(), STYLE_TYPE_GRADIENT); _MarkType(fGradientType->Menu(), fGradient->Type()); } else { fGradientControl->SetEnabled(false); fGradientType->SetEnabled(false); _MarkType(fStyleType->Menu(), STYLE_TYPE_COLOR); _MarkType(fGradientType->Menu(), -1); } if (sendMessage) { BMessage message(MSG_STYLE_TYPE_CHANGED); message.AddPointer("style", fStyle); Window()->PostMessage(&message); } } void StyleView::_MarkType(BMenu* menu, int32 type) const { for (int32 i = 0; BMenuItem* item = menu->ItemAt(i); i++) { BMessage* message = item->Message(); int32 t; if (message->FindInt32("type", &t) == B_OK && t == type) { if (!item->IsMarked()) item->SetMarked(true); return; } } } void StyleView::_SetStyleType(int32 type) { if (!fStyle) return; if (type == STYLE_TYPE_COLOR) { if (fCommandStack) { fCommandStack->Perform( new (nothrow) SetGradientCommand(fStyle, NULL)); } else { fStyle->SetGradient(NULL); } } else if (type == STYLE_TYPE_GRADIENT) { if (fCommandStack) { Gradient gradient(true); gradient.AddColor(fStyle->Color(), 0); gradient.AddColor(fStyle->Color(), 1); fCommandStack->Perform( new (nothrow) SetGradientCommand(fStyle, &gradient)); } else { fStyle->SetGradient(fGradientControl->Gradient()); } } } void StyleView::_SetGradientType(int32 type) { fGradientControl->Gradient()->SetType((gradients_type)type); } void StyleView::_AdoptCurrentColor(rgb_color color) { if (!fStyle) return; if (fGradient.IsSet()) { // set the focused gradient color stop if (fGradientControl->IsFocus()) { fGradientControl->SetCurrentStop(color); } } else { if (fCommandStack) { fCommandStack->Perform( new (nothrow) SetColorCommand(fStyle, color)); } else { fStyle->SetColor(color); } } } void StyleView::_TransferGradientStopColor() { if (fCurrentColor && fGradientControl->IsFocus()) { rgb_color color; if (fGradientControl->GetCurrentStop(&color)) { fIgnoreCurrentColorNotifications = true; fCurrentColor->SetColor(color); fIgnoreCurrentColorNotifications = false; } } }