1 /* This file is part of the KDE project
2 *
3 * Copyright (C) 2006-2007, 2009 Thomas Zander <zander@kde.org>
4 * Copyright (C) 2006 Thorsten Zachmann <zachmann@kde.org>
5 * Copyright (C) 2007-2010 Boudewijn Rempt <boud@valdyas.org>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23 #include "KoCanvasControllerWidgetViewport_p.h"
24 #include "KoShape.h"
25 #include "KoShape_p.h"
26 #include "KoShapeFactoryBase.h" // for the SHAPE mimetypes
27 #include "KoShapeRegistry.h"
28 #include "KoShapeController.h"
29 #include "KoShapeManager.h"
30 #include "KoSelection.h"
31 #include "KoCanvasBase.h"
32 #include "KoShapeLayer.h"
33 #include "KoShapePaste.h"
34 #include "KoShapePaintingContext.h"
35 #include "KoToolProxy.h"
36
37 #include <KoProperties.h>
38
39 #include <FlakeDebug.h>
40
41 #include <QPainter>
42 #include <QDragEnterEvent>
43
44 #include <limits.h>
45 #include <stdlib.h>
46
47 // ********** Viewport **********
Viewport(KoCanvasControllerWidget * parent)48 Viewport::Viewport(KoCanvasControllerWidget *parent)
49 : QWidget(parent)
50 , m_draggedShape(0)
51 , m_drawShadow(false)
52 , m_canvas(0)
53 , m_documentOffset(QPoint(0, 0))
54 , m_margin(0)
55 {
56 setAutoFillBackground(true);
57 setAcceptDrops(true);
58 setMouseTracking(true);
59 m_parent = parent;
60 }
61
setCanvas(QWidget * canvas)62 void Viewport::setCanvas(QWidget *canvas)
63 {
64 if (m_canvas) {
65 m_canvas->hide();
66 delete m_canvas;
67 }
68 m_canvas = canvas;
69 if (!canvas) return;
70 m_canvas->setParent(this);
71 m_canvas->show();
72 if (!m_canvas->minimumSize().isNull()) {
73 m_documentSize = m_canvas->minimumSize();
74 }
75 resetLayout();
76 }
77
setDocumentSize(const QSize & size)78 void Viewport::setDocumentSize(const QSize &size)
79 {
80 m_documentSize = size;
81 resetLayout();
82 }
83
documentOffsetMoved(const QPoint & pt)84 void Viewport::documentOffsetMoved(const QPoint &pt)
85 {
86 m_documentOffset = pt;
87 resetLayout();
88 }
89
setDrawShadow(bool drawShadow)90 void Viewport::setDrawShadow(bool drawShadow)
91 {
92 m_drawShadow = drawShadow;
93 }
94
95
handleDragEnterEvent(QDragEnterEvent * event)96 void Viewport::handleDragEnterEvent(QDragEnterEvent *event)
97 {
98 // if not a canvas set then ignore this, makes it possible to assume
99 // we have a canvas in all the support methods.
100 if (!(m_parent->canvas() && m_parent->canvas()->canvasWidget()))
101 return;
102
103 // only allow dropping when active layer is editable
104 KoSelection *selection = m_parent->canvas()->shapeManager()->selection();
105 KoShapeLayer *activeLayer = selection->activeLayer();
106 if (activeLayer && (!activeLayer->isEditable() || activeLayer->isGeometryProtected()))
107 return;
108
109 const QMimeData *data = event->mimeData();
110 if (data->hasFormat(SHAPETEMPLATE_MIMETYPE) ||
111 data->hasFormat(SHAPEID_MIMETYPE)) {
112 QByteArray itemData;
113 bool isTemplate = true;
114 if (data->hasFormat(SHAPETEMPLATE_MIMETYPE))
115 itemData = data->data(SHAPETEMPLATE_MIMETYPE);
116 else {
117 isTemplate = false;
118 itemData = data->data(SHAPEID_MIMETYPE);
119 }
120 QDataStream dataStream(&itemData, QIODevice::ReadOnly);
121 QString id;
122 dataStream >> id;
123 QString properties;
124 if (isTemplate)
125 dataStream >> properties;
126
127 // and finally, there is a point.
128 QPointF offset;
129 dataStream >> offset;
130
131 // The rest of this method is mostly a copy paste from the KoCreateShapeStrategy
132 // So, lets remove this again when Zagge adds his new class that does this kind of thing. (KoLoadSave)
133 KoShapeFactoryBase *factory = KoShapeRegistry::instance()->value(id);
134 if (! factory) {
135 warnFlake << "Application requested a shape that is not registered '" <<
136 id << "', Ignoring";
137 event->ignore();
138 return;
139 }
140 event->setDropAction(Qt::CopyAction);
141 event->accept();
142
143 if (isTemplate) {
144 KoProperties props;
145 props.load(properties);
146 m_draggedShape = factory->createShape(&props, m_parent->canvas()->shapeController()->resourceManager());
147 } else
148 m_draggedShape = factory->createDefaultShape(m_parent->canvas()->shapeController()->resourceManager());
149
150 Q_ASSERT(m_draggedShape);
151 if (!m_draggedShape) return;
152
153 if (m_draggedShape->shapeId().isEmpty())
154 m_draggedShape->setShapeId(factory->id());
155 m_draggedShape->setZIndex(KoShapePrivate::MaxZIndex);
156 m_draggedShape->setAbsolutePosition(correctPosition(event->pos()));
157
158 m_parent->canvas()->shapeManager()->addShape(m_draggedShape);
159 }
160 else if (data->hasFormat(KoOdf::mimeType(KoOdf::Text))) {
161 KoShapeManager *sm = m_parent->canvas()->shapeManager();
162 KoShapePaste paste(m_parent->canvas(), sm->selection()->activeLayer());
163 if (paste.paste(KoOdf::Text, data)) {
164 QList<KoShape *> shapes = paste.pastedShapes();
165 if (shapes.count() == 1) {
166 m_draggedShape = shapes.first();
167 m_draggedShape->setZIndex(KoShapePrivate::MaxZIndex);
168 event->setDropAction(Qt::CopyAction);
169 }
170 event->accept();
171 }
172 } else {
173 event->ignore();
174 }
175 }
176
handleDropEvent(QDropEvent * event)177 void Viewport::handleDropEvent(QDropEvent *event)
178 {
179 if (!m_draggedShape) {
180 m_parent->canvas()->toolProxy()->dropEvent(event, correctPosition(event->pos()));
181 return;
182 }
183
184 repaint(m_draggedShape);
185 m_parent->canvas()->shapeManager()->remove(m_draggedShape); // remove it to not interfere with z-index calc.
186
187 m_draggedShape->setPosition(QPointF(0, 0)); // always save position.
188 QPointF newPos = correctPosition(event->pos());
189 m_parent->canvas()->clipToDocument(m_draggedShape, newPos); // ensure the shape is dropped inside the document.
190 m_draggedShape->setAbsolutePosition(newPos);
191 KUndo2Command * cmd = m_parent->canvas()->shapeController()->addShape(m_draggedShape);
192 if (cmd) {
193 m_parent->canvas()->addCommand(cmd);
194 KoSelection *selection = m_parent->canvas()->shapeManager()->selection();
195
196 // repaint selection before selecting newly create shape
197 foreach(KoShape * shape, selection->selectedShapes())
198 shape->update();
199
200 selection->deselectAll();
201 selection->select(m_draggedShape);
202 } else
203 delete m_draggedShape;
204
205 m_draggedShape = 0;
206 }
207
correctPosition(const QPoint & point) const208 QPointF Viewport::correctPosition(const QPoint &point) const
209 {
210 QWidget *canvasWidget = m_parent->canvas()->canvasWidget();
211 Q_ASSERT(canvasWidget); // since we should not allow drag if there is not.
212 QPoint correctedPos(point.x() - canvasWidget->x(), point.y() - canvasWidget->y());
213 correctedPos += m_documentOffset;
214 return m_parent->canvas()->viewToDocument(correctedPos);
215 }
216
handleDragMoveEvent(QDragMoveEvent * event)217 void Viewport::handleDragMoveEvent(QDragMoveEvent *event)
218 {
219 if (!m_draggedShape) {
220 m_parent->canvas()->toolProxy()->dragMoveEvent(event, correctPosition(event->pos()));
221 return;
222 }
223
224 m_draggedShape->update();
225 repaint(m_draggedShape);
226 m_draggedShape->setAbsolutePosition(correctPosition(event->pos()));
227 m_draggedShape->update();
228 repaint(m_draggedShape);
229 }
230
repaint(KoShape * shape)231 void Viewport::repaint(KoShape *shape)
232 {
233 QRect rect = m_parent->canvas()->viewConverter()->documentToView(shape->boundingRect()).toRect();
234 QWidget *canvasWidget = m_parent->canvas()->canvasWidget();
235 Q_ASSERT(canvasWidget); // since we should not allow drag if there is not.
236 rect.moveLeft(rect.left() + canvasWidget->x() - m_documentOffset.x());
237 rect.moveTop(rect.top() + canvasWidget->y() - m_documentOffset.y());
238 rect.adjust(-2, -2, 2, 2); // adjust for antialias
239 update(rect);
240 }
241
handleDragLeaveEvent(QDragLeaveEvent * event)242 void Viewport::handleDragLeaveEvent(QDragLeaveEvent *event)
243 {
244 if (m_draggedShape) {
245 repaint(m_draggedShape);
246 m_parent->canvas()->shapeManager()->remove(m_draggedShape);
247 delete m_draggedShape;
248 m_draggedShape = 0;
249 } else {
250 m_parent->canvas()->toolProxy()->dragLeaveEvent(event);
251 }
252 }
253
handlePaintEvent(QPainter & painter,QPaintEvent * event)254 void Viewport::handlePaintEvent(QPainter &painter, QPaintEvent *event)
255 {
256 Q_UNUSED(event);
257 // Draw the shadow around the canvas.
258 if (m_parent->canvas() && m_parent->canvas()->canvasWidget() && m_drawShadow) {
259 QWidget *canvas = m_parent->canvas()->canvasWidget();
260 painter.setPen(QPen(Qt::black, 0));
261 QRect rect(canvas->x(), canvas->y(), canvas->width(), canvas->height());
262 rect.adjust(-1, -1, 0, 0);
263 painter.drawRect(rect);
264 painter.drawLine(rect.right() + 2, rect.top() + 2, rect.right() + 2, rect.bottom() + 2);
265 painter.drawLine(rect.left() + 2, rect.bottom() + 2, rect.right() + 2, rect.bottom() + 2);
266 }
267 if (m_draggedShape) {
268 const KoViewConverter *vc = m_parent->canvas()->viewConverter();
269
270 painter.save();
271 QWidget *canvasWidget = m_parent->canvas()->canvasWidget();
272 Q_ASSERT(canvasWidget); // since we should not allow drag if there is not.
273 painter.translate(canvasWidget->x() - m_documentOffset.x(),
274 canvasWidget->y() - m_documentOffset.y());
275 QPointF offset = vc->documentToView(m_draggedShape->position());
276 painter.setOpacity(0.6);
277 painter.translate(offset.x(), offset.y());
278 painter.setRenderHint(QPainter::Antialiasing);
279 KoShapePaintingContext paintContext; //FIXME
280 m_draggedShape->paint(painter, *vc, paintContext);
281 painter.restore();
282 }
283 }
284
resetLayout()285 void Viewport::resetLayout()
286 {
287 // Determine the area we have to show
288 QRect viewRect(m_documentOffset, size());
289
290 const int viewH = viewRect.height();
291 const int viewW = viewRect.width();
292
293 const int docH = m_documentSize.height();
294 const int docW = m_documentSize.width();
295
296 int moveX = 0;
297 int moveY = 0;
298
299 int resizeW = viewW;
300 int resizeH = viewH;
301
302 // debugFlake <<"viewH:" << viewH << endl
303 // << "docH: " << docH << endl
304 // << "viewW: " << viewW << endl
305 // << "docW: " << docW << endl;
306
307 if (viewH == docH && viewW == docW) {
308 // Do nothing
309 resizeW = docW;
310 resizeH = docH;
311 } else if (viewH > docH && viewW > docW) {
312 // Show entire canvas centered
313 moveX = (viewW - docW) / 2;
314 moveY = (viewH - docH) / 2;
315 resizeW = docW;
316 resizeH = docH;
317 } else if (viewW > docW) {
318 // Center canvas horizontally
319 moveX = (viewW - docW) / 2;
320 resizeW = docW;
321
322 int marginTop = m_margin - m_documentOffset.y();
323 int marginBottom = viewH - (m_documentSize.height() - m_documentOffset.y());
324
325 if (marginTop > 0) moveY = marginTop;
326 if (marginTop > 0) resizeH = viewH - marginTop;
327 if (marginBottom > 0) resizeH = viewH - marginBottom;
328 } else if (viewH > docH) {
329 // Center canvas vertically
330 moveY = (viewH - docH) / 2;
331 resizeH = docH;
332
333 int marginLeft = m_margin - m_documentOffset.x();
334 int marginRight = viewW - (m_documentSize.width() - m_documentOffset.x());
335
336 if (marginLeft > 0) moveX = marginLeft;
337 if (marginLeft > 0) resizeW = viewW - marginLeft;
338 if (marginRight > 0) resizeW = viewW - marginRight;
339 } else {
340 // Take care of the margin around the canvas
341 int marginTop = m_margin - m_documentOffset.y();
342 int marginLeft = m_margin - m_documentOffset.x();
343 int marginRight = viewW - (m_documentSize.width() - m_documentOffset.x());
344 int marginBottom = viewH - (m_documentSize.height() - m_documentOffset.y());
345
346 if (marginTop > 0) moveY = marginTop;
347 if (marginLeft > 0) moveX = marginLeft;
348
349 if (marginTop > 0) resizeH = viewH - marginTop;
350 if (marginLeft > 0) resizeW = viewW - marginLeft;
351 if (marginRight > 0) resizeW = viewW - marginRight;
352 if (marginBottom > 0) resizeH = viewH - marginBottom;
353 }
354 if (m_parent->canvasMode() == KoCanvasController::AlignTop) {
355 // have up to m_margin pixels at top.
356 moveY = qMin(m_margin, moveY);
357 }
358 if (m_canvas) {
359 QRect geom;
360 if (m_parent->canvasMode() == KoCanvasController::Infinite)
361 geom = QRect(0, 0, viewW, viewH);
362 else
363 geom = QRect(moveX, moveY, resizeW, resizeH);
364 if (m_canvas->geometry() != geom) {
365 m_canvas->setGeometry(geom);
366 m_canvas->update();
367 }
368 }
369 if (m_drawShadow) {
370 update();
371 }
372
373 emit sizeChanged();
374 #if 0
375 debugFlake <<"View port geom:" << geometry();
376 if (m_canvas)
377 debugFlake <<"Canvas widget geom:" << m_canvas->geometry();
378 #endif
379 }
380