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 "ShapeListView.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 "AddShapesCommand.h"
27 #include "AddStylesCommand.h"
28 #include "CommandStack.h"
29 #include "CompoundCommand.h"
30 #include "Container.h"
31 #include "FreezeTransformationCommand.h"
32 #include "MainWindow.h"
33 #include "MoveShapesCommand.h"
34 #include "Observer.h"
35 #include "PathSourceShape.h"
36 #include "ReferenceImage.h"
37 #include "RemoveShapesCommand.h"
38 #include "ResetTransformationCommand.h"
39 #include "Selection.h"
40 #include "Shape.h"
41 #include "Style.h"
42 #include "Util.h"
43 #include "VectorPath.h"
44
45 #undef B_TRANSLATION_CONTEXT
46 #define B_TRANSLATION_CONTEXT "Icon-O-Matic-ShapesList"
47
48
49 using std::nothrow;
50
51 class ShapeListItem : public SimpleItem, public Observer {
52 public:
ShapeListItem(Shape * s,ShapeListView * listView)53 ShapeListItem(Shape* s, ShapeListView* listView)
54 :
55 SimpleItem(""),
56 shape(NULL),
57 fListView(listView)
58 {
59 SetShape(s);
60 }
61
62
~ShapeListItem()63 virtual ~ShapeListItem()
64 {
65 SetShape(NULL);
66 }
67
68
ObjectChanged(const Observable * object)69 virtual void ObjectChanged(const Observable* object)
70 {
71 UpdateText();
72 }
73
SetShape(Shape * s)74 void SetShape(Shape* s)
75 {
76 if (s == shape)
77 return;
78
79 if (shape) {
80 shape->RemoveObserver(this);
81 shape->ReleaseReference();
82 }
83
84 shape = s;
85
86 if (shape) {
87 shape->AcquireReference();
88 shape->AddObserver(this);
89 UpdateText();
90 }
91 }
92
UpdateText()93 void UpdateText()
94 {
95 SetText(shape->Name());
96 if (fListView->LockLooper()) {
97 fListView->InvalidateItem(fListView->IndexOf(this));
98 fListView->UnlockLooper();
99 }
100 }
101
102 public:
103 Shape* shape;
104
105 private:
106 ShapeListView* fListView;
107 };
108
109
110 // #pragma mark -
111
112
113 enum {
114 MSG_REMOVE = 'rmsh',
115 MSG_DUPLICATE = 'dpsh',
116 MSG_RESET_TRANSFORMATION = 'rstr',
117 MSG_FREEZE_TRANSFORMATION = 'frzt',
118
119 MSG_DRAG_SHAPE = 'drgs',
120 };
121
122
ShapeListView(BRect frame,const char * name,BMessage * message,BHandler * target)123 ShapeListView::ShapeListView(BRect frame, const char* name, BMessage* message,
124 BHandler* target)
125 :
126 SimpleListView(frame, name, NULL, B_MULTIPLE_SELECTION_LIST),
127 fMessage(message),
128 fShapeContainer(NULL),
129 fStyleContainer(NULL),
130 fPathContainer(NULL),
131 fCommandStack(NULL)
132 {
133 SetDragCommand(MSG_DRAG_SHAPE);
134 SetTarget(target);
135 }
136
137
~ShapeListView()138 ShapeListView::~ShapeListView()
139 {
140 _MakeEmpty();
141 delete fMessage;
142
143 if (fShapeContainer != NULL)
144 fShapeContainer->RemoveListener(this);
145 }
146
147
148 void
SelectionChanged()149 ShapeListView::SelectionChanged()
150 {
151 SimpleListView::SelectionChanged();
152
153 if (!fSyncingToSelection) {
154 ShapeListItem* item
155 = dynamic_cast<ShapeListItem*>(ItemAt(CurrentSelection(0)));
156 if (fMessage) {
157 BMessage message(*fMessage);
158 message.AddPointer("shape", item ? (void*)item->shape : NULL);
159 Invoke(&message);
160 }
161 }
162
163 _UpdateMenu();
164 }
165
166
167 void
MessageReceived(BMessage * message)168 ShapeListView::MessageReceived(BMessage* message)
169 {
170 switch (message->what) {
171 case MSG_REMOVE:
172 RemoveSelected();
173 break;
174
175 case MSG_DUPLICATE:
176 {
177 int32 count = CountSelectedItems();
178 int32 index = 0;
179 BList items;
180 for (int32 i = 0; i < count; i++) {
181 index = CurrentSelection(i);
182 BListItem* item = ItemAt(index);
183 if (item)
184 items.AddItem((void*)item);
185 }
186 CopyItems(items, index + 1);
187 break;
188 }
189
190 case MSG_RESET_TRANSFORMATION:
191 {
192 BList shapes;
193 _GetSelectedShapes(shapes);
194 int32 count = shapes.CountItems();
195 if (count < 0)
196 break;
197
198 Transformable* transformables[count];
199 for (int32 i = 0; i < count; i++) {
200 Shape* shape = (Shape*)shapes.ItemAtFast(i);
201 transformables[i] = shape;
202 }
203
204 ResetTransformationCommand* command =
205 new ResetTransformationCommand(transformables, count);
206
207 fCommandStack->Perform(command);
208 break;
209 }
210
211 case MSG_FREEZE_TRANSFORMATION:
212 {
213 BList shapes;
214 _GetSelectedShapes(shapes);
215 int32 count = shapes.CountItems();
216 if (count < 0)
217 break;
218
219 BList pathSourceShapes;
220
221 for (int i = 0; i < count; i++) {
222 Shape* shape = (Shape*) shapes.ItemAtFast(i);
223 if (dynamic_cast<PathSourceShape*>(shape) != NULL)
224 pathSourceShapes.AddItem(shape);
225 }
226
227 count = pathSourceShapes.CountItems();
228
229 FreezeTransformationCommand* command
230 = new FreezeTransformationCommand(
231 (PathSourceShape**)pathSourceShapes.Items(),
232 count);
233
234 fCommandStack->Perform(command);
235 break;
236 }
237
238 default:
239 SimpleListView::MessageReceived(message);
240 break;
241 }
242 }
243
244
245 status_t
ArchiveSelection(BMessage * into,bool deep) const246 ShapeListView::ArchiveSelection(BMessage* into, bool deep) const
247 {
248 into->what = ShapeListView::kSelectionArchiveCode;
249
250 int32 count = CountSelectedItems();
251 for (int32 i = 0; i < count; i++) {
252 ShapeListItem* item = dynamic_cast<ShapeListItem*>(
253 ItemAt(CurrentSelection(i)));
254 if (item == NULL)
255 return B_ERROR;
256
257 PathSourceShape* shape = dynamic_cast<PathSourceShape*>(item->shape);
258 if (shape != NULL) {
259 BMessage archive;
260 archive.what = PathSourceShape::archive_code;
261
262 BMessage styleArchive;
263 if (shape->Style() != NULL) {
264 shape->Style()->Archive(&styleArchive, true);
265 archive.AddMessage("style", &styleArchive);
266 }
267
268 if (shape->Paths() != NULL) {
269 int32 pathsCount = shape->Paths()->CountItems();
270 for (int32 j = 0; j < pathsCount; j++) {
271 BMessage pathArchive;
272 if (shape->Paths()->ItemAt(j) != NULL) {
273 shape->Paths()->ItemAt(j)->Archive(&pathArchive, true);
274 archive.AddMessage("path", &pathArchive);
275 }
276 }
277 }
278
279 BMessage shapeArchive;
280 shape->Archive(&shapeArchive, true);
281 archive.AddMessage("shape", &shapeArchive);
282
283 into->AddMessage("shape archive", &archive);
284 continue;
285 }
286
287 ReferenceImage* referenceImage = dynamic_cast<ReferenceImage*>(item->shape);
288 if (referenceImage != NULL) {
289 BMessage archive;
290 archive.what = ReferenceImage::archive_code;
291
292 BMessage shapeArchive;
293 referenceImage->Archive(&shapeArchive, true);
294 archive.AddMessage("shape", &shapeArchive);
295
296 into->AddMessage("shape archive", &archive);
297 continue;
298 }
299 }
300
301 return B_OK;
302 }
303
304
305 bool
InstantiateSelection(const BMessage * archive,int32 dropIndex)306 ShapeListView::InstantiateSelection(const BMessage* archive, int32 dropIndex)
307 {
308 if (archive->what != ShapeListView::kSelectionArchiveCode
309 || fCommandStack == NULL || fShapeContainer == NULL
310 || fStyleContainer == NULL || fPathContainer == NULL) {
311 return false;
312 }
313
314 // Drag may have come from another instance, like in another window.
315 // Reconstruct the Shapes from the archive and add them at the drop
316 // index.
317 int index = 0;
318 BList styles;
319 BList paths;
320 BList shapes;
321 while (true) {
322 BMessage shapeArchive;
323 if (archive->FindMessage("shape archive", index, &shapeArchive) != B_OK)
324 break;
325
326 if (shapeArchive.what == PathSourceShape::archive_code) {
327 // Extract the style
328 BMessage styleArchive;
329 if (shapeArchive.FindMessage("style", &styleArchive) != B_OK)
330 break;
331
332 Style* style = new Style(&styleArchive);
333 if (style == NULL)
334 break;
335
336 Style* styleToAssign = style;
337 // Try to find an existing style that is the same as the extracted
338 // style and use that one instead.
339 for (int32 i = 0; i < fStyleContainer->CountItems(); i++) {
340 Style* other = fStyleContainer->ItemAtFast(i);
341 if (*other == *style) {
342 styleToAssign = other;
343 delete style;
344 style = NULL;
345 break;
346 }
347 }
348
349 if (style != NULL && !styles.AddItem(style)) {
350 delete style;
351 break;
352 }
353
354 // Create the shape using the given style
355 PathSourceShape* shape = new(std::nothrow) PathSourceShape(styleToAssign);
356 if (shape == NULL)
357 break;
358
359 // Extract the shape archive
360 BMessage shapeMessage;
361 if (shapeArchive.FindMessage("shape", &shapeMessage) != B_OK)
362 break;
363 if (shape->Unarchive(&shapeMessage) != B_OK
364 || !shapes.AddItem(shape)) {
365 delete shape;
366 if (style != NULL) {
367 styles.RemoveItem(style);
368 delete style;
369 }
370 break;
371 }
372
373 // Extract the paths
374 int pathIndex = 0;
375 while (true) {
376 BMessage pathArchive;
377 if (shapeArchive.FindMessage("path", pathIndex, &pathArchive) != B_OK)
378 break;
379
380 VectorPath* path = new(nothrow) VectorPath(&pathArchive);
381 if (path == NULL)
382 break;
383
384 VectorPath* pathToInclude = path;
385 for (int32 i = 0; i < fPathContainer->CountItems(); i++) {
386 VectorPath* other = fPathContainer->ItemAtFast(i);
387 if (*other == *path) {
388 pathToInclude = other;
389 delete path;
390 path = NULL;
391 break;
392 }
393 }
394
395 if (path != NULL && !paths.AddItem(path)) {
396 delete path;
397 break;
398 }
399
400 shape->Paths()->AddItem(pathToInclude);
401
402 pathIndex++;
403 }
404 } else if (shapeArchive.what == ReferenceImage::archive_code) {
405 BMessage shapeMessage;
406 if (shapeArchive.FindMessage("shape", &shapeMessage) != B_OK)
407 break;
408
409 ReferenceImage* shape = new (std::nothrow) ReferenceImage(&shapeMessage);
410 if (shape == NULL)
411 break;
412
413 if (shapes.AddItem(shape) != B_OK)
414 break;
415 }
416
417 index++;
418 }
419
420 int32 shapeCount = shapes.CountItems();
421 if (shapeCount == 0)
422 return false;
423
424 // TODO: Add allocation checks beyond this point.
425
426 AddStylesCommand* stylesCommand = new(std::nothrow) AddStylesCommand(
427 fStyleContainer, (Style**)styles.Items(), styles.CountItems(),
428 fStyleContainer->CountItems());
429
430 AddPathsCommand* pathsCommand = new(std::nothrow) AddPathsCommand(
431 fPathContainer, (VectorPath**)paths.Items(), paths.CountItems(),
432 true, fPathContainer->CountItems());
433
434 AddShapesCommand* shapesCommand = new(std::nothrow) AddShapesCommand(
435 fShapeContainer, (Shape**)shapes.Items(), shapeCount, dropIndex);
436
437 ::Command** commands = new(std::nothrow) ::Command*[3];
438
439 commands[0] = stylesCommand;
440 commands[1] = pathsCommand;
441 commands[2] = shapesCommand;
442
443 CompoundCommand* command = new CompoundCommand(commands, 3,
444 B_TRANSLATE("Drop shapes"), -1);
445
446 fCommandStack->Perform(command);
447
448 return true;
449 }
450
451
452 // #pragma mark -
453
454
455 void
MoveItems(BList & items,int32 toIndex)456 ShapeListView::MoveItems(BList& items, int32 toIndex)
457 {
458 if (fCommandStack == NULL || fShapeContainer == NULL)
459 return;
460
461 int32 count = items.CountItems();
462 Shape** shapes = new(nothrow) Shape*[count];
463 if (shapes == NULL)
464 return;
465
466 for (int32 i = 0; i < count; i++) {
467 ShapeListItem* item
468 = dynamic_cast<ShapeListItem*>((BListItem*)items.ItemAtFast(i));
469 shapes[i] = item ? item->shape : NULL;
470 }
471
472 MoveShapesCommand* command = new (nothrow) MoveShapesCommand(
473 fShapeContainer, shapes, count, toIndex);
474 if (command == NULL) {
475 delete[] shapes;
476 return;
477 }
478
479 fCommandStack->Perform(command);
480 }
481
482 // CopyItems
483 void
CopyItems(BList & items,int32 toIndex)484 ShapeListView::CopyItems(BList& items, int32 toIndex)
485 {
486 if (fCommandStack == NULL || fShapeContainer == NULL)
487 return;
488
489 int32 count = items.CountItems();
490 Shape* shapes[count];
491
492 for (int32 i = 0; i < count; i++) {
493 ShapeListItem* item
494 = dynamic_cast<ShapeListItem*>((BListItem*)items.ItemAtFast(i));
495 shapes[i] = item ? item->shape->Clone() : NULL;
496 }
497
498 AddShapesCommand* command = new(nothrow) AddShapesCommand(fShapeContainer,
499 shapes, count, toIndex);
500 if (command == NULL) {
501 for (int32 i = 0; i < count; i++)
502 delete shapes[i];
503 return;
504 }
505
506 fCommandStack->Perform(command);
507 }
508
509
510 void
RemoveItemList(BList & items)511 ShapeListView::RemoveItemList(BList& items)
512 {
513 if (fCommandStack == NULL || fShapeContainer == NULL)
514 return;
515
516 int32 count = items.CountItems();
517 int32 indices[count];
518 for (int32 i = 0; i < count; i++)
519 indices[i] = IndexOf((BListItem*)items.ItemAtFast(i));
520
521 RemoveShapesCommand* command = new(nothrow) RemoveShapesCommand(
522 fShapeContainer, indices, count);
523
524 fCommandStack->Perform(command);
525 }
526
527
528 BListItem*
CloneItem(int32 index) const529 ShapeListView::CloneItem(int32 index) const
530 {
531 ShapeListItem* item = dynamic_cast<ShapeListItem*>(ItemAt(index));
532 if (item != NULL) {
533 return new ShapeListItem(item->shape,
534 const_cast<ShapeListView*>(this));
535 }
536 return NULL;
537 }
538
539
540 int32
IndexOfSelectable(Selectable * selectable) const541 ShapeListView::IndexOfSelectable(Selectable* selectable) const
542 {
543 Shape* shape = dynamic_cast<Shape*>(selectable);
544 if (shape == NULL) {
545 Transformer* transformer = dynamic_cast<Transformer*>(selectable);
546 if (transformer == NULL)
547 return -1;
548 int32 count = CountItems();
549 for (int32 i = 0; i < count; i++) {
550 ShapeListItem* item = dynamic_cast<ShapeListItem*>(ItemAt(i));
551 if (item != NULL && item->shape->Transformers()->HasItem(transformer))
552 return i;
553 }
554 } else {
555 int32 count = CountItems();
556 for (int32 i = 0; i < count; i++) {
557 ShapeListItem* item = dynamic_cast<ShapeListItem*>(ItemAt(i));
558 if (item != NULL && item->shape == shape)
559 return i;
560 }
561 }
562
563 return -1;
564 }
565
566
567 Selectable*
SelectableFor(BListItem * item) const568 ShapeListView::SelectableFor(BListItem* item) const
569 {
570 ShapeListItem* shapeItem = dynamic_cast<ShapeListItem*>(item);
571 if (shapeItem != NULL)
572 return shapeItem->shape;
573 return NULL;
574 }
575
576
577 // #pragma mark -
578
579
580 void
ItemAdded(Shape * shape,int32 index)581 ShapeListView::ItemAdded(Shape* shape, int32 index)
582 {
583 // NOTE: we are in the thread that messed with the
584 // ShapeContainer, so no need to lock the
585 // container, when this is changed to asynchronous
586 // notifications, then it would need to be read-locked!
587 if (!LockLooper())
588 return;
589
590 if (_AddShape(shape, index))
591 Select(index);
592
593 UnlockLooper();
594 }
595
596
597 void
ItemRemoved(Shape * shape)598 ShapeListView::ItemRemoved(Shape* shape)
599 {
600 // NOTE: we are in the thread that messed with the
601 // ShapeContainer, so no need to lock the
602 // container, when this is changed to asynchronous
603 // notifications, then it would need to be read-locked!
604 if (!LockLooper())
605 return;
606
607 // NOTE: we're only interested in Shape objects
608 _RemoveShape(shape);
609
610 UnlockLooper();
611 }
612
613
614 // #pragma mark -
615
616
617 void
SetMenu(BMenu * menu)618 ShapeListView::SetMenu(BMenu* menu)
619 {
620 if (fMenu == menu)
621 return;
622
623 fMenu = menu;
624
625 if (fMenu == NULL)
626 return;
627
628 BMessage* message = new BMessage(MSG_ADD_SHAPE);
629 fAddEmptyMI = new BMenuItem(B_TRANSLATE("Add empty"), message);
630
631 message = new BMessage(MSG_ADD_SHAPE);
632 message->AddBool("path", true);
633 fAddWidthPathMI = new BMenuItem(B_TRANSLATE("Add with path"), message);
634
635 message = new BMessage(MSG_ADD_SHAPE);
636 message->AddBool("style", true);
637 fAddWidthStyleMI = new BMenuItem(B_TRANSLATE("Add with style"), message);
638
639 message = new BMessage(MSG_ADD_SHAPE);
640 message->AddBool("path", true);
641 message->AddBool("style", true);
642 fAddWidthPathAndStyleMI = new BMenuItem(
643 B_TRANSLATE("Add with path & style"), message);
644
645 message = new BMessage(MSG_OPEN);
646 message->AddBool("reference image", true);
647 fAddReferenceImageMI = new BMenuItem(B_TRANSLATE("Add reference image"), message);
648
649 fDuplicateMI = new BMenuItem(B_TRANSLATE("Duplicate"),
650 new BMessage(MSG_DUPLICATE));
651 fResetTransformationMI = new BMenuItem(B_TRANSLATE("Reset transformation"),
652 new BMessage(MSG_RESET_TRANSFORMATION));
653 fFreezeTransformationMI = new BMenuItem(
654 B_TRANSLATE("Freeze transformation"),
655 new BMessage(MSG_FREEZE_TRANSFORMATION));
656
657 fRemoveMI = new BMenuItem(B_TRANSLATE("Remove"), new BMessage(MSG_REMOVE));
658
659
660 fMenu->AddItem(fAddEmptyMI);
661 fMenu->AddItem(fAddWidthPathMI);
662 fMenu->AddItem(fAddWidthStyleMI);
663 fMenu->AddItem(fAddWidthPathAndStyleMI);
664
665 fMenu->AddSeparatorItem();
666
667 fMenu->AddItem(fAddReferenceImageMI);
668
669 fMenu->AddSeparatorItem();
670
671 fMenu->AddItem(fDuplicateMI);
672 fMenu->AddItem(fResetTransformationMI);
673 fMenu->AddItem(fFreezeTransformationMI);
674 fMenu->AddSeparatorItem();
675
676 fMenu->AddItem(fRemoveMI);
677
678 fDuplicateMI->SetTarget(this);
679 fResetTransformationMI->SetTarget(this);
680 fFreezeTransformationMI->SetTarget(this);
681 fRemoveMI->SetTarget(this);
682
683 _UpdateMenu();
684 }
685
686
687 void
SetShapeContainer(Container<Shape> * container)688 ShapeListView::SetShapeContainer(Container<Shape>* container)
689 {
690 if (fShapeContainer == container)
691 return;
692
693 // detach from old container
694 if (fShapeContainer != NULL)
695 fShapeContainer->RemoveListener(this);
696
697 _MakeEmpty();
698
699 fShapeContainer = container;
700
701 if (fShapeContainer == NULL)
702 return;
703
704 fShapeContainer->AddListener(this);
705
706 // sync
707 int32 count = fShapeContainer->CountItems();
708 for (int32 i = 0; i < count; i++)
709 _AddShape(fShapeContainer->ItemAtFast(i), i);
710 }
711
712
713 void
SetStyleContainer(Container<Style> * container)714 ShapeListView::SetStyleContainer(Container<Style>* container)
715 {
716 fStyleContainer = container;
717 }
718
719
720 void
SetPathContainer(Container<VectorPath> * container)721 ShapeListView::SetPathContainer(Container<VectorPath>* container)
722 {
723 fPathContainer = container;
724 }
725
726
727 void
SetCommandStack(CommandStack * stack)728 ShapeListView::SetCommandStack(CommandStack* stack)
729 {
730 fCommandStack = stack;
731 }
732
733
734 // #pragma mark -
735
736
737 bool
_AddShape(Shape * shape,int32 index)738 ShapeListView::_AddShape(Shape* shape, int32 index)
739 {
740 if (shape == NULL)
741 return false;
742
743 ShapeListItem* item = new(std::nothrow) ShapeListItem(shape, this);
744 if (item == NULL)
745 return false;
746
747 if (!AddItem(item, index)) {
748 delete item;
749 return false;
750 }
751
752 return true;
753 }
754
755
756 bool
_RemoveShape(Shape * shape)757 ShapeListView::_RemoveShape(Shape* shape)
758 {
759 ShapeListItem* item = _ItemForShape(shape);
760 if (item != NULL && RemoveItem(item)) {
761 delete item;
762 return true;
763 }
764 return false;
765 }
766
767
768 ShapeListItem*
_ItemForShape(Shape * shape) const769 ShapeListView::_ItemForShape(Shape* shape) const
770 {
771 int32 count = CountItems();
772 for (int32 i = 0; i < count; i++) {
773 ShapeListItem* item = dynamic_cast<ShapeListItem*>(ItemAt(i));
774 if (item != NULL && item->shape == shape)
775 return item;
776 }
777 return NULL;
778 }
779
780
781 void
_UpdateMenu()782 ShapeListView::_UpdateMenu()
783 {
784 if (fMenu == NULL)
785 return;
786
787 bool gotSelection = CurrentSelection(0) >= 0;
788
789 fDuplicateMI->SetEnabled(gotSelection);
790 fResetTransformationMI->SetEnabled(gotSelection);
791 fFreezeTransformationMI->SetEnabled(gotSelection);
792 fRemoveMI->SetEnabled(gotSelection);
793
794 if (gotSelection) {
795 bool hasPathSourceShape = false;
796
797 int32 count = CountSelectedItems();
798 for (int32 i = 0; i < count; i++) {
799 ShapeListItem* item
800 = dynamic_cast<ShapeListItem*>(ItemAt(CurrentSelection(i)));
801 bool isPathSourceShape
802 = item ? dynamic_cast<PathSourceShape*>(item->shape) != NULL : false;
803 hasPathSourceShape |= isPathSourceShape;
804 }
805
806 fFreezeTransformationMI->SetEnabled(hasPathSourceShape);
807 }
808 }
809
810
811 void
_GetSelectedShapes(BList & shapes) const812 ShapeListView::_GetSelectedShapes(BList& shapes) const
813 {
814 int32 count = CountSelectedItems();
815 for (int32 i = 0; i < count; i++) {
816 ShapeListItem* item = dynamic_cast<ShapeListItem*>(
817 ItemAt(CurrentSelection(i)));
818 if (item != NULL && item->shape != NULL) {
819 if (!shapes.AddItem((void*)item->shape))
820 break;
821 }
822 }
823 }
824