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