/* * Copyright 2006-2012, Stephan Aßmus . * Copyright 2023, Haiku. * All rights reserved. Distributed under the terms of the MIT License. * * Authors: * Zardshard */ #include "StyleListView.h" #include #include #include #include #include #include #include #include #include #include #include #include "AddStylesCommand.h" #include "AssignStyleCommand.h" #include "CurrentColor.h" #include "CommandStack.h" #include "GradientTransformable.h" #include "MoveStylesCommand.h" #include "PathSourceShape.h" #include "RemoveStylesCommand.h" #include "Style.h" #include "Observer.h" #include "ResetTransformationCommand.h" #include "Shape.h" #include "Selection.h" #include "Util.h" #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "Icon-O-Matic-StylesList" using std::nothrow; static const float kMarkWidth = 14.0; static const float kBorderOffset = 3.0; static const float kTextOffset = 4.0; enum { MSG_ADD = 'adst', MSG_REMOVE = 'rmst', MSG_DUPLICATE = 'dpst', MSG_RESET_TRANSFORMATION = 'rstr', }; class StyleListItem : public SimpleItem, public Observer { public: StyleListItem(Style* s, StyleListView* listView, bool markEnabled) : SimpleItem(""), style(NULL), fListView(listView), fMarkEnabled(markEnabled), fMarked(false) { SetStyle(s); } virtual ~StyleListItem() { SetStyle(NULL); } // SimpleItem interface virtual void DrawItem(BView* owner, BRect itemFrame, bool even) { SimpleItem::DrawBackground(owner, itemFrame, even); float offset = kBorderOffset + kMarkWidth + kTextOffset; SimpleItem::DrawItem(owner, itemFrame.OffsetByCopy(offset, 0), even); if (!fMarkEnabled) return; // mark BRect markRect = itemFrame; float markRectBorderTint = B_DARKEN_1_TINT; float markRectFillTint = 1.04; float markTint = B_DARKEN_4_TINT; // Dark Themes rgb_color lowColor = owner->LowColor(); if (lowColor.red + lowColor.green + lowColor.blue < 128 * 3) { markRectBorderTint = B_LIGHTEN_2_TINT; markRectFillTint = 0.85; markTint = 0.1; } markRect.left += kBorderOffset; markRect.right = markRect.left + kMarkWidth; markRect.top = (markRect.top + markRect.bottom - kMarkWidth) / 2.0; markRect.bottom = markRect.top + kMarkWidth; owner->SetHighColor(tint_color(owner->LowColor(), markRectBorderTint)); owner->StrokeRect(markRect); markRect.InsetBy(1, 1); owner->SetHighColor(tint_color(owner->LowColor(), markRectFillTint)); owner->FillRect(markRect); if (fMarked) { markRect.InsetBy(2, 2); owner->SetHighColor(tint_color(owner->LowColor(), markTint)); owner->SetPenSize(2); owner->StrokeLine(markRect.LeftTop(), markRect.RightBottom()); owner->StrokeLine(markRect.LeftBottom(), markRect.RightTop()); owner->SetPenSize(1); } } // Observer interface virtual void ObjectChanged(const Observable* object) { UpdateText(); } // StyleListItem void SetStyle(Style* s) { if (s == style) return; if (style) { style->RemoveObserver(this); style->ReleaseReference(); } style = s; if (style) { style->AcquireReference(); style->AddObserver(this); UpdateText(); } } void UpdateText() { SetText(style->Name()); Invalidate(); } void SetMarkEnabled(bool enabled) { if (fMarkEnabled == enabled) return; fMarkEnabled = enabled; Invalidate(); } void SetMarked(bool marked) { if (fMarked == marked) return; fMarked = marked; Invalidate(); } void Invalidate() { if (fListView->LockLooper()) { fListView->InvalidateItem(fListView->IndexOf(this)); fListView->UnlockLooper(); } } public: Style* style; private: StyleListView* fListView; bool fMarkEnabled; bool fMarked; }; class ShapeStyleListener : public ShapeListener, public ContainerListener { public: ShapeStyleListener(StyleListView* listView) : fListView(listView), fShape(NULL) { } virtual ~ShapeStyleListener() { SetShape(NULL); } // ShapeListener interface virtual void TransformerAdded(Transformer* t, int32 index) { } virtual void TransformerRemoved(Transformer* t) { } virtual void StyleChanged(Style* oldStyle, Style* newStyle) { fListView->_SetStyleMarked(oldStyle, false); fListView->_SetStyleMarked(newStyle, true); } // ContainerListener interface virtual void ItemAdded(Shape* shape, int32 index) { } virtual void ItemRemoved(Shape* shape) { fListView->SetCurrentShape(NULL); } // ShapeStyleListener void SetShape(PathSourceShape* shape) { if (fShape == shape) return; if (fShape) fShape->RemoveListener(this); fShape = shape; if (fShape) fShape->AddListener(this); } PathSourceShape* CurrentShape() const { return fShape; } private: StyleListView* fListView; PathSourceShape* fShape; }; // #pragma mark - StyleListView::StyleListView(BRect frame, const char* name, BMessage* message, BHandler* target) : SimpleListView(frame, name, NULL, B_SINGLE_SELECTION_LIST), fMessage(message), fStyleContainer(NULL), fShapeContainer(NULL), fCommandStack(NULL), fCurrentColor(NULL), fCurrentShape(NULL), fShapeListener(new ShapeStyleListener(this)), fMenu(NULL) { SetTarget(target); } StyleListView::~StyleListView() { _MakeEmpty(); delete fMessage; if (fStyleContainer != NULL) fStyleContainer->RemoveListener(this); if (fShapeContainer != NULL) fShapeContainer->RemoveListener(fShapeListener); delete fShapeListener; } // #pragma mark - void StyleListView::MessageReceived(BMessage* message) { switch (message->what) { case MSG_ADD: { Style* style; AddStylesCommand* command; rgb_color color; if (fCurrentColor != NULL) color = fCurrentColor->Color(); else { color.red = 0; color.green = 0; color.blue = 0; color.alpha = 255; } new_style(color, fStyleContainer, &style, &command); fCommandStack->Perform(command); break; } case MSG_REMOVE: RemoveSelected(); break; case MSG_DUPLICATE: { int32 count = CountSelectedItems(); int32 index = 0; BList items; for (int32 i = 0; i < count; i++) { index = CurrentSelection(i); BListItem* item = ItemAt(index); if (item) items.AddItem((void*)item); } CopyItems(items, index + 1); break; } case MSG_RESET_TRANSFORMATION: { int32 count = CountSelectedItems(); BList gradients; for (int32 i = 0; i < count; i++) { StyleListItem* item = dynamic_cast( ItemAt(CurrentSelection(i))); if (item && item->style && item->style->Gradient()) { if (!gradients.AddItem((void*)item->style->Gradient())) break; } } count = gradients.CountItems(); if (count <= 0) break; Transformable* transformables[count]; for (int32 i = 0; i < count; i++) { Gradient* gradient = (Gradient*)gradients.ItemAtFast(i); transformables[i] = gradient; } ResetTransformationCommand* command = new ResetTransformationCommand(transformables, count); fCommandStack->Perform(command); break; } default: SimpleListView::MessageReceived(message); break; } } void StyleListView::SelectionChanged() { SimpleListView::SelectionChanged(); if (!fSyncingToSelection) { // NOTE: single selection list StyleListItem* item = dynamic_cast(ItemAt(CurrentSelection(0))); if (fMessage) { BMessage message(*fMessage); message.AddPointer("style", item ? (void*)item->style : NULL); Invoke(&message); } } _UpdateMenu(); } void StyleListView::MouseDown(BPoint where) { if (fCurrentShape == NULL) { SimpleListView::MouseDown(where); return; } bool handled = false; int32 index = IndexOf(where); StyleListItem* item = dynamic_cast(ItemAt(index)); if (item != NULL) { BRect itemFrame(ItemFrame(index)); itemFrame.right = itemFrame.left + kBorderOffset + kMarkWidth + kTextOffset / 2.0; Style* style = item->style; if (itemFrame.Contains(where)) { // set the style on the shape if (fCommandStack) { ::Command* command = new AssignStyleCommand( fCurrentShape, style); fCommandStack->Perform(command); } else { fCurrentShape->SetStyle(style); } handled = true; } } if (!handled) SimpleListView::MouseDown(where); } status_t StyleListView::ArchiveSelection(BMessage* into, bool deep) const { into->what = StyleListView::kSelectionArchiveCode; int32 count = CountSelectedItems(); for (int32 i = 0; i < count; i++) { StyleListItem* item = dynamic_cast( ItemAt(CurrentSelection(i))); if (item != NULL) { BMessage archive; if (item->style->Archive(&archive, true) == B_OK) into->AddMessage("style", &archive); } else return B_ERROR; } return B_OK; } bool StyleListView::InstantiateSelection(const BMessage* archive, int32 dropIndex) { if (archive->what != StyleListView::kSelectionArchiveCode || fCommandStack == NULL || fStyleContainer == NULL) return false; // Drag may have come from another instance, like in another window. // Reconstruct the Styles from the archive and add them at the drop // index. int index = 0; BList styles; while (true) { BMessage styleArchive; if (archive->FindMessage("style", index, &styleArchive) != B_OK) break; Style* style = new(std::nothrow) Style(&styleArchive); if (style == NULL) break; if (!styles.AddItem(style)) { delete style; break; } index++; } int32 count = styles.CountItems(); if (count == 0) return false; AddCommand