KoCanvasControllerWidgetViewport_p.cpp 13 KB
Newer Older
1 2
/* This file is part of the KDE project
 *
3
 * Copyright (C) 2006-2007, 2009 Thomas Zander <zander@kde.org>
4
 * Copyright (C) 2006 Thorsten Zachmann <zachmann@kde.org>
Halla Rempt's avatar
Halla Rempt committed
5
 * Copyright (C) 2007-2010 Boudewijn Rempt <boud@valdyas.org>
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

Halla Rempt's avatar
Halla Rempt committed
23
#include "KoCanvasControllerWidgetViewport_p.h"
24
#include "KoShape.h"
Thomas Zander's avatar
Thomas Zander committed
25
#include "KoShape_p.h"
26
#include "KoShapeFactoryBase.h" // for the SHAPE mimetypes
27 28 29 30 31
#include "KoShapeRegistry.h"
#include "KoShapeController.h"
#include "KoShapeManager.h"
#include "KoSelection.h"
#include "KoCanvasBase.h"
32
#include "KoShapeLayer.h"
33
#include "KoShapePaste.h"
34
#include "KoShapePaintingContext.h"
35
#include "KoToolProxy.h"
36 37 38

#include <KoProperties.h>

39
#include <kdebug.h>
40
#include <ksharedconfig.h>
41
#include <kconfiggroup.h>
42 43 44 45

#include <QPainter>
#include <QDragEnterEvent>

Dirk Mueller's avatar
Dirk Mueller committed
46 47 48
#include <limits.h>
#include <stdlib.h>

49
// ********** Viewport **********
50
Viewport::Viewport(KoCanvasControllerWidget *parent)
51 52 53 54 55
        : QWidget(parent)
        , m_draggedShape(0)
        , m_drawShadow(false)
        , m_canvas(0)
        , m_documentOffset(QPoint(0, 0))
56
        , m_margin(0)
57 58 59 60
{
    setBackgroundRole(QPalette::Dark);
    setAutoFillBackground(true);
    setAcceptDrops(true);
61
    setMouseTracking(true);
62 63 64 65 66
    m_parent = parent;
}

void Viewport::setCanvas(QWidget *canvas)
{
67
    if (m_canvas) {
68 69 70 71
        m_canvas->hide();
        delete m_canvas;
    }
    m_canvas = canvas;
72 73
    if (!canvas) return;
    m_canvas->setParent(this);
74 75 76 77
    m_canvas->show();
    if (!m_canvas->minimumSize().isNull()) {
        m_documentSize = m_canvas->minimumSize();
    }
78 79 80
    resetLayout();
}

81
void Viewport::setDocumentSize(const QSize &size)
82 83 84 85 86
{
    m_documentSize = size;
    resetLayout();
}

87
void Viewport::documentOffsetMoved(const QPoint &pt)
88 89 90 91 92
{
    m_documentOffset = pt;
    resetLayout();
}

93
void Viewport::setDrawShadow(bool drawShadow)
94 95 96 97 98
{
    m_drawShadow = drawShadow;
}


99 100 101 102
void Viewport::handleDragEnterEvent(QDragEnterEvent *event)
{
    // if not a canvas set then ignore this, makes it possible to assume
    // we have a canvas in all the support methods.
103
    if (!(m_parent->canvas() && m_parent->canvas()->canvasWidget()))
104 105
        return;

106 107 108 109 110 111 112 113 114
    // only allow dropping when active layer is editable
    KoSelection *selection = m_parent->canvas()->shapeManager()->selection();
    KoShapeLayer *activeLayer = selection->activeLayer();
    if (activeLayer && (!activeLayer->isEditable() || activeLayer->isGeometryProtected()))
        return;

    const QMimeData *data = event->mimeData();
    if (data->hasFormat(SHAPETEMPLATE_MIMETYPE) ||
            data->hasFormat(SHAPEID_MIMETYPE)) {
115 116
        QByteArray itemData;
        bool isTemplate = true;
117 118
        if (data->hasFormat(SHAPETEMPLATE_MIMETYPE))
            itemData = data->data(SHAPETEMPLATE_MIMETYPE);
119 120
        else {
            isTemplate = false;
121
            itemData = data->data(SHAPEID_MIMETYPE);
122 123 124 125 126
        }
        QDataStream dataStream(&itemData, QIODevice::ReadOnly);
        QString id;
        dataStream >> id;
        QString properties;
127
        if (isTemplate)
128 129 130 131 132 133 134 135
            dataStream >> properties;

        // and finally, there is a point.
        QPointF offset;
        dataStream >> offset;

        // The rest of this method is mostly a copy paste from the KoCreateShapeStrategy
        // So, lets remove this again when Zagge adds his new class that does this kind of thing. (KoLoadSave)
136
        KoShapeFactoryBase *factory = KoShapeRegistry::instance()->value(id);
137
        if (! factory) {
138
            kWarning(30006) << "Application requested a shape that is not registered '" <<
Thomas Zander's avatar
Thomas Zander committed
139
            id << "', Ignoring";
140 141 142 143 144 145
            event->ignore();
            return;
        }
        event->setDropAction(Qt::CopyAction);
        event->accept();

146
        if (isTemplate) {
147 148
            KoProperties props;
            props.load(properties);
Thomas Zander's avatar
Thomas Zander committed
149
            m_draggedShape = factory->createShape(&props, m_parent->canvas()->shapeController()->resourceManager());
150
        } else
Thomas Zander's avatar
Thomas Zander committed
151
            m_draggedShape = factory->createDefaultShape(m_parent->canvas()->shapeController()->resourceManager());
152

153
        Q_ASSERT(m_draggedShape);
154 155
        if (!m_draggedShape) return;

156
        if (m_draggedShape->shapeId().isEmpty())
157
            m_draggedShape->setShapeId(factory->id());
Thomas Zander's avatar
Thomas Zander committed
158
        m_draggedShape->setZIndex(KoShapePrivate::MaxZIndex);
159
        m_draggedShape->setAbsolutePosition(correctPosition(event->pos()));
160

161
        m_parent->canvas()->shapeManager()->addShape(m_draggedShape);
162
    }
163 164 165 166 167
    else if (data->hasFormat(KoOdf::mimeType(KoOdf::Text))) {
        KoShapeManager *sm = m_parent->canvas()->shapeManager();
        KoShapePaste paste(m_parent->canvas(), sm->selection()->activeLayer());
        if (paste.paste(KoOdf::Text, data)) {
            QList<KoShape *> shapes = paste.pastedShapes();
168 169 170 171
            if (shapes.count() == 1) {
                m_draggedShape = shapes.first();
                m_draggedShape->setZIndex(KoShapePrivate::MaxZIndex);
                event->setDropAction(Qt::CopyAction);
172 173 174
            }
            event->accept();
        }
175
    } else {
176
        event->ignore();
177
    }
178 179
}

180 181
void Viewport::handleDropEvent(QDropEvent *event)
{
182 183 184 185
    if (!m_draggedShape) {
        m_parent->canvas()->toolProxy()->dropEvent(event, correctPosition(event->pos()));
        return;
    }
186

187
    repaint(m_draggedShape);
188
    m_parent->canvas()->shapeManager()->remove(m_draggedShape); // remove it to not interfere with z-index calc.
189

190
    m_draggedShape->setPosition(QPointF(0, 0));  // always save position.
191 192
    QPointF newPos = correctPosition(event->pos());
    m_parent->canvas()->clipToDocument(m_draggedShape, newPos); // ensure the shape is dropped inside the document.
193
    m_draggedShape->setAbsolutePosition(newPos);
Alexander Potashev's avatar
Alexander Potashev committed
194
    KUndo2Command * cmd = m_parent->canvas()->shapeController()->addShape(m_draggedShape);
195
    if (cmd) {
196 197
        m_parent->canvas()->addCommand(cmd);
        KoSelection *selection = m_parent->canvas()->shapeManager()->selection();
198 199

        // repaint selection before selecting newly create shape
200
        foreach(KoShape * shape, selection->selectedShapes())
Thomas Zander's avatar
Thomas Zander committed
201
            shape->update();
202

203 204
        selection->deselectAll();
        selection->select(m_draggedShape);
205
    } else
206
        delete m_draggedShape;
207

208 209 210
    m_draggedShape = 0;
}

211 212
QPointF Viewport::correctPosition(const QPoint &point) const
{
213 214 215
    QWidget *canvasWidget = m_parent->canvas()->canvasWidget();
    Q_ASSERT(canvasWidget); // since we should not allow drag if there is not.
    QPoint correctedPos(point.x() - canvasWidget->x(), point.y() - canvasWidget->y());
216
    correctedPos += m_documentOffset;
C. Boemann's avatar
C. Boemann committed
217
    return m_parent->canvas()->viewToDocument(correctedPos);
218 219
}

220
void Viewport::handleDragMoveEvent(QDragMoveEvent *event)
221
{
222 223
    if (!m_draggedShape) {
        m_parent->canvas()->toolProxy()->dragMoveEvent(event, correctPosition(event->pos()));
224
        return;
225 226
    }

227
    m_draggedShape->update();
228
    repaint(m_draggedShape);
229
    m_draggedShape->setAbsolutePosition(correctPosition(event->pos()));
230
    m_draggedShape->update();
231 232 233
    repaint(m_draggedShape);
}

234 235
void Viewport::repaint(KoShape *shape)
{
236 237 238
    QRect rect = m_parent->canvas()->viewConverter()->documentToView(shape->boundingRect()).toRect();
    QWidget *canvasWidget = m_parent->canvas()->canvasWidget();
    Q_ASSERT(canvasWidget); // since we should not allow drag if there is not.
239 240
    rect.moveLeft(rect.left() + canvasWidget->x() - m_documentOffset.x());
    rect.moveTop(rect.top() + canvasWidget->y() - m_documentOffset.y());
241
    rect.adjust(-2, -2, 2, 2); // adjust for antialias
242
    update(rect);
243 244
}

245
void Viewport::handleDragLeaveEvent(QDragLeaveEvent *event)
246 247
{
    if (m_draggedShape) {
248 249 250 251
        repaint(m_draggedShape);
        m_parent->canvas()->shapeManager()->remove(m_draggedShape);
        delete m_draggedShape;
        m_draggedShape = 0;
252 253
    } else {
        m_parent->canvas()->toolProxy()->dragLeaveEvent(event);
254 255 256
    }
}

Thomas Zander's avatar
Thomas Zander committed
257
void Viewport::handlePaintEvent(QPainter &painter, QPaintEvent *event)
258
{
259
    Q_UNUSED(event);
260
    // Draw the shadow around the canvas.
261
    if (m_parent->canvas() && m_parent->canvas()->canvasWidget() && m_drawShadow) {
262 263 264 265 266
        QWidget *canvas = m_parent->canvas()->canvasWidget();
        painter.setPen(Qt::black);
        QRect rect(canvas->x(), canvas->y(), canvas->width(), canvas->height());
        rect.adjust(-1, -1, 0, 0);
        painter.drawRect(rect);
267 268
        painter.drawLine(rect.right() + 2, rect.top() + 2, rect.right() + 2, rect.bottom() + 2);
        painter.drawLine(rect.left() + 2, rect.bottom() + 2, rect.right() + 2, rect.bottom() + 2);
269
    }
270
    if (m_draggedShape) {
271
        const KoViewConverter *vc = m_parent->canvas()->viewConverter();
272 273 274 275

        painter.save();
        QWidget *canvasWidget = m_parent->canvas()->canvasWidget();
        Q_ASSERT(canvasWidget); // since we should not allow drag if there is not.
276 277
        painter.translate(canvasWidget->x() - m_documentOffset.x(),
                canvasWidget->y() - m_documentOffset.y());
278 279 280 281
        QPointF offset = vc->documentToView(m_draggedShape->position());
        painter.setOpacity(0.6);
        painter.translate(offset.x(), offset.y());
        painter.setRenderHint(QPainter::Antialiasing);
282 283
        KoShapePaintingContext paintContext; //FIXME
        m_draggedShape->paint(painter, *vc, paintContext);
284 285 286 287 288 289 290
        painter.restore();
    }
}

void Viewport::resetLayout()
{
    // Determine the area we have to show
291
    QRect viewRect(m_documentOffset, size());
292

293 294
    const int viewH = viewRect.height();
    const int viewW = viewRect.width();
295

296 297
    const int docH = m_documentSize.height();
    const int docW = m_documentSize.width();
298 299 300 301 302 303 304

    int moveX = 0;
    int moveY = 0;

    int resizeW = viewW;
    int resizeH = viewH;

Halla Rempt's avatar
Halla Rempt committed
305
//     kDebug(30006) <<"viewH:" << viewH << endl
306 307 308 309
//              << "docH: " << docH << endl
//              << "viewW: " << viewW << endl
//              << "docW: " << docW << endl;

310
    if (viewH == docH && viewW == docW) {
311 312 313
        // Do nothing
        resizeW = docW;
        resizeH = docH;
314
    } else if (viewH > docH && viewW > docW) {
315
        // Show entire canvas centered
316 317
        moveX = (viewW - docW) / 2;
        moveY = (viewH - docH) / 2;
318 319
        resizeW = docW;
        resizeH = docH;
320
    } else  if (viewW > docW) {
321
        // Center canvas horizontally
322
        moveX = (viewW - docW) / 2;
323 324 325
        resizeW = docW;

        int marginTop = m_margin - m_documentOffset.y();
326
        int marginBottom = viewH  - (m_documentSize.height() - m_documentOffset.y());
327

328 329 330 331
        if (marginTop > 0) moveY = marginTop;
        if (marginTop > 0) resizeH = viewH - marginTop;
        if (marginBottom > 0) resizeH = viewH - marginBottom;
    } else  if (viewH > docH) {
332
        // Center canvas vertically
333
        moveY = (viewH - docH) / 2;
334 335 336
        resizeH = docH;

        int marginLeft = m_margin - m_documentOffset.x();
337
        int marginRight = viewW - (m_documentSize.width() - m_documentOffset.x());
338

339 340 341 342
        if (marginLeft > 0) moveX = marginLeft;
        if (marginLeft > 0) resizeW = viewW - marginLeft;
        if (marginRight > 0) resizeW = viewW - marginRight;
    } else {
343 344 345
        // Take care of the margin around the canvas
        int marginTop = m_margin - m_documentOffset.y();
        int marginLeft = m_margin - m_documentOffset.x();
346 347
        int marginRight = viewW - (m_documentSize.width() - m_documentOffset.x());
        int marginBottom = viewH  - (m_documentSize.height() - m_documentOffset.y());
348

349 350
        if (marginTop > 0) moveY = marginTop;
        if (marginLeft > 0) moveX = marginLeft;
351

352 353 354 355
        if (marginTop > 0) resizeH = viewH - marginTop;
        if (marginLeft > 0) resizeW = viewW - marginLeft;
        if (marginRight > 0) resizeW = viewW - marginRight;
        if (marginBottom > 0) resizeH = viewH - marginBottom;
356
    }
357
    if (m_parent->canvasMode() == KoCanvasController::AlignTop) {
358 359
        // have up to m_margin pixels at top.
        moveY = qMin(m_margin, moveY);
Thomas Zander's avatar
Thomas Zander committed
360
    }
361
    if (m_canvas) {
362
        QRect geom;
363
        if (m_parent->canvasMode() == KoCanvasController::Infinite)
364
            geom = QRect(0, 0, viewW, viewH);
365
        else
366 367 368 369 370
            geom = QRect(moveX, moveY, resizeW, resizeH);
        if (m_canvas->geometry() != geom) {
            m_canvas->setGeometry(geom);
            m_canvas->update();
        }
371
    }
Halla Rempt's avatar
Halla Rempt committed
372 373 374
    if (m_drawShadow) {
        update();
    }
375

376
    emit sizeChanged();
377 378 379 380 381
#if 0
     kDebug(30006) <<"View port geom:" << geometry();
     if (m_canvas)
        kDebug(30006) <<"Canvas widget geom:" << m_canvas->geometry();
#endif
382 383
}

Halla Rempt's avatar
Halla Rempt committed
384
#include <KoCanvasControllerWidgetViewport_p.moc>