xref: /haiku/src/apps/icon-o-matic/gui/StyleListView.cpp (revision 2b76973fa2401f7a5edf68e6470f3d3210cbcff3)
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 		owner->SetHighColor(0, 0, 0, 255);
81 		font_height fh;
82 		owner->GetFontHeight(&fh);
83 		BString truncatedString(Text());
84 		owner->TruncateString(&truncatedString, B_TRUNCATE_MIDDLE,
85 			itemFrame.Width() - kBorderOffset - kMarkWidth - kTextOffset
86 			- kBorderOffset);
87 		float height = itemFrame.Height();
88 		float textHeight = fh.ascent + fh.descent;
89 		BPoint pos;
90 		pos.x = itemFrame.left + kBorderOffset + kMarkWidth + kTextOffset;
91 		pos.y = itemFrame.top + ceilf((height - textHeight) / 2.0 + fh.ascent);
92 		owner->DrawString(truncatedString.String(), pos);
93 
94 		if (!fMarkEnabled)
95 			return;
96 
97 		// mark
98 		BRect markRect = itemFrame;
99 		markRect.left += kBorderOffset;
100 		markRect.right = markRect.left + kMarkWidth;
101 		markRect.top = (markRect.top + markRect.bottom - kMarkWidth) / 2.0;
102 		markRect.bottom = markRect.top + kMarkWidth;
103 		owner->SetHighColor(tint_color(owner->LowColor(), B_DARKEN_1_TINT));
104 		owner->StrokeRect(markRect);
105 		markRect.InsetBy(1, 1);
106 		owner->SetHighColor(tint_color(owner->LowColor(), 1.04));
107 		owner->FillRect(markRect);
108 		if (fMarked) {
109 			markRect.InsetBy(2, 2);
110 			owner->SetHighColor(tint_color(owner->LowColor(),
111 				B_DARKEN_4_TINT));
112 			owner->SetPenSize(2);
113 			owner->StrokeLine(markRect.LeftTop(), markRect.RightBottom());
114 			owner->StrokeLine(markRect.LeftBottom(), markRect.RightTop());
115 			owner->SetPenSize(1);
116 		}
117 	}
118 
119 	// Observer interface
120 	virtual	void	ObjectChanged(const Observable* object)
121 	{
122 		UpdateText();
123 	}
124 
125 	// StyleListItem
126 	void SetStyle(Style* s)
127 	{
128 		if (s == style)
129 			return;
130 
131 		if (style) {
132 			style->RemoveObserver(this);
133 			style->Release();
134 		}
135 
136 		style = s;
137 
138 		if (style) {
139 			style->Acquire();
140 			style->AddObserver(this);
141 			UpdateText();
142 		}
143 	}
144 
145 	void UpdateText()
146 	{
147 		SetText(style->Name());
148 		Invalidate();
149 	}
150 
151 	void SetMarkEnabled(bool enabled)
152 	{
153 		if (fMarkEnabled == enabled)
154 			return;
155 		fMarkEnabled = enabled;
156 		Invalidate();
157 	}
158 
159 	void SetMarked(bool marked)
160 	{
161 		if (fMarked == marked)
162 			return;
163 		fMarked = marked;
164 		Invalidate();
165 	}
166 
167 	void Invalidate()
168 	{
169 		if (fListView->LockLooper()) {
170 			fListView->InvalidateItem(fListView->IndexOf(this));
171 			fListView->UnlockLooper();
172 		}
173 	}
174 
175 public:
176 	Style*			style;
177 
178 private:
179 	StyleListView*	fListView;
180 	bool			fMarkEnabled;
181 	bool			fMarked;
182 };
183 
184 
185 class ShapeStyleListener : public ShapeListener,
186 	public ShapeContainerListener {
187 public:
188 	ShapeStyleListener(StyleListView* listView)
189 		:
190 		fListView(listView),
191 		fShape(NULL)
192 	{
193 	}
194 
195 	virtual ~ShapeStyleListener()
196 	{
197 		SetShape(NULL);
198 	}
199 
200 	// ShapeListener interface
201 	virtual	void TransformerAdded(Transformer* t, int32 index)
202 	{
203 	}
204 
205 	virtual	void TransformerRemoved(Transformer* t)
206 	{
207 	}
208 
209 	virtual void StyleChanged(Style* oldStyle, Style* newStyle)
210 	{
211 		fListView->_SetStyleMarked(oldStyle, false);
212 		fListView->_SetStyleMarked(newStyle, true);
213 	}
214 
215 	// ShapeContainerListener interface
216 	virtual void ShapeAdded(Shape* shape, int32 index)
217 	{
218 	}
219 
220 	virtual void ShapeRemoved(Shape* shape)
221 	{
222 		fListView->SetCurrentShape(NULL);
223 	}
224 
225 	// ShapeStyleListener
226 	void SetShape(Shape* shape)
227 	{
228 		if (fShape == shape)
229 			return;
230 
231 		if (fShape)
232 			fShape->RemoveListener(this);
233 
234 		fShape = shape;
235 
236 		if (fShape)
237 			fShape->AddListener(this);
238 	}
239 
240 	Shape* CurrentShape() const
241 	{
242 		return fShape;
243 	}
244 
245 private:
246 	StyleListView*	fListView;
247 	Shape*			fShape;
248 };
249 
250 
251 // #pragma mark -
252 
253 
254 StyleListView::StyleListView(BRect frame, const char* name, BMessage* message,
255 	BHandler* target)
256 	:
257 	SimpleListView(frame, name, NULL, B_SINGLE_SELECTION_LIST),
258 	fMessage(message),
259 	fStyleContainer(NULL),
260 	fShapeContainer(NULL),
261 	fCommandStack(NULL),
262 	fCurrentColor(NULL),
263 
264 	fCurrentShape(NULL),
265 	fShapeListener(new ShapeStyleListener(this)),
266 
267 	fMenu(NULL)
268 {
269 	SetTarget(target);
270 }
271 
272 
273 StyleListView::~StyleListView()
274 {
275 	_MakeEmpty();
276 	delete fMessage;
277 
278 	if (fStyleContainer != NULL)
279 		fStyleContainer->RemoveListener(this);
280 
281 	if (fShapeContainer != NULL)
282 		fShapeContainer->RemoveListener(fShapeListener);
283 
284 	delete fShapeListener;
285 }
286 
287 
288 // #pragma mark -
289 
290 
291 void
292 StyleListView::MessageReceived(BMessage* message)
293 {
294 	switch (message->what) {
295 		case MSG_ADD:
296 		{
297 			Style* style;
298 			AddStylesCommand* command;
299 			rgb_color color;
300 			if (fCurrentColor != NULL)
301 				color = fCurrentColor->Color();
302 			else {
303 				color.red = 0;
304 				color.green = 0;
305 				color.blue = 0;
306 				color.alpha = 255;
307 			}
308 			new_style(color, fStyleContainer, &style, &command);
309 			fCommandStack->Perform(command);
310 			break;
311 		}
312 
313 		case MSG_REMOVE:
314 			RemoveSelected();
315 			break;
316 
317 		case MSG_DUPLICATE:
318 		{
319 			int32 count = CountSelectedItems();
320 			int32 index = 0;
321 			BList items;
322 			for (int32 i = 0; i < count; i++) {
323 				index = CurrentSelection(i);
324 				BListItem* item = ItemAt(index);
325 				if (item)
326 					items.AddItem((void*)item);
327 			}
328 			CopyItems(items, index + 1);
329 			break;
330 		}
331 
332 		case MSG_RESET_TRANSFORMATION:
333 		{
334 			int32 count = CountSelectedItems();
335 			BList gradients;
336 			for (int32 i = 0; i < count; i++) {
337 				StyleListItem* item = dynamic_cast<StyleListItem*>(
338 					ItemAt(CurrentSelection(i)));
339 				if (item && item->style && item->style->Gradient()) {
340 					if (!gradients.AddItem((void*)item->style->Gradient()))
341 						break;
342 				}
343 			}
344 			count = gradients.CountItems();
345 			if (count <= 0)
346 				break;
347 
348 			Transformable* transformables[count];
349 			for (int32 i = 0; i < count; i++) {
350 				Gradient* gradient = (Gradient*)gradients.ItemAtFast(i);
351 				transformables[i] = gradient;
352 			}
353 
354 			ResetTransformationCommand* command
355 				= new ResetTransformationCommand(transformables, count);
356 
357 			fCommandStack->Perform(command);
358 			break;
359 		}
360 
361 		default:
362 			SimpleListView::MessageReceived(message);
363 			break;
364 	}
365 }
366 
367 
368 void
369 StyleListView::SelectionChanged()
370 {
371 	SimpleListView::SelectionChanged();
372 
373 	if (!fSyncingToSelection) {
374 		// NOTE: single selection list
375 		StyleListItem* item
376 			= dynamic_cast<StyleListItem*>(ItemAt(CurrentSelection(0)));
377 		if (fMessage) {
378 			BMessage message(*fMessage);
379 			message.AddPointer("style", item ? (void*)item->style : NULL);
380 			Invoke(&message);
381 		}
382 	}
383 
384 	_UpdateMenu();
385 }
386 
387 
388 void
389 StyleListView::MouseDown(BPoint where)
390 {
391 	if (fCurrentShape == NULL) {
392 		SimpleListView::MouseDown(where);
393 		return;
394 	}
395 
396 	bool handled = false;
397 	int32 index = IndexOf(where);
398 	StyleListItem* item = dynamic_cast<StyleListItem*>(ItemAt(index));
399 	if (item != NULL) {
400 		BRect itemFrame(ItemFrame(index));
401 		itemFrame.right = itemFrame.left + kBorderOffset + kMarkWidth
402 			+ kTextOffset / 2.0;
403 		Style* style = item->style;
404 		if (itemFrame.Contains(where)) {
405 			// set the style on the shape
406 			if (fCommandStack) {
407 				::Command* command = new AssignStyleCommand(
408 											fCurrentShape, style);
409 				fCommandStack->Perform(command);
410 			} else {
411 				fCurrentShape->SetStyle(style);
412 			}
413 			handled = true;
414 		}
415 	}
416 
417 	if (!handled)
418 		SimpleListView::MouseDown(where);
419 }
420 
421 
422 void
423 StyleListView::MakeDragMessage(BMessage* message) const
424 {
425 	SimpleListView::MakeDragMessage(message);
426 	message->AddPointer("container", fStyleContainer);
427 	int32 count = CountSelectedItems();
428 	for (int32 i = 0; i < count; i++) {
429 		StyleListItem* item = dynamic_cast<StyleListItem*>(
430 			ItemAt(CurrentSelection(i)));
431 		if (item != NULL) {
432 			message->AddPointer("style", (void*)item->style);
433 			BMessage archive;
434 			if (item->style->Archive(&archive, true) == B_OK)
435 				message->AddMessage("style archive", &archive);
436 		} else
437 			break;
438 	}
439 }
440 
441 
442 bool
443 StyleListView::AcceptDragMessage(const BMessage* message) const
444 {
445 	return SimpleListView::AcceptDragMessage(message);
446 }
447 
448 
449 void
450 StyleListView::SetDropTargetRect(const BMessage* message, BPoint where)
451 {
452 	SimpleListView::SetDropTargetRect(message, where);
453 }
454 
455 
456 bool
457 StyleListView::HandleDropMessage(const BMessage* message, int32 dropIndex)
458 {
459 	// Let SimpleListView handle drag-sorting (when drag came from ourself)
460 	if (SimpleListView::HandleDropMessage(message, dropIndex))
461 		return true;
462 
463 	if (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 archive;
473 		if (message->FindMessage("style archive", index, &archive) != B_OK)
474 			break;
475 		Style* style = new(std::nothrow) Style(&archive);
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 	AddStylesCommand* command = new(std::nothrow) AddStylesCommand(
492 		fStyleContainer, (Style**)styles.Items(), count, 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 	AddStylesCommand* command
550 		= new (nothrow) AddStylesCommand(fStyleContainer,
551 										 styles, count, toIndex);
552 	if (!command) {
553 		for (int32 i = 0; i < count; i++)
554 			delete styles[i];
555 		return;
556 	}
557 
558 	fCommandStack->Perform(command);
559 }
560 
561 
562 void
563 StyleListView::RemoveItemList(BList& items)
564 {
565 	if (!fCommandStack || !fStyleContainer)
566 		return;
567 
568 	int32 count = items.CountItems();
569 	Style* styles[count];
570 	for (int32 i = 0; i < count; i++) {
571 		StyleListItem* item = dynamic_cast<StyleListItem*>(
572 			(BListItem*)items.ItemAtFast(i));
573 		if (item)
574 			styles[i] = item->style;
575 		else
576 			styles[i] = NULL;
577 	}
578 
579 	RemoveStylesCommand* command
580 		= new (nothrow) RemoveStylesCommand(fStyleContainer,
581 											styles, count);
582 	fCommandStack->Perform(command);
583 }
584 
585 
586 BListItem*
587 StyleListView::CloneItem(int32 index) const
588 {
589 	if (StyleListItem* item = dynamic_cast<StyleListItem*>(ItemAt(index))) {
590 		return new StyleListItem(item->style,
591 			const_cast<StyleListView*>(this),
592 			fCurrentShape != NULL);
593 	}
594 	return NULL;
595 }
596 
597 
598 int32
599 StyleListView::IndexOfSelectable(Selectable* selectable) const
600 {
601 	Style* style = dynamic_cast<Style*>(selectable);
602 	if (style == NULL)
603 		return -1;
604 
605 	int count = CountItems();
606 	for (int32 i = 0; i < count; i++) {
607 		if (SelectableFor(ItemAt(i)) == style)
608 			return i;
609 	}
610 
611 	return -1;
612 }
613 
614 
615 Selectable*
616 StyleListView::SelectableFor(BListItem* item) const
617 {
618 	StyleListItem* styleItem = dynamic_cast<StyleListItem*>(item);
619 	if (styleItem != NULL)
620 		return styleItem->style;
621 	return NULL;
622 }
623 
624 
625 // #pragma mark -
626 
627 
628 void
629 StyleListView::StyleAdded(Style* style, int32 index)
630 {
631 	// NOTE: we are in the thread that messed with the
632 	// StyleContainer, so no need to lock the
633 	// container, when this is changed to asynchronous
634 	// notifications, then it would need to be read-locked!
635 	if (!LockLooper())
636 		return;
637 
638 	if (_AddStyle(style, index))
639 		Select(index);
640 
641 	UnlockLooper();
642 }
643 
644 
645 void
646 StyleListView::StyleRemoved(Style* style)
647 {
648 	// NOTE: we are in the thread that messed with the
649 	// StyleContainer, so no need to lock the
650 	// container, when this is changed to asynchronous
651 	// notifications, then it would need to be read-locked!
652 	if (!LockLooper())
653 		return;
654 
655 	// NOTE: we're only interested in Style objects
656 	_RemoveStyle(style);
657 
658 	UnlockLooper();
659 }
660 
661 
662 // #pragma mark -
663 
664 
665 void
666 StyleListView::SetMenu(BMenu* menu)
667 {
668 	if (fMenu == menu)
669 		return;
670 
671 	fMenu = menu;
672 	if (fMenu == NULL)
673 		return;
674 
675 	fAddMI = new BMenuItem(B_TRANSLATE("Add"), new BMessage(MSG_ADD));
676 	fMenu->AddItem(fAddMI);
677 
678 	fMenu->AddSeparatorItem();
679 
680 	fDuplicateMI = new BMenuItem(B_TRANSLATE("Duplicate"),
681 		new BMessage(MSG_DUPLICATE));
682 	fMenu->AddItem(fDuplicateMI);
683 
684 	fResetTransformationMI = new BMenuItem(B_TRANSLATE("Reset transformation"),
685 		new BMessage(MSG_RESET_TRANSFORMATION));
686 	fMenu->AddItem(fResetTransformationMI);
687 
688 	fMenu->AddSeparatorItem();
689 
690 	fRemoveMI = new BMenuItem(B_TRANSLATE("Remove"), new BMessage(MSG_REMOVE));
691 	fMenu->AddItem(fRemoveMI);
692 
693 	fMenu->SetTargetForItems(this);
694 
695 	_UpdateMenu();
696 }
697 
698 
699 void
700 StyleListView::SetStyleContainer(StyleContainer* container)
701 {
702 	if (fStyleContainer == container)
703 		return;
704 
705 	// detach from old container
706 	if (fStyleContainer != NULL)
707 		fStyleContainer->RemoveListener(this);
708 
709 	_MakeEmpty();
710 
711 	fStyleContainer = container;
712 
713 	if (fStyleContainer == NULL)
714 		return;
715 
716 	fStyleContainer->AddListener(this);
717 
718 	// sync
719 	int32 count = fStyleContainer->CountStyles();
720 	for (int32 i = 0; i < count; i++)
721 		_AddStyle(fStyleContainer->StyleAtFast(i), i);
722 }
723 
724 
725 void
726 StyleListView::SetShapeContainer(ShapeContainer* container)
727 {
728 	if (fShapeContainer == container)
729 		return;
730 
731 	// detach from old container
732 	if (fShapeContainer)
733 		fShapeContainer->RemoveListener(fShapeListener);
734 
735 	fShapeContainer = container;
736 
737 	if (fShapeContainer)
738 		fShapeContainer->AddListener(fShapeListener);
739 }
740 
741 
742 void
743 StyleListView::SetCommandStack(CommandStack* stack)
744 {
745 	fCommandStack = stack;
746 }
747 
748 
749 void
750 StyleListView::SetCurrentColor(CurrentColor* color)
751 {
752 	fCurrentColor = color;
753 }
754 
755 
756 void
757 StyleListView::SetCurrentShape(Shape* shape)
758 {
759 	if (fCurrentShape == shape)
760 		return;
761 
762 	fCurrentShape = shape;
763 	fShapeListener->SetShape(shape);
764 
765 	_UpdateMarks();
766 }
767 
768 
769 // #pragma mark -
770 
771 
772 bool
773 StyleListView::_AddStyle(Style* style, int32 index)
774 {
775 	if (style != NULL) {
776 		 return AddItem(new StyleListItem(
777 		 	style, this, fCurrentShape != NULL), index);
778 	}
779 	return false;
780 }
781 
782 
783 bool
784 StyleListView::_RemoveStyle(Style* style)
785 {
786 	StyleListItem* item = _ItemForStyle(style);
787 	if (item != NULL && RemoveItem(item)) {
788 		delete item;
789 		return true;
790 	}
791 	return false;
792 }
793 
794 
795 StyleListItem*
796 StyleListView::_ItemForStyle(Style* style) const
797 {
798 	int count = CountItems();
799 	for (int32 i = 0; i < count; i++) {
800 		StyleListItem* item = dynamic_cast<StyleListItem*>(ItemAt(i));
801 		if (item == NULL)
802 			continue;
803 		if (item->style == style)
804 			return item;
805 	}
806 	return NULL;
807 }
808 
809 
810 // #pragma mark -
811 
812 
813 void
814 StyleListView::_UpdateMarks()
815 {
816 	int32 count = CountItems();
817 	if (fCurrentShape) {
818 		// enable display of marks and mark items whoes
819 		// style is contained in fCurrentShape
820 		for (int32 i = 0; i < count; i++) {
821 			StyleListItem* item = dynamic_cast<StyleListItem*>(ItemAt(i));
822 			if (item == NULL)
823 				continue;
824 			item->SetMarkEnabled(true);
825 			item->SetMarked(fCurrentShape->Style() == item->style);
826 		}
827 	} else {
828 		// disable display of marks
829 		for (int32 i = 0; i < count; i++) {
830 			StyleListItem* item = dynamic_cast<StyleListItem*>(ItemAt(i));
831 			if (item == NULL)
832 				continue;
833 			item->SetMarkEnabled(false);
834 		}
835 	}
836 
837 	Invalidate();
838 }
839 
840 
841 void
842 StyleListView::_SetStyleMarked(Style* style, bool marked)
843 {
844 	StyleListItem* item = _ItemForStyle(style);
845 	if (item != NULL)
846 		item->SetMarked(marked);
847 }
848 
849 
850 void
851 StyleListView::_UpdateMenu()
852 {
853 	if (fMenu == NULL)
854 		return;
855 
856 	bool gotSelection = CurrentSelection(0) >= 0;
857 
858 	fDuplicateMI->SetEnabled(gotSelection);
859 	// TODO: only enable fResetTransformationMI if styles
860 	// with gradients are selected!
861 	fResetTransformationMI->SetEnabled(gotSelection);
862 	fRemoveMI->SetEnabled(gotSelection);
863 }
864 
865