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