HaikuDepot: notify work status from main window
[haiku.git] / src / apps / icon-o-matic / gui / StyleListView.cpp
blob9565b7bf49ff95d6f3a0c1ac83356f6cb07bed0d
1 /*
2 * Copyright 2006-2012, Stephan Aßmus <superstippi@gmx.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
7 #include "StyleListView.h"
9 #include <new>
10 #include <stdio.h>
12 #include <Application.h>
13 #include <Catalog.h>
14 #include <ListItem.h>
15 #include <Locale.h>
16 #include <Menu.h>
17 #include <MenuItem.h>
18 #include <Message.h>
19 #include <Mime.h>
20 #include <Window.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"
29 #include "Style.h"
30 #include "Observer.h"
31 #include "ResetTransformationCommand.h"
32 #include "Shape.h"
33 #include "ShapeContainer.h"
34 #include "Selection.h"
35 #include "Util.h"
38 #undef B_TRANSLATION_CONTEXT
39 #define B_TRANSLATION_CONTEXT "Icon-O-Matic-StylesList"
42 using std::nothrow;
44 static const float kMarkWidth = 14.0;
45 static const float kBorderOffset = 3.0;
46 static const float kTextOffset = 4.0;
48 enum {
49 MSG_ADD = 'adst',
50 MSG_REMOVE = 'rmst',
51 MSG_DUPLICATE = 'dpst',
52 MSG_RESET_TRANSFORMATION = 'rstr',
55 class StyleListItem : public SimpleItem,
56 public Observer {
57 public:
58 StyleListItem(Style* s, StyleListView* listView, bool markEnabled)
60 SimpleItem(""),
61 style(NULL),
62 fListView(listView),
63 fMarkEnabled(markEnabled),
64 fMarked(false)
66 SetStyle(s);
69 virtual ~StyleListItem()
71 SetStyle(NULL);
74 // SimpleItem interface
75 virtual void Draw(BView* owner, BRect itemFrame, uint32 flags)
77 SimpleItem::DrawBackground(owner, itemFrame, flags);
79 // text
80 if (IsSelected())
81 owner->SetHighColor(ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR));
82 else
83 owner->SetHighColor(ui_color(B_LIST_ITEM_TEXT_COLOR));
84 font_height fh;
85 owner->GetFontHeight(&fh);
86 BString truncatedString(Text());
87 owner->TruncateString(&truncatedString, B_TRUNCATE_MIDDLE,
88 itemFrame.Width() - kBorderOffset - kMarkWidth - kTextOffset
89 - kBorderOffset);
90 float height = itemFrame.Height();
91 float textHeight = fh.ascent + fh.descent;
92 BPoint pos;
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);
97 if (!fMarkEnabled)
98 return;
100 // mark
101 BRect markRect = itemFrame;
102 float markRectBorderTint = B_DARKEN_1_TINT;
103 float markRectFillTint = 1.04;
104 float markTint = B_DARKEN_4_TINT;
105 // Dark Themes
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;
110 markTint = 0.1;
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);
121 if (fMarked) {
122 markRect.InsetBy(2, 2);
123 owner->SetHighColor(tint_color(owner->LowColor(),
124 markTint));
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)
135 UpdateText();
138 // StyleListItem
139 void SetStyle(Style* s)
141 if (s == style)
142 return;
144 if (style) {
145 style->RemoveObserver(this);
146 style->ReleaseReference();
149 style = s;
151 if (style) {
152 style->AcquireReference();
153 style->AddObserver(this);
154 UpdateText();
158 void UpdateText()
160 SetText(style->Name());
161 Invalidate();
164 void SetMarkEnabled(bool enabled)
166 if (fMarkEnabled == enabled)
167 return;
168 fMarkEnabled = enabled;
169 Invalidate();
172 void SetMarked(bool marked)
174 if (fMarked == marked)
175 return;
176 fMarked = marked;
177 Invalidate();
180 void Invalidate()
182 if (fListView->LockLooper()) {
183 fListView->InvalidateItem(fListView->IndexOf(this));
184 fListView->UnlockLooper();
188 public:
189 Style* style;
191 private:
192 StyleListView* fListView;
193 bool fMarkEnabled;
194 bool fMarked;
198 class ShapeStyleListener : public ShapeListener,
199 public ShapeContainerListener {
200 public:
201 ShapeStyleListener(StyleListView* listView)
203 fListView(listView),
204 fShape(NULL)
208 virtual ~ShapeStyleListener()
210 SetShape(NULL);
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)
241 if (fShape == shape)
242 return;
244 if (fShape)
245 fShape->RemoveListener(this);
247 fShape = shape;
249 if (fShape)
250 fShape->AddListener(this);
253 Shape* CurrentShape() const
255 return fShape;
258 private:
259 StyleListView* fListView;
260 Shape* fShape;
264 // #pragma mark -
267 StyleListView::StyleListView(BRect frame, const char* name, BMessage* message,
268 BHandler* target)
270 SimpleListView(frame, name, NULL, B_SINGLE_SELECTION_LIST),
271 fMessage(message),
272 fStyleContainer(NULL),
273 fShapeContainer(NULL),
274 fCommandStack(NULL),
275 fCurrentColor(NULL),
277 fCurrentShape(NULL),
278 fShapeListener(new ShapeStyleListener(this)),
280 fMenu(NULL)
282 SetTarget(target);
286 StyleListView::~StyleListView()
288 _MakeEmpty();
289 delete fMessage;
291 if (fStyleContainer != NULL)
292 fStyleContainer->RemoveListener(this);
294 if (fShapeContainer != NULL)
295 fShapeContainer->RemoveListener(fShapeListener);
297 delete fShapeListener;
301 // #pragma mark -
304 void
305 StyleListView::MessageReceived(BMessage* message)
307 switch (message->what) {
308 case MSG_ADD:
310 Style* style;
311 AddStylesCommand* command;
312 rgb_color color;
313 if (fCurrentColor != NULL)
314 color = fCurrentColor->Color();
315 else {
316 color.red = 0;
317 color.green = 0;
318 color.blue = 0;
319 color.alpha = 255;
321 new_style(color, fStyleContainer, &style, &command);
322 fCommandStack->Perform(command);
323 break;
326 case MSG_REMOVE:
327 RemoveSelected();
328 break;
330 case MSG_DUPLICATE:
332 int32 count = CountSelectedItems();
333 int32 index = 0;
334 BList items;
335 for (int32 i = 0; i < count; i++) {
336 index = CurrentSelection(i);
337 BListItem* item = ItemAt(index);
338 if (item)
339 items.AddItem((void*)item);
341 CopyItems(items, index + 1);
342 break;
345 case MSG_RESET_TRANSFORMATION:
347 int32 count = CountSelectedItems();
348 BList gradients;
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()))
354 break;
357 count = gradients.CountItems();
358 if (count <= 0)
359 break;
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);
371 break;
374 default:
375 SimpleListView::MessageReceived(message);
376 break;
381 void
382 StyleListView::SelectionChanged()
384 SimpleListView::SelectionChanged();
386 if (!fSyncingToSelection) {
387 // NOTE: single selection list
388 StyleListItem* item
389 = dynamic_cast<StyleListItem*>(ItemAt(CurrentSelection(0)));
390 if (fMessage) {
391 BMessage message(*fMessage);
392 message.AddPointer("style", item ? (void*)item->style : NULL);
393 Invoke(&message);
397 _UpdateMenu();
401 void
402 StyleListView::MouseDown(BPoint where)
404 if (fCurrentShape == NULL) {
405 SimpleListView::MouseDown(where);
406 return;
409 bool handled = false;
410 int32 index = IndexOf(where);
411 StyleListItem* item = dynamic_cast<StyleListItem*>(ItemAt(index));
412 if (item != NULL) {
413 BRect itemFrame(ItemFrame(index));
414 itemFrame.right = itemFrame.left + kBorderOffset + kMarkWidth
415 + kTextOffset / 2.0;
416 Style* style = item->style;
417 if (itemFrame.Contains(where)) {
418 // set the style on the shape
419 if (fCommandStack) {
420 ::Command* command = new AssignStyleCommand(
421 fCurrentShape, style);
422 fCommandStack->Perform(command);
423 } else {
424 fCurrentShape->SetStyle(style);
426 handled = true;
430 if (!handled)
431 SimpleListView::MouseDown(where);
435 void
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)));
444 if (item != NULL) {
445 message->AddPointer("style", (void*)item->style);
446 BMessage archive;
447 if (item->style->Archive(&archive, true) == B_OK)
448 message->AddMessage("style archive", &archive);
449 } else
450 break;
455 bool
456 StyleListView::AcceptDragMessage(const BMessage* message) const
458 return SimpleListView::AcceptDragMessage(message);
462 void
463 StyleListView::SetDropTargetRect(const BMessage* message, BPoint where)
465 SimpleListView::SetDropTargetRect(message, where);
469 bool
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))
474 return true;
476 if (fCommandStack == NULL || fStyleContainer == NULL)
477 return false;
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
481 // index.
482 int index = 0;
483 BList styles;
484 while (true) {
485 BMessage archive;
486 if (message->FindMessage("style archive", index, &archive) != B_OK)
487 break;
488 Style* style = new(std::nothrow) Style(&archive);
489 if (style == NULL)
490 break;
492 if (!styles.AddItem(style)) {
493 delete style;
494 break;
497 index++;
500 int32 count = styles.CountItems();
501 if (count == 0)
502 return false;
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);
510 return false;
513 fCommandStack->Perform(command);
515 return true;
519 void
520 StyleListView::MoveItems(BList& items, int32 toIndex)
522 if (fCommandStack == NULL || fStyleContainer == NULL)
523 return;
525 int32 count = items.CountItems();
526 Style** styles = new (nothrow) Style*[count];
527 if (styles == NULL)
528 return;
530 for (int32 i = 0; i < count; i++) {
531 StyleListItem* item
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) {
539 delete[] styles;
540 return;
543 fCommandStack->Perform(command);
547 void
548 StyleListView::CopyItems(BList& items, int32 toIndex)
550 if (fCommandStack == NULL || fStyleContainer == NULL)
551 return;
553 int32 count = items.CountItems();
554 Style* styles[count];
556 for (int32 i = 0; i < count; i++) {
557 StyleListItem* item
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);
565 if (!command) {
566 for (int32 i = 0; i < count; i++)
567 delete styles[i];
568 return;
571 fCommandStack->Perform(command);
575 void
576 StyleListView::RemoveItemList(BList& items)
578 if (!fCommandStack || !fStyleContainer)
579 return;
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));
586 if (item)
587 styles[i] = item->style;
588 else
589 styles[i] = NULL;
592 RemoveStylesCommand* command
593 = new (nothrow) RemoveStylesCommand(fStyleContainer,
594 styles, count);
595 fCommandStack->Perform(command);
599 BListItem*
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);
607 return NULL;
611 int32
612 StyleListView::IndexOfSelectable(Selectable* selectable) const
614 Style* style = dynamic_cast<Style*>(selectable);
615 if (style == NULL)
616 return -1;
618 int count = CountItems();
619 for (int32 i = 0; i < count; i++) {
620 if (SelectableFor(ItemAt(i)) == style)
621 return i;
624 return -1;
628 Selectable*
629 StyleListView::SelectableFor(BListItem* item) const
631 StyleListItem* styleItem = dynamic_cast<StyleListItem*>(item);
632 if (styleItem != NULL)
633 return styleItem->style;
634 return NULL;
638 // #pragma mark -
641 void
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!
648 if (!LockLooper())
649 return;
651 if (_AddStyle(style, index))
652 Select(index);
654 UnlockLooper();
658 void
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!
665 if (!LockLooper())
666 return;
668 // NOTE: we're only interested in Style objects
669 _RemoveStyle(style);
671 UnlockLooper();
675 // #pragma mark -
678 void
679 StyleListView::SetMenu(BMenu* menu)
681 if (fMenu == menu)
682 return;
684 fMenu = menu;
685 if (fMenu == NULL)
686 return;
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);
708 _UpdateMenu();
712 void
713 StyleListView::SetStyleContainer(StyleContainer* container)
715 if (fStyleContainer == container)
716 return;
718 // detach from old container
719 if (fStyleContainer != NULL)
720 fStyleContainer->RemoveListener(this);
722 _MakeEmpty();
724 fStyleContainer = container;
726 if (fStyleContainer == NULL)
727 return;
729 fStyleContainer->AddListener(this);
731 // sync
732 int32 count = fStyleContainer->CountStyles();
733 for (int32 i = 0; i < count; i++)
734 _AddStyle(fStyleContainer->StyleAtFast(i), i);
738 void
739 StyleListView::SetShapeContainer(ShapeContainer* container)
741 if (fShapeContainer == container)
742 return;
744 // detach from old container
745 if (fShapeContainer)
746 fShapeContainer->RemoveListener(fShapeListener);
748 fShapeContainer = container;
750 if (fShapeContainer)
751 fShapeContainer->AddListener(fShapeListener);
755 void
756 StyleListView::SetCommandStack(CommandStack* stack)
758 fCommandStack = stack;
762 void
763 StyleListView::SetCurrentColor(CurrentColor* color)
765 fCurrentColor = color;
769 void
770 StyleListView::SetCurrentShape(Shape* shape)
772 if (fCurrentShape == shape)
773 return;
775 fCurrentShape = shape;
776 fShapeListener->SetShape(shape);
778 _UpdateMarks();
782 // #pragma mark -
785 bool
786 StyleListView::_AddStyle(Style* style, int32 index)
788 if (style != NULL) {
789 return AddItem(new StyleListItem(
790 style, this, fCurrentShape != NULL), index);
792 return false;
796 bool
797 StyleListView::_RemoveStyle(Style* style)
799 StyleListItem* item = _ItemForStyle(style);
800 if (item != NULL && RemoveItem(item)) {
801 delete item;
802 return true;
804 return false;
808 StyleListItem*
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));
814 if (item == NULL)
815 continue;
816 if (item->style == style)
817 return item;
819 return NULL;
823 // #pragma mark -
826 void
827 StyleListView::_UpdateMarks()
829 int32 count = CountItems();
830 if (fCurrentShape) {
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));
835 if (item == NULL)
836 continue;
837 item->SetMarkEnabled(true);
838 item->SetMarked(fCurrentShape->Style() == item->style);
840 } else {
841 // disable display of marks
842 for (int32 i = 0; i < count; i++) {
843 StyleListItem* item = dynamic_cast<StyleListItem*>(ItemAt(i));
844 if (item == NULL)
845 continue;
846 item->SetMarkEnabled(false);
850 Invalidate();
854 void
855 StyleListView::_SetStyleMarked(Style* style, bool marked)
857 StyleListItem* item = _ItemForStyle(style);
858 if (item != NULL)
859 item->SetMarked(marked);
863 void
864 StyleListView::_UpdateMenu()
866 if (fMenu == NULL)
867 return;
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);