]> Shamusworld >> Repos - architektonas/blob - src/container.cpp
Readded click to add dimension to object (for Line).
[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*/ void Container::PointerMoved(Vector point)
205 {
206         std::vector<Object *>::iterator i;
207
208         if (!isTopLevelContainer)
209         {
210                 // check for selection rectangle too
211                 if (selectionInProgress)
212                 {
213                         if (selection.contains(Extents()))
214                                 state = OSSelected;
215                         else
216                                 state = OSInactive;
217
218                         return;
219                 }
220
221                 // No need to do any checking if we're already selected...
222 //              if (state == OSSelected)
223 //                      return;
224
225 //              oldState = state;
226 //              needUpdate = true;
227 //              if (dragging &&
228                 bool oldHit = hit;
229                 hit = false;
230
231                 for(i=objects.begin(); i!=objects.end(); i++)
232                 {
233                         if ((*i)->HitTest(point))
234                         {
235 //                              state = OSSelected;
236 //                              return;
237                                 hit = true;
238                                 break;
239                         }
240                 }
241
242                 needUpdate = (oldHit != hit ? true : false);
243 //              state = oldState;
244
245                 if (dragging)
246                 {
247                         Vector delta = point - oldPoint;
248
249                         for(i=objects.begin(); i!=objects.end(); i++)
250                                 (*i)->Translate(delta);
251
252                         oldPoint = point;
253                         needUpdate = true;
254                 }
255
256                 return;
257         }
258
259         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
260         {
261 //              if (objects[i]->GetState() == OSSelected)
262                 (*i)->PointerMoved(point);
263         }
264
265         // Generic container doesn't need this???
266 //      needUpdate = false;
267 }
268
269
270 /*virtual*/ void Container::PointerReleased(void)
271 {
272         if (!isTopLevelContainer)
273         {
274                 dragging = false;
275                 return;
276         }
277 #if 0
278         dragging = false;
279         draggingHandle1 = false;
280         draggingHandle2 = false;
281
282         // Here we check for just a click: If object was clicked and dragged, then
283         // revert to the old state (OSInactive). Otherwise, keep the new state that
284         // we set.
285 /*
286 Maybe it would be better to just check for "object was dragged" state and not
287 have to worry about keeping track of old states...
288 */
289         if (objectWasDragged)
290                 state = oldState;
291 //Note that the preceeding is unnecessary for a generic container!
292 #endif
293
294 //      for(int i=0; i<(int)objects.size(); i++)
295 //              objects[i]->PointerReleased();
296         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
297                 (*i)->PointerReleased();
298 }
299
300
301 /*virtual*/ bool Container::NeedsUpdate(void)
302 {
303         // If this is *not* a top level container, then we treat it as an
304         // aggregate object.
305         if (!isTopLevelContainer)
306         {
307                 return needUpdate;
308         }
309
310         // Search through objects for one that needs an update; if one is found,
311         // return immediately.
312         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
313         {
314                 if ((*i)->NeedsUpdate())
315                         return true;
316         }
317
318         return false;
319 }
320
321
322 /*virtual*/ void Container::Add(Object * object)
323 {
324         objects.push_back(object);
325 printf("Container: Added object (=$%X). size = %li\n", object, objects.size());
326 }
327
328
329 /*virtual*/ QRectF Container::Extents(void)
330 {
331         QRectF bounds;
332
333         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
334                 bounds = bounds.united((*i)->Extents());
335
336         return bounds;
337 }
338
339
340 void Container::Delete(Object * objectToDelete)
341 {
342         std::vector<Object *>::iterator i = objects.begin();
343
344         while (i != objects.end())
345         {
346                 if (*i == objectToDelete)
347                 {
348                         objects.erase(i);
349                         delete objectToDelete;
350                         return;
351                 }
352
353                 i++;
354         }
355 }
356
357
358 void Container::DeleteSelectedItems(void)
359 {
360         std::vector<Object *>::iterator i = objects.begin();
361
362         while (i != objects.end())
363         {
364                 if ((*i)->state == OSSelected)
365                 {
366                         delete *i;
367                         objects.erase(i);
368                 }
369                 else
370                         i++;
371         }
372 }
373
374
375 void Container::Clear(void)
376 {
377         std::vector<Object *>::iterator i = objects.begin();
378
379         while (i != objects.end())
380         {
381 printf("Container: Deleting object ($%X)...\n", *i);
382                 delete (*i);
383                 objects.erase(i);
384         }
385 }
386
387
388 void Container::SelectAll(void)
389 {
390         for(unsigned int i=0; i<objects.size(); i++)
391                 objects[i]->state = OSSelected;
392 }
393
394
395 void Container::DeselectAll(void)
396 {
397         for(unsigned int i=0; i<objects.size(); i++)
398                 objects[i]->state = OSInactive;
399 }
400
401
402 int Container::ItemsSelected(void)
403 {
404         int selected = 0;
405
406         for(uint i=0; i<objects.size(); i++)
407                 if (objects[i]->state == OSSelected)
408                         selected++;
409
410         return selected;
411 }
412
413
414 Object * Container::SelectedItem(unsigned int index)
415 {
416         unsigned int selectedIndex = 0;
417
418         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
419         {
420                 if ((*i)->state == OSSelected)
421                 {
422                         if (selectedIndex == index)
423                                 return *i;
424                         else
425                                 selectedIndex++;
426                 }
427         }
428
429         return NULL;
430 }
431
432
433 void Container::MoveContentsTo(Container * newContainer)
434 {
435         // Sanity check
436         if (newContainer == NULL)
437                 return;
438
439         // Shuffle the contents of this container to the new one
440         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
441         {
442                 newContainer->Add(*i);
443                 (*i)->Reparent(newContainer);
444         }
445
446         // & clear our vector
447         objects.clear();
448 }
449
450
451 void Container::CopyContentsTo(Container * newContainer)
452 {
453         // Sanity check
454         if (newContainer == NULL)
455                 return;
456
457         // Shuffle the contents of this container to the new one
458         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
459                 newContainer->Add((*i)->Copy());
460 }
461
462
463 void Container::MoveSelectedContentsTo(Container * newContainer)
464 {
465         // Sanity check
466         if (newContainer == NULL)
467                 return;
468
469         // Shuffle the contents of this container to the new one
470         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end();)
471         {
472                 if ((*i)->state != OSSelected)
473                 {
474                         i++;
475                         continue;
476                 }
477
478                 newContainer->Add(*i);
479                 (*i)->Reparent(newContainer);
480                 objects.erase(i);
481         }
482 }
483
484
485 void Container::CopySelectedContentsTo(Container * newContainer)
486 {
487         // Sanity check
488         if (newContainer == NULL)
489                 return;
490
491         // Copy the contents of this container to the new one
492         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
493         {
494                 if ((*i)->state == OSSelected)
495                         newContainer->Add((*i)->Copy());
496         }
497 }
498
499
500 void Container::ResizeAllDimensions(double newSize)
501 {
502         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
503         {
504                 if ((*i)->type == OTDimension)
505                         ((Dimension *)(*i))->size = newSize;
506                 if ((*i)->type == OTContainer)
507                         ((Container *)(*i))->ResizeAllDimensions(newSize);
508         }
509 }
510
511
512 /*virtual*/ void Container::Enumerate(FILE * file)
513 {
514         // Only put "CONTAINER" markers if *not* the top level container
515 //      if (parent != NULL)
516         if (!isTopLevelContainer)
517                 fprintf(file, "CONTAINER %i\n", layer);
518
519         for(uint i=0; i<objects.size(); i++)
520                 objects[i]->Enumerate(file);
521
522 //      if (parent != NULL)
523         if (!isTopLevelContainer)
524                 fprintf(file, "ENDCONTAINER\n");
525 }
526
527
528 /*virtual*/ Object * Container::Copy(void)
529 {
530 #warning "!!! This doesn't take care of attached Dimensions !!!"
531 /*
532 This is a real problem. While having a pointer in the Dimension to this line's points
533 is fast & easy, it creates a huge problem when trying to replicate an object like this.
534
535 Maybe a way to fix that then, is to have reference numbers instead of pointers. That
536 way, if you copy them, ... you might still have problems. Because you can't be sure if
537 a copy will be persistant or not, you then *definitely* do not want them to have the
538 same reference number.
539 */
540         Container * c = new Container(position, parent);
541         *c = *this;
542         return c;
543 }
544
545
546 /*virtual*/ void Container::Rotate(Point point, double angle)
547 {
548         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
549                 (*i)->Rotate(point, angle);
550 }
551
552
553 /*virtual*/ void Container::RotateSelected(Point point, double angle)
554 {
555         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
556         {
557                 if ((*i)->state == OSSelected)
558                         (*i)->Rotate(point, angle);
559         }
560 }
561
562
563 /*virtual*/ void Container::Mirror(Point p1, Point p2)
564 {
565         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
566                 (*i)->Mirror(p1, p2);
567 }
568
569
570 /*virtual*/ void Container::MirrorSelected(Point p1, Point p2)
571 {
572         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
573         {
574                 if ((*i)->state == OSSelected)
575                         (*i)->Mirror(p1, p2);
576         }
577 }
578
579
580 /*virtual*/ void Container::Save(void)
581 {
582         Object::Save();
583
584         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
585                 (*i)->Save();
586 }
587
588
589 /*virtual*/ void Container::Restore(void)
590 {
591         Object::Restore();
592
593         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
594                 (*i)->Restore();
595 }
596