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