]> Shamusworld >> Repos - architektonas/blob - src/base/modification.cpp
In the middle of chasing down MDI not activating bug, renaming of Graphic to
[architektonas] / src / base / modification.cpp
1 // modification.cpp
2 //
3 // Part of the Architektonas Project
4 // Originally part of QCad Community Edition by Andrew Mustun
5 // Extensively rewritten and refactored by James L. Hammons
6 // Portions copyright (C) 2001-2003 RibbonSoft
7 // Copyright (C) 2010 Underground Software
8 // See the README and GPLv2 files for licensing and warranty information
9 //
10 // JLH = James L. Hammons <jlhamm@acm.org>
11 //
12 // Who  When        What
13 // ---  ----------  -----------------------------------------------------------
14 // JLH  06/01/2010  Added this text. :-)
15 //
16
17 #include "modification.h"
18
19 #include "clipboard.h"
20 #include "creation.h"
21 #include "entity.h"
22 #include "drawing.h"
23 #include "information.h"
24 #include "insert.h"
25 #include "polyline.h"
26 #include "text.h"
27 #include "units.h"
28
29 /**
30  * Default constructor.
31  *
32  * @param container The container to which we will add
33  *        entities. Usually that's an Drawing entity but
34  *        it can also be a polyline, text, ...
35  * @param graphicView Pointer to graphic view or NULL if you don't want the
36  *        any views to be updated.
37  * @param handleUndo true: Handle undo functionalitiy.
38  */
39 Modification::Modification(EntityContainer & container,
40         GraphicView * graphicView, bool handleUndo)
41 {
42         this->container = &container;
43         this->graphicView = graphicView;
44         this->handleUndo = handleUndo;
45 #warning "!!! Need to rename graphic to drawing !!!"
46         graphic = container.GetDrawing();
47         document = container.getDocument();
48 }
49
50 /**
51  * Deletes all selected entities.
52  */
53 void Modification::remove()
54 {
55         if (container == NULL)
56         {
57                 DEBUG->print("Modification::remove: no valid container", Debug::D_WARNING);
58                 return;
59         }
60
61         if (document)
62                 document->startUndoCycle();
63
64         // not safe (?)
65         for(Entity * e=container->firstEntity(); e!=NULL; e=container->nextEntity())
66         {
67                 if (e && e->isSelected())
68                 {
69                         e->setSelected(false);
70                         e->changeUndoState();
71
72                         if (document)
73                                 document->addUndoable(e);
74                 }
75         }
76
77         if (document)
78                 document->endUndoCycle();
79
80         graphicView->redraw();
81 }
82
83 /**
84  * Changes the attributes of all selected
85  */
86 bool Modification::changeAttributes(AttributesData & data)
87 {
88         if (!container)
89         {
90                 DEBUG->print("Modification::changeAttributes: no valid container", Debug::D_WARNING);
91                 return false;
92         }
93
94         QList<Entity *> addList;
95
96         if (document)
97                 document->startUndoCycle();
98
99         for(Entity * e=container->firstEntity(); e!=NULL; e=container->nextEntity())
100         {
101                 //for (uint i=0; i<container->count(); ++i) {
102                 //Entity* e = container->entityAt(i);
103                 if (e && e->isSelected())
104                 {
105                         Entity * ec = e->clone();
106                         ec->setSelected(false);
107
108                         Pen pen = ec->getPen(false);
109
110                         if (data.changeLayer == true)
111                                 ec->setLayer(data.layer);
112
113                         if (data.changeColor == true)
114                                 pen.setColor(data.pen.getColor());
115
116                         if (data.changeLineType == true)
117                                 pen.setLineType(data.pen.getLineType());
118
119                         if (data.changeWidth == true)
120                                 pen.setWidth(data.pen.getWidth());
121
122                         ec->setPen(pen);
123
124                         //if (data.useCurrentLayer) {
125                         //    ec->setLayerToActive();
126                         //}
127                         //if (data.useCurrentAttributes) {
128                         //    ec->setPenToActive();
129                         //}
130                         //if (ec->rtti()==RS2::EntityInsert) {
131                         //    ((Insert*)ec)->update();
132                         //}
133                         ec->update();
134                         addList.append(ec);
135                 }
136         }
137
138         deselectOriginals(true);
139         addNewEntities(addList);
140
141         if (document)
142                 document->endUndoCycle();
143
144         if (graphicView)
145                 graphicView->redraw();
146
147         return true;
148 }
149
150
151 /**
152  * Copies all selected entities from the given container to the clipboard.
153  * Layers and blocks that are needed are also copied if the container is
154  * or is part of an Drawing.
155  *
156  * @param container The entity container.
157  * @param ref Reference point. The entities will be moved by -ref.
158  * @param cut true: cut instead of copying, false: copy
159  */
160 void Modification::copy(const Vector& ref, const bool cut) {
161
162     if (container==NULL) {
163         DEBUG->print("Modification::copy: no valid container",
164                         Debug::D_WARNING);
165         return;
166     }
167
168     CLIPBOARD->clear();
169     if (graphic!=NULL) {
170         CLIPBOARD->getGraphic()->setUnit(graphic->getUnit());
171     } else {
172         CLIPBOARD->getGraphic()->setUnit(RS2::None);
173     }
174
175     // start undo cycle for the container if we're cutting
176     if (cut && document!=NULL) {
177         document->startUndoCycle();
178     }
179
180     // copy entities / layers / blocks
181     for (Entity* e=container->firstEntity(); e!=NULL;
182             e=container->nextEntity()) {
183         //for (uint i=0; i<container->count(); ++i) {
184         //Entity* e = container->entityAt(i);
185
186         if (e!=NULL && e->isSelected()) {
187             copyEntity(e, ref, cut);
188         }
189     }
190
191     if (cut && document!=NULL) {
192         document->endUndoCycle();
193     }
194 }
195
196
197
198 /**
199  * Copies the given entity from the given container to the clipboard.
200  * Layers and blocks that are needed are also copied if the container is
201  * or is part of an Drawing.
202  *
203  * @param e The entity.
204  * @param ref Reference point. The entities will be moved by -ref.
205  * @param cut true: cut instead of copying, false: copy
206  */
207 void Modification::copyEntity(Entity * e, const Vector & ref, const bool cut)
208 {
209         if (e && e->isSelected())
210         {
211                 // delete entity in graphic view:
212                 if (cut)
213                 {
214 #warning "!!! Old rendering path needs upgrading !!!"
215 #if 0
216                         if (graphicView)
217                                 graphicView->deleteEntity(e);
218 #endif
219
220                         e->setSelected(false);
221                 }
222                 else
223                 {
224 #warning "!!! Old rendering path needs upgrading !!!"
225 #if 0
226                         if (graphicView)
227                                 graphicView->deleteEntity(e);
228 #endif
229
230                         e->setSelected(false);
231
232 #warning "!!! Old rendering path needs upgrading !!!"
233 #if 0
234                         if (graphicView)
235                                 graphicView->drawEntity(e);
236 #endif
237                 }
238
239                 // add entity to clipboard:
240                 Entity * c = e->clone();
241                 c->move(-ref);
242                 CLIPBOARD->addEntity(c);
243
244                 copyLayers(e);
245                 copyBlocks(e);
246
247                 // set layer to the layer clone:
248                 Layer * l = e->getLayer();
249
250                 if (l)
251                         c->setLayer(l->getName());
252
253                 // make sure all sub entities point to layers of the clipboard
254                 if (c->isContainer())
255                 {
256                         EntityContainer * ec = (EntityContainer *)c;
257
258                         for(Entity * e2=ec->firstEntity(RS2::ResolveAll); e2!=NULL;
259                                 e2=ec->nextEntity(RS2::ResolveAll))
260                         {
261                                 //Entity* e2 = ec->entityAt(i);
262                                 Layer * l2 = e2->getLayer();
263
264                                 if (l2)
265                                         e2->setLayer(l2->getName());
266                         }
267                 }
268
269                 if (cut)
270                 {
271                         e->changeUndoState();
272
273                         if (document)
274                                 document->addUndoable(e);
275                 }
276         }
277 }
278
279
280
281 /**
282  * Copies all layers of the given entity to the clipboard.
283  */
284 void Modification::copyLayers(Entity* e) {
285
286     if (e==NULL) {
287         return;
288     }
289
290     // add layer(s) of the entity if it's not an insert
291     //  (inserts are on layer '0'):
292     if (e->rtti()!=RS2::EntityInsert) {
293         Layer* l = e->getLayer();
294         if (l!=NULL) {
295             if (!CLIPBOARD->hasLayer(l->getName())) {
296                 CLIPBOARD->addLayer(l->clone());
297             }
298         }
299     }
300
301     // special handling of inserts:
302     else {
303         // insert: add layer(s) of subentities:
304         Block* b = ((Insert*)e)->getBlockForInsert();
305         if (b!=NULL) {
306             for (Entity* e2=b->firstEntity(); e2!=NULL;
307                     e2=b->nextEntity()) {
308                 //for (uint i=0; i<b->count(); ++i) {
309                 //Entity* e2 = b->entityAt(i);
310                 copyLayers(e2);
311             }
312         }
313     }
314 }
315
316
317
318 /**
319  * Copies all blocks of the given entity to the clipboard.
320  */
321 void Modification::copyBlocks(Entity* e) {
322
323     if (e==NULL) {
324         return;
325     }
326
327     // add block of the entity if it's an insert
328     if (e->rtti()==RS2::EntityInsert) {
329         Block* b = ((Insert*)e)->getBlockForInsert();
330         if (b!=NULL) {
331             // add block of an insert:
332             if (!CLIPBOARD->hasBlock(b->getName())) {
333                 CLIPBOARD->addBlock((Block*)b->clone());
334             }
335
336             for (Entity* e2=b->firstEntity(); e2!=NULL;
337                     e2=b->nextEntity()) {
338                 //for (uint i=0; i<b->count(); ++i) {
339                 //Entity* e2 = b->entityAt(i);
340                 copyBlocks(e2);
341             }
342         }
343     }
344 }
345
346
347
348 /**
349  * Pastes all entities from the clipboard into the container.
350  * Layers and blocks that are needed are also copied if the container is
351  * or is part of an Drawing.
352  *
353  * @param data Paste data.
354  * @param source The source from where to paste. NULL means the source
355  *      is the clipboard.
356  */
357 void Modification::paste(const PasteData& data, Drawing* source) {
358
359     if (graphic==NULL) {
360         DEBUG->print(Debug::D_WARNING,
361                         "Modification::paste: Graphic is NULL");
362         return;
363     }
364
365     double factor = 1.0;
366
367     if (source==NULL) {
368         source = CLIPBOARD->getGraphic();
369
370         // graphics from the clipboard need to be scaled. from the part lib not:
371         RS2::Unit sourceUnit = source->getUnit();
372         RS2::Unit targetUnit = graphic->getUnit();
373         factor = Units::convert(1.0, sourceUnit, targetUnit);
374     }
375
376     if (document!=NULL) {
377         document->startUndoCycle();
378     }
379
380
381     // insert layers:
382     if (graphic!=NULL) {
383         Layer* layer = graphic->getActiveLayer();
384         for(uint i=0; i<source->countLayers(); ++i) {
385             Layer* l = source->layerAt(i);
386             if (l!=NULL) {
387                 if (graphic->findLayer(l->getName())==NULL) {
388                     graphic->addLayer(l->clone());
389                 }
390             }
391         }
392         graphic->activateLayer(layer);
393     }
394
395     // insert blocks:
396     if (graphic!=NULL) {
397         for(uint i=0; i<source->countBlocks(); ++i) {
398             Block* b = source->blockAt(i);
399             if (b!=NULL) {
400                 if (graphic->findBlock(b->getName())==NULL) {
401                     Block* bc = (Block*)b->clone();
402                     bc->reparent(container);
403                     //bc->scale(bc->getBasePoint(), Vector(factor, factor));
404                     // scale block but don't scale inserts in block
405                     //  (they already scale with their block)
406                     for(uint i2=0; i2<bc->count(); ++i2) {
407                         Entity* e = bc->entityAt(i2);
408                         if (e!=NULL && e->rtti()!=RS2::EntityInsert) {
409                             e->scale(bc->getBasePoint(),
410                                      Vector(factor, factor));
411                         } else {
412                             Vector ip = ((Insert*)e)->getInsertionPoint();
413                             ip.scale(bc->getBasePoint(),
414                                      Vector(factor, factor));
415                             ((Insert*)e)->setInsertionPoint(ip);
416                             e->update();
417                         }
418                     }
419
420                     graphic->addBlock(bc);
421                 }
422             }
423         }
424     }
425
426     // add entities to this host (graphic or a new block)
427     EntityContainer* host = container;
428     QString blockName;
429
430     // create new block:
431     if (graphic!=NULL) {
432         if (data.asInsert==true) {
433             BlockList* blkList = graphic->getBlockList();
434             if (blkList!=NULL) {
435                 blockName = blkList->newName(data.blockName);
436
437                 Block* blk =
438                     new Block(graphic,
439                                  BlockData(blockName,
440                                               Vector(0.0,0.0), false));
441                 graphic->addBlock(blk);
442
443                 host = blk;
444             }
445         }
446     }
447
448     // insert entities:
449     //for (uint i=0; i<((EntityContainer*)source)->count(); ++i) {
450     //Entity* e = source->entityAt(i);
451     for (Entity* e=((EntityContainer*)source)->firstEntity();
452             e!=NULL;
453             e=((EntityContainer*)source)->nextEntity()) {
454
455         if (e!=NULL) {
456
457             QString layerName = "0";
458             Layer* layer = e->getLayer();
459             if (layer!=NULL) {
460                 layerName = layer->getName();
461             }
462             Entity* e2 = e->clone();
463             e2->reparent(host);
464             if (data.asInsert==false) {
465                 e2->move(data.insertionPoint);
466             }
467             // don't adjust insert factor - block was already adjusted to unit
468             if (e2->rtti()==RS2::EntityInsert) {
469                 Vector ip = ((Insert*)e2)->getInsertionPoint();
470                 ip.scale(data.insertionPoint, Vector(factor, factor));
471                 ((Insert*)e2)->setInsertionPoint(ip);
472                 e2->update();
473             } else {
474                 e2->scale(data.insertionPoint, Vector(factor, factor));
475             }
476             host->addEntity(e2);
477             e2->setLayer(layerName);
478
479             // make sure all sub entities point to layers of the container
480             if (e2->isContainer()) {
481                 EntityContainer* ec = (EntityContainer*)e2;
482
483                 for (Entity* e3 = ec->firstEntity(RS2::ResolveAll); e3!=NULL;
484                         e3 = ec->nextEntity(RS2::ResolveAll)) {
485
486                     //Entity* e3 = ec->entityAt(i);
487                     Layer* l2 = e3->getLayer();
488                     if (l2!=NULL) {
489                         e3->setLayer(l2->getName());
490                     }
491                 }
492             }
493
494             if (document!=NULL && data.asInsert==false) {
495                 document->addUndoable(e2);
496             }
497         }
498     }
499
500     if (data.asInsert==true) {
501         Insert* ins =
502             new Insert(container,
503                           InsertData(
504                               blockName,
505                               data.insertionPoint,
506                               Vector(data.factor, data.factor),
507                               data.angle,
508                               1,1,Vector(0.0,0.0)));
509         container->addEntity(ins);
510         ins->setLayerToActive();
511         ins->setPenToActive();
512
513         if (document!=NULL) {
514             document->addUndoable(ins);
515         }
516     }
517
518     if (document!=NULL) {
519         document->endUndoCycle();
520     }
521 }
522
523
524 /**
525  * Splits a polyline into two leaving out a gap.
526  *
527  * @param polyline The original polyline
528  * @param e1 1st entity on which the first cutting point is.
529  * @param v1 1st cutting point.
530  * @param e2 2nd entity on which the first cutting point is.
531  * @param v2 2nd cutting point.
532  * @param polyline1 Pointer to a polyline pointer which will hold the
533  *        1st resulting new polyline. Pass NULL if you don't
534  *        need those pointers.
535  * @param polyline2 Pointer to a polyline pointer which will hold the
536  *        2nd resulting new polyline. Pass NULL if you don't
537  *        need those pointers.
538  *
539  * @todo Support arcs in polylines, check for wrong parameters
540  *
541  * @return true
542  */
543 bool Modification::splitPolyline(Polyline& polyline,
544                                     Entity& e1, Vector v1,
545                                     Entity& e2, Vector v2,
546                                     Polyline** polyline1,
547                                     Polyline** polyline2) const {
548
549     if (container==NULL) {
550         DEBUG->print("Modification::splitPolyline: no valid container",
551                         Debug::D_WARNING);
552         return false;
553     }
554
555     Entity* firstEntity = polyline.firstEntity();
556     Vector firstPoint(false);
557     if (firstEntity->rtti()==RS2::EntityLine) {
558         firstPoint = ((Line*)firstEntity)->getStartpoint();
559     }
560     Polyline* pl1 =
561         new Polyline(container,
562                         PolylineData(firstPoint, Vector(0.0,0.0), 0));
563     Polyline* pl2 = new Polyline(container);
564     Polyline* pl = pl1; // Current polyline
565     Line* line = NULL;
566     Arc* arc = NULL;
567
568     if (polyline1!=NULL) {
569         *polyline1 = pl1;
570     }
571     if (polyline2!=NULL) {
572         *polyline2 = pl2;
573     }
574
575     for (Entity* e = polyline.firstEntity();
576             e != NULL;
577             e = polyline.nextEntity()) {
578
579         if (e->rtti()==RS2::EntityLine) {
580             line = (Line*)e;
581             arc = NULL;
582         } else if (e->rtti()==RS2::EntityArc) {
583             arc = (Arc*)e;
584             line = NULL;
585         } else {
586             line = NULL;
587             arc = NULL;
588         }
589
590         if (line!=NULL /*|| arc!=NULL*/) {
591
592             if (e==&e1 && e==&e2) {
593                 // Trim within a single entity:
594                 Vector sp = line->getStartpoint();
595                 double dist1 = (v1-sp).magnitude();
596                 double dist2 = (v2-sp).magnitude();
597                 pl->addVertex(dist1<dist2 ? v1 : v2, 0.0);
598                 pl = pl2;
599                 pl->setStartpoint(dist1<dist2 ? v2 : v1);
600                 pl->addVertex(line->getEndpoint(), 0.0);
601             } else if (e==&e1 || e==&e2) {
602                 // Trim entities:
603                 Vector v = (e==&e1 ? v1 : v2);
604                 if (pl==pl1) {
605                     // Trim endpoint of entity to first vector
606                     pl->addVertex(v, 0.0);
607                     pl = NULL;
608                 } else {
609                     // Trim startpoint of entity to second vector
610                     pl = pl2;
611                     pl->setStartpoint(v);
612                     pl->addVertex(line->getEndpoint(), 0.0);
613                 }
614             } else {
615                 // Add entities to polylines
616                 if (line!=NULL && pl!=NULL) {
617                     pl->addVertex(line->getEndpoint(), 0.0);
618                 }
619             }
620         }
621     }
622
623     container->addEntity(pl1);
624     container->addEntity(pl2);
625     //container->removeEntity(&polyline);
626     polyline.changeUndoState();
627
628     return true;
629 }
630
631
632
633 /**
634  * Adds a node to the given polyline. The new node is placed between
635  * the start and end point of the given segment.
636  *
637  * @param node The position of the new node.
638  *
639  * @return Pointer to the new polyline or NULL.
640  */
641 /*
642 Polyline* Modification::addPolylineNode(Polyline& polyline,
643         const AtomicEntity& segment,
644         const Vector& node) {
645     DEBUG->print("Modification::addPolylineNode");
646
647     if (container==NULL) {
648         DEBUG->print("Modification::addPolylineNode: no valid container",
649                         Debug::D_WARNING);
650         return NULL;
651     }
652
653     if (segment.getParent()!=&polyline) {
654         DEBUG->print("Modification::addPolylineNode: "
655                         "segment not part of the polyline",
656                         Debug::D_WARNING);
657         return NULL;
658     }
659
660     Polyline* newPolyline = new Polyline(container);
661     newPolyline->setClosed(polyline.isClosed());
662     newPolyline->setSelected(polyline.isSelected());
663     newPolyline->setLayer(polyline.getLayer());
664     newPolyline->setPen(polyline.getPen());
665
666     // copy polyline and add new node:
667     bool first = true;
668     Entity* lastEntity = polyline.lastEntity();
669     for (Entity* e=polyline.firstEntity(); e!=NULL;
670             e=polyline.nextEntity()) {
671
672         if (e->isAtomic()) {
673             AtomicEntity* ae = (AtomicEntity*)e;
674             double bulge = 0.0;
675             if (ae->rtti()==RS2::EntityArc) {
676                 DEBUG->print("Modification::addPolylineNode: arc segment");
677                 bulge = ((Arc*)ae)->getBulge();
678             } else {
679                 DEBUG->print("Modification::addPolylineNode: line segment");
680                 bulge = 0.0;
681             }
682
683             if (first) {
684                 DEBUG->print("Modification::addPolylineNode: first segment: %f/%f",
685                                 ae->getStartpoint().x, ae->getStartpoint().y);
686
687                 newPolyline->setNextBulge(bulge);
688                 newPolyline->addVertex(ae->getStartpoint());
689                 first = false;
690             }
691
692             // segment to split:
693             if (ae==&segment) {
694                 DEBUG->print("Modification::addPolylineNode: split segment found");
695
696                 DEBUG->print("Modification::addPolylineNode: node: %f/%f",
697                                 node.x, node.y);
698
699                 newPolyline->setNextBulge(0.0);
700                 newPolyline->addVertex(node);
701
702                 DEBUG->print("Modification::addPolylineNode: after node: %f/%f",
703                                 ae->getEndpoint().x, ae->getEndpoint().y);
704
705                 if (ae!=lastEntity || polyline.isClosed()==false) {
706                     newPolyline->setNextBulge(0.0);
707                     newPolyline->addVertex(ae->getEndpoint());
708                 }
709             } else {
710                 DEBUG->print("Modification::addPolylineNode: normal vertex found: %f/%f",
711                                 ae->getEndpoint().x, ae->getEndpoint().y);
712
713                 if (ae!=lastEntity || polyline.isClosed()==false) {
714                     newPolyline->setNextBulge(bulge);
715                     newPolyline->addVertex(ae->getEndpoint());
716                 }
717             }
718         } else {
719             DEBUG->print("Modification::addPolylineNode: "
720                             "Polyline contains non-atomic entities",
721                             Debug::D_WARNING);
722         }
723     }
724
725     newPolyline->setNextBulge(polyline.getClosingBulge());
726     newPolyline->endPolyline();
727
728     // add new polyline:
729     container->addEntity(newPolyline);
730     if (graphicView!=NULL) {
731         graphicView->deleteEntity(&polyline);
732         graphicView->drawEntity(newPolyline);
733     }
734
735     if (document!=NULL && handleUndo) {
736         document->startUndoCycle();
737
738         polyline.setUndoState(true);
739         document->addUndoable(&polyline);
740         document->addUndoable(newPolyline);
741
742         document->endUndoCycle();
743     }
744
745     return newPolyline;
746 }
747 */
748
749
750
751 /**
752  * Deletes a node from a polyline.
753  *
754  * @param node The node to delete.
755  *
756  * @return Pointer to the new polyline or NULL.
757  */
758 /*
759 Polyline* Modification::deletePolylineNode(Polyline& polyline,
760         const Vector& node) {
761
762     DEBUG->print("Modification::deletePolylineNode");
763
764     if (container==NULL) {
765         DEBUG->print("Modification::addPolylineNode: no valid container",
766                         Debug::D_WARNING);
767         return NULL;
768     }
769
770     if (node.valid==false) {
771         DEBUG->print("Modification::deletePolylineNode: "
772                         "node not valid",
773                         Debug::D_WARNING);
774         return NULL;
775     }
776
777     // check if the polyline is no longer there after deleting the node:
778     if (polyline.count()==1) {
779         Entity* e = polyline.firstEntity();
780         if (e!=NULL && e->isAtomic()) {
781             AtomicEntity* ae = (AtomicEntity*)e;
782             if (node.distanceTo(ae->getStartpoint())<1.0e-6 ||
783                     node.distanceTo(ae->getEndpoint())<1.0e-6) {
784
785                 if (graphicView!=NULL) {
786                     graphicView->deleteEntity(&polyline);
787                 }
788
789                 if (document!=NULL && handleUndo) {
790                     document->startUndoCycle();
791                     polyline.setUndoState(true);
792                     document->addUndoable(&polyline);
793                     document->endUndoCycle();
794                 }
795             }
796         }
797         return NULL;
798     }
799
800     Polyline* newPolyline = new Polyline(container);
801     newPolyline->setClosed(polyline.isClosed());
802     newPolyline->setSelected(polyline.isSelected());
803     newPolyline->setLayer(polyline.getLayer());
804     newPolyline->setPen(polyline.getPen());
805
806     // copy polyline and drop deleted node:
807     bool first = true;
808     bool lastDropped = false;
809     Entity* lastEntity = polyline.lastEntity();
810     for (Entity* e=polyline.firstEntity(); e!=NULL;
811             e=polyline.nextEntity()) {
812
813         if (e->isAtomic()) {
814             AtomicEntity* ae = (AtomicEntity*)e;
815             double bulge = 0.0;
816             if (ae->rtti()==RS2::EntityArc) {
817                 DEBUG->print("Modification::deletePolylineNode: arc segment");
818                 bulge = ((Arc*)ae)->getBulge();
819             } else {
820                 DEBUG->print("Modification::deletePolylineNode: line segment");
821                 bulge = 0.0;
822             }
823
824             // last entity is closing entity and will be added below with endPolyline()
825             if (e==lastEntity && polyline.isClosed()) {
826                 continue;
827             }
828
829             // first vertex (startpoint)
830             if (first && node.distanceTo(ae->getStartpoint())>1.0e-6) {
831                 DEBUG->print("Modification::deletePolylineNode: first node: %f/%f",
832                                 ae->getStartpoint().x, ae->getStartpoint().y);
833
834                 newPolyline->setNextBulge(bulge);
835                 newPolyline->addVertex(ae->getStartpoint());
836                 first = false;
837             }
838
839             // normal node (not deleted):
840             if (first==false && node.distanceTo(ae->getEndpoint())>1.0e-6) {
841                 DEBUG->print("Modification::deletePolylineNode: normal vertex found: %f/%f",
842                                 ae->getEndpoint().x, ae->getEndpoint().y);
843                 if (lastDropped) {
844                     //bulge = 0.0;
845                 }
846                 newPolyline->setNextBulge(bulge);
847                 newPolyline->addVertex(ae->getEndpoint());
848                 lastDropped = false;
849             }
850
851             // drop deleted node:
852             else {
853                 DEBUG->print("Modification::deletePolylineNode: deleting vertex: %f/%f",
854                                 ae->getEndpoint().x, ae->getEndpoint().y);
855                 lastDropped = true;
856             }
857         } else {
858             DEBUG->print("Modification::deletePolylineNode: "
859                             "Polyline contains non-atomic entities",
860                             Debug::D_WARNING);
861         }
862     }
863
864     DEBUG->print("Modification::deletePolylineNode: ending polyline");
865     newPolyline->setNextBulge(polyline.getClosingBulge());
866     newPolyline->endPolyline();
867
868     //if (newPolyline->count()==1) {
869     //}
870
871     // add new polyline:
872     DEBUG->print("Modification::deletePolylineNode: adding new polyline");
873     container->addEntity(newPolyline);
874     if (graphicView!=NULL) {
875         graphicView->deleteEntity(&polyline);
876         graphicView->drawEntity(newPolyline);
877     }
878
879     DEBUG->print("Modification::deletePolylineNode: handling undo");
880     if (document!=NULL && handleUndo) {
881         document->startUndoCycle();
882
883         polyline.setUndoState(true);
884         document->addUndoable(&polyline);
885         document->addUndoable(newPolyline);
886
887         document->endUndoCycle();
888     }
889
890     return newPolyline;
891 }
892 */
893
894
895
896 /**
897  * Deletes all nodes between the two given nodes (exclusive).
898  *
899  * @param node1 First limiting node.
900  * @param node2 Second limiting node.
901  *
902  * @return Pointer to the new polyline or NULL.
903  */
904 /*
905 Polyline* Modification::deletePolylineNodesBetween(Polyline& polyline,
906         AtomicEntity& segment, const Vector& node1, const Vector& node2) {
907
908     DEBUG->print("Modification::deletePolylineNodesBetween");
909
910     if (container==NULL) {
911         DEBUG->print("Modification::addPolylineNodesBetween: no valid container",
912                         Debug::D_WARNING);
913         return NULL;
914     }
915
916     if (node1.valid==false || node2.valid==false) {
917         DEBUG->print("Modification::deletePolylineNodesBetween: "
918                         "node not valid",
919                         Debug::D_WARNING);
920         return NULL;
921     }
922
923     if (node1.distanceTo(node2)<1.0e-6) {
924         DEBUG->print("Modification::deletePolylineNodesBetween: "
925                         "nodes are identical",
926                         Debug::D_WARNING);
927         return NULL;
928     }
929
930     // check if there's nothing to delete:
931     for (Entity* e=polyline.firstEntity(); e!=NULL;
932             e=polyline.nextEntity()) {
933
934         if (e->isAtomic()) {
935             AtomicEntity* ae = (AtomicEntity*)e;
936
937             if ((node1.distanceTo(ae->getStartpoint())<1.0e-6 &&
938                     node2.distanceTo(ae->getEndpoint())<1.0e-6) ||
939                     (node2.distanceTo(ae->getStartpoint())<1.0e-6 &&
940                      node1.distanceTo(ae->getEndpoint())<1.0e-6)) {
941
942                 DEBUG->print("Modification::deletePolylineNodesBetween: "
943                                 "nothing to delete",
944                                 Debug::D_WARNING);
945                 return NULL;
946             }
947         }
948     }
949
950
951     // check if the start point is involved:
952     bool startpointInvolved = false;
953     if (node1.distanceTo(polyline.getStartpoint())<1.0e-6 ||
954             node2.distanceTo(polyline.getStartpoint())<1.0e-6) {
955         startpointInvolved = true;
956     }
957
958
959     // check which part of the polyline has to be deleted:
960     bool deleteStart = false;
961     if (polyline.isClosed()) {
962         bool found = false;
963         double length1 = 0.0;
964         double length2 = 0.0;
965         Entity* e=polyline.firstEntity();
966
967         if (startpointInvolved) {
968             if (e->isAtomic()) {
969                 AtomicEntity* ae = (AtomicEntity*)e;
970                 length1+=ae->getLength();
971                         }
972             e = polyline.nextEntity();
973         }
974         for (; e!=NULL; e=polyline.nextEntity()) {
975
976             if (e->isAtomic()) {
977                 AtomicEntity* ae = (AtomicEntity*)e;
978
979                 if (node1.distanceTo(ae->getStartpoint())<1.0e-6 ||
980                         node2.distanceTo(ae->getStartpoint())<1.0e-6) {
981
982                     found = !found;
983                 }
984
985                 if (found) {
986                     length2+=ae->getLength();
987                 } else {
988                     length1+=ae->getLength();
989                 }
990             }
991         }
992         if (length1<length2) {
993             deleteStart = true;
994         } else {
995             deleteStart = false;
996         }
997     }
998
999     Polyline* newPolyline = new Polyline(container);
1000     newPolyline->setClosed(polyline.isClosed());
1001     newPolyline->setSelected(polyline.isSelected());
1002     newPolyline->setLayer(polyline.getLayer());
1003     newPolyline->setPen(polyline.getPen());
1004
1005     if (startpointInvolved && deleteStart && polyline.isClosed()) {
1006         newPolyline->setNextBulge(0.0);
1007         newPolyline->addVertex(polyline.getStartpoint());
1008     }
1009
1010     // copy polyline and drop deleted nodes:
1011     bool first = true;
1012     bool removing = deleteStart;
1013     bool done = false;
1014     bool nextIsStraight = false;
1015     Entity* lastEntity = polyline.lastEntity();
1016     int i=0;
1017     double bulge = 0.0;
1018     for (Entity* e=polyline.firstEntity(); e!=NULL;
1019             e=polyline.nextEntity()) {
1020
1021         DEBUG->print("Modification::deletePolylineNodesBetween: entity: %d", i++);
1022         DEBUG->print("Modification::deletePolylineNodesBetween: removing: %d", (int)removing);
1023
1024         if (e->isAtomic()) {
1025             AtomicEntity* ae = (AtomicEntity*)e;
1026             if (ae->rtti()==RS2::EntityArc) {
1027                 DEBUG->print("Modification::deletePolylineNodesBetween: arc segment");
1028                 bulge = ((Arc*)ae)->getBulge();
1029             } else {
1030                 DEBUG->print("Modification::deletePolylineNodesBetween: line segment");
1031                 bulge = 0.0;
1032             }
1033
1034             // last entity is closing entity and will be added below with endPolyline()
1035             if (e==lastEntity && polyline.isClosed()) {
1036                 DEBUG->print("Modification::deletePolylineNodesBetween: "
1037                                 "dropping last vertex of closed polyline");
1038                 continue;
1039             }
1040
1041             // first vertex (startpoint)
1042             if (first) {
1043                 if (!removing) {
1044                     DEBUG->print("Modification::deletePolylineNodesBetween: first node: %f/%f",
1045                                     ae->getStartpoint().x, ae->getStartpoint().y);
1046                     newPolyline->setNextBulge(bulge);
1047                     newPolyline->addVertex(ae->getStartpoint());
1048                     first = false;
1049                 }
1050             }
1051
1052             // stop removing nodes:
1053             if (removing==true &&
1054                     (node1.distanceTo(ae->getEndpoint())<1.0e-6 ||
1055                      node2.distanceTo(ae->getEndpoint())<1.0e-6)) {
1056                 DEBUG->print("Modification::deletePolylineNodesBetween: "
1057                                 "stop removing at: %f/%f",
1058                                 ae->getEndpoint().x, ae->getEndpoint().y);
1059                 removing = false;
1060                 done = true;
1061                                 if (first==false) {
1062                         nextIsStraight = true;
1063                                 }
1064             }
1065
1066             // normal node (not deleted):
1067             if (removing==false && (done==false || deleteStart==false)) {
1068                 DEBUG->print("Modification::deletePolylineNodesBetween: "
1069                                 "normal vertex found: %f/%f",
1070                                 ae->getEndpoint().x, ae->getEndpoint().y);
1071                 if (nextIsStraight) {
1072                     bulge = 0.0;
1073                     nextIsStraight = false;
1074                 }
1075                 newPolyline->setNextBulge(bulge);
1076                 newPolyline->addVertex(ae->getEndpoint());
1077             }
1078
1079             // drop deleted node:
1080             else {
1081                 DEBUG->print("Modification::deletePolylineNodesBetween: "
1082                                 "deleting vertex: %f/%f",
1083                                 ae->getEndpoint().x, ae->getEndpoint().y);
1084             }
1085
1086             // start to remove nodes from now on:
1087             if (done==false && removing==false &&
1088                     (node1.distanceTo(ae->getEndpoint())<1.0e-6 ||
1089                      node2.distanceTo(ae->getEndpoint())<1.0e-6)) {
1090                 DEBUG->print("Modification::deletePolylineNodesBetween: "
1091                                 "start removing at: %f/%f",
1092                                 ae->getEndpoint().x, ae->getEndpoint().y);
1093                 removing = true;
1094             }
1095
1096             if (done) {
1097                 done=false;
1098             }
1099         } else {
1100             DEBUG->print("Modification::deletePolylineNodesBetween: "
1101                             "Polyline contains non-atomic entities",
1102                             Debug::D_WARNING);
1103         }
1104     }
1105
1106     DEBUG->print("Modification::deletePolylineNodesBetween: ending polyline");
1107     newPolyline->setNextBulge(polyline.getClosingBulge());
1108     newPolyline->endPolyline();
1109
1110     // add new polyline:
1111     DEBUG->print("Modification::deletePolylineNodesBetween: adding new polyline");
1112     container->addEntity(newPolyline);
1113     if (graphicView!=NULL) {
1114         graphicView->deleteEntity(&polyline);
1115         graphicView->drawEntity(newPolyline);
1116     }
1117
1118     DEBUG->print("Modification::deletePolylineNodesBetween: handling undo");
1119     if (document!=NULL && handleUndo) {
1120         document->startUndoCycle();
1121
1122         polyline.setUndoState(true);
1123         document->addUndoable(&polyline);
1124         document->addUndoable(newPolyline);
1125
1126         document->endUndoCycle();
1127     }
1128
1129     return newPolyline;
1130 }
1131 */
1132
1133
1134
1135 /**
1136  * Trims two segments of a polyline all nodes between the two trim segments
1137  * are removed.
1138  *
1139  * @param polyline The polyline entity.
1140  * @param segment1 First segment to trim.
1141  * @param segment2 Second segment to trim.
1142  *
1143  * @return Pointer to the new polyline or NULL.
1144  */
1145 /*
1146 Polyline* Modification::polylineTrim(Polyline& polyline,
1147         AtomicEntity& segment1,
1148         AtomicEntity& segment2) {
1149
1150     DEBUG->print("Modification::polylineTrim");
1151
1152     if (container==NULL) {
1153         DEBUG->print("Modification::addPolylineNodesBetween: no valid container",
1154                         Debug::D_WARNING);
1155         return NULL;
1156     }
1157
1158     if (segment1.getParent()!=&polyline || segment2.getParent()!=&polyline) {
1159         DEBUG->print("Modification::polylineTrim: "
1160                         "segments not in polyline",
1161                         Debug::D_WARNING);
1162         return NULL;
1163     }
1164
1165     if (&segment1==&segment2) {
1166         DEBUG->print("Modification::polylineTrim: "
1167                         "segments are identical",
1168                         Debug::D_WARNING);
1169         return NULL;
1170     }
1171
1172     VectorSolutions sol;
1173     sol = Information::getIntersection(&segment1, &segment2, false);
1174
1175     if (sol.getNumber()==0) {
1176         DEBUG->print("Modification::polylineTrim: "
1177                         "segments cannot be trimmed",
1178                         Debug::D_WARNING);
1179         return NULL;
1180     }
1181
1182     // check which segment comes first in the polyline:
1183     AtomicEntity* firstSegment;
1184     if (polyline.findEntity(&segment1) > polyline.findEntity(&segment2)) {
1185         firstSegment = &segment2;
1186     } else {
1187         firstSegment = &segment1;
1188     }
1189
1190     // find out if we need to trim towards the open part of the polyline
1191     bool reverseTrim;
1192     reverseTrim = !Math::isSameDirection(firstSegment->getDirection1(),
1193                                             firstSegment->getStartpoint().angleTo(sol.get(0)), M_PI/2.0);
1194     //reverseTrim = reverseTrim || !Math::isSameDirection(segment2.getDirection1(),
1195     //  segment2.getStartpoint().angleTo(sol.get(0)), M_PI/2.0);
1196
1197     Polyline* newPolyline = new Polyline(container);
1198     newPolyline->setClosed(polyline.isClosed());
1199     newPolyline->setSelected(polyline.isSelected());
1200     newPolyline->setLayer(polyline.getLayer());
1201     newPolyline->setPen(polyline.getPen());
1202
1203     // normal trimming: start removing nodes at trim segment. ends stay the same
1204     if (reverseTrim==false) {
1205         // copy polyline, trim segments and drop between nodes:
1206         bool first = true;
1207         bool removing = false;
1208         bool nextIsStraight = false;
1209         Entity* lastEntity = polyline.lastEntity();
1210         for (Entity* e=polyline.firstEntity(); e!=NULL;
1211                 e=polyline.nextEntity()) {
1212
1213             if (e->isAtomic()) {
1214                 AtomicEntity* ae = (AtomicEntity*)e;
1215                 double bulge = 0.0;
1216                 if (ae->rtti()==RS2::EntityArc) {
1217                     DEBUG->print("Modification::polylineTrim: arc segment");
1218                     bulge = ((Arc*)ae)->getBulge();
1219                 } else {
1220                     DEBUG->print("Modification::polylineTrim: line segment");
1221                     bulge = 0.0;
1222                 }
1223
1224                 // last entity is closing entity and will be added below with endPolyline()
1225                 if (e==lastEntity && polyline.isClosed()) {
1226                     DEBUG->print("Modification::polylineTrim: "
1227                                     "dropping last vertex of closed polyline");
1228                     continue;
1229                 }
1230
1231                 // first vertex (startpoint)
1232                 if (first) {
1233                     DEBUG->print("Modification::polylineTrim: first node: %f/%f",
1234                                     ae->getStartpoint().x, ae->getStartpoint().y);
1235
1236                     newPolyline->setNextBulge(bulge);
1237                     newPolyline->addVertex(ae->getStartpoint());
1238                     first = false;
1239                 }
1240
1241                 // trim and start removing nodes:
1242                 if (removing==false && (ae==&segment1 || ae==&segment2)) {
1243                     DEBUG->print("Modification::polylineTrim: "
1244                                     "start removing at trim point %f/%f",
1245                                     sol.get(0).x, sol.get(0).y);
1246                     newPolyline->setNextBulge(0.0);
1247                     newPolyline->addVertex(sol.get(0));
1248                     removing = true;
1249                     nextIsStraight = true;
1250                 }
1251
1252                 // stop removing nodes:
1253                 else if (removing==true && (ae==&segment1 || ae==&segment2)) {
1254                     DEBUG->print("Modification::polylineTrim: stop removing at: %f/%f",
1255                                     ae->getEndpoint().x, ae->getEndpoint().y);
1256                     removing = false;
1257                 }
1258
1259                 // normal node (not deleted):
1260                 if (removing==false) {
1261                     DEBUG->print("Modification::polylineTrim: normal vertex found: %f/%f",
1262                                     ae->getEndpoint().x, ae->getEndpoint().y);
1263                     if (nextIsStraight) {
1264                         newPolyline->setNextBulge(0.0);
1265                         nextIsStraight = false;
1266                     } else {
1267                         newPolyline->setNextBulge(bulge);
1268                     }
1269                     newPolyline->addVertex(ae->getEndpoint());
1270                 }
1271             } else {
1272                 DEBUG->print("Modification::polylineTrim: "
1273                                 "Polyline contains non-atomic entities",
1274                                 Debug::D_WARNING);
1275             }
1276         }
1277     }
1278
1279     // reverse trimming: remove nodes at the ends and keep those in between
1280     else {
1281         // copy polyline, trim segments and drop between nodes:
1282         //bool first = true;
1283         bool removing = true;
1284         bool nextIsStraight = false;
1285         Entity* lastEntity = polyline.lastEntity();
1286         for (Entity* e=polyline.firstEntity(); e!=NULL;
1287                 e=polyline.nextEntity()) {
1288
1289             if (e->isAtomic()) {
1290                 AtomicEntity* ae = (AtomicEntity*)e;
1291                 double bulge = 0.0;
1292                 if (ae->rtti()==RS2::EntityArc) {
1293                     DEBUG->print("Modification::polylineTrim: arc segment");
1294                     bulge = ((Arc*)ae)->getBulge();
1295                 } else {
1296                     DEBUG->print("Modification::polylineTrim: line segment");
1297                     bulge = 0.0;
1298                 }
1299
1300                 // last entity is closing entity and will be added below with endPolyline()
1301                 if (e==lastEntity && polyline.isClosed()) {
1302                     DEBUG->print("Modification::polylineTrim: "
1303                                     "dropping last vertex of closed polyline");
1304                     continue;
1305                 }
1306
1307                 // trim and stop removing nodes:
1308                 if (removing==true && (ae==&segment1 || ae==&segment2)) {
1309                     DEBUG->print("Modification::polylineTrim: "
1310                                     "stop removing at trim point %f/%f",
1311                                     sol.get(0).x, sol.get(0).y);
1312                     newPolyline->setNextBulge(0.0);
1313                     // start of new polyline:
1314                     newPolyline->addVertex(sol.get(0));
1315                     removing = false;
1316                     nextIsStraight = true;
1317                 }
1318
1319                 // start removing nodes again:
1320                 else if (removing==false && (ae==&segment1 || ae==&segment2)) {
1321                     DEBUG->print("Modification::polylineTrim: start removing at: %f/%f",
1322                                     ae->getEndpoint().x, ae->getEndpoint().y);
1323                     newPolyline->setNextBulge(0.0);
1324                     // start of new polyline:
1325                     newPolyline->addVertex(sol.get(0));
1326                     removing = true;
1327                 }
1328
1329                 // normal node (not deleted):
1330                 if (removing==false) {
1331                     DEBUG->print("Modification::polylineTrim: normal vertex found: %f/%f",
1332                                     ae->getEndpoint().x, ae->getEndpoint().y);
1333                     if (nextIsStraight) {
1334                         newPolyline->setNextBulge(0.0);
1335                         nextIsStraight = false;
1336                     } else {
1337                         newPolyline->setNextBulge(bulge);
1338                     }
1339                     newPolyline->addVertex(ae->getEndpoint());
1340                 }
1341             } else {
1342                 DEBUG->print("Modification::polylineTrim: "
1343                                 "Polyline contains non-atomic entities",
1344                                 Debug::D_WARNING);
1345             }
1346         }
1347     }
1348
1349     DEBUG->print("Modification::polylineTrim: ending polyline");
1350     newPolyline->setNextBulge(polyline.getClosingBulge());
1351     newPolyline->endPolyline();
1352
1353     // add new polyline:
1354     DEBUG->print("Modification::polylineTrim: adding new polyline");
1355     container->addEntity(newPolyline);
1356     if (graphicView!=NULL) {
1357         graphicView->deleteEntity(&polyline);
1358         graphicView->drawEntity(newPolyline);
1359     }
1360
1361     DEBUG->print("Modification::polylineTrim: handling undo");
1362     if (document!=NULL && handleUndo) {
1363         document->startUndoCycle();
1364
1365         polyline.setUndoState(true);
1366         document->addUndoable(&polyline);
1367         document->addUndoable(newPolyline);
1368
1369         document->endUndoCycle();
1370     }
1371
1372     return newPolyline;
1373 }
1374 */
1375
1376 /**
1377  * Moves all selected entities with the given data for the move
1378  * modification.
1379  */
1380 bool Modification::move(MoveData & data)
1381 {
1382         if (container == NULL)
1383         {
1384                 DEBUG->print("Modification::move: no valid container", Debug::D_WARNING);
1385                 return false;
1386         }
1387
1388 //    Q3PtrList<Entity> addList;
1389 //    addList.setAutoDelete(false);
1390         QList<Entity *> addList;
1391
1392         if (document != NULL && handleUndo)
1393                 document->startUndoCycle();
1394
1395         // Create new entites
1396         for(int num=1; num<=data.number || (data.number==0 && num<=1); num++)
1397         {
1398                 // too slow:
1399                 //for (uint i=0; i<container->count(); ++i) {
1400                 //Entity* e = container->entityAt(i);
1401                 for(Entity* e=container->firstEntity(); e!=NULL; e=container->nextEntity())
1402                 {
1403                         if (e != NULL && e->isSelected())
1404                         {
1405                                 Entity * ec = e->clone();
1406                                 ec->move(data.offset * num);
1407
1408                                 if (data.useCurrentLayer)
1409                                         ec->setLayerToActive();
1410
1411                                 if (data.useCurrentAttributes)
1412                                         ec->setPenToActive();
1413
1414                                 if (ec->rtti() == RS2::EntityInsert)
1415                                         ((Insert *)ec)->update();
1416
1417                                 // since 2.0.4.0: keep selection
1418                                 ec->setSelected(true);
1419                                 addList.append(ec);
1420                         }
1421                 }
1422         }
1423
1424         deselectOriginals(data.number==0);
1425         addNewEntities(addList);
1426
1427         if (document != NULL && handleUndo)
1428                 document->endUndoCycle();
1429
1430         if (graphicView != NULL)
1431                 graphicView->redraw();
1432
1433         return true;
1434 }
1435
1436 /**
1437  * Rotates all selected entities with the given data for the rotation.
1438  */
1439 bool Modification::rotate(RotateData & data)
1440 {
1441         if (container == NULL)
1442         {
1443                 DEBUG->print("Modification::rotate: no valid container",
1444                                                 Debug::D_WARNING);
1445                 return false;
1446         }
1447
1448 //      Q3PtrList<Entity> addList;
1449 //      addList.setAutoDelete(false);
1450         QList<Entity *> addList;
1451
1452         if (document!=NULL && handleUndo)
1453                 document->startUndoCycle();
1454
1455         // Create new entites
1456         for(int num=1; num<=data.number || (data.number==0 && num<=1); num++)
1457         {
1458                 for (Entity * e=container->firstEntity(); e!=NULL; e=container->nextEntity())
1459                 {
1460                         //for (uint i=0; i<container->count(); ++i) {
1461                         //Entity* e = container->entityAt(i);
1462
1463                         if (e != NULL && e->isSelected())
1464                         {
1465                                 Entity * ec = e->clone();
1466                                 ec->setSelected(false);
1467                                 ec->rotate(data.center, data.angle*num);
1468
1469                                 if (data.useCurrentLayer)
1470                                         ec->setLayerToActive();
1471
1472                                 if (data.useCurrentAttributes)
1473                                         ec->setPenToActive();
1474
1475                                 if (ec->rtti() == RS2::EntityInsert)
1476                                         ((Insert *)ec)->update();
1477
1478                                 addList.append(ec);
1479                         }
1480                 }
1481         }
1482
1483         deselectOriginals(data.number == 0);
1484         addNewEntities(addList);
1485
1486         if (document != NULL && handleUndo)
1487                 document->endUndoCycle();
1488
1489         if (graphicView != NULL)
1490                 graphicView->redraw();
1491
1492         return true;
1493 }
1494
1495 /**
1496  * Moves all selected entities with the given data for the scale
1497  * modification.
1498  */
1499 bool Modification::scale(ScaleData & data)
1500 {
1501         if (container == NULL)
1502         {
1503                 DEBUG->print("Modification::scale: no valid container", Debug::D_WARNING);
1504                 return false;
1505         }
1506
1507 //      Q3PtrList<Entity> addList;
1508 //      addList.setAutoDelete(false);
1509         QList<Entity *> addList;
1510
1511         if (document!=NULL && handleUndo)
1512                 document->startUndoCycle();
1513
1514         // Create new entites
1515         for(int num=1; num<=data.number || (data.number==0 && num<=1); num++)
1516         {
1517                 for(Entity* e=container->firstEntity(); e!=NULL; e=container->nextEntity())
1518                 {
1519                         //for (uint i=0; i<container->count(); ++i) {
1520                         //Entity* e = container->entityAt(i);
1521                         if (e != NULL && e->isSelected())
1522                         {
1523                                 Entity * ec = e->clone();
1524                                 ec->setSelected(false);
1525                                 ec->scale(data.referencePoint, Math::pow(data.factor, num));
1526
1527                                 if (data.useCurrentLayer)
1528                                         ec->setLayerToActive();
1529
1530                                 if (data.useCurrentAttributes)
1531                                         ec->setPenToActive();
1532
1533                                 if (ec->rtti()==RS2::EntityInsert)
1534                                         ((Insert*)ec)->update();
1535
1536                                 addList.append(ec);
1537                         }
1538                 }
1539         }
1540
1541         deselectOriginals(data.number == 0);
1542         addNewEntities(addList);
1543
1544         if (document != NULL && handleUndo)
1545                 document->endUndoCycle();
1546
1547         if (graphicView != NULL)
1548                 graphicView->redraw();
1549
1550         return true;
1551 }
1552
1553 /**
1554  * Mirror all selected entities with the given data for the mirror
1555  * modification.
1556  */
1557 bool Modification::mirror(MirrorData & data)
1558 {
1559         if (container==NULL) {
1560                 DEBUG->print("Modification::mirror: no valid container",
1561                                                 Debug::D_WARNING);
1562                 return false;
1563         }
1564
1565 //      Q3PtrList<Entity> addList;
1566 //      addList.setAutoDelete(false);
1567         QList<Entity *> addList;
1568
1569         if (document!=NULL && handleUndo) {
1570                 document->startUndoCycle();
1571         }
1572
1573         // Create new entites
1574         for (int num=1;
1575                         num<=(int)data.copy || (data.copy==false && num<=1);
1576                         num++) {
1577                 for (Entity* e=container->firstEntity();
1578                                 e!=NULL;
1579                                 e=container->nextEntity()) {
1580                         //for (uint i=0; i<container->count(); ++i) {
1581                         //Entity* e = container->entityAt(i);
1582
1583                         if (e!=NULL && e->isSelected()) {
1584                                 Entity* ec = e->clone();
1585                                 ec->setSelected(false);
1586
1587                                 ec->mirror(data.axisPoint1, data.axisPoint2);
1588                                 if (data.useCurrentLayer) {
1589                                         ec->setLayerToActive();
1590                                 }
1591                                 if (data.useCurrentAttributes) {
1592                                         ec->setPenToActive();
1593                                 }
1594                                 if (ec->rtti()==RS2::EntityInsert) {
1595                                         ((Insert*)ec)->update();
1596                                 }
1597                                 addList.append(ec);
1598                         }
1599                 }
1600         }
1601
1602         deselectOriginals(data.copy==false);
1603         addNewEntities(addList);
1604
1605         if (document!=NULL && handleUndo) {
1606                 document->endUndoCycle();
1607         }
1608
1609         if (graphicView!=NULL) {
1610                 graphicView->redraw();
1611         }
1612         return true;
1613 }
1614
1615 /**
1616  * Rotates entities around two centers with the given parameters.
1617  */
1618 bool Modification::rotate2(Rotate2Data & data)
1619 {
1620         if (container==NULL) {
1621                 DEBUG->print("Modification::rotate2: no valid container",
1622                                                 Debug::D_WARNING);
1623                 return false;
1624         }
1625
1626 //      Q3PtrList<Entity> addList;
1627 //      addList.setAutoDelete(false);
1628         QList<Entity *> addList;
1629
1630         if (document!=NULL && handleUndo) {
1631                 document->startUndoCycle();
1632         }
1633
1634         // Create new entites
1635         for (int num=1;
1636                         num<=data.number || (data.number==0 && num<=1);
1637                         num++) {
1638
1639                 for (Entity* e=container->firstEntity();
1640                                 e!=NULL;
1641                                 e=container->nextEntity()) {
1642                         //for (uint i=0; i<container->count(); ++i) {
1643                         //Entity* e = container->entityAt(i);
1644
1645                         if (e!=NULL && e->isSelected()) {
1646                                 Entity* ec = e->clone();
1647                                 ec->setSelected(false);
1648
1649                                 ec->rotate(data.center1, data.angle1*num);
1650                                 Vector center2 = data.center2;
1651                                 center2.rotate(data.center1, data.angle1*num);
1652
1653                                 ec->rotate(center2, data.angle2*num);
1654                                 if (data.useCurrentLayer) {
1655                                         ec->setLayerToActive();
1656                                 }
1657                                 if (data.useCurrentAttributes) {
1658                                         ec->setPenToActive();
1659                                 }
1660                                 if (ec->rtti()==RS2::EntityInsert) {
1661                                         ((Insert*)ec)->update();
1662                                 }
1663                                 addList.append(ec);
1664                         }
1665                 }
1666         }
1667
1668         deselectOriginals(data.number==0);
1669         addNewEntities(addList);
1670
1671         if (document!=NULL && handleUndo) {
1672                 document->endUndoCycle();
1673         }
1674
1675         if (graphicView!=NULL) {
1676                 graphicView->redraw();
1677         }
1678         return true;
1679 }
1680
1681 /**
1682  * Moves and rotates entities with the given parameters.
1683  */
1684 bool Modification::moveRotate(MoveRotateData & data)
1685 {
1686     if (container==NULL) {
1687         DEBUG->print("Modification::moveRotate: no valid container",
1688                         Debug::D_WARNING);
1689         return false;
1690     }
1691
1692 //      Q3PtrList<Entity> addList;
1693 //      addList.setAutoDelete(false);
1694         QList<Entity *> addList;
1695
1696     if (document!=NULL && handleUndo) {
1697         document->startUndoCycle();
1698     }
1699
1700     // Create new entites
1701     for (int num=1;
1702             num<=data.number || (data.number==0 && num<=1);
1703             num++) {
1704         for (Entity* e=container->firstEntity();
1705                 e!=NULL;
1706                 e=container->nextEntity()) {
1707             //for (uint i=0; i<container->count(); ++i) {
1708             //Entity* e = container->entityAt(i);
1709
1710             if (e!=NULL && e->isSelected()) {
1711                 Entity* ec = e->clone();
1712                 ec->setSelected(false);
1713
1714                 ec->move(data.offset*num);
1715                 ec->rotate(data.referencePoint + data.offset*num,
1716                            data.angle*num);
1717                 if (data.useCurrentLayer) {
1718                     ec->setLayerToActive();
1719                 }
1720                 if (data.useCurrentAttributes) {
1721                     ec->setPenToActive();
1722                 }
1723                 if (ec->rtti()==RS2::EntityInsert) {
1724                     ((Insert*)ec)->update();
1725                 }
1726                 addList.append(ec);
1727             }
1728         }
1729     }
1730
1731     deselectOriginals(data.number==0);
1732     addNewEntities(addList);
1733
1734     if (document!=NULL && handleUndo) {
1735         document->endUndoCycle();
1736     }
1737     if (graphicView!=NULL) {
1738         graphicView->redraw();
1739     }
1740
1741     return true;
1742 }
1743
1744 /**
1745  * Deselects all selected entities and removes them if remove is true;
1746  *
1747  * @param remove true: Remove entites.
1748  */
1749 void Modification::deselectOriginals(bool remove)
1750 {
1751         for(Entity* e=container->firstEntity(); e!=NULL; e=container->nextEntity())
1752         {
1753                 //for (uint i=0; i<container->count(); ++i) {
1754                 //Entity* e = container->entityAt(i);
1755
1756                 if (e != NULL)
1757                 {
1758                         bool selected = false;
1759                         /*
1760                                         if (e->isAtomic()) {
1761                                                 AtomicEntity* ae = (AtomicEntity*)e;
1762                                                 if (ae->isStartpointSelected() ||
1763                                                                 ae->isEndpointSelected()) {
1764
1765                                                         selected = true;
1766                                                 }
1767                                         }
1768                         */
1769
1770                         if (e->isSelected())
1771                                 selected = true;
1772
1773                         if (selected)
1774                         {
1775                                 e->setSelected(false);
1776
1777                                 if (remove)
1778                                 {
1779                                         //if (graphicView!=NULL) {
1780                                         //    graphicView->deleteEntity(e);
1781                                         //}
1782                                         e->changeUndoState();
1783
1784                                         if (document != NULL && handleUndo)
1785                                                 document->addUndoable(e);
1786                                 }
1787                                 else
1788                                 {
1789                                         //if (graphicView!=NULL) {
1790                                         //    graphicView->drawEntity(e);
1791                                         //}
1792                                 }
1793                         }
1794                 }
1795         }
1796 }
1797
1798 /**
1799  * Adds the given entities to the container and draws the entities if
1800  * there's a graphic view available.
1801  *
1802  * @param addList Entities to add.
1803  */
1804 //void Modification::addNewEntities(Q3PtrList<Entity> & addList)
1805 void Modification::addNewEntities(QList<Entity *> & addList)
1806 {
1807 //      for(Entity * e=addList.first(); e!=NULL; e=addList.next())
1808         for(int i=0; i<addList.size(); i++)
1809         {
1810                 Entity * e = addList[i];
1811
1812                 if (e != NULL)
1813                 {
1814                         container->addEntity(e);
1815
1816                         if (document != NULL && handleUndo)
1817                                 document->addUndoable(e);
1818                         //if (graphicView!=NULL) {
1819                         //    graphicView->drawEntity(e);
1820                         //}
1821                 }
1822         }
1823 }
1824
1825 /**
1826  * Trims or extends the given trimEntity to the intersection point of the
1827  * trimEntity and the limitEntity.
1828  *
1829  * @param trimCoord Coordinate which defines which endpoint of the
1830  *   trim entity to trim.
1831  * @param trimEntity Entity which will be trimmed.
1832  * @param limitCoord Coordinate which defines the intersection to which the
1833  *    trim entity will be trimmed.
1834  * @param limitEntity Entity to which the trim entity will be trimmed.
1835  * @param both true: Trim both entities. false: trim trimEntity only.
1836  */
1837 bool Modification::trim(const Vector& trimCoord, AtomicEntity* trimEntity,
1838         const Vector& limitCoord, Entity* limitEntity, bool both)
1839 {
1840     if (trimEntity==NULL || limitEntity==NULL)
1841         {
1842         DEBUG->print(Debug::D_WARNING,
1843                         "Modification::trim: At least one entity is NULL");
1844         return false;
1845     }
1846
1847     if (both && !limitEntity->isAtomic())
1848         {
1849         DEBUG->print(Debug::D_WARNING,
1850                         "Modification::trim: limitEntity is not atomic");
1851     }
1852
1853     VectorSolutions sol;
1854
1855         if (limitEntity->isAtomic())
1856         {
1857         // intersection(s) of the two entities:
1858         sol = Information::getIntersection(trimEntity, limitEntity, false);
1859     } else if (limitEntity->isContainer()) {
1860         EntityContainer* ec = (EntityContainer*)limitEntity;
1861
1862         sol.alloc(128);
1863         int i=0;
1864
1865         for (Entity* e=ec->firstEntity(RS2::ResolveAll); e!=NULL;
1866                 e=ec->nextEntity(RS2::ResolveAll)) {
1867             //for (int i=0; i<container->count(); ++i) {
1868             //    Entity* e = container->entityAt(i);
1869
1870             if (e!=NULL) {
1871
1872                 VectorSolutions s2 = Information::getIntersection(trimEntity,
1873                                         e, false);
1874
1875                 if (s2.hasValid()) {
1876                     for (int k=0; k<s2.getNumber(); ++k) {
1877                         if (i<128 && s2.get(k).valid) {
1878                             if (e->isPointOnEntity(s2.get(k), 1.0e-4)) {
1879                                 sol.set(i++, s2.get(k));
1880                             }
1881                         }
1882                     }
1883                     //break;
1884                 }
1885             }
1886         }
1887     }
1888
1889         if (!sol.hasValid())
1890                 return false;
1891
1892         AtomicEntity * trimmed1 = NULL;
1893         AtomicEntity * trimmed2 = NULL;
1894
1895         // remove trim entity from view:
1896         if (trimEntity->rtti() == RS2::EntityCircle)
1897         {
1898                 // convert a circle into a trimmable arc
1899                 Circle * c = (Circle *)trimEntity;
1900                 double am = c->getCenter().angleTo(trimCoord);
1901                 ArcData d(c->getCenter(), c->getRadius(),
1902                         Math::correctAngle(am - M_PI / 2),
1903                         Math::correctAngle(am + M_PI / 2), false);
1904                 trimmed1 = new Arc(trimEntity->getParent(), d);
1905         }
1906         else
1907         {
1908                 trimmed1 = (AtomicEntity *)trimEntity->clone();
1909                 trimmed1->setHighlighted(false);
1910         }
1911
1912 #warning "!!! Old rendering path needs upgrading !!!"
1913 #if 0
1914         if (graphicView)
1915                 graphicView->deleteEntity(trimEntity);
1916 #endif
1917
1918         // remove limit entity from view:
1919         if (both)
1920         {
1921                 trimmed2 = (AtomicEntity *)limitEntity->clone();
1922                 trimmed2->setHighlighted(false);
1923
1924 #warning "!!! Old rendering path needs upgrading !!!"
1925 #if 0
1926                 if (graphicView)
1927                         graphicView->deleteEntity(limitEntity);
1928 #endif
1929         }
1930
1931     // trim trim entity
1932     int ind = 0;
1933     Vector is = sol.getClosest(limitCoord, NULL, &ind);
1934     //sol.getClosest(limitCoord, NULL, &ind);
1935     DEBUG->print("Modification::trim: limitCoord: %f/%f", limitCoord.x, limitCoord.y);
1936     DEBUG->print("Modification::trim: sol.get(0): %f/%f", sol.get(0).x, sol.get(0).y);
1937     DEBUG->print("Modification::trim: sol.get(1): %f/%f", sol.get(1).x, sol.get(1).y);
1938     DEBUG->print("Modification::trim: ind: %d", ind);
1939     Vector is2 = sol.get(ind==0 ? 1 : 0);
1940     //Vector is2 = sol.get(ind);
1941     DEBUG->print("Modification::trim: is2: %f/%f", is2.x, is2.y);
1942
1943     //RS2::Ending ending = trimmed1->getTrimPoint(trimCoord, is);
1944     RS2::Ending ending = trimmed1->getTrimPoint(trimCoord, is);
1945
1946     switch (ending) {
1947     case RS2::EndingStart:
1948         trimmed1->trimStartpoint(is);
1949         if (trimEntity->rtti()==RS2::EntityCircle) {
1950             trimmed1->trimEndpoint(is2);
1951         }
1952         break;
1953     case RS2::EndingEnd:
1954         trimmed1->trimEndpoint(is);
1955         if (trimEntity->rtti()==RS2::EntityCircle) {
1956             trimmed1->trimStartpoint(is2);
1957         }
1958         break;
1959     default:
1960         break;
1961     }
1962
1963     // trim limit entity:
1964     if (both) {
1965         Vector is = sol.getClosest(limitCoord);
1966
1967         RS2::Ending ending = trimmed2->getTrimPoint(limitCoord, is);
1968
1969         switch (ending) {
1970         case RS2::EndingStart:
1971             trimmed2->trimStartpoint(is);
1972             break;
1973         case RS2::EndingEnd:
1974             trimmed2->trimEndpoint(is);
1975             break;
1976         default:
1977             break;
1978         }
1979     }
1980
1981     // add new trimmed trim entity:
1982     container->addEntity(trimmed1);
1983     if (graphicView!=NULL) {
1984         graphicView->drawEntity(trimmed1);
1985     }
1986
1987     // add new trimmed limit entity:
1988     if (both) {
1989         container->addEntity(trimmed2);
1990         if (graphicView!=NULL) {
1991             graphicView->drawEntity(trimmed2);
1992         }
1993     }
1994
1995     if (document!=NULL && handleUndo) {
1996         document->startUndoCycle();
1997         document->addUndoable(trimmed1);
1998         trimEntity->setUndoState(true);
1999         document->addUndoable(trimEntity);
2000         if (both) {
2001             document->addUndoable(trimmed2);
2002             limitEntity->setUndoState(true);
2003             document->addUndoable(limitEntity);
2004         }
2005         document->endUndoCycle();
2006     }
2007
2008     return true;
2009 }
2010
2011
2012
2013 /**
2014  * Trims or extends the given trimEntity by the given amount.
2015  *
2016  * @param trimCoord Coordinate which defines which endpoint of the
2017  *   trim entity to trim.
2018  * @param trimEntity Entity which will be trimmed.
2019  * @param dist Amount to trim by.
2020  */
2021 bool Modification::trimAmount(const Vector & trimCoord,
2022         AtomicEntity * trimEntity, double dist)
2023 {
2024         if (!trimEntity)
2025         {
2026                 DEBUG->print(Debug::D_WARNING, "Modification::trimAmount: Entity is NULL");
2027                 return false;
2028         }
2029
2030         AtomicEntity    * trimmed = NULL;
2031
2032         // remove trim entity:
2033         trimmed = (AtomicEntity*)trimEntity->clone();
2034
2035 #warning "!!! Old rendering path needs upgrading !!!"
2036 #if 0
2037         if (graphicView)
2038                 graphicView->deleteEntity(trimEntity);
2039 #endif
2040
2041         // trim trim entity
2042         Vector is = trimmed->getNearestDist(-dist, trimCoord);
2043         if (trimCoord.distanceTo(trimmed->getStartpoint()) <
2044                         trimCoord.distanceTo(trimmed->getEndpoint()))
2045         {
2046                 trimmed->trimStartpoint(is);
2047         }
2048         else
2049         {
2050                 trimmed->trimEndpoint(is);
2051         }
2052
2053         // add new trimmed trim entity:
2054         container->addEntity(trimmed);
2055
2056 #warning "!!! Old rendering path needs upgrading !!!"
2057 #if 0
2058         if (graphicView)
2059                 graphicView->drawEntity(trimmed);
2060 #endif
2061
2062         if (document && handleUndo)
2063         {
2064                 document->startUndoCycle();
2065                 document->addUndoable(trimmed);
2066                 trimEntity->setUndoState(true);
2067                 document->addUndoable(trimEntity);
2068                 document->endUndoCycle();
2069         }
2070
2071         return true;
2072 }
2073
2074
2075
2076 /**
2077  * Cuts the given entity at the given point.
2078  */
2079 bool Modification::cut(const Vector& cutCoord,
2080                           AtomicEntity* cutEntity) {
2081
2082     if (cutEntity==NULL) {
2083         DEBUG->print(Debug::D_WARNING,
2084                         "Modification::cut: Entity is NULL");
2085         return false;
2086     }
2087
2088     if (!cutCoord.valid) {
2089         DEBUG->print(Debug::D_WARNING,
2090                         "Modification::cut: Point invalid.");
2091         return false;
2092     }
2093
2094     // cut point is at endpoint of entity:
2095     if (cutCoord.distanceTo(cutEntity->getStartpoint())<1.0e-6 ||
2096             cutCoord.distanceTo(cutEntity->getEndpoint())<1.0e-6) {
2097         DEBUG->print(Debug::D_WARNING,
2098                         "Modification::cut: Cutting point on endpoint");
2099         return false;
2100     }
2101
2102 #warning "!!! Old rendering path needs upgrading !!!"
2103 #if 0
2104         // delete cut entity on the screen:
2105         if (graphicView)
2106                 graphicView->deleteEntity(cutEntity);
2107 #endif
2108
2109         AtomicEntity * cut1 = NULL;
2110         AtomicEntity * cut2 = NULL;
2111
2112     // create new two halves:
2113     if (cutEntity->rtti() == RS2::EntityCircle)
2114         {
2115         Circle * c = (Circle *)cutEntity;
2116         cut1 = new Arc(cutEntity->getParent(),
2117                           ArcData(c->getCenter(),
2118                                      c->getRadius(),
2119                                      0.0,0.0, false));
2120         cut1->setPen(cutEntity->getPen());
2121         cut1->setLayer(cutEntity->getLayer());
2122         cut2 = NULL;
2123
2124         cut1->trimEndpoint(cutCoord);
2125         cut1->trimStartpoint(cutCoord);
2126     }
2127     else
2128         {
2129         cut1 = (AtomicEntity*)cutEntity->clone();
2130         cut2 = (AtomicEntity*)cutEntity->clone();
2131
2132         cut1->trimEndpoint(cutCoord);
2133         cut2->trimStartpoint(cutCoord);
2134     }
2135
2136     // add new cut entity:
2137     container->addEntity(cut1);
2138     if (cut2!=NULL) {
2139         container->addEntity(cut2);
2140     }
2141
2142     if (graphicView!=NULL) {
2143         graphicView->drawEntity(cut1);
2144         if (cut2!=NULL) {
2145             graphicView->drawEntity(cut2);
2146         }
2147     }
2148
2149     if (document!=NULL && handleUndo) {
2150         document->startUndoCycle();
2151         document->addUndoable(cut1);
2152         if (cut2!=NULL) {
2153             document->addUndoable(cut2);
2154         }
2155         cutEntity->setUndoState(true);
2156         document->addUndoable(cutEntity);
2157         document->endUndoCycle();
2158     }
2159
2160     return true;
2161 }
2162
2163 /**
2164  * Stretching.
2165  */
2166 bool Modification::stretch(const Vector& firstCorner, const Vector& secondCorner,
2167         const Vector& offset)
2168 {
2169     if (!offset.valid)
2170         {
2171         DEBUG->print(Debug::D_WARNING,
2172                         "Modification::stretch: Offset invalid");
2173         return false;
2174     }
2175
2176 //      Q3PtrList<Entity> addList;
2177 //      addList.setAutoDelete(false);
2178         QList<Entity *> addList;
2179
2180     if (document!=NULL && handleUndo) {
2181         document->startUndoCycle();
2182     }
2183
2184     // Create new entites
2185     for (Entity* e=container->firstEntity();
2186             e!=NULL;
2187             e=container->nextEntity()) {
2188         //for (int i=0; i<container->count(); ++i) {
2189         //    Entity* e = container->entityAt(i);
2190
2191         if (e!=NULL &&
2192                 e->isVisible() &&
2193                 !e->isLocked() &&
2194                 (e->isInWindow(firstCorner, secondCorner) ||
2195                  e->hasEndpointsWithinWindow(firstCorner, secondCorner))) {
2196
2197             Entity* ec = e->clone();
2198             ec->stretch(firstCorner, secondCorner, offset);
2199             addList.append(ec);
2200             e->setSelected(true);
2201         }
2202     }
2203
2204     deselectOriginals(true);
2205     addNewEntities(addList);
2206
2207     if (document!=NULL && handleUndo) {
2208         document->endUndoCycle();
2209     }
2210
2211     if (graphicView!=NULL) {
2212         graphicView->redraw();
2213     }
2214     return true;
2215 }
2216
2217
2218
2219 /**
2220  * Bevels a corner.
2221  *
2222  * @param coord1 Mouse coordinate to specify direction from intersection.
2223  * @param entity1 First entity of the corner.
2224  * @param coord2 Mouse coordinate to specify direction from intersection.
2225  * @param entity2 Second entity of the corner.
2226  * @param data Lengths and trim flag.
2227  */
2228 bool Modification::bevel(const Vector& coord1, AtomicEntity* entity1,
2229                             const Vector& coord2, AtomicEntity* entity2,
2230                             BevelData& data) {
2231
2232     DEBUG->print("Modification::bevel");
2233
2234     if (entity1==NULL || entity2==NULL) {
2235         DEBUG->print(Debug::D_WARNING,
2236                         "Modification::bevel: At least one entity is NULL");
2237         return false;
2238     }
2239
2240     EntityContainer* baseContainer = container;
2241     bool isPolyline = false;
2242     bool isClosedPolyline = false;
2243
2244     if (document!=NULL && handleUndo) {
2245         document->startUndoCycle();
2246     }
2247
2248     // find out whether we're bevelling within a polyline:
2249     if (entity1->getParent()!=NULL && entity1->getParent()->rtti()==RS2::EntityPolyline) {
2250         DEBUG->print("Modification::bevel: trimming polyline segments");
2251         if (entity1->getParent()!=entity2->getParent()) {
2252             DEBUG->print(Debug::D_WARNING,
2253                             "Modification::bevel: entities not in the same polyline");
2254             return false;
2255         }
2256         // clone polyline for undo
2257         if (document!=NULL && handleUndo) {
2258             EntityContainer* cl =
2259                 (EntityContainer*)entity1->getParent()->clone();
2260             container->addEntity(cl);
2261             //cl->setUndoState(true);
2262             document->addUndoable(cl);
2263
2264             document->addUndoable(entity1->getParent());
2265             entity1->getParent()->setUndoState(true);
2266
2267             baseContainer = cl;
2268         }
2269
2270         entity1 = (AtomicEntity*)baseContainer->entityAt(entity1->getParent()->findEntity(entity1));
2271         entity2 = (AtomicEntity*)baseContainer->entityAt(entity2->getParent()->findEntity(entity2));
2272
2273         //baseContainer = entity1->getParent();
2274         isPolyline = true;
2275                 isClosedPolyline = ((Polyline*)entity1)->isClosed();
2276     }
2277
2278     DEBUG->print("Modification::bevel: getting intersection");
2279
2280     VectorSolutions sol =
2281         Information::getIntersection(entity1, entity2, false);
2282
2283     if (sol.getNumber()==0) {
2284         return false;
2285     }
2286
2287     AtomicEntity* trimmed1 = NULL;
2288     AtomicEntity* trimmed2 = NULL;
2289
2290     //if (data.trim || isPolyline) {
2291         if (isPolyline) {
2292             trimmed1 = entity1;
2293             trimmed2 = entity2;
2294         } else {
2295             trimmed1 = (AtomicEntity*)entity1->clone();
2296             trimmed2 = (AtomicEntity*)entity2->clone();
2297         }
2298
2299 #warning "!!! Old rendering path needs upgrading !!!"
2300 #if 0
2301                 // remove trim entity (on screen):
2302                 if (data.trim || isPolyline)
2303                 {
2304                         if (graphicView)
2305                         {
2306                                 if (isPolyline)
2307                                 {
2308                                         graphicView->deleteEntity(baseContainer);
2309                                 }
2310                                 else
2311                                 {
2312                                         graphicView->deleteEntity(entity1);
2313                                         graphicView->deleteEntity(entity2);
2314                                 }
2315                         }
2316                 }
2317 #endif
2318
2319         // trim entities to intersection
2320         DEBUG->print("Modification::bevel: trim entities to intersection 01");
2321         bool start1 = false;
2322         Vector is = sol.getClosest(coord2);
2323         RS2::Ending ending1 = trimmed1->getTrimPoint(coord1, is);
2324         switch (ending1) {
2325         case RS2::EndingStart:
2326             trimmed1->trimStartpoint(is);
2327             start1 = true;
2328             break;
2329         case RS2::EndingEnd:
2330             trimmed1->trimEndpoint(is);
2331             start1 = false;
2332             break;
2333         default:
2334             break;
2335         }
2336
2337         DEBUG->print("Modification::bevel: trim entities to intersection 02");
2338         bool start2 = false;
2339         is = sol.getClosest(coord1);
2340         RS2::Ending ending2 = trimmed2->getTrimPoint(coord2, is);
2341         switch (ending2) {
2342         case RS2::EndingStart:
2343             trimmed2->trimStartpoint(is);
2344             start2 = true;
2345             break;
2346         case RS2::EndingEnd:
2347             trimmed2->trimEndpoint(is);
2348             start2 = false;
2349             break;
2350         default:
2351             break;
2352         }
2353     //}
2354
2355
2356     // find definitive bevel points
2357     DEBUG->print("Modification::bevel: find definitive bevel points");
2358     Vector bp1 = trimmed1->getNearestDist(data.length1, start1);
2359     Vector bp2 = trimmed2->getNearestDist(data.length2, start2);
2360
2361     // final trim:
2362     DEBUG->print("Modification::bevel: final trim");
2363     if (data.trim==true) {
2364         switch (ending1) {
2365         case RS2::EndingStart:
2366             trimmed1->trimStartpoint(bp1);
2367             break;
2368         case RS2::EndingEnd:
2369             trimmed1->trimEndpoint(bp1);
2370             break;
2371         default:
2372             break;
2373         }
2374
2375         switch (ending2) {
2376         case RS2::EndingStart:
2377             trimmed2->trimStartpoint(bp2);
2378             break;
2379         case RS2::EndingEnd:
2380             trimmed2->trimEndpoint(bp2);
2381             break;
2382         default:
2383             break;
2384         }
2385
2386         // add new trimmed entities:
2387         if (isPolyline==false) {
2388             container->addEntity(trimmed1);
2389             container->addEntity(trimmed2);
2390         }
2391         if (graphicView!=NULL) {
2392             if (!isPolyline) {
2393                 graphicView->drawEntity(trimmed1);
2394                 graphicView->drawEntity(trimmed2);
2395             }
2396         }
2397     }
2398
2399
2400     // add bevel line:
2401     DEBUG->print("Modification::bevel: add bevel line");
2402     Line* bevel = new Line(baseContainer, LineData(bp1, bp2));
2403
2404     if (isPolyline==false) {
2405         baseContainer->addEntity(bevel);
2406     } else {
2407         int idx1 = baseContainer->findEntity(trimmed1);
2408         int idx2 = baseContainer->findEntity(trimmed2);
2409
2410         bevel->setSelected(baseContainer->isSelected());
2411         bevel->setLayer(baseContainer->getLayer());
2412         bevel->setPen(baseContainer->getPen());
2413
2414                 bool insertAfter1 = false;
2415                 if (!isClosedPolyline) {
2416                         insertAfter1 = (idx1<idx2);
2417                 }
2418                 else {
2419                         insertAfter1 = ((idx1<idx2 && idx1!=0) ||
2420                                 (idx2==0 && idx1==(int)baseContainer->count()-1));
2421                 }
2422
2423         // insert bevel at the right position:
2424         //if ((idx1<idx2 && idx1!=0) ||
2425                 //      (idx2==0 && idx1==(int)baseContainer->count()-1)) {
2426                 if (insertAfter1) {
2427             if (trimmed1->getEndpoint().distanceTo(bevel->getStartpoint())>1.0e-4) {
2428                 bevel->reverse();
2429             }
2430             baseContainer->insertEntity(idx1+1, bevel);
2431         } else {
2432             if (trimmed2->getEndpoint().distanceTo(bevel->getStartpoint())>1.0e-4) {
2433                 bevel->reverse();
2434             }
2435             baseContainer->insertEntity(idx2+1, bevel);
2436         }
2437     }
2438
2439     if (isPolyline) {
2440         ((Polyline*)baseContainer)->updateEndpoints();
2441     }
2442
2443     if (graphicView!=NULL) {
2444         if (isPolyline) {
2445             graphicView->drawEntity(baseContainer);
2446         } else {
2447             graphicView->drawEntity(bevel);
2448         }
2449     }
2450
2451     DEBUG->print("Modification::bevel: handling undo");
2452
2453     if (document!=NULL && handleUndo) {
2454         //document->startUndoCycle();
2455
2456         if (isPolyline==false && data.trim==true) {
2457             document->addUndoable(trimmed1);
2458             entity1->setUndoState(true);
2459             document->addUndoable(entity1);
2460
2461             document->addUndoable(trimmed2);
2462             entity2->setUndoState(true);
2463             document->addUndoable(entity2);
2464         }
2465
2466         if (isPolyline==false) {
2467             document->addUndoable(bevel);
2468         }
2469
2470         document->endUndoCycle();
2471     }
2472
2473     if (data.trim==false) {
2474         DEBUG->print("Modification::bevel: delete trimmed elements");
2475         delete trimmed1;
2476         delete trimmed2;
2477         DEBUG->print("Modification::bevel: delete trimmed elements: ok");
2478     }
2479
2480     return true;
2481
2482 }
2483
2484
2485
2486 /**
2487  * Rounds a corner.
2488  *
2489  * @param coord Mouse coordinate to specify the rounding.
2490  * @param entity1 First entity of the corner.
2491  * @param entity2 Second entity of the corner.
2492  * @param data Radius and trim flag.
2493  */
2494 bool Modification::round(const Vector& coord,
2495                             const Vector& coord1,
2496                             AtomicEntity* entity1,
2497                             const Vector& coord2,
2498                             AtomicEntity* entity2,
2499                             RoundData& data) {
2500
2501     if (entity1==NULL || entity2==NULL) {
2502         DEBUG->print(Debug::D_WARNING,
2503                         "Modification::round: At least one entity is NULL");
2504         return false;
2505     }
2506
2507     EntityContainer* baseContainer = container;
2508     bool isPolyline = false;
2509     bool isClosedPolyline = false;
2510
2511     if (document!=NULL && handleUndo) {
2512         document->startUndoCycle();
2513     }
2514
2515     // find out whether we're rounding within a polyline:
2516     if (entity1->getParent()!=NULL &&
2517                 entity1->getParent()->rtti()==RS2::EntityPolyline) {
2518
2519         if (entity1->getParent()!=entity2->getParent()) {
2520             DEBUG->print(Debug::D_WARNING,
2521                             "Modification::round: entities not in "
2522                                                         "the same polyline");
2523             if (document!=NULL && handleUndo) {
2524                 document->endUndoCycle();
2525             }
2526             return false;
2527         }
2528
2529         // clone polyline for undo
2530         if (document!=NULL && handleUndo) {
2531             EntityContainer* cl =
2532                 (EntityContainer*)entity1->getParent()->clone();
2533             container->addEntity(cl);
2534             document->addUndoable(cl);
2535
2536             document->addUndoable(entity1->getParent());
2537             entity1->getParent()->setUndoState(true);
2538
2539             baseContainer = cl;
2540         }
2541
2542         entity1 = (AtomicEntity*)baseContainer->entityAt(entity1->getParent()->findEntity(entity1));
2543         entity2 = (AtomicEntity*)baseContainer->entityAt(entity2->getParent()->findEntity(entity2));
2544
2545         isPolyline = true;
2546                 isClosedPolyline = ((Polyline*)entity1)->isClosed();
2547     }
2548
2549     // create 2 tmp parallels
2550     Creation creation(NULL, NULL);
2551     Entity* par1 = creation.createParallel(coord, data.radius, 1, entity1);
2552     Entity* par2 = creation.createParallel(coord, data.radius, 1, entity2);
2553
2554     VectorSolutions sol2 =
2555         Information::getIntersection(entity1, entity2, false);
2556
2557     VectorSolutions sol =
2558         Information::getIntersection(par1, par2, false);
2559
2560     if (sol.getNumber()==0) {
2561         if (document!=NULL && handleUndo) {
2562             document->endUndoCycle();
2563         }
2564         return false;
2565     }
2566
2567     // there might be two intersections: choose the closest:
2568     Vector is = sol.getClosest(coord);
2569     Vector p1 = entity1->getNearestPointOnEntity(is, false);
2570     Vector p2 = entity2->getNearestPointOnEntity(is, false);
2571     double ang1 = is.angleTo(p1);
2572     double ang2 = is.angleTo(p2);
2573     bool reversed = (Math::getAngleDifference(ang1, ang2)>M_PI);
2574
2575     Arc* arc = new Arc(baseContainer,
2576                              ArcData(is,
2577                                         data.radius,
2578                                         ang1, ang2,
2579                                         reversed));
2580
2581
2582     AtomicEntity* trimmed1 = NULL;
2583     AtomicEntity* trimmed2 = NULL;
2584
2585     if (data.trim || isPolyline) {
2586         if (isPolyline) {
2587             trimmed1 = entity1;
2588             trimmed2 = entity2;
2589         } else {
2590             trimmed1 = (AtomicEntity*)entity1->clone();
2591             trimmed2 = (AtomicEntity*)entity2->clone();
2592         }
2593
2594 #warning "!!! Old rendering path needs upgrading !!!"
2595 #if 0
2596                 // remove trim entity:
2597                 if (graphicView!=NULL)
2598                 {
2599                         if (isPolyline)
2600                         {
2601                                 graphicView->deleteEntity(baseContainer);
2602                         }
2603                         else
2604                         {
2605                                 graphicView->deleteEntity(entity1);
2606                                 graphicView->deleteEntity(entity2);
2607                         }
2608                 }
2609 #endif
2610
2611         // trim entities to intersection
2612         Vector is2 = sol2.getClosest(coord2);
2613         RS2::Ending ending1 = trimmed1->getTrimPoint(coord1, is2);
2614         switch (ending1) {
2615         case RS2::EndingStart:
2616             trimmed1->trimStartpoint(p1);
2617             break;
2618         case RS2::EndingEnd:
2619             trimmed1->trimEndpoint(p1);
2620             break;
2621         default:
2622             break;
2623         }
2624
2625         is2 = sol2.getClosest(coord1);
2626         RS2::Ending ending2 = trimmed2->getTrimPoint(coord2, is2);
2627         switch (ending2) {
2628         case RS2::EndingStart:
2629             trimmed2->trimStartpoint(p2);
2630             break;
2631         case RS2::EndingEnd:
2632             trimmed2->trimEndpoint(p2);
2633             break;
2634         default:
2635             break;
2636         }
2637
2638         // add new trimmed entities:
2639         if (isPolyline==false) {
2640             container->addEntity(trimmed1);
2641             container->addEntity(trimmed2);
2642         }
2643         if (graphicView!=NULL) {
2644             if (!isPolyline) {
2645                 graphicView->drawEntity(trimmed1);
2646                 graphicView->drawEntity(trimmed2);
2647             }
2648         }
2649     }
2650
2651     // add rounding:
2652     if (isPolyline==false) {
2653         baseContainer->addEntity(arc);
2654     } else {
2655         // find out which base entity is before the rounding:
2656         int idx1 = baseContainer->findEntity(trimmed1);
2657         int idx2 = baseContainer->findEntity(trimmed2);
2658
2659         arc->setSelected(baseContainer->isSelected());
2660         arc->setLayer(baseContainer->getLayer());
2661         arc->setPen(baseContainer->getPen());
2662
2663                 DEBUG->print("Modification::round: idx1<idx2: %d", (int)(idx1<idx2));
2664                 DEBUG->print("Modification::round: idx1!=0: %d", (int)(idx1!=0));
2665                 DEBUG->print("Modification::round: idx2==0: %d", (int)(idx2==0));
2666                 DEBUG->print("Modification::round: idx1==(int)baseContainer->count()-1: %d",
2667                         (int)(idx1==(int)baseContainer->count()-1));
2668
2669                 bool insertAfter1 = false;
2670                 if (!isClosedPolyline) {
2671                         insertAfter1 = (idx1<idx2);
2672                 }
2673                 else {
2674                         insertAfter1 = ((idx1<idx2 && idx1!=0) ||
2675                                 (idx2==0 && idx1==(int)baseContainer->count()-1));
2676                 }
2677
2678         // insert rounding at the right position:
2679         //if ((idx1<idx2 && idx1!=0) ||
2680                 //      (idx2==0 && idx1==(int)baseContainer->count()-1)) {
2681         //if (idx1<idx2) {
2682         if (insertAfter1) {
2683             if (trimmed1->getEndpoint().distanceTo(arc->getStartpoint())>1.0e-4) {
2684                 arc->reverse();
2685             }
2686             baseContainer->insertEntity(idx1+1, arc);
2687         } else {
2688             if (trimmed2->getEndpoint().distanceTo(arc->getStartpoint())>1.0e-4) {
2689                 arc->reverse();
2690             }
2691             baseContainer->insertEntity(idx2+1, arc);
2692         }
2693     }
2694
2695     if (isPolyline) {
2696         ((Polyline*)baseContainer)->updateEndpoints();
2697     }
2698
2699     if (graphicView!=NULL) {
2700         if (isPolyline) {
2701             graphicView->drawEntity(baseContainer);
2702         } else {
2703             graphicView->drawEntity(arc);
2704         }
2705     }
2706
2707     if (document!=NULL && handleUndo) {
2708         if (isPolyline==false && data.trim==true) {
2709             document->addUndoable(trimmed1);
2710             entity1->setUndoState(true);
2711             document->addUndoable(entity1);
2712
2713             document->addUndoable(trimmed2);
2714             entity2->setUndoState(true);
2715             document->addUndoable(entity2);
2716         }
2717
2718         if (isPolyline==false) {
2719             document->addUndoable(arc);
2720         }
2721
2722         document->endUndoCycle();
2723     }
2724
2725     delete par1;
2726     delete par2;
2727
2728     return true;
2729 }
2730
2731 /**
2732  * Removes the selected entity containers and adds the entities in them as
2733  * new single entities.
2734  */
2735 bool Modification::explode()
2736 {
2737     if (container == NULL)
2738         {
2739         DEBUG->print("Modification::explode: no valid container for addinge entities",
2740                         Debug::D_WARNING);
2741         return false;
2742     }
2743
2744 //      Q3PtrList<Entity> addList;
2745 //      addList.setAutoDelete(false);
2746         QList<Entity *> addList;
2747
2748     if (document!=NULL && handleUndo) {
2749         document->startUndoCycle();
2750     }
2751
2752     for (Entity* e=container->firstEntity();
2753             e!=NULL;
2754             e=container->nextEntity()) {
2755         //for (uint i=0; i<container->count(); ++i) {
2756         //Entity* e = container->entityAt(i);
2757
2758         if (e!=NULL && e->isSelected()) {
2759             if (e->isContainer()) {
2760
2761                 // add entities from container:
2762                 EntityContainer* ec = (EntityContainer*)e;
2763                 //ec->setSelected(false);
2764
2765                 // iterate and explode container:
2766                 //for (uint i2=0; i2<ec->count(); ++i2) {
2767                 //    Entity* e2 = ec->entityAt(i2);
2768                 RS2::ResolveLevel rl;
2769                 bool resolvePen;
2770                 bool resolveLayer;
2771
2772                 switch (ec->rtti()) {
2773                 case RS2::EntityText:
2774                 case RS2::EntityHatch:
2775                 case RS2::EntityPolyline:
2776                     rl = RS2::ResolveAll;
2777                     resolveLayer = true;
2778                     resolvePen = false;
2779                     break;
2780
2781                 case RS2::EntityInsert:
2782                     resolvePen = false;
2783                     resolveLayer = false;
2784                     rl = RS2::ResolveNone;
2785                     break;
2786
2787                 case RS2::EntityDimAligned:
2788                 case RS2::EntityDimLinear:
2789                 case RS2::EntityDimRadial:
2790                 case RS2::EntityDimDiametric:
2791                 case RS2::EntityDimAngular:
2792                 case RS2::EntityDimLeader:
2793                     rl = RS2::ResolveNone;
2794                     resolveLayer = true;
2795                     resolvePen = false;
2796                     break;
2797
2798                 default:
2799                     rl = RS2::ResolveAll;
2800                     resolveLayer = true;
2801                     resolvePen = false;
2802                     break;
2803                 }
2804
2805                 for (Entity* e2 = ec->firstEntity(rl); e2!=NULL;
2806                         e2 = ec->nextEntity(rl)) {
2807
2808                     if (e2!=NULL) {
2809                         Entity* clone = e2->clone();
2810                         clone->setSelected(false);
2811                         clone->reparent(container);
2812
2813                         if (resolveLayer) {
2814                             clone->setLayer(ec->getLayer());
2815                         } else {
2816                             clone->setLayer(e2->getLayer());
2817                         }
2818
2819                         clone->setPen(ec->getPen(resolvePen));
2820
2821                         addList.append(clone);
2822
2823                         clone->update();
2824                     }
2825                 }
2826             } else {
2827                 e->setSelected(false);
2828             }
2829         }
2830     }
2831
2832     deselectOriginals(true);
2833     addNewEntities(addList);
2834
2835     if (document!=NULL && handleUndo) {
2836         document->endUndoCycle();
2837     }
2838
2839     if (graphicView!=NULL) {
2840         graphicView->redraw();
2841     }
2842
2843     return true;
2844 }
2845
2846 bool Modification::explodeTextIntoLetters()
2847 {
2848     if (container == NULL)
2849         {
2850         DEBUG->print("Modification::explodeTextIntoLetters: no valid container"
2851                         " for addinge entities", Debug::D_WARNING);
2852         return false;
2853     }
2854
2855 //      Q3PtrList<Entity> addList;
2856 //      addList.setAutoDelete(false);
2857         QList<Entity *> addList;
2858
2859     if (document!=NULL && handleUndo) {
2860         document->startUndoCycle();
2861     }
2862
2863     for (Entity* e=container->firstEntity();
2864             e!=NULL;
2865             e=container->nextEntity()) {
2866         if (e!=NULL && e->isSelected()) {
2867             if (e->rtti()==RS2::EntityText) {
2868                 // add letters of text:
2869                 Text* text = (Text*)e;
2870                 explodeTextIntoLetters(text, addList);
2871             } else {
2872                 e->setSelected(false);
2873             }
2874         }
2875     }
2876
2877     deselectOriginals(true);
2878     addNewEntities(addList);
2879
2880     if (document!=NULL && handleUndo) {
2881         document->endUndoCycle();
2882     }
2883
2884     if (graphicView!=NULL) {
2885         graphicView->redraw();
2886     }
2887
2888     return true;
2889 }
2890
2891 //bool Modification::explodeTextIntoLetters(Text* text, Q3PtrList<Entity>& addList)
2892 bool Modification::explodeTextIntoLetters(Text * text, QList<Entity *> & addList)
2893 {
2894         if (text == NULL)
2895                 return false;
2896
2897         // iterate though lines:
2898         for(Entity * e2=text->firstEntity(); e2!=NULL; e2=text->nextEntity())
2899         {
2900                 if (e2 == NULL)
2901                         break;
2902
2903                 // text lines:
2904                 if (e2->rtti() == RS2::EntityContainer)
2905                 {
2906                         EntityContainer * line = (EntityContainer *)e2;
2907
2908                         // iterate though letters:
2909                         for(Entity * e3=line->firstEntity(); e3!=NULL; e3=line->nextEntity())
2910                         {
2911                                 if (e3 == NULL)
2912                                         break;
2913
2914                                 // super / sub texts:
2915                                 if (e3->rtti() == RS2::EntityText)
2916                                         explodeTextIntoLetters((Text *)e3, addList);
2917                                 // normal letters:
2918                                 else if (e3->rtti() == RS2::EntityInsert)
2919                                 {
2920                                         Insert * letter = (Insert *)e3;
2921
2922                                         Text * tl = new Text(container, TextData(letter->getInsertionPoint(),
2923                                                 text->getHeight(), 100.0, RS2::VAlignBottom, RS2::HAlignLeft,
2924                                                 RS2::LeftToRight, RS2::Exact, 1.0, letter->getName(), text->getStyle(),
2925                                                 letter->getAngle(), RS2::Update));
2926
2927                                         tl->setLayer(text->getLayer());
2928                                         tl->setPen(text->getPen());
2929
2930                                         addList.append(tl);
2931                                         tl->update();
2932                                 }
2933                         }
2934                 }
2935         }
2936
2937         return true;
2938 }
2939
2940 /**
2941  * Moves all reference points of selected entities with the given data.
2942  */
2943 bool Modification::moveRef(MoveRefData& data)
2944 {
2945         if (container == NULL)
2946         {
2947                 DEBUG->print("Modification::moveRef: no valid container", Debug::D_WARNING);
2948                 return false;
2949         }
2950
2951         //      Q3PtrList<Entity> addList;
2952         //      addList.setAutoDelete(false);
2953         QList<Entity *> addList;
2954
2955         if (document != NULL && handleUndo)
2956                 document->startUndoCycle();
2957
2958         // Create new entites
2959         for(Entity * e=container->firstEntity(); e!=NULL; e=container->nextEntity())
2960         {
2961                 if (e != NULL && e->isSelected())
2962                 {
2963                         Entity * ec = e->clone();
2964                         ec->moveRef(data.ref, data.offset);
2965                         // since 2.0.4.0: keep it selected
2966                         ec->setSelected(true);
2967                         addList.append(ec);
2968                 }
2969         }
2970
2971         deselectOriginals(true);
2972         addNewEntities(addList);
2973
2974         if (document != NULL && handleUndo)
2975                 document->endUndoCycle();
2976
2977         if (graphicView != NULL)
2978                 graphicView->redraw();
2979
2980         return true;
2981 }