File indexing completed on 2024-05-12 15:56:39
0001 /* This file is part of the KDE project 0002 * 0003 * SPDX-FileCopyrightText: 2006-2007, 2009 Thomas Zander <zander@kde.org> 0004 * SPDX-FileCopyrightText: 2006 Thorsten Zachmann <zachmann@kde.org> 0005 * SPDX-FileCopyrightText: 2007-2010 Boudewijn Rempt <boud@valdyas.org> 0006 * 0007 * SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "KoCanvasControllerWidgetViewport_p.h" 0011 0012 #include <limits.h> 0013 #include <stdlib.h> 0014 0015 #include <QPainter> 0016 #include <QDragEnterEvent> 0017 #include <QMimeData> 0018 0019 #include <KoProperties.h> 0020 0021 #include <FlakeDebug.h> 0022 0023 #include "KoShape.h" 0024 #include "KoShape_p.h" 0025 #include "KoShapeFactoryBase.h" // for the SHAPE mimetypes 0026 #include "KoShapeRegistry.h" 0027 #include "KoShapeController.h" 0028 #include "KoShapeManager.h" 0029 #include "KoSelection.h" 0030 #include "KoCanvasBase.h" 0031 #include "KoShapeLayer.h" 0032 #include "KoToolProxy.h" 0033 #include "KoCanvasControllerWidget.h" 0034 #include "KoViewConverter.h" 0035 #include "KoSvgPaste.h" 0036 0037 // ********** Viewport ********** 0038 Viewport::Viewport(KoCanvasControllerWidget *parent) 0039 : QWidget(parent) 0040 , m_draggedShape(0) 0041 , m_canvas(0) 0042 , m_documentOffset(QPoint(0, 0)) 0043 , m_margin(0) 0044 { 0045 setAutoFillBackground(true); 0046 setAcceptDrops(true); 0047 setMouseTracking(true); 0048 m_parent = parent; 0049 } 0050 0051 void Viewport::setCanvas(QWidget *canvas) 0052 { 0053 if (m_canvas) { 0054 m_canvas->hide(); 0055 delete m_canvas; 0056 } 0057 m_canvas = canvas; 0058 if (!canvas) return; 0059 m_canvas->setParent(this); 0060 m_canvas->show(); 0061 if (!m_canvas->minimumSize().isNull()) { 0062 m_documentSize = m_canvas->minimumSize(); 0063 } 0064 resetLayout(); 0065 } 0066 0067 void Viewport::setDocumentSize(const QSizeF &size) 0068 { 0069 m_documentSize = size; 0070 resetLayout(); 0071 } 0072 0073 void Viewport::documentOffsetMoved(const QPoint &pt) 0074 { 0075 m_documentOffset = pt; 0076 resetLayout(); 0077 } 0078 0079 void Viewport::handleDragEnterEvent(QDragEnterEvent *event) 0080 { 0081 // if not a canvas set then ignore this, makes it possible to assume 0082 // we have a canvas in all the support methods. 0083 if (!(m_parent->canvas() && m_parent->canvas()->canvasWidget())) { 0084 event->ignore(); 0085 return; 0086 } 0087 0088 delete m_draggedShape; 0089 m_draggedShape = 0; 0090 0091 // only allow dropping when active layer is editable 0092 KoSelection *selection = m_parent->canvas()->shapeManager()->selection(); 0093 KoShapeLayer *activeLayer = selection->activeLayer(); 0094 if (activeLayer && (!activeLayer->isShapeEditable() || activeLayer->isGeometryProtected())) { 0095 event->ignore(); 0096 return; 0097 } 0098 0099 const QMimeData *data = event->mimeData(); 0100 0101 if (data->hasFormat(SHAPETEMPLATE_MIMETYPE) || 0102 data->hasFormat(SHAPEID_MIMETYPE) || 0103 data->hasFormat("image/svg+xml")) 0104 { 0105 if (data->hasFormat("image/svg+xml")) { 0106 KoCanvasBase *canvas = m_parent->canvas(); 0107 QSizeF fragmentSize; 0108 0109 QList<KoShape*> shapes = KoSvgPaste::fetchShapesFromData(data->data("image/svg+xml"), 0110 canvas->shapeController()->documentRectInPixels(), 0111 canvas->shapeController()->pixelsPerInch(), 0112 &fragmentSize); 0113 0114 if (!shapes.isEmpty()) { 0115 m_draggedShape = shapes[0]; 0116 } 0117 } 0118 else { 0119 QByteArray itemData; 0120 bool isTemplate = true; 0121 0122 if (data->hasFormat(SHAPETEMPLATE_MIMETYPE)) { 0123 itemData = data->data(SHAPETEMPLATE_MIMETYPE); 0124 } 0125 else if (data->hasFormat(SHAPEID_MIMETYPE)) { 0126 isTemplate = false; 0127 itemData = data->data(SHAPEID_MIMETYPE); 0128 } 0129 0130 0131 QDataStream dataStream(&itemData, QIODevice::ReadOnly); 0132 QString id; 0133 dataStream >> id; 0134 QString properties; 0135 if (isTemplate) 0136 dataStream >> properties; 0137 0138 // and finally, there is a point. 0139 QPointF offset; 0140 dataStream >> offset; 0141 0142 // The rest of this method is mostly a copy paste from the KoCreateShapeStrategy 0143 // So, lets remove this again when Zagge adds his new class that does this kind of thing. (KoLoadSave) 0144 KoShapeFactoryBase *factory = KoShapeRegistry::instance()->value(id); 0145 if (! factory) { 0146 warnFlake << "Application requested a shape that is not registered '" << 0147 id << "', Ignoring"; 0148 event->ignore(); 0149 return; 0150 } 0151 if (isTemplate) { 0152 KoProperties props; 0153 props.load(properties); 0154 m_draggedShape = factory->createShape(&props, m_parent->canvas()->shapeController()->resourceManager()); 0155 } 0156 else { 0157 m_draggedShape = factory->createDefaultShape(m_parent->canvas()->shapeController()->resourceManager()); 0158 } 0159 0160 if (m_draggedShape->shapeId().isEmpty()) { 0161 m_draggedShape->setShapeId(factory->id()); 0162 } 0163 } 0164 0165 event->setDropAction(Qt::CopyAction); 0166 event->accept(); 0167 0168 Q_ASSERT(m_draggedShape); 0169 if (!m_draggedShape) return; 0170 0171 // calculate maximum existing shape zIndex 0172 0173 int pasteZIndex = 0; 0174 0175 { 0176 QList<KoShape*> allShapes = m_parent->canvas()->shapeManager()->topLevelShapes(); 0177 0178 if (!allShapes.isEmpty()) { 0179 std::sort(allShapes.begin(), allShapes.end(), KoShape::compareShapeZIndex); 0180 pasteZIndex = qMin(int(KoShape::maxZIndex), allShapes.last()->zIndex() + 1); 0181 } 0182 } 0183 0184 m_draggedShape->setZIndex(pasteZIndex); 0185 m_draggedShape->setAbsolutePosition(correctPosition(event->pos())); 0186 0187 m_parent->canvas()->shapeManager()->addShape(m_draggedShape); 0188 } else { 0189 event->ignore(); 0190 } 0191 } 0192 0193 void Viewport::handleDropEvent(QDropEvent *event) 0194 { 0195 if (!m_draggedShape) { 0196 m_parent->canvas()->toolProxy()->dropEvent(event, correctPosition(event->pos())); 0197 return; 0198 } 0199 0200 repaint(m_draggedShape); 0201 m_parent->canvas()->shapeManager()->remove(m_draggedShape); // remove it to not interfere with z-index calc. 0202 0203 m_draggedShape->setPosition(QPointF(0, 0)); // always save position. 0204 QPointF newPos = correctPosition(event->pos()); 0205 m_parent->canvas()->clipToDocument(m_draggedShape, newPos); // ensure the shape is dropped inside the document. 0206 m_draggedShape->setAbsolutePosition(newPos); 0207 0208 0209 KUndo2Command * cmd = m_parent->canvas()->shapeController()->addShape(m_draggedShape, 0); 0210 0211 if (cmd) { 0212 m_parent->canvas()->addCommand(cmd); 0213 KoSelection *selection = m_parent->canvas()->shapeManager()->selection(); 0214 0215 // repaint selection before selecting newly create shape 0216 Q_FOREACH (KoShape * shape, selection->selectedShapes()) { 0217 shape->update(); 0218 } 0219 0220 selection->deselectAll(); 0221 selection->select(m_draggedShape); 0222 } else { 0223 0224 delete m_draggedShape; 0225 } 0226 0227 m_draggedShape = 0; 0228 } 0229 0230 QPointF Viewport::correctPosition(const QPoint &point) const 0231 { 0232 QWidget *canvasWidget = m_parent->canvas()->canvasWidget(); 0233 Q_ASSERT(canvasWidget); // since we should not allow drag if there is not. 0234 QPoint correctedPos(point.x() - canvasWidget->x(), point.y() - canvasWidget->y()); 0235 correctedPos += m_documentOffset; 0236 return m_parent->canvas()->viewToDocument(correctedPos); 0237 } 0238 0239 void Viewport::handleDragMoveEvent(QDragMoveEvent *event) 0240 { 0241 if (!m_draggedShape) { 0242 m_parent->canvas()->toolProxy()->dragMoveEvent(event, correctPosition(event->pos())); 0243 return; 0244 } 0245 0246 m_draggedShape->update(); 0247 repaint(m_draggedShape); 0248 m_draggedShape->setAbsolutePosition(correctPosition(event->pos())); 0249 m_draggedShape->update(); 0250 repaint(m_draggedShape); 0251 } 0252 0253 void Viewport::repaint(KoShape *shape) 0254 { 0255 QRect rect = m_parent->canvas()->viewConverter()->documentToView(shape->boundingRect()).toRect(); 0256 QWidget *canvasWidget = m_parent->canvas()->canvasWidget(); 0257 Q_ASSERT(canvasWidget); // since we should not allow drag if there is not. 0258 rect.moveLeft(rect.left() + canvasWidget->x() - m_documentOffset.x()); 0259 rect.moveTop(rect.top() + canvasWidget->y() - m_documentOffset.y()); 0260 rect.adjust(-2, -2, 2, 2); // adjust for antialias 0261 update(rect); 0262 } 0263 0264 void Viewport::handleDragLeaveEvent(QDragLeaveEvent *event) 0265 { 0266 if (m_draggedShape) { 0267 repaint(m_draggedShape); 0268 m_parent->canvas()->shapeManager()->remove(m_draggedShape); 0269 delete m_draggedShape; 0270 m_draggedShape = 0; 0271 } else { 0272 m_parent->canvas()->toolProxy()->dragLeaveEvent(event); 0273 } 0274 } 0275 0276 void Viewport::handlePaintEvent(QPainter &painter, QPaintEvent *event) 0277 { 0278 Q_UNUSED(event); 0279 if (m_draggedShape) { 0280 const KoViewConverter *vc = m_parent->canvas()->viewConverter(); 0281 0282 painter.save(); 0283 QWidget *canvasWidget = m_parent->canvas()->canvasWidget(); 0284 Q_ASSERT(canvasWidget); // since we should not allow drag if there is not. 0285 painter.translate(canvasWidget->x() - m_documentOffset.x(), 0286 canvasWidget->y() - m_documentOffset.y()); 0287 QPointF offset = vc->documentToView(m_draggedShape->position()); 0288 painter.setOpacity(0.6); 0289 painter.translate(offset.x(), offset.y()); 0290 painter.setRenderHint(QPainter::Antialiasing); 0291 painter.setTransform(vc->documentToView()); 0292 m_draggedShape->paint(painter); 0293 painter.restore(); 0294 } 0295 } 0296 0297 void Viewport::resetLayout() 0298 { 0299 // Determine the area we have to show 0300 QRect viewRect(m_documentOffset, size()); 0301 0302 const int viewH = viewRect.height(); 0303 const int viewW = viewRect.width(); 0304 0305 if (m_canvas) { 0306 QRect geom = QRect(0, 0, viewW, viewH); 0307 if (m_canvas->geometry() != geom) { 0308 m_canvas->setGeometry(geom); 0309 m_canvas->update(); 0310 } 0311 } 0312 emit sizeChanged(); 0313 #if 0 0314 debugFlake <<"View port geom:" << geometry(); 0315 if (m_canvas) 0316 debugFlake <<"Canvas widget geom:" << m_canvas->geometry(); 0317 #endif 0318 }