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