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