xref: /haiku/src/apps/icon-o-matic/gui/StyleListView.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
1 /*
2  * Copyright 2006-2012, Stephan Aßmus <superstippi@gmx.de>.
3  * Copyright 2023, Haiku.
4  * All rights reserved. Distributed under the terms of the MIT License.
5  *
6  * Authors:
7  *		Zardshard
8  */
9 
10 
11 #include "StyleListView.h"
12 
13 #include <new>
14 #include <stdio.h>
15 
16 #include <Application.h>
17 #include <Catalog.h>
18 #include <ListItem.h>
19 #include <Locale.h>
20 #include <Menu.h>
21 #include <MenuItem.h>
22 #include <Message.h>
23 #include <Mime.h>
24 #include <Window.h>
25 
26 #include "AddStylesCommand.h"
27 #include "AssignStyleCommand.h"
28 #include "CurrentColor.h"
29 #include "CommandStack.h"
30 #include "GradientTransformable.h"
31 #include "MoveStylesCommand.h"
32 #include "PathSourceShape.h"
33 #include "RemoveStylesCommand.h"
34 #include "Style.h"
35 #include "Observer.h"
36 #include "ResetTransformationCommand.h"
37 #include "Shape.h"
38 #include "Selection.h"
39 #include "Util.h"
40 
41 
42 #undef B_TRANSLATION_CONTEXT
43 #define B_TRANSLATION_CONTEXT "Icon-O-Matic-StylesList"
44 
45 
46 using std::nothrow;
47 
48 static const float kMarkWidth		= 14.0;
49 static const float kBorderOffset	= 3.0;
50 static const float kTextOffset		= 4.0;
51 
52 enum {
53 	MSG_ADD							= 'adst',
54 	MSG_REMOVE						= 'rmst',
55 	MSG_DUPLICATE					= 'dpst',
56 	MSG_RESET_TRANSFORMATION		= 'rstr',
57 };
58 
59 class StyleListItem : public SimpleItem,
60 					 public Observer {
61 public:
62 	StyleListItem(Style* s, StyleListView* listView, bool markEnabled)
63 		:
64 		SimpleItem(""),
65 		style(NULL),
66 		fListView(listView),
67 		fMarkEnabled(markEnabled),
68 		fMarked(false)
69 	{
70 		SetStyle(s);
71 	}
72 
73 	virtual ~StyleListItem()
74 	{
75 		SetStyle(NULL);
76 	}
77 
78 	// SimpleItem interface
79 	virtual	void Draw(BView* owner, BRect itemFrame, uint32 flags)
80 	{
81 		SimpleItem::DrawBackground(owner, itemFrame, flags);
82 
83 		// text
84 		if (IsSelected())
85 			owner->SetHighColor(ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR));
86 		else
87 			owner->SetHighColor(ui_color(B_LIST_ITEM_TEXT_COLOR));
88 		font_height fh;
89 		owner->GetFontHeight(&fh);
90 		BString truncatedString(Text());
91 		owner->TruncateString(&truncatedString, B_TRUNCATE_MIDDLE,
92 			itemFrame.Width() - kBorderOffset - kMarkWidth - kTextOffset
93 			- kBorderOffset);
94 		float height = itemFrame.Height();
95 		float textHeight = fh.ascent + fh.descent;
96 		BPoint pos;
97 		pos.x = itemFrame.left + kBorderOffset + kMarkWidth + kTextOffset;
98 		pos.y = itemFrame.top + ceilf((height - textHeight) / 2.0 + fh.ascent);
99 		owner->DrawString(truncatedString.String(), pos);
100 
101 		if (!fMarkEnabled)
102 			return;
103 
104 		// mark
105 		BRect markRect = itemFrame;
106 		float markRectBorderTint = B_DARKEN_1_TINT;
107 		float markRectFillTint = 1.04;
108 		float markTint = B_DARKEN_4_TINT;
109 					// Dark Themes
110 		rgb_color lowColor = owner->LowColor();
111 		if (lowColor.red + lowColor.green + lowColor.blue < 128 * 3) {
112 			markRectBorderTint = B_LIGHTEN_2_TINT;
113 			markRectFillTint = 0.85;
114 			markTint = 0.1;
115 		}
116 		markRect.left += kBorderOffset;
117 		markRect.right = markRect.left + kMarkWidth;
118 		markRect.top = (markRect.top + markRect.bottom - kMarkWidth) / 2.0;
119 		markRect.bottom = markRect.top + kMarkWidth;
120 		owner->SetHighColor(tint_color(owner->LowColor(), markRectBorderTint));
121 		owner->StrokeRect(markRect);
122 		markRect.InsetBy(1, 1);
123 		owner->SetHighColor(tint_color(owner->LowColor(), markRectFillTint));
124 		owner->FillRect(markRect);
125 		if (fMarked) {
126 			markRect.InsetBy(2, 2);
127 			owner->SetHighColor(tint_color(owner->LowColor(),
128 				markTint));
129 			owner->SetPenSize(2);
130 			owner->StrokeLine(markRect.LeftTop(), markRect.RightBottom());
131 			owner->StrokeLine(markRect.LeftBottom(), markRect.RightTop());
132 			owner->SetPenSize(1);
133 		}
134 	}
135 
136 	// Observer interface
137 	virtual	void	ObjectChanged(const Observable* object)
138 	{
139 		UpdateText();
140 	}
141 
142 	// StyleListItem
143 	void SetStyle(Style* s)
144 	{
145 		if (s == style)
146 			return;
147 
148 		if (style) {
149 			style->RemoveObserver(this);
150 			style->ReleaseReference();
151 		}
152 
153 		style = s;
154 
155 		if (style) {
156 			style->AcquireReference();
157 			style->AddObserver(this);
158 			UpdateText();
159 		}
160 	}
161 
162 	void UpdateText()
163 	{
164 		SetText(style->Name());
165 		Invalidate();
166 	}
167 
168 	void SetMarkEnabled(bool enabled)
169 	{
170 		if (fMarkEnabled == enabled)
171 			return;
172 		fMarkEnabled = enabled;
173 		Invalidate();
174 	}
175 
176 	void SetMarked(bool marked)
177 	{
178 		if (fMarked == marked)
179 			return;
180 		fMarked = marked;
181 		Invalidate();
182 	}
183 
184 	void Invalidate()
185 	{
186 		if (fListView->LockLooper()) {
187 			fListView->InvalidateItem(fListView->IndexOf(this));
188 			fListView->UnlockLooper();
189 		}
190 	}
191 
192 public:
193 	Style*			style;
194 
195 private:
196 	StyleListView*	fListView;
197 	bool			fMarkEnabled;
198 	bool			fMarked;
199 };
200 
201 
202 class ShapeStyleListener : public ShapeListener,
203 	public ContainerListener<Shape> {
204 public:
205 	ShapeStyleListener(StyleListView* listView)
206 		:
207 		fListView(listView),
208 		fShape(NULL)
209 	{
210 	}
211 
212 	virtual ~ShapeStyleListener()
213 	{
214 		SetShape(NULL);
215 	}
216 
217 	// ShapeListener interface
218 	virtual	void TransformerAdded(Transformer* t, int32 index)
219 	{
220 	}
221 
222 	virtual	void TransformerRemoved(Transformer* t)
223 	{
224 	}
225 
226 	virtual void StyleChanged(Style* oldStyle, Style* newStyle)
227 	{
228 		fListView->_SetStyleMarked(oldStyle, false);
229 		fListView->_SetStyleMarked(newStyle, true);
230 	}
231 
232 	// ContainerListener<Shape> interface
233 	virtual void ItemAdded(Shape* shape, int32 index)
234 	{
235 	}
236 
237 	virtual void ItemRemoved(Shape* shape)
238 	{
239 		fListView->SetCurrentShape(NULL);
240 	}
241 
242 	// ShapeStyleListener
243 	void SetShape(PathSourceShape* shape)
244 	{
245 		if (fShape == shape)
246 			return;
247 
248 		if (fShape)
249 			fShape->RemoveListener(this);
250 
251 		fShape = shape;
252 
253 		if (fShape)
254 			fShape->AddListener(this);
255 	}
256 
257 	PathSourceShape* CurrentShape() const
258 	{
259 		return fShape;
260 	}
261 
262 private:
263 	StyleListView*		fListView;
264 	PathSourceShape*	fShape;
265 };
266 
267 
268 // #pragma mark -
269 
270 
271 StyleListView::StyleListView(BRect frame, const char* name, BMessage* message,
272 	BHandler* target)
273 	:
274 	SimpleListView(frame, name, NULL, B_SINGLE_SELECTION_LIST),
275 	fMessage(message),
276 	fStyleContainer(NULL),
277 	fShapeContainer(NULL),
278 	fCommandStack(NULL),
279 	fCurrentColor(NULL),
280 
281 	fCurrentShape(NULL),
282 	fShapeListener(new ShapeStyleListener(this)),
283 
284 	fMenu(NULL)
285 {
286 	SetTarget(target);
287 }
288 
289 
290 StyleListView::~StyleListView()
291 {
292 	_MakeEmpty();
293 	delete fMessage;
294 
295 	if (fStyleContainer != NULL)
296 		fStyleContainer->RemoveListener(this);
297 
298 	if (fShapeContainer != NULL)
299 		fShapeContainer->RemoveListener(fShapeListener);
300 
301 	delete fShapeListener;
302 }
303 
304 
305 // #pragma mark -
306 
307 
308 void
309 StyleListView::MessageReceived(BMessage* message)
310 {
311 	switch (message->what) {
312 		case MSG_ADD:
313 		{
314 			Style* style;
315 			AddStylesCommand* command;
316 			rgb_color color;
317 			if (fCurrentColor != NULL)
318 				color = fCurrentColor->Color();
319 			else {
320 				color.red = 0;
321 				color.green = 0;
322 				color.blue = 0;
323 				color.alpha = 255;
324 			}
325 			new_style(color, fStyleContainer, &style, &command);
326 			fCommandStack->Perform(command);
327 			break;
328 		}
329 
330 		case MSG_REMOVE:
331 			RemoveSelected();
332 			break;
333 
334 		case MSG_DUPLICATE:
335 		{
336 			int32 count = CountSelectedItems();
337 			int32 index = 0;
338 			BList items;
339 			for (int32 i = 0; i < count; i++) {
340 				index = CurrentSelection(i);
341 				BListItem* item = ItemAt(index);
342 				if (item)
343 					items.AddItem((void*)item);
344 			}
345 			CopyItems(items, index + 1);
346 			break;
347 		}
348 
349 		case MSG_RESET_TRANSFORMATION:
350 		{
351 			int32 count = CountSelectedItems();
352 			BList gradients;
353 			for (int32 i = 0; i < count; i++) {
354 				StyleListItem* item = dynamic_cast<StyleListItem*>(
355 					ItemAt(CurrentSelection(i)));
356 				if (item && item->style && item->style->Gradient()) {
357 					if (!gradients.AddItem((void*)item->style->Gradient()))
358 						break;
359 				}
360 			}
361 			count = gradients.CountItems();
362 			if (count <= 0)
363 				break;
364 
365 			Transformable* transformables[count];
366 			for (int32 i = 0; i < count; i++) {
367 				Gradient* gradient = (Gradient*)gradients.ItemAtFast(i);
368 				transformables[i] = gradient;
369 			}
370 
371 			ResetTransformationCommand* command
372 				= new ResetTransformationCommand(transformables, count);
373 
374 			fCommandStack->Perform(command);
375 			break;
376 		}
377 
378 		default:
379 			SimpleListView::MessageReceived(message);
380 			break;
381 	}
382 }
383 
384 
385 void
386 StyleListView::SelectionChanged()
387 {
388 	SimpleListView::SelectionChanged();
389 
390 	if (!fSyncingToSelection) {
391 		// NOTE: single selection list
392 		StyleListItem* item
393 			= dynamic_cast<StyleListItem*>(ItemAt(CurrentSelection(0)));
394 		if (fMessage) {
395 			BMessage message(*fMessage);
396 			message.AddPointer("style", item ? (void*)item->style : NULL);
397 			Invoke(&message);
398 		}
399 	}
400 
401 	_UpdateMenu();
402 }
403 
404 
405 void
406 StyleListView::MouseDown(BPoint where)
407 {
408 	if (fCurrentShape == NULL) {
409 		SimpleListView::MouseDown(where);
410 		return;
411 	}
412 
413 	bool handled = false;
414 	int32 index = IndexOf(where);
415 	StyleListItem* item = dynamic_cast<StyleListItem*>(ItemAt(index));
416 	if (item != NULL) {
417 		BRect itemFrame(ItemFrame(index));
418 		itemFrame.right = itemFrame.left + kBorderOffset + kMarkWidth
419 			+ kTextOffset / 2.0;
420 		Style* style = item->style;
421 		if (itemFrame.Contains(where)) {
422 			// set the style on the shape
423 			if (fCommandStack) {
424 				::Command* command = new AssignStyleCommand(
425 											fCurrentShape, style);
426 				fCommandStack->Perform(command);
427 			} else {
428 				fCurrentShape->SetStyle(style);
429 			}
430 			handled = true;
431 		}
432 	}
433 
434 	if (!handled)
435 		SimpleListView::MouseDown(where);
436 }
437 
438 
439 status_t
440 StyleListView::ArchiveSelection(BMessage* into, bool deep) const
441 {
442 	into->what = StyleListView::kSelectionArchiveCode;
443 
444 	int32 count = CountSelectedItems();
445 	for (int32 i = 0; i < count; i++) {
446 		StyleListItem* item = dynamic_cast<StyleListItem*>(
447 			ItemAt(CurrentSelection(i)));
448 		if (item != NULL) {
449 			BMessage archive;
450 			if (item->style->Archive(&archive, true) == B_OK)
451 				into->AddMessage("style", &archive);
452 		} else
453 			return B_ERROR;
454 	}
455 	return B_OK;
456 }
457 
458 
459 bool
460 StyleListView::InstantiateSelection(const BMessage* archive, int32 dropIndex)
461 {
462 	if (archive->what != StyleListView::kSelectionArchiveCode
463 		|| fCommandStack == NULL || fStyleContainer == NULL)
464 		return false;
465 
466 	// Drag may have come from another instance, like in another window.
467 	// Reconstruct the Styles from the archive and add them at the drop
468 	// index.
469 	int index = 0;
470 	BList styles;
471 	while (true) {
472 		BMessage styleArchive;
473 		if (archive->FindMessage("style", index, &styleArchive) != B_OK)
474 			break;
475 		Style* style = new(std::nothrow) Style(&styleArchive);
476 		if (style == NULL)
477 			break;
478 
479 		if (!styles.AddItem(style)) {
480 			delete style;
481 			break;
482 		}
483 
484 		index++;
485 	}
486 
487 	int32 count = styles.CountItems();
488 	if (count == 0)
489 		return false;
490 
491 	AddCommand<Style>* command = new(std::nothrow) AddCommand<Style>(
492 		fStyleContainer, (Style**)styles.Items(), count, true, dropIndex);
493 
494 	if (command == NULL) {
495 		for (int32 i = 0; i < count; i++)
496 			delete (Style*)styles.ItemAtFast(i);
497 		return false;
498 	}
499 
500 	fCommandStack->Perform(command);
501 
502 	return true;
503 }
504 
505 
506 void
507 StyleListView::MoveItems(BList& items, int32 toIndex)
508 {
509 	if (fCommandStack == NULL || fStyleContainer == NULL)
510 		return;
511 
512 	int32 count = items.CountItems();
513 	Style** styles = new (nothrow) Style*[count];
514 	if (styles == NULL)
515 		return;
516 
517 	for (int32 i = 0; i < count; i++) {
518 		StyleListItem* item
519 			= dynamic_cast<StyleListItem*>((BListItem*)items.ItemAtFast(i));
520 		styles[i] = item ? item->style : NULL;
521 	}
522 
523 	MoveStylesCommand* command = new (nothrow) MoveStylesCommand(
524 		fStyleContainer, styles, count, toIndex);
525 	if (command == NULL) {
526 		delete[] styles;
527 		return;
528 	}
529 
530 	fCommandStack->Perform(command);
531 }
532 
533 
534 void
535 StyleListView::CopyItems(BList& items, int32 toIndex)
536 {
537 	if (fCommandStack == NULL || fStyleContainer == NULL)
538 		return;
539 
540 	int32 count = items.CountItems();
541 	Style* styles[count];
542 
543 	for (int32 i = 0; i < count; i++) {
544 		StyleListItem* item
545 			= dynamic_cast<StyleListItem*>((BListItem*)items.ItemAtFast(i));
546 		styles[i] = item ? new (nothrow) Style(*item->style) : NULL;
547 	}
548 
549 	AddCommand<Style>* command
550 		= new (nothrow) AddCommand<Style>(fStyleContainer, styles, count, true, toIndex);
551 	if (!command) {
552 		for (int32 i = 0; i < count; i++)
553 			delete styles[i];
554 		return;
555 	}
556 
557 	fCommandStack->Perform(command);
558 }
559 
560 
561 void
562 StyleListView::RemoveItemList(BList& items)
563 {
564 	if (!fCommandStack || !fStyleContainer)
565 		return;
566 
567 	int32 count = items.CountItems();
568 	int32 indices[count];
569 	for (int32 i = 0; i < count; i++)
570 		indices[i] = IndexOf((BListItem*)items.ItemAtFast(i));
571 
572 	RemoveStylesCommand* command
573 		= new (nothrow) RemoveStylesCommand(fStyleContainer, indices, count);
574 	fCommandStack->Perform(command);
575 }
576 
577 
578 BListItem*
579 StyleListView::CloneItem(int32 index) const
580 {
581 	if (StyleListItem* item = dynamic_cast<StyleListItem*>(ItemAt(index))) {
582 		return new StyleListItem(item->style,
583 			const_cast<StyleListView*>(this),
584 			fCurrentShape != NULL);
585 	}
586 	return NULL;
587 }
588 
589 
590 int32
591 StyleListView::IndexOfSelectable(Selectable* selectable) const
592 {
593 	Style* style = dynamic_cast<Style*>(selectable);
594 	if (style == NULL)
595 		return -1;
596 
597 	int count = CountItems();
598 	for (int32 i = 0; i < count; i++) {
599 		if (SelectableFor(ItemAt(i)) == style)
600 			return i;
601 	}
602 
603 	return -1;
604 }
605 
606 
607 Selectable*
608 StyleListView::SelectableFor(BListItem* item) const
609 {
610 	StyleListItem* styleItem = dynamic_cast<StyleListItem*>(item);
611 	if (styleItem != NULL)
612 		return styleItem->style;
613 	return NULL;
614 }
615 
616 
617 // #pragma mark -
618 
619 
620 void
621 StyleListView::ItemAdded(Style* style, int32 index)
622 {
623 	// NOTE: we are in the thread that messed with the
624 	// StyleContainer, so no need to lock the
625 	// container, when this is changed to asynchronous
626 	// notifications, then it would need to be read-locked!
627 	if (!LockLooper())
628 		return;
629 
630 	if (_AddStyle(style, index))
631 		Select(index);
632 
633 	UnlockLooper();
634 }
635 
636 
637 void
638 StyleListView::ItemRemoved(Style* style)
639 {
640 	// NOTE: we are in the thread that messed with the
641 	// StyleContainer, so no need to lock the
642 	// container, when this is changed to asynchronous
643 	// notifications, then it would need to be read-locked!
644 	if (!LockLooper())
645 		return;
646 
647 	// NOTE: we're only interested in Style objects
648 	_RemoveStyle(style);
649 
650 	UnlockLooper();
651 }
652 
653 
654 // #pragma mark -
655 
656 
657 void
658 StyleListView::SetMenu(BMenu* menu)
659 {
660 	if (fMenu == menu)
661 		return;
662 
663 	fMenu = menu;
664 	if (fMenu == NULL)
665 		return;
666 
667 	fAddMI = new BMenuItem(B_TRANSLATE("Add"), new BMessage(MSG_ADD));
668 	fMenu->AddItem(fAddMI);
669 
670 	fMenu->AddSeparatorItem();
671 
672 	fDuplicateMI = new BMenuItem(B_TRANSLATE("Duplicate"),
673 		new BMessage(MSG_DUPLICATE));
674 	fMenu->AddItem(fDuplicateMI);
675 
676 	fResetTransformationMI = new BMenuItem(B_TRANSLATE("Reset transformation"),
677 		new BMessage(MSG_RESET_TRANSFORMATION));
678 	fMenu->AddItem(fResetTransformationMI);
679 
680 	fMenu->AddSeparatorItem();
681 
682 	fRemoveMI = new BMenuItem(B_TRANSLATE("Remove"), new BMessage(MSG_REMOVE));
683 	fMenu->AddItem(fRemoveMI);
684 
685 	fMenu->SetTargetForItems(this);
686 
687 	_UpdateMenu();
688 }
689 
690 
691 void
692 StyleListView::SetStyleContainer(Container<Style>* container)
693 {
694 	if (fStyleContainer == container)
695 		return;
696 
697 	// detach from old container
698 	if (fStyleContainer != NULL)
699 		fStyleContainer->RemoveListener(this);
700 
701 	_MakeEmpty();
702 
703 	fStyleContainer = container;
704 
705 	if (fStyleContainer == NULL)
706 		return;
707 
708 	fStyleContainer->AddListener(this);
709 
710 	// sync
711 	int32 count = fStyleContainer->CountItems();
712 	for (int32 i = 0; i < count; i++)
713 		_AddStyle(fStyleContainer->ItemAtFast(i), i);
714 }
715 
716 
717 void
718 StyleListView::SetShapeContainer(Container<Shape>* container)
719 {
720 	if (fShapeContainer == container)
721 		return;
722 
723 	// detach from old container
724 	if (fShapeContainer)
725 		fShapeContainer->RemoveListener(fShapeListener);
726 
727 	fShapeContainer = container;
728 
729 	if (fShapeContainer)
730 		fShapeContainer->AddListener(fShapeListener);
731 }
732 
733 
734 void
735 StyleListView::SetCommandStack(CommandStack* stack)
736 {
737 	fCommandStack = stack;
738 }
739 
740 
741 void
742 StyleListView::SetCurrentColor(CurrentColor* color)
743 {
744 	fCurrentColor = color;
745 }
746 
747 
748 void
749 StyleListView::SetCurrentShape(Shape* shape)
750 {
751 	PathSourceShape* pathSourceShape = dynamic_cast<PathSourceShape*>(shape);
752 
753 	if (fCurrentShape == pathSourceShape)
754 		return;
755 
756 	fCurrentShape = pathSourceShape;
757 	fShapeListener->SetShape(pathSourceShape);
758 
759 	_UpdateMarks();
760 }
761 
762 
763 // #pragma mark -
764 
765 
766 bool
767 StyleListView::_AddStyle(Style* style, int32 index)
768 {
769 	if (style != NULL) {
770 		 return AddItem(new StyleListItem(
771 		 	style, this, fCurrentShape != NULL), index);
772 	}
773 	return false;
774 }
775 
776 
777 bool
778 StyleListView::_RemoveStyle(Style* style)
779 {
780 	StyleListItem* item = _ItemForStyle(style);
781 	if (item != NULL && RemoveItem(item)) {
782 		delete item;
783 		return true;
784 	}
785 	return false;
786 }
787 
788 
789 StyleListItem*
790 StyleListView::_ItemForStyle(Style* style) const
791 {
792 	int count = CountItems();
793 	for (int32 i = 0; i < count; i++) {
794 		StyleListItem* item = dynamic_cast<StyleListItem*>(ItemAt(i));
795 		if (item == NULL)
796 			continue;
797 		if (item->style == style)
798 			return item;
799 	}
800 	return NULL;
801 }
802 
803 
804 // #pragma mark -
805 
806 
807 void
808 StyleListView::_UpdateMarks()
809 {
810 	int32 count = CountItems();
811 	if (fCurrentShape) {
812 		// enable display of marks and mark items whoes
813 		// style is contained in fCurrentShape
814 		for (int32 i = 0; i < count; i++) {
815 			StyleListItem* item = dynamic_cast<StyleListItem*>(ItemAt(i));
816 			if (item == NULL)
817 				continue;
818 			item->SetMarkEnabled(true);
819 			item->SetMarked(fCurrentShape->Style() == item->style);
820 		}
821 	} else {
822 		// disable display of marks
823 		for (int32 i = 0; i < count; i++) {
824 			StyleListItem* item = dynamic_cast<StyleListItem*>(ItemAt(i));
825 			if (item == NULL)
826 				continue;
827 			item->SetMarkEnabled(false);
828 		}
829 	}
830 
831 	Invalidate();
832 }
833 
834 
835 void
836 StyleListView::_SetStyleMarked(Style* style, bool marked)
837 {
838 	StyleListItem* item = _ItemForStyle(style);
839 	if (item != NULL)
840 		item->SetMarked(marked);
841 }
842 
843 
844 void
845 StyleListView::_UpdateMenu()
846 {
847 	if (fMenu == NULL)
848 		return;
849 
850 	bool gotSelection = CurrentSelection(0) >= 0;
851 
852 	fDuplicateMI->SetEnabled(gotSelection);
853 	// TODO: only enable fResetTransformationMI if styles
854 	// with gradients are selected!
855 	fResetTransformationMI->SetEnabled(gotSelection);
856 	fRemoveMI->SetEnabled(gotSelection);
857 }
858 
859