]> Shamusworld >> Repos - architektonas/blob - src/container.cpp
Mirror tool now works successfully for all object types. :-D
[architektonas] / src / container.cpp
1 // container.cpp: Container object
2 //
3 // Part of the Architektonas Project
4 // (C) 2011 Underground Software
5 // See the README and GPLv3 files for licensing and warranty information
6 //
7 // JLH = James Hammons <jlhamm@acm.org>
8 //
9 // WHO  WHEN        WHAT
10 // ---  ----------  ------------------------------------------------------------
11 // JLH  03/30/2011  Created this file
12 // JLH  06/02/2011  Added code to delete objects in this container when they go
13 //                  out of scope
14 //
15
16 #include "container.h"
17
18 #include <QtGui>
19 #include "dimension.h"
20 #include "painter.h"
21
22
23 Container::Container(Vector p1, Object * p/*= NULL*/): Object(p1, p),
24         isTopLevelContainer(false),
25         dragging(false), draggingHandle1(false), draggingHandle2(false),
26         hit(false)//, needUpdate(false)
27 {
28         type = OTContainer;
29         state = OSInactive;
30 }
31
32
33 // Copy constructor
34 Container::Container(const Container & copy): Object(copy.position, copy.parent)
35 {
36         // Use overloaded assignment operator
37         *this = copy;
38         type = OTContainer;
39         state = OSInactive;
40 }
41
42
43 Container::~Container()
44 {
45         Clear();
46 }
47
48
49 // Assignment operator
50 Container & Container::operator=(const Container & from)
51 {
52         // Take care of self-assignment
53         if (this == &from)
54                 return *this;
55
56         Clear();
57
58         // Small problem with this approach: if the copied object goes out of scope,
59         // all of the objects we copied in here will be deleted. D'oh!
60         // For this COPY constructor to be meaningful, we have to actually COPY the
61         // objects in this Container, not just MOVE a copy of the POINTER! D-:
62         std::vector<Object *>::const_iterator i;
63
64 //      for(uint i=0; i<from.objects.size(); i++)
65         for(i=from.objects.begin(); i!=from.objects.end(); i++)
66         {
67 //              Object * object = from.objects[i];
68                 Object * object = (*i)->Copy();
69
70                 // Need to actually COPY the object here, not copy the pointer only!!
71                 // (which we do now, above :-P)
72
73                 objects.push_back(object);
74         }
75
76         return *this;
77 }
78
79
80 /*virtual*/ void Container::Draw(Painter * painter)
81 {
82         QRectF boundary;
83
84 //int a=1;
85         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
86         {
87 //printf("Containter::Draw item #%i [%X]...\n", a++, *i);
88                 (*i)->Draw(painter);
89                 boundary = boundary.united((*i)->Extents());
90         }
91
92         if ((state == OSSelected) || hit)
93         {
94                 if (hit)
95                         painter->SetPen(QPen(Qt::magenta, 1.0, Qt::DashLine));
96                 else
97                         painter->SetPen(QPen(Qt::blue, 2.0, Qt::DashLine));
98
99                 painter->SetBrush(QBrush(Qt::NoBrush));
100                 painter->DrawPaddedRect(boundary);
101         }
102 }
103
104
105 /*virtual*/ Vector Container::Center(void)
106 {
107         return position;
108 }
109
110
111 /*
112  We need at least *three* handles for this object:
113  - one for moving
114  - one for resizing
115  - one for rotation
116
117 We need to think about the intuitive way (if there is any) to grab and
118 manipulate a complex object like this... Need to think, "What should happen when
119 I click here and drag there?"
120
121 Also: should put the snap logic into the Object base class (as a static method)...
122 */
123
124
125 // Need to add checking here for clicking on a member of a group (Container),
126 // and checking for if it's a top level container (the DrawingView's document).
127 /*
128 One approach is to check for the parent of the container: If it's NULL, then it's
129 the DrawingView's document. It might be better, though, to set a boolean like
130 isTopLevelContainer so that we can do things like edit members of a group without
131 having to ungroup them first (like Inkscape).
132 */
133 /*virtual*/ bool Container::Collided(Vector point)
134 {
135         objectWasDragged = false;
136 //      Vector v1 = position - point;
137
138         bool collision = false;
139
140         // NOTE that this deletes the object on mouse down instead of mouse up. Have to
141         // check to see how it feels to do it that way...
142         if (deleteActive)
143         {
144                 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end();)
145                 {
146                         if ((*i)->Collided(point))
147                         {
148 printf("Container::Collided: Deleting object ($%X)\n", *i);
149                                 Object * objectToDelete = *i;
150                                 objects.erase(i);
151                                 delete objectToDelete;
152                         }
153                         else
154                                 i++;
155                 }
156         }
157         else
158         {
159                 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
160                 {
161                         if ((*i)->Collided(point))
162                                 collision = true;
163                 }
164         }
165
166         if (snapToGrid)
167                 point = SnapPointToGrid(point);
168
169         // We check to see if the container we're trying to access is the
170         // DrawingView's document. If so, we ignore the state of the container.
171         // Otherwise, we care about the state of the container. :-)
172         if (isTopLevelContainer)
173                 state = OSInactive;
174         else
175         {
176                 state = (collision ? OSSelected : OSInactive);
177
178                 if (state == OSSelected)
179                 {
180                         DeselectAll();
181                         dragging = true;
182                         oldPoint = point;
183                 }
184         }
185
186         return collision;
187 }
188
189 /*
190 What we need to do is check for whether or not we're a top level container,
191 and override the passing of stuff into the objects held. So here, if we're *NOT*
192 a top level container, instead of passing PointerMoved to our contained objects,
193 we check to see if our bounds are met (for selection rectangle, e.g.).
194
195 Also, for things like being able to split point's hot spots we need to be able
196 to check for that crap in the top level container. Which means that objects can
197 still know how to move themselves, but they can also defer to their container
198 as well. Which also means that things like HitTest() need to be in the Object
199 class so that we can leverage that stuff here as well.
200 */
201
202 // The TLC is passing all mouse movement here, so we're doing the same here.
203 // Need to adjust all other objects to handle things correctly.
204
205 // One optimization that will need to be done eventually is to subdivide the screen
206 // into parts and keep subdividing until an acceptable number of objects lie within
207 // the slice. This way, the GUI will still be responsive and *not* have to test
208 // every object for collision.
209 /*virtual*/ void Container::PointerMoved(Vector point)
210 {
211         std::vector<Object *>::iterator i;
212
213         if (!isTopLevelContainer)
214         {
215                 // check for selection rectangle too
216                 if (selectionInProgress)
217                 {
218                         if (selection.contains(Extents()))
219                                 state = OSSelected;
220                         else
221                                 state = OSInactive;
222
223                         return;
224                 }
225
226                 // No need to do any checking if we're already selected...
227 //              if (state == OSSelected)
228 //                      return;
229
230 //              oldState = state;
231 //              needUpdate = true;
232 //              if (dragging &&
233                 bool oldHit = hit;
234                 hit = false;
235
236                 for(i=objects.begin(); i!=objects.end(); i++)
237                 {
238                         if ((*i)->HitTest(point))
239                         {
240 //                              state = OSSelected;
241 //                              return;
242                                 hit = true;
243                                 break;
244                         }
245                 }
246
247                 needUpdate = (oldHit != hit ? true : false);
248 //              state = oldState;
249
250                 if (dragging)
251                 {
252                         Vector delta = point - oldPoint;
253
254                         for(i=objects.begin(); i!=objects.end(); i++)
255                                 (*i)->Translate(delta);
256
257                         oldPoint = point;
258                         needUpdate = true;
259                 }
260
261                 return;
262         }
263
264         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
265         {
266 //              if (objects[i]->GetState() == OSSelected)
267                 (*i)->PointerMoved(point);
268         }
269
270         // Generic container doesn't need this???
271 //      needUpdate = false;
272 }
273
274
275 /*virtual*/ void Container::PointerReleased(void)
276 {
277         if (!isTopLevelContainer)
278         {
279                 dragging = false;
280                 return;
281         }
282 #if 0
283         dragging = false;
284         draggingHandle1 = false;
285         draggingHandle2 = false;
286
287         // Here we check for just a click: If object was clicked and dragged, then
288         // revert to the old state (OSInactive). Otherwise, keep the new state that
289         // we set.
290 /*
291 Maybe it would be better to just check for "object was dragged" state and not
292 have to worry about keeping track of old states...
293 */
294         if (objectWasDragged)
295                 state = oldState;
296 //Note that the preceeding is unnecessary for a generic container!
297 #endif
298
299 //      for(int i=0; i<(int)objects.size(); i++)
300 //              objects[i]->PointerReleased();
301         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
302                 (*i)->PointerReleased();
303 }
304
305
306 /*virtual*/ bool Container::NeedsUpdate(void)
307 {
308         // If this is *not* a top level container, then we treat it as an
309         // aggregate object.
310         if (!isTopLevelContainer)
311         {
312                 return needUpdate;
313         }
314
315         // Search through objects for one that needs an update; if one is found,
316         // return immediately.
317         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
318         {
319                 if ((*i)->NeedsUpdate())
320                         return true;
321         }
322
323         return false;
324 }
325
326
327 /*virtual*/ void Container::Add(Object * object)
328 {
329         objects.push_back(object);
330 printf("Container: Added object (=$%X). size = %li\n", object, objects.size());
331 }
332
333
334 /*virtual*/ QRectF Container::Extents(void)
335 {
336         QRectF bounds;
337
338         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
339                 bounds = bounds.united((*i)->Extents());
340
341         return bounds;
342 }
343
344
345 void Container::Delete(Object * objectToDelete)
346 {
347         std::vector<Object *>::iterator i = objects.begin();
348
349         while (i != objects.end())
350         {
351                 if (*i == objectToDelete)
352                 {
353                         objects.erase(i);
354                         delete objectToDelete;
355                         return;
356                 }
357
358                 i++;
359         }
360 }
361
362
363 void Container::DeleteSelectedItems(void)
364 {
365         std::vector<Object *>::iterator i = objects.begin();
366
367         while (i != objects.end())
368         {
369                 if ((*i)->state == OSSelected)
370                 {
371                         delete *i;
372                         objects.erase(i);
373                 }
374                 else
375                         i++;
376         }
377 }
378
379
380 void Container::Clear(void)
381 {
382         std::vector<Object *>::iterator i = objects.begin();
383
384         while (i != objects.end())
385         {
386 printf("Container: Deleting object ($%X)...\n", *i);
387                 delete (*i);
388                 objects.erase(i);
389         }
390 }
391
392
393 void Container::SelectAll(void)
394 {
395         for(unsigned int i=0; i<objects.size(); i++)
396                 objects[i]->state = OSSelected;
397 }
398
399
400 void Container::DeselectAll(void)
401 {
402         for(unsigned int i=0; i<objects.size(); i++)
403                 objects[i]->state = OSInactive;
404 }
405
406
407 int Container::ItemsSelected(void)
408 {
409         int selected = 0;
410
411         for(uint i=0; i<objects.size(); i++)
412                 if (objects[i]->state == OSSelected)
413                         selected++;
414
415         return selected;
416 }
417
418
419 Object * Container::SelectedItem(unsigned int index)
420 {
421         unsigned int selectedIndex = 0;
422
423         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
424         {
425                 if ((*i)->state == OSSelected)
426                 {
427                         if (selectedIndex == index)
428                                 return *i;
429                         else
430                                 selectedIndex++;
431                 }
432         }
433
434         return NULL;
435 }
436
437
438 void Container::MoveContentsTo(Container * newContainer)
439 {
440         // Sanity check
441         if (newContainer == NULL)
442                 return;
443
444         // Shuffle the contents of this container to the new one
445 //      for(unsigned int i=0; i<objects.size(); i++)
446         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
447         {
448                 newContainer->Add(*i);
449                 (*i)->Reparent(newContainer);
450         }
451
452         // & clear our vector
453         objects.clear();
454 }
455
456
457 void Container::MoveSelectedContentsTo(Container * newContainer)
458 {
459         // Sanity check
460         if (newContainer == NULL)
461                 return;
462
463         // Shuffle the contents of this container to the new one
464         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end();)
465         {
466                 if ((*i)->state != OSSelected)
467                 {
468                         i++;
469                         continue;
470                 }
471
472                 newContainer->Add(*i);
473                 (*i)->Reparent(newContainer);
474                 objects.erase(i);
475         }
476 }
477
478
479 void Container::CopySelectedContentsTo(Container * newContainer)
480 {
481         // Sanity check
482         if (newContainer == NULL)
483                 return;
484
485         // Shuffle the contents of this container to the new one
486         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
487         {
488                 if ((*i)->state == OSSelected)
489                         newContainer->Add((*i)->Copy());
490         }
491 }
492
493
494 void Container::ResizeAllDimensions(double newSize)
495 {
496         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
497         {
498                 if ((*i)->type == OTDimension)
499                         ((Dimension *)(*i))->size = newSize;
500                 if ((*i)->type == OTContainer)
501                         ((Container *)(*i))->ResizeAllDimensions(newSize);
502         }
503 }
504
505
506 /*virtual*/ void Container::Enumerate(FILE * file)
507 {
508         // Only put "CONTAINER" markers if *not* the top level container
509 //      if (parent != NULL)
510         if (!isTopLevelContainer)
511                 fprintf(file, "CONTAINER %i\n", layer);
512
513         for(uint i=0; i<objects.size(); i++)
514                 objects[i]->Enumerate(file);
515
516 //      if (parent != NULL)
517         if (!isTopLevelContainer)
518                 fprintf(file, "ENDCONTAINER\n");
519 }
520
521
522 /*virtual*/ Object * Container::Copy(void)
523 {
524 #warning "!!! This doesn't take care of attached Dimensions !!!"
525 /*
526 This is a real problem. While having a pointer in the Dimension to this line's points
527 is fast & easy, it creates a huge problem when trying to replicate an object like this.
528
529 Maybe a way to fix that then, is to have reference numbers instead of pointers. That
530 way, if you copy them, ... you might still have problems. Because you can't be sure if
531 a copy will be persistant or not, you then *definitely* do not want them to have the
532 same reference number.
533 */
534         Container * c = new Container(position, parent);
535         *c = *this;
536         return c;
537 }
538
539
540 /*virtual*/ void Container::Mirror(Point p1, Point p2)
541 {
542         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
543                 (*i)->Mirror(p1, p2);
544 }
545
546
547 /*virtual*/ void Container::Save(void)
548 {
549         Object::Save();
550
551         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
552                 (*i)->Save();
553 }
554
555
556 /*virtual*/ void Container::Restore(void)
557 {
558         Object::Restore();
559
560         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
561                 (*i)->Restore();
562 }
563