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
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:
PathListItem(VectorPath * p,PathListView * listView,bool markEnabled)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
~PathListItem()66 virtual ~PathListItem()
67 {
68 SetPath(NULL);
69 }
70
71
72 // SimpleItem interface
DrawItem(BView * owner,BRect itemFrame,bool even)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
ObjectChanged(const Observable * object)117 virtual void ObjectChanged(const Observable* object)
118 {
119 UpdateText();
120 }
121
122
123 // PathListItem
SetPath(VectorPath * p)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
UpdateText()144 void UpdateText()
145 {
146 SetText(path->Name());
147 Invalidate();
148 }
149
150
SetMarkEnabled(bool enabled)151 void SetMarkEnabled(bool enabled)
152 {
153 if (fMarkEnabled == enabled)
154 return;
155 fMarkEnabled = enabled;
156 Invalidate();
157 }
158
159
SetMarked(bool marked)160 void SetMarked(bool marked)
161 {
162 if (fMarked == marked)
163 return;
164 fMarked = marked;
165 Invalidate();
166 }
167
168
Invalidate()169 void Invalidate()
170 {
171 if (fListView->LockLooper()) {
172 fListView->InvalidateItem(
173 fListView->IndexOf(this));
174 fListView->UnlockLooper();
175 }
176 }
177
178 public:
179 VectorPath* path;
180
181 private:
182 PathListView* fListView;
183 bool fMarkEnabled;
184 bool fMarked;
185 };
186
187
188 class ShapePathListener : public ContainerListener<VectorPath>,
189 public ContainerListener<Shape> {
190 public:
ShapePathListener(PathListView * listView)191 ShapePathListener(PathListView* listView)
192 :
193 fListView(listView),
194 fShape(NULL)
195 {
196 }
197
198
~ShapePathListener()199 virtual ~ShapePathListener()
200 {
201 SetShape(NULL);
202 }
203
204
205 // ContainerListener<VectorPath> interface
ItemAdded(VectorPath * path,int32 index)206 virtual void ItemAdded(VectorPath* path, int32 index)
207 {
208 fListView->_SetPathMarked(path, true);
209 }
210
211
ItemRemoved(VectorPath * path)212 virtual void ItemRemoved(VectorPath* path)
213 {
214 fListView->_SetPathMarked(path, false);
215 }
216
217
218 // ContainerListener<Shape> interface
ItemAdded(Shape * shape,int32 index)219 virtual void ItemAdded(Shape* shape, int32 index)
220 {
221 }
222
223
ItemRemoved(Shape * shape)224 virtual void ItemRemoved(Shape* shape)
225 {
226 fListView->SetCurrentShape(NULL);
227 }
228
229
230 // ShapePathListener
SetShape(PathSourceShape * shape)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
CurrentShape() const246 Shape* CurrentShape() const
247 {
248 return fShape;
249 }
250
251 private:
252 PathListView* fListView;
253 PathSourceShape* fShape;
254 };
255
256
257 // #pragma mark -
258
259
260 enum {
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
PathListView(BRect frame,const char * name,BMessage * message,BHandler * target)278 PathListView::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
~PathListView()296 PathListView::~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
311 void
SelectionChanged()312 PathListView::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
331 void
MouseDown(BPoint where)332 PathListView::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
369 void
MessageReceived(BMessage * message)370 PathListView::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
501 status_t
ArchiveSelection(BMessage * into,bool deep) const502 PathListView::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
522 bool
InstantiateSelection(const BMessage * archive,int32 dropIndex)523 PathListView::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
569 void
MoveItems(BList & items,int32 toIndex)570 PathListView::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
597 void
CopyItems(BList & items,int32 toIndex)598 PathListView::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
624 void
RemoveItemList(BList & items)625 PathListView::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
642 BListItem*
CloneItem(int32 index) const643 PathListView::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
653 int32
IndexOfSelectable(Selectable * selectable) const654 PathListView::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
670 Selectable*
SelectableFor(BListItem * item) const671 PathListView::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
683 void
ItemAdded(VectorPath * path,int32 index)684 PathListView::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
700 void
ItemRemoved(VectorPath * path)701 PathListView::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
720 void
SetPathContainer(Container<VectorPath> * container)721 PathListView::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
751 void
SetShapeContainer(Container<Shape> * container)752 PathListView::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
768 void
SetCommandStack(CommandStack * stack)769 PathListView::SetCommandStack(CommandStack* stack)
770 {
771 fCommandStack = stack;
772 }
773
774
775 void
SetMenu(BMenu * menu)776 PathListView::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
829 void
SetCurrentShape(Shape * shape)830 PathListView::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
845 bool
_AddPath(VectorPath * path,int32 index)846 PathListView::_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
865 bool
_RemovePath(VectorPath * path)866 PathListView::_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
877 PathListItem*
_ItemForPath(VectorPath * path) const878 PathListView::_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
895 void
_UpdateMarks()896 PathListView::_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
923 void
_SetPathMarked(VectorPath * path,bool marked)924 PathListView::_SetPathMarked(VectorPath* path, bool marked)
925 {
926 PathListItem* item = _ItemForPath(path);
927 if (item != NULL)
928 item->SetMarked(marked);
929 }
930
931
932 void
_UpdateMenu()933 PathListView::_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