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