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