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