]> Shamusworld >> Repos - architektonas/blob - src/container.cpp
757828aad73e064af821d04816807a82fa93702a
[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)//, needUpdate(false)
26 {
27         type = OTContainer;
28 }
29
30
31 // Copy constructor
32 Container::Container(const Container & copy): Object(copy.position, copy.parent)
33 {
34         // Use overloaded assignment operator
35         *this = copy;
36         type = OTContainer;
37 }
38
39
40 Container::~Container()
41 {
42         Clear();
43 }
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
55         // Small problem with this approach: if the copied object goes out of scope,
56         // all of the objects we copied in here will be deleted. D'oh!
57         for(uint i=0; i<from.objects.size(); i++)
58         {
59                 Object * object = from.objects[i];
60
61                 // Need to copy the object here...
62
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         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
75         {
76                 (*i)->Draw(painter);
77                 boundary = boundary.united((*i)->Extents());
78         }
79
80         if ((state == OSSelected) || hit)
81         {
82                 if (hit)
83                         painter->SetPen(QPen(Qt::magenta, 1.0, Qt::DashLine));
84                 else
85                         painter->SetPen(QPen(Qt::blue, 2.0, Qt::DashLine));
86
87                 painter->SetBrush(QBrush(Qt::NoBrush));
88                 painter->DrawRect(boundary);
89         }
90 }
91
92
93 /*virtual*/ Vector Container::Center(void)
94 {
95         return position;
96 }
97
98
99 /*
100  We need at least *three* handles for this object:
101  - one for moving
102  - one for resizing
103  - one for rotation
104
105 We need to think about the intuitive way (if there is any) to grab and
106 manipulate a complex object like this... Need to think, "What should happen when
107 I click here and drag there?"
108
109 Also: should put the snap logic into the Object base class (as a static method)...
110 */
111
112
113 // Need to add checking here for clicking on a member of a group (Container),
114 // and checking for if it's a top level container (the DrawingView's document).
115 /*
116 One approach is to check for the parent of the container: If it's NULL, then it's
117 the DrawingView's document. It might be better, though, to set a boolean like
118 isTopLevelContainer so that we can do things like edit members of a group without
119 having to ungroup them first (like Inkscape).
120 */
121 /*virtual*/ bool Container::Collided(Vector point)
122 {
123         objectWasDragged = false;
124 //      Vector v1 = position - point;
125
126         bool collision = false;
127
128         // NOTE that this deletes the object on mouse down instead of mouse up. Have to
129         // check to see how it feels to do it that way...
130         if (deleteActive)
131         {
132                 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end();)
133                 {
134                         if ((*i)->Collided(point))
135                         {
136 printf("Container::Collided: Deleting object ($%X)\n", *i);
137                                 Object * objectToDelete = *i;
138                                 objects.erase(i);
139                                 delete objectToDelete;
140                         }
141                         else
142                                 i++;
143                 }
144         }
145         else
146         {
147                 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
148                 {
149                         if ((*i)->Collided(point))
150                                 collision = true;
151                 }
152         }
153
154         // We check to see if the container we're trying to access is the
155         // DrawingView's document. If so, we ignore the state of the container.
156         // Otherwise, we care about the state of the container. :-)
157         if (isTopLevelContainer)
158                 state = OSInactive;
159         else
160         {
161                 state = (collision ? OSSelected : OSInactive);
162
163                 if (state == OSSelected)
164                 {
165                         DeselectAll();
166                         dragging = true;
167                         oldPoint = point;
168                 }
169         }
170
171         return collision;
172 }
173
174 /*
175 What we need to do is check for whether or not we're a top level container,
176 and override the passing of stuff into the objects held. So here, if we're *NOT*
177 a top level container, instead of passing PointerMoved to our contained objects,
178 we check to see if our bounds are met (for selection rectangle, e.g.).
179
180 Also, for things like being able to split point's hot spots we need to be able
181 to check for that crap in the top level container. Which means that objects can
182 still know how to move themselves, but they can also defer to their container
183 as well. Which also means that things like HitTest() need to be in the Object
184 class so that we can leverage that stuff here as well.
185 */
186
187 // The TLC is passing all mouse movement here, so we're doing the same here.
188 // Need to adjust all other objects to handle things correctly.
189
190 // One optimization that will need to be done eventually is to subdivide the screen
191 // into parts and keep subdividing until an acceptable number of objects lie within
192 // the slice. This way, the GUI will still be responsive and *not* have to test
193 // every object for collision.
194 /*virtual*/ void Container::PointerMoved(Vector point)
195 {
196         std::vector<Object *>::iterator i;
197
198         if (!isTopLevelContainer)
199         {
200                 // check for selection rectangle too
201                 if (selectionInProgress)
202                 {
203                         if (selection.contains(Extents()))
204                                 state = OSSelected;
205                         else
206                                 state = OSInactive;
207
208                         return;
209                 }
210
211                 // No need to do any checking if we're already selected...
212 //              if (state == OSSelected)
213 //                      return;
214
215 //              oldState = state;
216 //              needUpdate = true;
217 //              if (dragging &&
218                 bool oldHit = hit;
219                 hit = false;
220
221                 for(i=objects.begin(); i!=objects.end(); i++)
222                 {
223                         if ((*i)->HitTest(point))
224                         {
225 //                              state = OSSelected;
226 //                              return;
227                                 hit = true;
228                                 break;
229                         }
230                 }
231
232                 needUpdate = (oldHit != hit ? true : false);
233 //              state = oldState;
234
235                 if (dragging)
236                 {
237                         Vector delta = point - oldPoint;
238
239                         for(i=objects.begin(); i!=objects.end(); i++)
240                                 (*i)->Translate(delta);
241
242                         oldPoint = point;
243                         needUpdate = true;
244                 }
245
246                 return;
247         }
248
249         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
250         {
251 //              if (objects[i]->GetState() == OSSelected)
252                 (*i)->PointerMoved(point);
253         }
254
255         // Generic container doesn't need this???
256 //      needUpdate = false;
257 }
258
259
260 /*virtual*/ void Container::PointerReleased(void)
261 {
262         if (!isTopLevelContainer)
263         {
264                 dragging = false;
265                 return;
266         }
267 #if 0
268         dragging = false;
269         draggingHandle1 = false;
270         draggingHandle2 = false;
271
272         // Here we check for just a click: If object was clicked and dragged, then
273         // revert to the old state (OSInactive). Otherwise, keep the new state that
274         // we set.
275 /*
276 Maybe it would be better to just check for "object was dragged" state and not
277 have to worry about keeping track of old states...
278 */
279         if (objectWasDragged)
280                 state = oldState;
281 //Note that the preceeding is unnecessary for a generic container!
282 #endif
283
284 //      for(int i=0; i<(int)objects.size(); i++)
285 //              objects[i]->PointerReleased();
286         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
287                 (*i)->PointerReleased();
288 }
289
290
291 /*virtual*/ bool Container::NeedsUpdate(void)
292 {
293         // If this is *not* a top level container, then we treat it as an
294         // aggregate object.
295         if (!isTopLevelContainer)
296         {
297                 return needUpdate;
298         }
299
300         // Search through objects for one that needs an update; if one is found,
301         // return immediately.
302         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
303         {
304                 if ((*i)->NeedsUpdate())
305                         return true;
306         }
307
308         return false;
309 }
310
311
312 /*virtual*/ void Container::Add(Object * object)
313 {
314         objects.push_back(object);
315 printf("Container: Added object (=$%X). size = %li\n", object, objects.size());
316 }
317
318
319 /*virtual*/ QRectF Container::Extents(void)
320 {
321         QRectF bounds;
322
323         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
324                 bounds = bounds.united((*i)->Extents());
325
326         return bounds;
327 }
328
329
330 void Container::Delete(Object * objectToDelete)
331 {
332         std::vector<Object *>::iterator i = objects.begin();
333
334         while (i != objects.end())
335         {
336                 if (*i == objectToDelete)
337                 {
338                         objects.erase(i);
339                         delete objectToDelete;
340                         return;
341                 }
342
343                 i++;
344         }
345 }
346
347
348 void Container::DeleteSelectedItems(void)
349 {
350         std::vector<Object *>::iterator i = objects.begin();
351
352         while (i != objects.end())
353         {
354                 if ((*i)->state == OSSelected)
355                 {
356                         delete *i;
357                         objects.erase(i);
358                 }
359                 else
360                         i++;
361         }
362 }
363
364
365 void Container::Clear(void)
366 {
367         std::vector<Object *>::iterator i = objects.begin();
368
369         while (i != objects.end())
370         {
371 printf("Container: Deleting object ($%X)...\n", *i);
372                 delete (*i);
373                 objects.erase(i);
374         }
375 }
376
377
378 void Container::SelectAll(void)
379 {
380         for(unsigned int i=0; i<objects.size(); i++)
381                 objects[i]->state = OSSelected;
382 }
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
392 int Container::ItemsSelected(void)
393 {
394         int selected = 0;
395
396         for(uint i=0; i<objects.size(); i++)
397                 if (objects[i]->state == OSSelected)
398                         selected++;
399
400         return selected;
401 }
402
403
404 Object * Container::SelectedItem(unsigned int index)
405 {
406         unsigned int selectedIndex = 0;
407
408         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
409         {
410                 if ((*i)->state == OSSelected)
411                 {
412                         if (selectedIndex == index)
413                                 return *i;
414                         else
415                                 selectedIndex++;
416                 }
417         }
418
419         return NULL;
420 }
421
422
423 void Container::MoveContentsTo(Container * newContainer)
424 {
425         // Sanity check
426         if (newContainer == NULL)
427                 return;
428
429         // Shuffle the contents of this container to the new one
430 //      for(unsigned int i=0; i<objects.size(); i++)
431         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
432         {
433                 newContainer->Add(*i);
434                 (*i)->Reparent(newContainer);
435         }
436
437         // & clear our vector
438         objects.clear();
439 }
440
441
442 void Container::MoveSelectedContentsTo(Container * newContainer)
443 {
444         // Sanity check
445         if (newContainer == NULL)
446                 return;
447
448         // Shuffle the contents of this container to the new one
449         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end();)
450         {
451                 if ((*i)->state != OSSelected)
452                 {
453                         i++;
454                         continue;
455                 }
456
457                 newContainer->Add(*i);
458                 (*i)->Reparent(newContainer);
459                 objects.erase(i);
460         }
461 }
462
463
464 void Container::ResizeAllDimensions(double newSize)
465 {
466         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
467         {
468                 if ((*i)->type == OTDimension)
469                         ((Dimension *)(*i))->size = newSize;
470                 if ((*i)->type == OTContainer)
471                         ((Container *)(*i))->ResizeAllDimensions(newSize);
472         }
473 }
474
475
476 /*virtual*/ void Container::Enumerate(FILE * file)
477 {
478         // Only put "CONTAINER" markers if *not* the top level container
479 //      if (parent != NULL)
480         if (!isTopLevelContainer)
481                 fprintf(file, "CONTAINER\n");
482
483         for(uint i=0; i<objects.size(); i++)
484                 objects[i]->Enumerate(file);
485
486 //      if (parent != NULL)
487         if (!isTopLevelContainer)
488                 fprintf(file, "ENDCONTAINER\n");
489 }
490