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