Warning, file /office/calligra/libs/flake/KoCanvasControllerWidgetViewport_p.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the KDE project
0002  *
0003  * Copyright (C) 2006-2007, 2009 Thomas Zander <zander@kde.org>
0004  * Copyright (C) 2006 Thorsten Zachmann <zachmann@kde.org>
0005  * Copyright (C) 2007-2010 Boudewijn Rempt <boud@valdyas.org>
0006  *
0007  * This library is free software; you can redistribute it and/or
0008  * modify it under the terms of the GNU Library General Public
0009  * License as published by the Free Software Foundation; either
0010  * version 2 of the License, or (at your option) any later version.
0011  *
0012  * This library is distributed in the hope that it will be useful,
0013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015  * Library General Public License for more details.
0016  *
0017  * You should have received a copy of the GNU Library General Public License
0018  * along with this library; see the file COPYING.LIB.  If not, write to
0019  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0020  * Boston, MA 02110-1301, USA.
0021  */
0022 
0023 #include "KoCanvasControllerWidgetViewport_p.h"
0024 #include "KoShape.h"
0025 #include "KoShape_p.h"
0026 #include "KoShapeFactoryBase.h" // for the SHAPE mimetypes
0027 #include "KoShapeRegistry.h"
0028 #include "KoShapeController.h"
0029 #include "KoShapeManager.h"
0030 #include "KoSelection.h"
0031 #include "KoCanvasBase.h"
0032 #include "KoShapeLayer.h"
0033 #include "KoShapePaste.h"
0034 #include "KoShapePaintingContext.h"
0035 #include "KoToolProxy.h"
0036 
0037 #include <KoProperties.h>
0038 
0039 #include <FlakeDebug.h>
0040 
0041 #include <QPainter>
0042 #include <QDragEnterEvent>
0043 
0044 #include <limits.h>
0045 #include <stdlib.h>
0046 
0047 // ********** Viewport **********
0048 Viewport::Viewport(KoCanvasControllerWidget *parent)
0049         : QWidget(parent)
0050         , m_draggedShape(0)
0051         , m_drawShadow(false)
0052         , m_canvas(0)
0053         , m_documentOffset(QPoint(0, 0))
0054         , m_margin(0)
0055 {
0056     setAutoFillBackground(true);
0057     setAcceptDrops(true);
0058     setMouseTracking(true);
0059     m_parent = parent;
0060 }
0061 
0062 void Viewport::setCanvas(QWidget *canvas)
0063 {
0064     if (m_canvas) {
0065         m_canvas->hide();
0066         delete m_canvas;
0067     }
0068     m_canvas = canvas;
0069     if (!canvas) return;
0070     m_canvas->setParent(this);
0071     m_canvas->show();
0072     if (!m_canvas->minimumSize().isNull()) {
0073         m_documentSize = m_canvas->minimumSize();
0074     }
0075     resetLayout();
0076 }
0077 
0078 void Viewport::setDocumentSize(const QSize &size)
0079 {
0080     m_documentSize = size;
0081     resetLayout();
0082 }
0083 
0084 void Viewport::documentOffsetMoved(const QPoint &pt)
0085 {
0086     m_documentOffset = pt;
0087     resetLayout();
0088 }
0089 
0090 void Viewport::setDrawShadow(bool drawShadow)
0091 {
0092     m_drawShadow = drawShadow;
0093 }
0094 
0095 
0096 void Viewport::handleDragEnterEvent(QDragEnterEvent *event)
0097 {
0098     // if not a canvas set then ignore this, makes it possible to assume
0099     // we have a canvas in all the support methods.
0100     if (!(m_parent->canvas() && m_parent->canvas()->canvasWidget()))
0101         return;
0102 
0103     // only allow dropping when active layer is editable
0104     KoSelection *selection = m_parent->canvas()->shapeManager()->selection();
0105     KoShapeLayer *activeLayer = selection->activeLayer();
0106     if (activeLayer && (!activeLayer->isEditable() || activeLayer->isGeometryProtected()))
0107         return;
0108 
0109     const QMimeData *data = event->mimeData();
0110     if (data->hasFormat(SHAPETEMPLATE_MIMETYPE) ||
0111             data->hasFormat(SHAPEID_MIMETYPE)) {
0112         QByteArray itemData;
0113         bool isTemplate = true;
0114         if (data->hasFormat(SHAPETEMPLATE_MIMETYPE))
0115             itemData = data->data(SHAPETEMPLATE_MIMETYPE);
0116         else {
0117             isTemplate = false;
0118             itemData = data->data(SHAPEID_MIMETYPE);
0119         }
0120         QDataStream dataStream(&itemData, QIODevice::ReadOnly);
0121         QString id;
0122         dataStream >> id;
0123         QString properties;
0124         if (isTemplate)
0125             dataStream >> properties;
0126 
0127         // and finally, there is a point.
0128         QPointF offset;
0129         dataStream >> offset;
0130 
0131         // The rest of this method is mostly a copy paste from the KoCreateShapeStrategy
0132         // So, lets remove this again when Zagge adds his new class that does this kind of thing. (KoLoadSave)
0133         KoShapeFactoryBase *factory = KoShapeRegistry::instance()->value(id);
0134         if (! factory) {
0135             warnFlake << "Application requested a shape that is not registered '" <<
0136             id << "', Ignoring";
0137             event->ignore();
0138             return;
0139         }
0140         event->setDropAction(Qt::CopyAction);
0141         event->accept();
0142 
0143         if (isTemplate) {
0144             KoProperties props;
0145             props.load(properties);
0146             m_draggedShape = factory->createShape(&props, m_parent->canvas()->shapeController()->resourceManager());
0147         } else
0148             m_draggedShape = factory->createDefaultShape(m_parent->canvas()->shapeController()->resourceManager());
0149 
0150         Q_ASSERT(m_draggedShape);
0151         if (!m_draggedShape) return;
0152 
0153         if (m_draggedShape->shapeId().isEmpty())
0154             m_draggedShape->setShapeId(factory->id());
0155         m_draggedShape->setZIndex(KoShapePrivate::MaxZIndex);
0156         m_draggedShape->setAbsolutePosition(correctPosition(event->pos()));
0157 
0158         m_parent->canvas()->shapeManager()->addShape(m_draggedShape);
0159     }
0160     else if (data->hasFormat(KoOdf::mimeType(KoOdf::Text))) {
0161         KoShapeManager *sm = m_parent->canvas()->shapeManager();
0162         KoShapePaste paste(m_parent->canvas(), sm->selection()->activeLayer());
0163         if (paste.paste(KoOdf::Text, data)) {
0164             QList<KoShape *> shapes = paste.pastedShapes();
0165             if (shapes.count() == 1) {
0166                 m_draggedShape = shapes.first();
0167                 m_draggedShape->setZIndex(KoShapePrivate::MaxZIndex);
0168                 event->setDropAction(Qt::CopyAction);
0169             }
0170             event->accept();
0171         }
0172     } else {
0173         event->ignore();
0174     }
0175 }
0176 
0177 void Viewport::handleDropEvent(QDropEvent *event)
0178 {
0179     if (!m_draggedShape) {
0180         m_parent->canvas()->toolProxy()->dropEvent(event, correctPosition(event->pos()));
0181         return;
0182     }
0183 
0184     repaint(m_draggedShape);
0185     m_parent->canvas()->shapeManager()->remove(m_draggedShape); // remove it to not interfere with z-index calc.
0186 
0187     m_draggedShape->setPosition(QPointF(0, 0));  // always save position.
0188     QPointF newPos = correctPosition(event->pos());
0189     m_parent->canvas()->clipToDocument(m_draggedShape, newPos); // ensure the shape is dropped inside the document.
0190     m_draggedShape->setAbsolutePosition(newPos);
0191     KUndo2Command * cmd = m_parent->canvas()->shapeController()->addShape(m_draggedShape);
0192     if (cmd) {
0193         m_parent->canvas()->addCommand(cmd);
0194         KoSelection *selection = m_parent->canvas()->shapeManager()->selection();
0195 
0196         // repaint selection before selecting newly create shape
0197         foreach(KoShape * shape, selection->selectedShapes())
0198             shape->update();
0199 
0200         selection->deselectAll();
0201         selection->select(m_draggedShape);
0202     } else
0203         delete m_draggedShape;
0204 
0205     m_draggedShape = 0;
0206 }
0207 
0208 QPointF Viewport::correctPosition(const QPoint &point) const
0209 {
0210     QWidget *canvasWidget = m_parent->canvas()->canvasWidget();
0211     Q_ASSERT(canvasWidget); // since we should not allow drag if there is not.
0212     QPoint correctedPos(point.x() - canvasWidget->x(), point.y() - canvasWidget->y());
0213     correctedPos += m_documentOffset;
0214     return m_parent->canvas()->viewToDocument(correctedPos);
0215 }
0216 
0217 void Viewport::handleDragMoveEvent(QDragMoveEvent *event)
0218 {
0219     if (!m_draggedShape) {
0220         m_parent->canvas()->toolProxy()->dragMoveEvent(event, correctPosition(event->pos()));
0221         return;
0222     }
0223 
0224     m_draggedShape->update();
0225     repaint(m_draggedShape);
0226     m_draggedShape->setAbsolutePosition(correctPosition(event->pos()));
0227     m_draggedShape->update();
0228     repaint(m_draggedShape);
0229 }
0230 
0231 void Viewport::repaint(KoShape *shape)
0232 {
0233     QRect rect = m_parent->canvas()->viewConverter()->documentToView(shape->boundingRect()).toRect();
0234     QWidget *canvasWidget = m_parent->canvas()->canvasWidget();
0235     Q_ASSERT(canvasWidget); // since we should not allow drag if there is not.
0236     rect.moveLeft(rect.left() + canvasWidget->x() - m_documentOffset.x());
0237     rect.moveTop(rect.top() + canvasWidget->y() - m_documentOffset.y());
0238     rect.adjust(-2, -2, 2, 2); // adjust for antialias
0239     update(rect);
0240 }
0241 
0242 void Viewport::handleDragLeaveEvent(QDragLeaveEvent *event)
0243 {
0244     if (m_draggedShape) {
0245         repaint(m_draggedShape);
0246         m_parent->canvas()->shapeManager()->remove(m_draggedShape);
0247         delete m_draggedShape;
0248         m_draggedShape = 0;
0249     } else {
0250         m_parent->canvas()->toolProxy()->dragLeaveEvent(event);
0251     }
0252 }
0253 
0254 void Viewport::handlePaintEvent(QPainter &painter, QPaintEvent *event)
0255 {
0256     Q_UNUSED(event);
0257     // Draw the shadow around the canvas.
0258     if (m_parent->canvas() && m_parent->canvas()->canvasWidget() && m_drawShadow) {
0259         QWidget *canvas = m_parent->canvas()->canvasWidget();
0260         painter.setPen(QPen(Qt::black, 0));
0261         QRect rect(canvas->x(), canvas->y(), canvas->width(), canvas->height());
0262         rect.adjust(-1, -1, 0, 0);
0263         painter.drawRect(rect);
0264         painter.drawLine(rect.right() + 2, rect.top() + 2, rect.right() + 2, rect.bottom() + 2);
0265         painter.drawLine(rect.left() + 2, rect.bottom() + 2, rect.right() + 2, rect.bottom() + 2);
0266     }
0267     if (m_draggedShape) {
0268         const KoViewConverter *vc = m_parent->canvas()->viewConverter();
0269 
0270         painter.save();
0271         QWidget *canvasWidget = m_parent->canvas()->canvasWidget();
0272         Q_ASSERT(canvasWidget); // since we should not allow drag if there is not.
0273         painter.translate(canvasWidget->x() - m_documentOffset.x(),
0274                 canvasWidget->y() - m_documentOffset.y());
0275         QPointF offset = vc->documentToView(m_draggedShape->position());
0276         painter.setOpacity(0.6);
0277         painter.translate(offset.x(), offset.y());
0278         painter.setRenderHint(QPainter::Antialiasing);
0279         KoShapePaintingContext paintContext; //FIXME
0280         m_draggedShape->paint(painter, *vc, paintContext);
0281         painter.restore();
0282     }
0283 }
0284 
0285 void Viewport::resetLayout()
0286 {
0287     // Determine the area we have to show
0288     QRect viewRect(m_documentOffset, size());
0289 
0290     const int viewH = viewRect.height();
0291     const int viewW = viewRect.width();
0292 
0293     const int docH = m_documentSize.height();
0294     const int docW = m_documentSize.width();
0295 
0296     int moveX = 0;
0297     int moveY = 0;
0298 
0299     int resizeW = viewW;
0300     int resizeH = viewH;
0301 
0302 //     debugFlake <<"viewH:" << viewH << endl
0303 //              << "docH: " << docH << endl
0304 //              << "viewW: " << viewW << endl
0305 //              << "docW: " << docW << endl;
0306 
0307     if (viewH == docH && viewW == docW) {
0308         // Do nothing
0309         resizeW = docW;
0310         resizeH = docH;
0311     } else if (viewH > docH && viewW > docW) {
0312         // Show entire canvas centered
0313         moveX = (viewW - docW) / 2;
0314         moveY = (viewH - docH) / 2;
0315         resizeW = docW;
0316         resizeH = docH;
0317     } else  if (viewW > docW) {
0318         // Center canvas horizontally
0319         moveX = (viewW - docW) / 2;
0320         resizeW = docW;
0321 
0322         int marginTop = m_margin - m_documentOffset.y();
0323         int marginBottom = viewH  - (m_documentSize.height() - m_documentOffset.y());
0324 
0325         if (marginTop > 0) moveY = marginTop;
0326         if (marginTop > 0) resizeH = viewH - marginTop;
0327         if (marginBottom > 0) resizeH = viewH - marginBottom;
0328     } else  if (viewH > docH) {
0329         // Center canvas vertically
0330         moveY = (viewH - docH) / 2;
0331         resizeH = docH;
0332 
0333         int marginLeft = m_margin - m_documentOffset.x();
0334         int marginRight = viewW - (m_documentSize.width() - m_documentOffset.x());
0335 
0336         if (marginLeft > 0) moveX = marginLeft;
0337         if (marginLeft > 0) resizeW = viewW - marginLeft;
0338         if (marginRight > 0) resizeW = viewW - marginRight;
0339     } else {
0340         // Take care of the margin around the canvas
0341         int marginTop = m_margin - m_documentOffset.y();
0342         int marginLeft = m_margin - m_documentOffset.x();
0343         int marginRight = viewW - (m_documentSize.width() - m_documentOffset.x());
0344         int marginBottom = viewH  - (m_documentSize.height() - m_documentOffset.y());
0345 
0346         if (marginTop > 0) moveY = marginTop;
0347         if (marginLeft > 0) moveX = marginLeft;
0348 
0349         if (marginTop > 0) resizeH = viewH - marginTop;
0350         if (marginLeft > 0) resizeW = viewW - marginLeft;
0351         if (marginRight > 0) resizeW = viewW - marginRight;
0352         if (marginBottom > 0) resizeH = viewH - marginBottom;
0353     }
0354     if (m_parent->canvasMode() == KoCanvasController::AlignTop) {
0355         // have up to m_margin pixels at top.
0356         moveY = qMin(m_margin, moveY);
0357     }
0358     if (m_canvas) {
0359         QRect geom;
0360         if (m_parent->canvasMode() == KoCanvasController::Infinite)
0361             geom = QRect(0, 0, viewW, viewH);
0362         else
0363             geom = QRect(moveX, moveY, resizeW, resizeH);
0364         if (m_canvas->geometry() != geom) {
0365             m_canvas->setGeometry(geom);
0366             m_canvas->update();
0367         }
0368     }
0369     if (m_drawShadow) {
0370         update();
0371     }
0372 
0373     emit sizeChanged();
0374 #if 0
0375      debugFlake <<"View port geom:" << geometry();
0376      if (m_canvas)
0377         debugFlake <<"Canvas widget geom:" << m_canvas->geometry();
0378 #endif
0379 }