xref: /haiku/src/apps/icon-o-matic/gui/StyleListView.cpp (revision 26bda37ca592675f71c0adbcd4794b93aa670424)
1 /*
2  * Copyright 2006-2012, Stephan Aßmus <superstippi@gmx.de>.
3  * All rights reserved. Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "StyleListView.h"
8 
9 #include <new>
10 #include <stdio.h>
11 
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>
21 
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 "PathSourceShape.h"
29 #include "RemoveStylesCommand.h"
30 #include "Style.h"
31 #include "Observer.h"
32 #include "ResetTransformationCommand.h"
33 #include "Shape.h"
34 #include "Selection.h"
35 #include "Util.h"
36 
37 
38 #undef B_TRANSLATION_CONTEXT
39 #define B_TRANSLATION_CONTEXT "Icon-O-Matic-StylesList"
40 
41 
42 using std::nothrow;
43 
44 static const float kMarkWidth		= 14.0;
45 static const float kBorderOffset	= 3.0;
46 static const float kTextOffset		= 4.0;
47 
48 enum {
49 	MSG_ADD							= 'adst',
50 	MSG_REMOVE						= 'rmst',
51 	MSG_DUPLICATE					= 'dpst',
52 	MSG_RESET_TRANSFORMATION		= 'rstr',
53 };
54 
55 class StyleListItem : public SimpleItem,
56 					 public Observer {
57 public:
58 	StyleListItem(Style* s, StyleListView* listView, bool markEnabled)
59 		:
60 		SimpleItem(""),
61 		style(NULL),
62 		fListView(listView),
63 		fMarkEnabled(markEnabled),
64 		fMarked(false)
65 	{
66 		SetStyle(s);
67 	}
68 
69 	virtual ~StyleListItem()
70 	{
71 		SetStyle(NULL);
72 	}
73 
74 	// SimpleItem interface
75 	virtual	void Draw(BView* owner, BRect itemFrame, uint32 flags)
76 	{
77 		SimpleItem::DrawBackground(owner, itemFrame, flags);
78 
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);
96 
97 		if (!fMarkEnabled)
98 			return;
99 
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;
111 		}
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);
129 		}
130 	}
131 
132 	// Observer interface
133 	virtual	void	ObjectChanged(const Observable* object)
134 	{
135 		UpdateText();
136 	}
137 
138 	// StyleListItem
139 	void SetStyle(Style* s)
140 	{
141 		if (s == style)
142 			return;
143 
144 		if (style) {
145 			style->RemoveObserver(this);
146 			style->ReleaseReference();
147 		}
148 
149 		style = s;
150 
151 		if (style) {
152 			style->AcquireReference();
153 			style->AddObserver(this);
154 			UpdateText();
155 		}
156 	}
157 
158 	void UpdateText()
159 	{
160 		SetText(style->Name());
161 		Invalidate();
162 	}
163 
164 	void SetMarkEnabled(bool enabled)
165 	{
166 		if (fMarkEnabled == enabled)
167 			return;
168 		fMarkEnabled = enabled;
169 		Invalidate();
170 	}
171 
172 	void SetMarked(bool marked)
173 	{
174 		if (fMarked == marked)
175 			return;
176 		fMarked = marked;
177 		Invalidate();
178 	}
179 
180 	void Invalidate()
181 	{
182 		if (fListView->LockLooper()) {
183 			fListView->InvalidateItem(fListView->IndexOf(this));
184 			fListView->UnlockLooper();
185 		}
186 	}
187 
188 public:
189 	Style*			style;
190 
191 private:
192 	StyleListView*	fListView;
193 	bool			fMarkEnabled;
194 	bool			fMarked;
195 };
196 
197 
198 class ShapeStyleListener : public ShapeListener,
199 	public ContainerListener<Shape> {
200 public:
201 	ShapeStyleListener(StyleListView* listView)
202 		:
203 		fListView(listView),
204 		fShape(NULL)
205 	{
206 	}
207 
208 	virtual ~ShapeStyleListener()
209 	{
210 		SetShape(NULL);
211 	}
212 
213 	// ShapeListener interface
214 	virtual	void TransformerAdded(Transformer* t, int32 index)
215 	{
216 	}
217 
218 	virtual	void TransformerRemoved(Transformer* t)
219 	{
220 	}
221 
222 	virtual void StyleChanged(Style* oldStyle, Style* newStyle)
223 	{
224 		fListView->_SetStyleMarked(oldStyle, false);
225 		fListView->_SetStyleMarked(newStyle, true);
226 	}
227 
228 	// ContainerListener<Shape> interface
229 	virtual void ItemAdded(Shape* shape, int32 index)
230 	{
231 	}
232 
233 	virtual void ItemRemoved(Shape* shape)
234 	{
235 		fListView->SetCurrentShape(NULL);
236 	}
237 
238 	// ShapeStyleListener
239 	void SetShape(PathSourceShape* shape)
240 	{
241 		if (fShape == shape)
242 			return;
243 
244 		if (fShape)
245 			fShape->RemoveListener(this);
246 
247 		fShape = shape;
248 
249 		if (fShape)
250 			fShape->AddListener(this);
251 	}
252 
253 	PathSourceShape* CurrentShape() const
254 	{
255 		return fShape;
256 	}
257 
258 private:
259 	StyleListView*		fListView;
260 	PathSourceShape*	fShape;
261 };
262 
263 
264 // #pragma mark -
265 
266 
267 StyleListView::StyleListView(BRect frame, const char* name, BMessage* message,
268 	BHandler* target)
269 	:
270 	SimpleListView(frame, name, NULL, B_SINGLE_SELECTION_LIST),
271 	fMessage(message),
272 	fStyleContainer(NULL),
273 	fShapeContainer(NULL),
274 	fCommandStack(NULL),
275 	fCurrentColor(NULL),
276 
277 	fCurrentShape(NULL),
278 	fShapeListener(new ShapeStyleListener(this)),
279 
280 	fMenu(NULL)
281 {
282 	SetTarget(target);
283 }
284 
285 
286 StyleListView::~StyleListView()
287 {
288 	_MakeEmpty();
289 	delete fMessage;
290 
291 	if (fStyleContainer != NULL)
292 		fStyleContainer->RemoveListener(this);
293 
294 	if (fShapeContainer != NULL)
295 		fShapeContainer->RemoveListener(fShapeListener);
296 
297 	delete fShapeListener;
298 }
299 
300 
301 // #pragma mark -
302 
303 
304 void
305 StyleListView::MessageReceived(BMessage* message)
306 {
307 	switch (message->what) {
308 		case MSG_ADD:
309 		{
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;
320 			}
321 			new_style(color, fStyleContainer, &style, &command);
322 			fCommandStack->Perform(command);
323 			break;
324 		}
325 
326 		case MSG_REMOVE:
327 			RemoveSelected();
328 			break;
329 
330 		case MSG_DUPLICATE:
331 		{
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);
340 			}
341 			CopyItems(items, index + 1);
342 			break;
343 		}
344 
345 		case MSG_RESET_TRANSFORMATION:
346 		{
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;
355 				}
356 			}
357 			count = gradients.CountItems();
358 			if (count <= 0)
359 				break;
360 
361 			Transformable* transformables[count];
362 			for (int32 i = 0; i < count; i++) {
363 				Gradient* gradient = (Gradient*)gradients.ItemAtFast(i);
364 				transformables[i] = gradient;
365 			}
366 
367 			ResetTransformationCommand* command
368 				= new ResetTransformationCommand(transformables, count);
369 
370 			fCommandStack->Perform(command);
371 			break;
372 		}
373 
374 		default:
375 			SimpleListView::MessageReceived(message);
376 			break;
377 	}
378 }
379 
380 
381 void
382 StyleListView::SelectionChanged()
383 {
384 	SimpleListView::SelectionChanged();
385 
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);
394 		}
395 	}
396 
397 	_UpdateMenu();
398 }
399 
400 
401 void
402 StyleListView::MouseDown(BPoint where)
403 {
404 	if (fCurrentShape == NULL) {
405 		SimpleListView::MouseDown(where);
406 		return;
407 	}
408 
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);
425 			}
426 			handled = true;
427 		}
428 	}
429 
430 	if (!handled)
431 		SimpleListView::MouseDown(where);
432 }
433 
434 
435 void
436 StyleListView::MakeDragMessage(BMessage* message) const
437 {
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;
451 	}
452 }
453 
454 
455 bool
456 StyleListView::AcceptDragMessage(const BMessage* message) const
457 {
458 	return SimpleListView::AcceptDragMessage(message);
459 }
460 
461 
462 void
463 StyleListView::SetDropTargetRect(const BMessage* message, BPoint where)
464 {
465 	SimpleListView::SetDropTargetRect(message, where);
466 }
467 
468 
469 bool
470 StyleListView::HandleDropMessage(const BMessage* message, int32 dropIndex)
471 {
472 	// Let SimpleListView handle drag-sorting (when drag came from ourself)
473 	if (SimpleListView::HandleDropMessage(message, dropIndex))
474 		return true;
475 
476 	if (fCommandStack == NULL || fStyleContainer == NULL)
477 		return false;
478 
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;
491 
492 		if (!styles.AddItem(style)) {
493 			delete style;
494 			break;
495 		}
496 
497 		index++;
498 	}
499 
500 	int32 count = styles.CountItems();
501 	if (count == 0)
502 		return false;
503 
504 	AddCommand<Style>* command = new(std::nothrow) AddCommand<Style>(
505 		fStyleContainer, (Style**)styles.Items(), count, true, dropIndex);
506 
507 	if (command == NULL) {
508 		for (int32 i = 0; i < count; i++)
509 			delete (Style*)styles.ItemAtFast(i);
510 		return false;
511 	}
512 
513 	fCommandStack->Perform(command);
514 
515 	return true;
516 }
517 
518 
519 void
520 StyleListView::MoveItems(BList& items, int32 toIndex)
521 {
522 	if (fCommandStack == NULL || fStyleContainer == NULL)
523 		return;
524 
525 	int32 count = items.CountItems();
526 	Style** styles = new (nothrow) Style*[count];
527 	if (styles == NULL)
528 		return;
529 
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;
534 	}
535 
536 	MoveStylesCommand* command = new (nothrow) MoveStylesCommand(
537 		fStyleContainer, styles, count, toIndex);
538 	if (command == NULL) {
539 		delete[] styles;
540 		return;
541 	}
542 
543 	fCommandStack->Perform(command);
544 }
545 
546 
547 void
548 StyleListView::CopyItems(BList& items, int32 toIndex)
549 {
550 	if (fCommandStack == NULL || fStyleContainer == NULL)
551 		return;
552 
553 	int32 count = items.CountItems();
554 	Style* styles[count];
555 
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;
560 	}
561 
562 	AddCommand<Style>* command
563 		= new (nothrow) AddCommand<Style>(fStyleContainer, styles, count, true, toIndex);
564 	if (!command) {
565 		for (int32 i = 0; i < count; i++)
566 			delete styles[i];
567 		return;
568 	}
569 
570 	fCommandStack->Perform(command);
571 }
572 
573 
574 void
575 StyleListView::RemoveItemList(BList& items)
576 {
577 	if (!fCommandStack || !fStyleContainer)
578 		return;
579 
580 	int32 count = items.CountItems();
581 	int32 indices[count];
582 	for (int32 i = 0; i < count; i++)
583 		indices[i] = IndexOf((BListItem*)items.ItemAtFast(i));
584 
585 	RemoveStylesCommand* command
586 		= new (nothrow) RemoveStylesCommand(fStyleContainer, indices, count);
587 	fCommandStack->Perform(command);
588 }
589 
590 
591 BListItem*
592 StyleListView::CloneItem(int32 index) const
593 {
594 	if (StyleListItem* item = dynamic_cast<StyleListItem*>(ItemAt(index))) {
595 		return new StyleListItem(item->style,
596 			const_cast<StyleListView*>(this),
597 			fCurrentShape != NULL);
598 	}
599 	return NULL;
600 }
601 
602 
603 int32
604 StyleListView::IndexOfSelectable(Selectable* selectable) const
605 {
606 	Style* style = dynamic_cast<Style*>(selectable);
607 	if (style == NULL)
608 		return -1;
609 
610 	int count = CountItems();
611 	for (int32 i = 0; i < count; i++) {
612 		if (SelectableFor(ItemAt(i)) == style)
613 			return i;
614 	}
615 
616 	return -1;
617 }
618 
619 
620 Selectable*
621 StyleListView::SelectableFor(BListItem* item) const
622 {
623 	StyleListItem* styleItem = dynamic_cast<StyleListItem*>(item);
624 	if (styleItem != NULL)
625 		return styleItem->style;
626 	return NULL;
627 }
628 
629 
630 // #pragma mark -
631 
632 
633 void
634 StyleListView::ItemAdded(Style* style, int32 index)
635 {
636 	// NOTE: we are in the thread that messed with the
637 	// StyleContainer, so no need to lock the
638 	// container, when this is changed to asynchronous
639 	// notifications, then it would need to be read-locked!
640 	if (!LockLooper())
641 		return;
642 
643 	if (_AddStyle(style, index))
644 		Select(index);
645 
646 	UnlockLooper();
647 }
648 
649 
650 void
651 StyleListView::ItemRemoved(Style* style)
652 {
653 	// NOTE: we are in the thread that messed with the
654 	// StyleContainer, so no need to lock the
655 	// container, when this is changed to asynchronous
656 	// notifications, then it would need to be read-locked!
657 	if (!LockLooper())
658 		return;
659 
660 	// NOTE: we're only interested in Style objects
661 	_RemoveStyle(style);
662 
663 	UnlockLooper();
664 }
665 
666 
667 // #pragma mark -
668 
669 
670 void
671 StyleListView::SetMenu(BMenu* menu)
672 {
673 	if (fMenu == menu)
674 		return;
675 
676 	fMenu = menu;
677 	if (fMenu == NULL)
678 		return;
679 
680 	fAddMI = new BMenuItem(B_TRANSLATE("Add"), new BMessage(MSG_ADD));
681 	fMenu->AddItem(fAddMI);
682 
683 	fMenu->AddSeparatorItem();
684 
685 	fDuplicateMI = new BMenuItem(B_TRANSLATE("Duplicate"),
686 		new BMessage(MSG_DUPLICATE));
687 	fMenu->AddItem(fDuplicateMI);
688 
689 	fResetTransformationMI = new BMenuItem(B_TRANSLATE("Reset transformation"),
690 		new BMessage(MSG_RESET_TRANSFORMATION));
691 	fMenu->AddItem(fResetTransformationMI);
692 
693 	fMenu->AddSeparatorItem();
694 
695 	fRemoveMI = new BMenuItem(B_TRANSLATE("Remove"), new BMessage(MSG_REMOVE));
696 	fMenu->AddItem(fRemoveMI);
697 
698 	fMenu->SetTargetForItems(this);
699 
700 	_UpdateMenu();
701 }
702 
703 
704 void
705 StyleListView::SetStyleContainer(Container<Style>* container)
706 {
707 	if (fStyleContainer == container)
708 		return;
709 
710 	// detach from old container
711 	if (fStyleContainer != NULL)
712 		fStyleContainer->RemoveListener(this);
713 
714 	_MakeEmpty();
715 
716 	fStyleContainer = container;
717 
718 	if (fStyleContainer == NULL)
719 		return;
720 
721 	fStyleContainer->AddListener(this);
722 
723 	// sync
724 	int32 count = fStyleContainer->CountItems();
725 	for (int32 i = 0; i < count; i++)
726 		_AddStyle(fStyleContainer->ItemAtFast(i), i);
727 }
728 
729 
730 void
731 StyleListView::SetShapeContainer(Container<Shape>* container)
732 {
733 	if (fShapeContainer == container)
734 		return;
735 
736 	// detach from old container
737 	if (fShapeContainer)
738 		fShapeContainer->RemoveListener(fShapeListener);
739 
740 	fShapeContainer = container;
741 
742 	if (fShapeContainer)
743 		fShapeContainer->AddListener(fShapeListener);
744 }
745 
746 
747 void
748 StyleListView::SetCommandStack(CommandStack* stack)
749 {
750 	fCommandStack = stack;
751 }
752 
753 
754 void
755 StyleListView::SetCurrentColor(CurrentColor* color)
756 {
757 	fCurrentColor = color;
758 }
759 
760 
761 void
762 StyleListView::SetCurrentShape(Shape* shape)
763 {
764 	PathSourceShape* pathSourceShape = dynamic_cast<PathSourceShape*>(shape);
765 
766 	if (fCurrentShape == pathSourceShape)
767 		return;
768 
769 	fCurrentShape = pathSourceShape;
770 	fShapeListener->SetShape(pathSourceShape);
771 
772 	_UpdateMarks();
773 }
774 
775 
776 // #pragma mark -
777 
778 
779 bool
780 StyleListView::_AddStyle(Style* style, int32 index)
781 {
782 	if (style != NULL) {
783 		 return AddItem(new StyleListItem(
784 		 	style, this, fCurrentShape != NULL), index);
785 	}
786 	return false;
787 }
788 
789 
790 bool
791 StyleListView::_RemoveStyle(Style* style)
792 {
793 	StyleListItem* item = _ItemForStyle(style);
794 	if (item != NULL && RemoveItem(item)) {
795 		delete item;
796 		return true;
797 	}
798 	return false;
799 }
800 
801 
802 StyleListItem*
803 StyleListView::_ItemForStyle(Style* style) const
804 {
805 	int count = CountItems();
806 	for (int32 i = 0; i < count; i++) {
807 		StyleListItem* item = dynamic_cast<StyleListItem*>(ItemAt(i));
808 		if (item == NULL)
809 			continue;
810 		if (item->style == style)
811 			return item;
812 	}
813 	return NULL;
814 }
815 
816 
817 // #pragma mark -
818 
819 
820 void
821 StyleListView::_UpdateMarks()
822 {
823 	int32 count = CountItems();
824 	if (fCurrentShape) {
825 		// enable display of marks and mark items whoes
826 		// style is contained in fCurrentShape
827 		for (int32 i = 0; i < count; i++) {
828 			StyleListItem* item = dynamic_cast<StyleListItem*>(ItemAt(i));
829 			if (item == NULL)
830 				continue;
831 			item->SetMarkEnabled(true);
832 			item->SetMarked(fCurrentShape->Style() == item->style);
833 		}
834 	} else {
835 		// disable display of marks
836 		for (int32 i = 0; i < count; i++) {
837 			StyleListItem* item = dynamic_cast<StyleListItem*>(ItemAt(i));
838 			if (item == NULL)
839 				continue;
840 			item->SetMarkEnabled(false);
841 		}
842 	}
843 
844 	Invalidate();
845 }
846 
847 
848 void
849 StyleListView::_SetStyleMarked(Style* style, bool marked)
850 {
851 	StyleListItem* item = _ItemForStyle(style);
852 	if (item != NULL)
853 		item->SetMarked(marked);
854 }
855 
856 
857 void
858 StyleListView::_UpdateMenu()
859 {
860 	if (fMenu == NULL)
861 		return;
862 
863 	bool gotSelection = CurrentSelection(0) >= 0;
864 
865 	fDuplicateMI->SetEnabled(gotSelection);
866 	// TODO: only enable fResetTransformationMI if styles
867 	// with gradients are selected!
868 	fResetTransformationMI->SetEnabled(gotSelection);
869 	fRemoveMI->SetEnabled(gotSelection);
870 }
871 
872