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