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