xref: /haiku/src/apps/icon-o-matic/gui/StyleListView.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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 "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"
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 ShapeContainerListener {
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 	// ShapeContainerListener interface
229 	virtual void ShapeAdded(Shape* shape, int32 index)
230 	{
231 	}
232 
233 	virtual void ShapeRemoved(Shape* shape)
234 	{
235 		fListView->SetCurrentShape(NULL);
236 	}
237 
238 	// ShapeStyleListener
239 	void SetShape(Shape* 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 	Shape* CurrentShape() const
254 	{
255 		return fShape;
256 	}
257 
258 private:
259 	StyleListView*	fListView;
260 	Shape*			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 	AddStylesCommand* command = new(std::nothrow) AddStylesCommand(
505 		fStyleContainer, (Style**)styles.Items(), count, 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 	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;
569 	}
570 
571 	fCommandStack->Perform(command);
572 }
573 
574 
575 void
576 StyleListView::RemoveItemList(BList& items)
577 {
578 	if (!fCommandStack || !fStyleContainer)
579 		return;
580 
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;
590 	}
591 
592 	RemoveStylesCommand* command
593 		= new (nothrow) RemoveStylesCommand(fStyleContainer,
594 											styles, count);
595 	fCommandStack->Perform(command);
596 }
597 
598 
599 BListItem*
600 StyleListView::CloneItem(int32 index) const
601 {
602 	if (StyleListItem* item = dynamic_cast<StyleListItem*>(ItemAt(index))) {
603 		return new StyleListItem(item->style,
604 			const_cast<StyleListView*>(this),
605 			fCurrentShape != NULL);
606 	}
607 	return NULL;
608 }
609 
610 
611 int32
612 StyleListView::IndexOfSelectable(Selectable* selectable) const
613 {
614 	Style* style = dynamic_cast<Style*>(selectable);
615 	if (style == NULL)
616 		return -1;
617 
618 	int count = CountItems();
619 	for (int32 i = 0; i < count; i++) {
620 		if (SelectableFor(ItemAt(i)) == style)
621 			return i;
622 	}
623 
624 	return -1;
625 }
626 
627 
628 Selectable*
629 StyleListView::SelectableFor(BListItem* item) const
630 {
631 	StyleListItem* styleItem = dynamic_cast<StyleListItem*>(item);
632 	if (styleItem != NULL)
633 		return styleItem->style;
634 	return NULL;
635 }
636 
637 
638 // #pragma mark -
639 
640 
641 void
642 StyleListView::StyleAdded(Style* style, int32 index)
643 {
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;
650 
651 	if (_AddStyle(style, index))
652 		Select(index);
653 
654 	UnlockLooper();
655 }
656 
657 
658 void
659 StyleListView::StyleRemoved(Style* style)
660 {
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;
667 
668 	// NOTE: we're only interested in Style objects
669 	_RemoveStyle(style);
670 
671 	UnlockLooper();
672 }
673 
674 
675 // #pragma mark -
676 
677 
678 void
679 StyleListView::SetMenu(BMenu* menu)
680 {
681 	if (fMenu == menu)
682 		return;
683 
684 	fMenu = menu;
685 	if (fMenu == NULL)
686 		return;
687 
688 	fAddMI = new BMenuItem(B_TRANSLATE("Add"), new BMessage(MSG_ADD));
689 	fMenu->AddItem(fAddMI);
690 
691 	fMenu->AddSeparatorItem();
692 
693 	fDuplicateMI = new BMenuItem(B_TRANSLATE("Duplicate"),
694 		new BMessage(MSG_DUPLICATE));
695 	fMenu->AddItem(fDuplicateMI);
696 
697 	fResetTransformationMI = new BMenuItem(B_TRANSLATE("Reset transformation"),
698 		new BMessage(MSG_RESET_TRANSFORMATION));
699 	fMenu->AddItem(fResetTransformationMI);
700 
701 	fMenu->AddSeparatorItem();
702 
703 	fRemoveMI = new BMenuItem(B_TRANSLATE("Remove"), new BMessage(MSG_REMOVE));
704 	fMenu->AddItem(fRemoveMI);
705 
706 	fMenu->SetTargetForItems(this);
707 
708 	_UpdateMenu();
709 }
710 
711 
712 void
713 StyleListView::SetStyleContainer(StyleContainer* container)
714 {
715 	if (fStyleContainer == container)
716 		return;
717 
718 	// detach from old container
719 	if (fStyleContainer != NULL)
720 		fStyleContainer->RemoveListener(this);
721 
722 	_MakeEmpty();
723 
724 	fStyleContainer = container;
725 
726 	if (fStyleContainer == NULL)
727 		return;
728 
729 	fStyleContainer->AddListener(this);
730 
731 	// sync
732 	int32 count = fStyleContainer->CountStyles();
733 	for (int32 i = 0; i < count; i++)
734 		_AddStyle(fStyleContainer->StyleAtFast(i), i);
735 }
736 
737 
738 void
739 StyleListView::SetShapeContainer(ShapeContainer* container)
740 {
741 	if (fShapeContainer == container)
742 		return;
743 
744 	// detach from old container
745 	if (fShapeContainer)
746 		fShapeContainer->RemoveListener(fShapeListener);
747 
748 	fShapeContainer = container;
749 
750 	if (fShapeContainer)
751 		fShapeContainer->AddListener(fShapeListener);
752 }
753 
754 
755 void
756 StyleListView::SetCommandStack(CommandStack* stack)
757 {
758 	fCommandStack = stack;
759 }
760 
761 
762 void
763 StyleListView::SetCurrentColor(CurrentColor* color)
764 {
765 	fCurrentColor = color;
766 }
767 
768 
769 void
770 StyleListView::SetCurrentShape(Shape* shape)
771 {
772 	if (fCurrentShape == shape)
773 		return;
774 
775 	fCurrentShape = shape;
776 	fShapeListener->SetShape(shape);
777 
778 	_UpdateMarks();
779 }
780 
781 
782 // #pragma mark -
783 
784 
785 bool
786 StyleListView::_AddStyle(Style* style, int32 index)
787 {
788 	if (style != NULL) {
789 		 return AddItem(new StyleListItem(
790 		 	style, this, fCurrentShape != NULL), index);
791 	}
792 	return false;
793 }
794 
795 
796 bool
797 StyleListView::_RemoveStyle(Style* style)
798 {
799 	StyleListItem* item = _ItemForStyle(style);
800 	if (item != NULL && RemoveItem(item)) {
801 		delete item;
802 		return true;
803 	}
804 	return false;
805 }
806 
807 
808 StyleListItem*
809 StyleListView::_ItemForStyle(Style* style) const
810 {
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;
818 	}
819 	return NULL;
820 }
821 
822 
823 // #pragma mark -
824 
825 
826 void
827 StyleListView::_UpdateMarks()
828 {
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);
839 		}
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);
847 		}
848 	}
849 
850 	Invalidate();
851 }
852 
853 
854 void
855 StyleListView::_SetStyleMarked(Style* style, bool marked)
856 {
857 	StyleListItem* item = _ItemForStyle(style);
858 	if (item != NULL)
859 		item->SetMarked(marked);
860 }
861 
862 
863 void
864 StyleListView::_UpdateMenu()
865 {
866 	if (fMenu == NULL)
867 		return;
868 
869 	bool gotSelection = CurrentSelection(0) >= 0;
870 
871 	fDuplicateMI->SetEnabled(gotSelection);
872 	// TODO: only enable fResetTransformationMI if styles
873 	// with gradients are selected!
874 	fResetTransformationMI->SetEnabled(gotSelection);
875 	fRemoveMI->SetEnabled(gotSelection);
876 }
877 
878