2 * Copyright 2006-2012, Stephan Aßmus <superstippi@gmx.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
7 #include "StyleListView.h"
12 #include <Application.h>
22 #include "AddStylesCommand.h"
23 #include "AssignStyleCommand.h"
24 #include "CurrentColor.h"
25 #include "CommandStack.h"
26 #include "GradientTransformable.h"
27 #include "MoveStylesCommand.h"
28 #include "RemoveStylesCommand.h"
31 #include "ResetTransformationCommand.h"
33 #include "ShapeContainer.h"
34 #include "Selection.h"
38 #undef B_TRANSLATION_CONTEXT
39 #define B_TRANSLATION_CONTEXT "Icon-O-Matic-StylesList"
44 static const float kMarkWidth
= 14.0;
45 static const float kBorderOffset
= 3.0;
46 static const float kTextOffset
= 4.0;
51 MSG_DUPLICATE
= 'dpst',
52 MSG_RESET_TRANSFORMATION
= 'rstr',
55 class StyleListItem
: public SimpleItem
,
58 StyleListItem(Style
* s
, StyleListView
* listView
, bool markEnabled
)
63 fMarkEnabled(markEnabled
),
69 virtual ~StyleListItem()
74 // SimpleItem interface
75 virtual void Draw(BView
* owner
, BRect itemFrame
, uint32 flags
)
77 SimpleItem::DrawBackground(owner
, itemFrame
, flags
);
81 owner
->SetHighColor(ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR
));
83 owner
->SetHighColor(ui_color(B_LIST_ITEM_TEXT_COLOR
));
85 owner
->GetFontHeight(&fh
);
86 BString
truncatedString(Text());
87 owner
->TruncateString(&truncatedString
, B_TRUNCATE_MIDDLE
,
88 itemFrame
.Width() - kBorderOffset
- kMarkWidth
- kTextOffset
90 float height
= itemFrame
.Height();
91 float textHeight
= fh
.ascent
+ fh
.descent
;
93 pos
.x
= itemFrame
.left
+ kBorderOffset
+ kMarkWidth
+ kTextOffset
;
94 pos
.y
= itemFrame
.top
+ ceilf((height
- textHeight
) / 2.0 + fh
.ascent
);
95 owner
->DrawString(truncatedString
.String(), pos
);
101 BRect markRect
= itemFrame
;
102 float markRectBorderTint
= B_DARKEN_1_TINT
;
103 float markRectFillTint
= 1.04;
104 float markTint
= B_DARKEN_4_TINT
;
106 rgb_color lowColor
= owner
->LowColor();
107 if (lowColor
.red
+ lowColor
.green
+ lowColor
.blue
< 128 * 3) {
108 markRectBorderTint
= B_LIGHTEN_2_TINT
;
109 markRectFillTint
= 0.85;
112 markRect
.left
+= kBorderOffset
;
113 markRect
.right
= markRect
.left
+ kMarkWidth
;
114 markRect
.top
= (markRect
.top
+ markRect
.bottom
- kMarkWidth
) / 2.0;
115 markRect
.bottom
= markRect
.top
+ kMarkWidth
;
116 owner
->SetHighColor(tint_color(owner
->LowColor(), markRectBorderTint
));
117 owner
->StrokeRect(markRect
);
118 markRect
.InsetBy(1, 1);
119 owner
->SetHighColor(tint_color(owner
->LowColor(), markRectFillTint
));
120 owner
->FillRect(markRect
);
122 markRect
.InsetBy(2, 2);
123 owner
->SetHighColor(tint_color(owner
->LowColor(),
125 owner
->SetPenSize(2);
126 owner
->StrokeLine(markRect
.LeftTop(), markRect
.RightBottom());
127 owner
->StrokeLine(markRect
.LeftBottom(), markRect
.RightTop());
128 owner
->SetPenSize(1);
132 // Observer interface
133 virtual void ObjectChanged(const Observable
* object
)
139 void SetStyle(Style
* s
)
145 style
->RemoveObserver(this);
146 style
->ReleaseReference();
152 style
->AcquireReference();
153 style
->AddObserver(this);
160 SetText(style
->Name());
164 void SetMarkEnabled(bool enabled
)
166 if (fMarkEnabled
== enabled
)
168 fMarkEnabled
= enabled
;
172 void SetMarked(bool marked
)
174 if (fMarked
== marked
)
182 if (fListView
->LockLooper()) {
183 fListView
->InvalidateItem(fListView
->IndexOf(this));
184 fListView
->UnlockLooper();
192 StyleListView
* fListView
;
198 class ShapeStyleListener
: public ShapeListener
,
199 public ShapeContainerListener
{
201 ShapeStyleListener(StyleListView
* listView
)
208 virtual ~ShapeStyleListener()
213 // ShapeListener interface
214 virtual void TransformerAdded(Transformer
* t
, int32 index
)
218 virtual void TransformerRemoved(Transformer
* t
)
222 virtual void StyleChanged(Style
* oldStyle
, Style
* newStyle
)
224 fListView
->_SetStyleMarked(oldStyle
, false);
225 fListView
->_SetStyleMarked(newStyle
, true);
228 // ShapeContainerListener interface
229 virtual void ShapeAdded(Shape
* shape
, int32 index
)
233 virtual void ShapeRemoved(Shape
* shape
)
235 fListView
->SetCurrentShape(NULL
);
238 // ShapeStyleListener
239 void SetShape(Shape
* shape
)
245 fShape
->RemoveListener(this);
250 fShape
->AddListener(this);
253 Shape
* CurrentShape() const
259 StyleListView
* fListView
;
267 StyleListView::StyleListView(BRect frame
, const char* name
, BMessage
* message
,
270 SimpleListView(frame
, name
, NULL
, B_SINGLE_SELECTION_LIST
),
272 fStyleContainer(NULL
),
273 fShapeContainer(NULL
),
278 fShapeListener(new ShapeStyleListener(this)),
286 StyleListView::~StyleListView()
291 if (fStyleContainer
!= NULL
)
292 fStyleContainer
->RemoveListener(this);
294 if (fShapeContainer
!= NULL
)
295 fShapeContainer
->RemoveListener(fShapeListener
);
297 delete fShapeListener
;
305 StyleListView::MessageReceived(BMessage
* message
)
307 switch (message
->what
) {
311 AddStylesCommand
* command
;
313 if (fCurrentColor
!= NULL
)
314 color
= fCurrentColor
->Color();
321 new_style(color
, fStyleContainer
, &style
, &command
);
322 fCommandStack
->Perform(command
);
332 int32 count
= CountSelectedItems();
335 for (int32 i
= 0; i
< count
; i
++) {
336 index
= CurrentSelection(i
);
337 BListItem
* item
= ItemAt(index
);
339 items
.AddItem((void*)item
);
341 CopyItems(items
, index
+ 1);
345 case MSG_RESET_TRANSFORMATION
:
347 int32 count
= CountSelectedItems();
349 for (int32 i
= 0; i
< count
; i
++) {
350 StyleListItem
* item
= dynamic_cast<StyleListItem
*>(
351 ItemAt(CurrentSelection(i
)));
352 if (item
&& item
->style
&& item
->style
->Gradient()) {
353 if (!gradients
.AddItem((void*)item
->style
->Gradient()))
357 count
= gradients
.CountItems();
361 Transformable
* transformables
[count
];
362 for (int32 i
= 0; i
< count
; i
++) {
363 Gradient
* gradient
= (Gradient
*)gradients
.ItemAtFast(i
);
364 transformables
[i
] = gradient
;
367 ResetTransformationCommand
* command
368 = new ResetTransformationCommand(transformables
, count
);
370 fCommandStack
->Perform(command
);
375 SimpleListView::MessageReceived(message
);
382 StyleListView::SelectionChanged()
384 SimpleListView::SelectionChanged();
386 if (!fSyncingToSelection
) {
387 // NOTE: single selection list
389 = dynamic_cast<StyleListItem
*>(ItemAt(CurrentSelection(0)));
391 BMessage
message(*fMessage
);
392 message
.AddPointer("style", item
? (void*)item
->style
: NULL
);
402 StyleListView::MouseDown(BPoint where
)
404 if (fCurrentShape
== NULL
) {
405 SimpleListView::MouseDown(where
);
409 bool handled
= false;
410 int32 index
= IndexOf(where
);
411 StyleListItem
* item
= dynamic_cast<StyleListItem
*>(ItemAt(index
));
413 BRect
itemFrame(ItemFrame(index
));
414 itemFrame
.right
= itemFrame
.left
+ kBorderOffset
+ kMarkWidth
416 Style
* style
= item
->style
;
417 if (itemFrame
.Contains(where
)) {
418 // set the style on the shape
420 ::Command
* command
= new AssignStyleCommand(
421 fCurrentShape
, style
);
422 fCommandStack
->Perform(command
);
424 fCurrentShape
->SetStyle(style
);
431 SimpleListView::MouseDown(where
);
436 StyleListView::MakeDragMessage(BMessage
* message
) const
438 SimpleListView::MakeDragMessage(message
);
439 message
->AddPointer("container", fStyleContainer
);
440 int32 count
= CountSelectedItems();
441 for (int32 i
= 0; i
< count
; i
++) {
442 StyleListItem
* item
= dynamic_cast<StyleListItem
*>(
443 ItemAt(CurrentSelection(i
)));
445 message
->AddPointer("style", (void*)item
->style
);
447 if (item
->style
->Archive(&archive
, true) == B_OK
)
448 message
->AddMessage("style archive", &archive
);
456 StyleListView::AcceptDragMessage(const BMessage
* message
) const
458 return SimpleListView::AcceptDragMessage(message
);
463 StyleListView::SetDropTargetRect(const BMessage
* message
, BPoint where
)
465 SimpleListView::SetDropTargetRect(message
, where
);
470 StyleListView::HandleDropMessage(const BMessage
* message
, int32 dropIndex
)
472 // Let SimpleListView handle drag-sorting (when drag came from ourself)
473 if (SimpleListView::HandleDropMessage(message
, dropIndex
))
476 if (fCommandStack
== NULL
|| fStyleContainer
== NULL
)
479 // Drag may have come from another instance, like in another window.
480 // Reconstruct the Styles from the archive and add them at the drop
486 if (message
->FindMessage("style archive", index
, &archive
) != B_OK
)
488 Style
* style
= new(std::nothrow
) Style(&archive
);
492 if (!styles
.AddItem(style
)) {
500 int32 count
= styles
.CountItems();
504 AddStylesCommand
* command
= new(std::nothrow
) AddStylesCommand(
505 fStyleContainer
, (Style
**)styles
.Items(), count
, dropIndex
);
507 if (command
== NULL
) {
508 for (int32 i
= 0; i
< count
; i
++)
509 delete (Style
*)styles
.ItemAtFast(i
);
513 fCommandStack
->Perform(command
);
520 StyleListView::MoveItems(BList
& items
, int32 toIndex
)
522 if (fCommandStack
== NULL
|| fStyleContainer
== NULL
)
525 int32 count
= items
.CountItems();
526 Style
** styles
= new (nothrow
) Style
*[count
];
530 for (int32 i
= 0; i
< count
; i
++) {
532 = dynamic_cast<StyleListItem
*>((BListItem
*)items
.ItemAtFast(i
));
533 styles
[i
] = item
? item
->style
: NULL
;
536 MoveStylesCommand
* command
= new (nothrow
) MoveStylesCommand(
537 fStyleContainer
, styles
, count
, toIndex
);
538 if (command
== NULL
) {
543 fCommandStack
->Perform(command
);
548 StyleListView::CopyItems(BList
& items
, int32 toIndex
)
550 if (fCommandStack
== NULL
|| fStyleContainer
== NULL
)
553 int32 count
= items
.CountItems();
554 Style
* styles
[count
];
556 for (int32 i
= 0; i
< count
; i
++) {
558 = dynamic_cast<StyleListItem
*>((BListItem
*)items
.ItemAtFast(i
));
559 styles
[i
] = item
? new (nothrow
) Style(*item
->style
) : NULL
;
562 AddStylesCommand
* command
563 = new (nothrow
) AddStylesCommand(fStyleContainer
,
564 styles
, count
, toIndex
);
566 for (int32 i
= 0; i
< count
; i
++)
571 fCommandStack
->Perform(command
);
576 StyleListView::RemoveItemList(BList
& items
)
578 if (!fCommandStack
|| !fStyleContainer
)
581 int32 count
= items
.CountItems();
582 Style
* styles
[count
];
583 for (int32 i
= 0; i
< count
; i
++) {
584 StyleListItem
* item
= dynamic_cast<StyleListItem
*>(
585 (BListItem
*)items
.ItemAtFast(i
));
587 styles
[i
] = item
->style
;
592 RemoveStylesCommand
* command
593 = new (nothrow
) RemoveStylesCommand(fStyleContainer
,
595 fCommandStack
->Perform(command
);
600 StyleListView::CloneItem(int32 index
) const
602 if (StyleListItem
* item
= dynamic_cast<StyleListItem
*>(ItemAt(index
))) {
603 return new StyleListItem(item
->style
,
604 const_cast<StyleListView
*>(this),
605 fCurrentShape
!= NULL
);
612 StyleListView::IndexOfSelectable(Selectable
* selectable
) const
614 Style
* style
= dynamic_cast<Style
*>(selectable
);
618 int count
= CountItems();
619 for (int32 i
= 0; i
< count
; i
++) {
620 if (SelectableFor(ItemAt(i
)) == style
)
629 StyleListView::SelectableFor(BListItem
* item
) const
631 StyleListItem
* styleItem
= dynamic_cast<StyleListItem
*>(item
);
632 if (styleItem
!= NULL
)
633 return styleItem
->style
;
642 StyleListView::StyleAdded(Style
* style
, int32 index
)
644 // NOTE: we are in the thread that messed with the
645 // StyleContainer, so no need to lock the
646 // container, when this is changed to asynchronous
647 // notifications, then it would need to be read-locked!
651 if (_AddStyle(style
, index
))
659 StyleListView::StyleRemoved(Style
* style
)
661 // NOTE: we are in the thread that messed with the
662 // StyleContainer, so no need to lock the
663 // container, when this is changed to asynchronous
664 // notifications, then it would need to be read-locked!
668 // NOTE: we're only interested in Style objects
679 StyleListView::SetMenu(BMenu
* menu
)
688 fAddMI
= new BMenuItem(B_TRANSLATE("Add"), new BMessage(MSG_ADD
));
689 fMenu
->AddItem(fAddMI
);
691 fMenu
->AddSeparatorItem();
693 fDuplicateMI
= new BMenuItem(B_TRANSLATE("Duplicate"),
694 new BMessage(MSG_DUPLICATE
));
695 fMenu
->AddItem(fDuplicateMI
);
697 fResetTransformationMI
= new BMenuItem(B_TRANSLATE("Reset transformation"),
698 new BMessage(MSG_RESET_TRANSFORMATION
));
699 fMenu
->AddItem(fResetTransformationMI
);
701 fMenu
->AddSeparatorItem();
703 fRemoveMI
= new BMenuItem(B_TRANSLATE("Remove"), new BMessage(MSG_REMOVE
));
704 fMenu
->AddItem(fRemoveMI
);
706 fMenu
->SetTargetForItems(this);
713 StyleListView::SetStyleContainer(StyleContainer
* container
)
715 if (fStyleContainer
== container
)
718 // detach from old container
719 if (fStyleContainer
!= NULL
)
720 fStyleContainer
->RemoveListener(this);
724 fStyleContainer
= container
;
726 if (fStyleContainer
== NULL
)
729 fStyleContainer
->AddListener(this);
732 int32 count
= fStyleContainer
->CountStyles();
733 for (int32 i
= 0; i
< count
; i
++)
734 _AddStyle(fStyleContainer
->StyleAtFast(i
), i
);
739 StyleListView::SetShapeContainer(ShapeContainer
* container
)
741 if (fShapeContainer
== container
)
744 // detach from old container
746 fShapeContainer
->RemoveListener(fShapeListener
);
748 fShapeContainer
= container
;
751 fShapeContainer
->AddListener(fShapeListener
);
756 StyleListView::SetCommandStack(CommandStack
* stack
)
758 fCommandStack
= stack
;
763 StyleListView::SetCurrentColor(CurrentColor
* color
)
765 fCurrentColor
= color
;
770 StyleListView::SetCurrentShape(Shape
* shape
)
772 if (fCurrentShape
== shape
)
775 fCurrentShape
= shape
;
776 fShapeListener
->SetShape(shape
);
786 StyleListView::_AddStyle(Style
* style
, int32 index
)
789 return AddItem(new StyleListItem(
790 style
, this, fCurrentShape
!= NULL
), index
);
797 StyleListView::_RemoveStyle(Style
* style
)
799 StyleListItem
* item
= _ItemForStyle(style
);
800 if (item
!= NULL
&& RemoveItem(item
)) {
809 StyleListView::_ItemForStyle(Style
* style
) const
811 int count
= CountItems();
812 for (int32 i
= 0; i
< count
; i
++) {
813 StyleListItem
* item
= dynamic_cast<StyleListItem
*>(ItemAt(i
));
816 if (item
->style
== style
)
827 StyleListView::_UpdateMarks()
829 int32 count
= CountItems();
831 // enable display of marks and mark items whoes
832 // style is contained in fCurrentShape
833 for (int32 i
= 0; i
< count
; i
++) {
834 StyleListItem
* item
= dynamic_cast<StyleListItem
*>(ItemAt(i
));
837 item
->SetMarkEnabled(true);
838 item
->SetMarked(fCurrentShape
->Style() == item
->style
);
841 // disable display of marks
842 for (int32 i
= 0; i
< count
; i
++) {
843 StyleListItem
* item
= dynamic_cast<StyleListItem
*>(ItemAt(i
));
846 item
->SetMarkEnabled(false);
855 StyleListView::_SetStyleMarked(Style
* style
, bool marked
)
857 StyleListItem
* item
= _ItemForStyle(style
);
859 item
->SetMarked(marked
);
864 StyleListView::_UpdateMenu()
869 bool gotSelection
= CurrentSelection(0) >= 0;
871 fDuplicateMI
->SetEnabled(gotSelection
);
872 // TODO: only enable fResetTransformationMI if styles
873 // with gradients are selected!
874 fResetTransformationMI
->SetEnabled(gotSelection
);
875 fRemoveMI
->SetEnabled(gotSelection
);