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