1 /* This file is part of the KDE project
2    Copyright (C) 2007 Thorsten Zachmann <zachmann@kde.org>
3    Copyright (C) 2009 Thomas Zander <zander@kde.org>
4    Copyright (C) 2010-2011 Jan Hambrecht <jaham@gmx.net>
5 
6    This library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public
8    License as published by the Free Software Foundation; either
9    version 2 of the License, or (at your option) any later version.
10 
11    This library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15 
16    You should have received a copy of the GNU Library General Public License
17    along with this library; see the file COPYING.LIB.  If not, write to
18    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20 */
21 
22 #include "KoShapePaste.h"
23 
24 #include <FlakeDebug.h>
25 #include <klocalizedstring.h>
26 
27 #include <KoOdfLoadingContext.h>
28 #include <KoOdfReadStore.h>
29 
30 #include "KoCanvasBase.h"
31 #include "KoShapeController.h"
32 #include "KoShape.h"
33 #include "KoShapeLayer.h"
34 #include "KoShapeLoadingContext.h"
35 #include "KoShapeManager.h"
36 #include "KoShapeBasedDocumentBase.h"
37 #include "KoShapeRegistry.h"
38 #include "KoCanvasController.h"
39 #include "KoDocumentResourceManager.h"
40 #include "commands/KoShapeCreateCommand.h"
41 
42 class Q_DECL_HIDDEN KoShapePaste::Private
43 {
44 public:
Private(KoCanvasBase * cb,KoShapeLayer * l)45     Private(KoCanvasBase *cb, KoShapeLayer *l) : canvas(cb), layer(l) {}
46 
47     KoCanvasBase *canvas;
48     KoShapeLayer *layer;
49     QList<KoShape*> pastedShapes;
50 };
51 
KoShapePaste(KoCanvasBase * canvas,KoShapeLayer * layer)52 KoShapePaste::KoShapePaste(KoCanvasBase *canvas, KoShapeLayer *layer)
53         : d(new Private(canvas, layer))
54 {
55 }
56 
~KoShapePaste()57 KoShapePaste::~KoShapePaste()
58 {
59     delete d;
60 }
61 
process(const KoXmlElement & body,KoOdfReadStore & odfStore)62 bool KoShapePaste::process(const KoXmlElement & body, KoOdfReadStore & odfStore)
63 {
64     d->pastedShapes.clear();
65     KoOdfLoadingContext loadingContext(odfStore.styles(), odfStore.store());
66     KoShapeLoadingContext context(loadingContext, d->canvas->shapeController()->resourceManager());
67 
68     QList<KoShape*> shapes(d->layer ? d->layer->shapes(): d->canvas->shapeManager()->topLevelShapes());
69 
70     int zIndex = 0;
71     if (!shapes.isEmpty()) {
72         zIndex = shapes.first()->zIndex();
73         foreach (KoShape * shape, shapes) {
74             zIndex = qMax(zIndex, shape->zIndex());
75         }
76         ++zIndex;
77     }
78     context.setZIndex(zIndex);
79 
80     KoDocumentResourceManager *rm = d->canvas->shapeController()->resourceManager();
81     Q_ASSERT(rm);
82 
83     QPointF pasteOffset(rm->pasteOffset(), rm->pasteOffset());
84     const bool pasteAtCursor = rm->pasteAtCursor();
85 
86     // get hold of the canvas' shape manager
87     KoShapeManager *sm = d->canvas->shapeManager();
88     Q_ASSERT(sm);
89 
90     // TODO if this is a text create a text shape and load the text inside the new shape.
91     // create the shape from the clipboard
92     KoXmlElement element;
93     forEachElement(element, body) {
94         debugFlake << "loading shape" << element.localName();
95 
96         KoShape * shape = KoShapeRegistry::instance()->createShapeFromOdf(element, context);
97         if (shape) {
98             d->pastedShapes << shape;
99         }
100     }
101 
102     if (d->pastedShapes.isEmpty())
103         return true;
104 
105     // position shapes
106     if (pasteAtCursor) {
107         QRectF bbox;
108         // determine bounding rect of all pasted shapes
109         foreach (KoShape *shape, d->pastedShapes) {
110             if (bbox.isEmpty())
111                 bbox = shape->boundingRect();
112             else
113                 bbox |= shape->boundingRect();
114         }
115         // where is the cursor now?
116         QWidget *canvasWidget = d->canvas->canvasWidget();
117         KoCanvasController *cc = d->canvas->canvasController();
118         // map mouse screen position to the canvas widget coordinates
119         QPointF mouseCanvasPos = canvasWidget->mapFromGlobal(QCursor::pos());
120         // apply the canvas offset
121         mouseCanvasPos -= QPoint(cc->canvasOffsetX(), cc->canvasOffsetY());
122         // apply offset of document origin
123         mouseCanvasPos -= d->canvas->documentOrigin();
124         // convert to document coordinates
125         QPointF mouseDocumentPos = d->canvas->viewConverter()->viewToDocument(mouseCanvasPos);
126         // now we can determine the offset to apply, with the center of the pasted shapes
127         // bounding rect at the current mouse position
128         QPointF pasteOffset = mouseDocumentPos - bbox.center();
129         foreach (KoShape *shape, d->pastedShapes) {
130             QPointF move(pasteOffset);
131             d->canvas->clipToDocument(shape, move);
132             if (move.x() != 0 || move.y() != 0) {
133                 shape->setPosition(shape->position() + move);
134             }
135         }
136     } else {
137         foreach (KoShape *shape, d->pastedShapes) {
138             bool done = true;
139             do {
140                 // find a nice place for our shape.
141                 done = true;
142                 foreach (const KoShape *s, sm->shapesAt(shape->boundingRect()) + d->pastedShapes) {
143                     if (d->layer && s->parent() != d->layer)
144                         continue;
145                     if (s->name() != shape->name())
146                         continue;
147                     if (qAbs(s->position().x() - shape->position().x()) > 0.001)
148                         continue;
149                     if (qAbs(s->position().y() - shape->position().y()) > 0.001)
150                         continue;
151                     if (qAbs(s->size().width() - shape->size().width()) > 0.001)
152                         continue;
153                     if (qAbs(s->size().height() - shape->size().height()) > 0.001)
154                         continue;
155                     // move it and redo our iteration.
156                     QPointF move(pasteOffset);
157                     d->canvas->clipToDocument(shape, move);
158                     if (move.x() != 0 || move.y() != 0) {
159                         shape->setPosition(shape->position() + move);
160                         done = false;
161                         break;
162                     }
163                 }
164             } while (!done);
165         }
166     }
167 
168     KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Paste Shapes"));
169     if (!cmd) {
170         qDeleteAll(d->pastedShapes);
171         d->pastedShapes.clear();
172         return false;
173     }
174 
175     // add shapes to the document
176     foreach (KoShape *shape, d->pastedShapes) {
177         if (!shape->parent()) {
178             shape->setParent(d->layer);
179         }
180         d->canvas->shapeController()->addShapeDirect(shape, cmd);
181     }
182 
183     d->canvas->addCommand(cmd);
184 
185     return true;
186 }
187 
pastedShapes() const188 QList<KoShape*> KoShapePaste::pastedShapes() const
189 {
190     return d->pastedShapes;
191 }
192