class KActionCollection;
class QRect;
class QRectF;
class KoShape;
class KoCanvasBase;
class KoCanvasControllerProxyObject;
/**
* KoCanvasController is the base class for wrappers around your canvas
* that provides scrolling and zooming for your canvas.
*
* Flake does not provide a canvas, the application will have to
* implement a canvas themselves. You canvas can be QWidget-based
* or something we haven't invented yet -- as long the class that holds the canvas
* imlements KoCanvasController, tools, scrolling and zooming will work.
*
* A KoCanvasController implementation acts as a decorator around the canvas widget
* and provides a way to scroll the canvas, allows the canvas to be centered
* in the viewArea and manages tool activation.
*
* The using application can instantiate this class and add its
* canvas using the setCanvas() call. Which is designed so it can be
* called multiple times if you need to exchange one canvas
* widget for another, for instance, switching between a plain QWidget or a QOpenGLWidget.
*
*
There is _one_ KoCanvasController per canvas in your
* application.
*
*
The canvas widget is at most as big as the viewport of the scroll
* area, and when the view on the document is near its edges, smaller.
* In your canvas widget code, you can find the right place in your
* document in view coordinates (pixels) by adding the documentOffset
*/
class KRITAFLAKE_EXPORT KoCanvasController
{
public:
/// An enum to alter the positioning and size of the canvas inside the canvas controller
enum CanvasMode {
AlignTop, ///< canvas is top aligned if smaller than the viewport
Centered, ///< canvas is centered if smaller than the viewport
Infinite, ///< canvas is never smaller than the viewport
Spreadsheet ///< same as Infinite, but supports right-to-left layouts
};
// proxy QObject: use this to connect to slots and signals.
KoCanvasControllerProxyObject *proxyObject;
/**
* Constructor.
* @param actionCollection the action collection for this canvas
*/
explicit KoCanvasController(KActionCollection* actionCollection);
virtual ~KoCanvasController();
public:
/**
* Returns the current margin that is used to pad the canvas with.
* This value is read from the KConfig property "canvasmargin"
*/
virtual int margin() const;
/**
* Set the new margin to pad the canvas with.
*/
virtual void setMargin(int margin);
/**
* Sets the how the canvas behaves if the zoomed document becomes smaller than the viewport.
* @param mode the new canvas mode, CanvasMode::Centered is the default value
*/
virtual void setCanvasMode(KoCanvasController::CanvasMode mode);
/// Returns the current canvas mode
virtual KoCanvasController::CanvasMode canvasMode() const;
/**
* compatibility with QAbstractScrollArea
*/
virtual void scrollContentsBy(int dx, int dy) = 0;
/**
* @return the size of the viewport
*/
virtual QSize viewportSize() const = 0;
/**
* Set the shadow option -- by default the canvas controller draws
* a black shadow around the canvas widget, which you may or may
* not want.
*
* @param drawShadow if true, the shadow is drawn, if false, not
*/
virtual void setDrawShadow(bool drawShadow) = 0;
/**
* Set the new canvas to be shown as a child
* Calling this will emit canvasRemoved() if there was a canvas before, and will emit
* canvasSet() with the new canvas.
* @param canvas the new canvas. The KoCanvasBase::canvas() will be called to retrieve the
* actual widget which will then be added as child of this one.
*/
virtual void setCanvas(KoCanvasBase *canvas) = 0;
/**
* Return the currently set canvas. The default implementation will return Null
* @return the currently set canvas
*/
virtual KoCanvasBase *canvas() const;
/**
* return the amount of pixels vertically visible of the child canvas.
* @return the amount of pixels vertically visible of the child canvas.
*/
virtual int visibleHeight() const = 0;
/**
* return the amount of pixels horizontally visible of the child canvas.
* @return the amount of pixels horizontally visible of the child canvas.
*/
virtual int visibleWidth() const = 0;
/**
* return the amount of pixels that are not visible on the left side of the canvas.
* The leftmost pixel that is shown is returned.
*/
virtual int canvasOffsetX() const = 0;
/**
* return the amount of pixels that are not visible on the top side of the canvas.
* The topmost pixel that is shown is returned.
*/
virtual int canvasOffsetY() const = 0;
/**
* @brief Scrolls the content of the canvas so that the given rect is visible.
*
* The rect is to be specified in view coordinates (pixels). The scrollbar positions
* are changed so that the centerpoint of the rectangle is centered if possible.
*
* @param rect the rectangle to make visible
* @param smooth if true the viewport translation will make be just enough to ensure visibility, no more.
* @see KoViewConverter::documentToView()
*/
virtual void ensureVisible(const QRectF &rect, bool smooth = false) = 0;
/**
* @brief Scrolls the content of the canvas so that the given shape is visible.
*
* This is just a wrapper function of the above function.
*
* @param shape the shape to make visible
*/
virtual void ensureVisible(KoShape *shape) = 0;
/**
* @brief zooms in around the center.
*
* The center must be specified in view coordinates (pixels). The scrollbar positions
* are changed so that the center becomes center if possible.
*
* @param center the position to zoom in on
*/
virtual void zoomIn(const QPoint ¢er) = 0;
/**
* @brief zooms out around the center.
*
* The center must be specified in view coordinates (pixels). The scrollbar positions
* are changed so that the center becomes center if possible.
*
* @param center the position to zoom out around
*/
virtual void zoomOut(const QPoint ¢er) = 0;
/**
* @brief zooms around the center.
*
* The center must be specified in view coordinates (pixels). The scrollbar positions
* are changed so that the center becomes center if possible.
*
* @param center the position to zoom around
* @param zoom the zoom to apply
*/
virtual void zoomBy(const QPoint ¢er, qreal zoom) = 0;
/**
* @brief zoom so that rect is exactly visible (as close as possible)
*
* The rect must be specified in view coordinates (pixels). The scrollbar positions
* are changed so that the center of the rect becomes center if possible.
*
* @param rect the rect in view coordinates (pixels) that should fit the view afterwards
*/
virtual void zoomTo(const QRect &rect) = 0;
/**
* @brief repositions the scrollbars so previous center is once again center
*
* The previous center is cached from when the user uses the scrollbars or zoomTo
* are called. zoomTo is mostly used when a zoom tool of sorts have marked an area
* to zoom in on
*
* The success of this method is limited by the size of thing. But we try our best.
*/
virtual void recenterPreferred() = 0;
/**
* Sets the preferred center point in view coordinates (pixels).
* @param viewPoint the new preferred center
*/
virtual void setPreferredCenter(const QPointF &viewPoint) = 0;
/// Returns the currently set preferred center point in view coordinates (pixels)
virtual QPointF preferredCenter() const = 0;
/**
* Move the canvas over the x and y distance of the parameter distance
* @param distance the distance in view coordinates (pixels). A positive distance means moving the canvas up/left.
*/
virtual void pan(const QPoint &distance) = 0;
/**
* Get the position of the scrollbar
*/
virtual QPoint scrollBarValue() const = 0;
/**
* Set the position of the scrollbar
* @param value the new values of the scroll bars
*/
virtual void setScrollBarValue(const QPoint &value) = 0;
/**
* Called when the size of your document in view coordinates (pixels) changes, for instance when zooming.
*
* @param newSize the new size, in view coordinates (pixels), of the document.
* @param recalculateCenter if true the offset in the document we center on after calling
* recenterPreferred() will be recalculated for the new document size so the visual offset stays the same.
*/
virtual void updateDocumentSize(const QSize &sz, bool recalculateCenter) = 0;
/**
* Set mouse wheel to zoom behaviour
* @param zoom if true wheel will zoom instead of scroll, control modifier will scroll
*/
virtual void setZoomWithWheel(bool zoom) = 0;
/**
* Set scroll area to be bigger than actual document.
* It allows the user to move the corner of the document
* to e.g. the center of the screen
*
* @param factor the coefficient, defining how much we can scroll out,
* measured in parts of the widget size. Null value means vast
* scrolling is disabled.
*/
virtual void setVastScrolling(qreal factor) = 0;
/**
* Returns the action collection for the canvas
* @returns action collection for this canvas, can be 0
*/
virtual KActionCollection* actionCollection() const;
QPoint documentOffset() const;
+ /**
+ * @return the current position of the cursor fetched from QCursor::pos() and
+ * converted into document coordinates
+ */
+ virtual QPointF currentCursorPosition() const = 0;
+
protected:
void setDocumentSize(const QSize &sz);
QSize documentSize() const;
void setPreferredCenterFractionX(qreal);
qreal preferredCenterFractionX() const;
void setPreferredCenterFractionY(qreal);
qreal preferredCenterFractionY() const;
void setDocumentOffset( QPoint &offset);
private:
class Private;
Private * const d;
};
/**
* Workaround class for the problem that Qt does not allow two QObject base classes.
* KoCanvasController can be implemented by for instance QWidgets, so it cannot be
* a QObject directly. The interface of this class should be considered public interface
* for KoCanvasController.
*/
class KRITAFLAKE_EXPORT KoCanvasControllerProxyObject : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(KoCanvasControllerProxyObject)
public:
explicit KoCanvasControllerProxyObject(KoCanvasController *canvasController, QObject *parent = 0);
public:
// Convenience methods to invoke the signals from subclasses
void emitCanvasRemoved(KoCanvasController *canvasController) { emit canvasRemoved(canvasController); }
void emitCanvasSet(KoCanvasController *canvasController) { emit canvasSet(canvasController); }
void emitCanvasOffsetXChanged(int offset) { emit canvasOffsetXChanged(offset); }
void emitCanvasOffsetYChanged(int offset) { emit canvasOffsetYChanged(offset); }
void emitCanvasMousePositionChanged(const QPoint &position) { emit canvasMousePositionChanged(position); }
void emitDocumentMousePositionChanged(const QPointF &position) { emit documentMousePositionChanged(position); }
void emitSizeChanged(const QSize &size) { emit sizeChanged(size); }
void emitMoveDocumentOffset(const QPoint &point) { emit moveDocumentOffset(point); }
void emitZoomRelative(const qreal factor, const QPointF &stillPoint) { emit zoomRelative(factor, stillPoint); }
// Convenience method to retrieve the canvas controller for who needs to use QPointer
KoCanvasController *canvasController() const { return m_canvasController; }
Q_SIGNALS:
/**
* Emitted when a previously added canvas is about to be removed.
* @param canvasController this object
*/
void canvasRemoved(KoCanvasController *canvasController);
/**
* Emitted when a canvas is set on this widget
* @param canvasController this object
*/
void canvasSet(KoCanvasController *canvasController);
/**
* Emitted when canvasOffsetX() changes
* @param offset the new canvas offset
*/
void canvasOffsetXChanged(int offset);
/**
* Emitted when canvasOffsetY() changes
* @param offset the new canvas offset
*/
void canvasOffsetYChanged(int offset);
/**
* Emitted when the cursor is moved over the canvas widget.
* @param position the position in view coordinates (pixels).
*/
void canvasMousePositionChanged(const QPoint &position);
/**
* Emitted when the cursor is moved over the canvas widget.
* @param position the position in document coordinates.
*
* Use \ref canvasMousePositionChanged to get the position
* in view coordinates.
*/
void documentMousePositionChanged(const QPointF &position);
/**
* Emitted when the entire controller size changes
* @param size the size in widget pixels.
*/
void sizeChanged(const QSize &size);
/**
* Emitted whenever the document is scrolled.
*
* @param point the new top-left point from which the document should
* be drawn.
*/
void moveDocumentOffset(const QPoint &point);
/**
* Emitted when zoomRelativeToPoint have calculated a factor by which
* the zoom should change and the point which should stand still
* on screen.
* Someone needs to connect to this and take action
*
* @param factor by how much the zoom needs to change.
* @param stillPoint the point which will not change its position
* in widget during the zooming. It is measured in
* view coordinate system *before* zoom.
*/
void zoomRelative(const qreal factor, const QPointF &stillPoint);
public Q_SLOTS:
/**
* Call this slot whenever the size of your document in view coordinates (pixels)
* changes, for instance when zooming.
* @param newSize the new size, in view coordinates (pixels), of the document.
* @param recalculateCenter if true the offset in the document we center on after calling
* recenterPreferred() will be recalculated for the new document size so the visual offset stays the same.
*/
void updateDocumentSize(const QSize &newSize, bool recalculateCenter = true);
private:
KoCanvasController *m_canvasController;
};
class KRITAFLAKE_EXPORT KoDummyCanvasController : public KoCanvasController {
public:
explicit KoDummyCanvasController(KActionCollection* actionCollection)
: KoCanvasController(actionCollection)
{}
virtual ~KoDummyCanvasController()
{}
virtual void scrollContentsBy(int /*dx*/, int /*dy*/) {}
virtual QSize viewportSize() const { return QSize(); }
virtual void setDrawShadow(bool /*drawShadow*/) {}
virtual void setCanvas(KoCanvasBase *canvas) {Q_UNUSED(canvas)}
virtual KoCanvasBase *canvas() const {return 0;}
virtual int visibleHeight() const {return 0;}
virtual int visibleWidth() const {return 0;}
virtual int canvasOffsetX() const {return 0;}
virtual int canvasOffsetY() const {return 0;}
virtual void ensureVisible(const QRectF &/*rect*/, bool /*smooth */ = false) {}
virtual void ensureVisible(KoShape *shape) {Q_UNUSED(shape)}
virtual void zoomIn(const QPoint &/*center*/) {}
virtual void zoomOut(const QPoint &/*center*/) {}
virtual void zoomBy(const QPoint &/*center*/, qreal /*zoom*/) {}
virtual void zoomTo(const QRect &/*rect*/) {}
virtual void recenterPreferred() {}
virtual void setPreferredCenter(const QPointF &/*viewPoint*/) {}
virtual QPointF preferredCenter() const {return QPointF();}
virtual void pan(const QPoint &/*distance*/) {}
virtual QPoint scrollBarValue() const {return QPoint();}
virtual void setScrollBarValue(const QPoint &/*value*/) {}
virtual void updateDocumentSize(const QSize &/*sz*/, bool /*recalculateCenter*/) {}
virtual void setZoomWithWheel(bool /*zoom*/) {}
virtual void setVastScrolling(qreal /*factor*/) {}
+ QPointF currentCursorPosition() const override { return QPointF(); }
};
#endif
diff --git a/libs/flake/KoCanvasControllerWidget.cpp b/libs/flake/KoCanvasControllerWidget.cpp
index 91695c757c..820559bd72 100644
--- a/libs/flake/KoCanvasControllerWidget.cpp
+++ b/libs/flake/KoCanvasControllerWidget.cpp
@@ -1,588 +1,595 @@
/* This file is part of the KDE project
*
* Copyright (C) 2006, 2008-2009 Thomas Zander
* Copyright (C) 2006 Peter Simonsson
* Copyright (C) 2006, 2009 Thorsten Zachmann
* Copyright (C) 2007-2010 Boudewijn Rempt
* Copyright (C) 2007 C. Boemann
* Copyright (C) 2006-2008 Jan Hambrecht
*
* 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.
*/
#include "KoCanvasControllerWidget.h"
#include "KoCanvasControllerWidget_p.h"
#include "KoCanvasControllerWidgetViewport_p.h"
#include "KoShape.h"
#include "KoViewConverter.h"
#include "KoCanvasBase.h"
#include "KoCanvasObserverBase.h"
#include "KoCanvasSupervisor.h"
#include "KoToolManager_p.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
void KoCanvasControllerWidget::Private::setDocumentOffset()
{
// The margins scroll the canvas widget inside the viewport, not
// the document. The documentOffset is meant to be the value that
// the canvas must add to the update rect in its paint event, to
// compensate.
QPoint pt(q->horizontalScrollBar()->value(), q->verticalScrollBar()->value());
q->proxyObject->emitMoveDocumentOffset(pt);
QWidget *canvasWidget = canvas->canvasWidget();
if (canvasWidget) {
// If it isn't an OpenGL canvas
if (qobject_cast(canvasWidget) == 0) {
QPoint diff = q->documentOffset() - pt;
if (q->canvasMode() == Spreadsheet && canvasWidget->layoutDirection() == Qt::RightToLeft) {
canvasWidget->scroll(-diff.x(), diff.y());
} else {
canvasWidget->scroll(diff.x(), diff.y());
}
}
}
q->setDocumentOffset(pt);
}
void KoCanvasControllerWidget::Private::resetScrollBars()
{
// The scrollbar value always points at the top-left corner of the
// bit of image we paint.
int docH = q->documentSize().height() + q->margin();
int docW = q->documentSize().width() + q->margin();
int drawH = viewportWidget->height();
int drawW = viewportWidget->width();
QScrollBar *hScroll = q->horizontalScrollBar();
QScrollBar *vScroll = q->verticalScrollBar();
int horizontalReserve = vastScrollingFactor * drawW;
int verticalReserve = vastScrollingFactor * drawH;
int xMin = -horizontalReserve;
int yMin = -verticalReserve;
int xMax = docW - drawW + horizontalReserve;
int yMax = docH - drawH + verticalReserve;
hScroll->setRange(xMin, xMax);
vScroll->setRange(yMin, yMax);
int fontheight = QFontMetrics(q->font()).height();
vScroll->setPageStep(drawH);
vScroll->setSingleStep(fontheight);
hScroll->setPageStep(drawW);
hScroll->setSingleStep(fontheight);
}
void KoCanvasControllerWidget::Private::emitPointerPositionChangedSignals(QEvent *event)
{
if (!canvas) return;
if (!canvas->viewConverter()) return;
QPoint pointerPos;
QMouseEvent *mouseEvent = dynamic_cast(event);
if (mouseEvent) {
pointerPos = mouseEvent->pos();
} else {
QTabletEvent *tabletEvent = dynamic_cast(event);
if (tabletEvent) {
pointerPos = tabletEvent->pos();
}
}
QPoint pixelPos = (pointerPos - canvas->documentOrigin()) + q->documentOffset();
QPointF documentPos = canvas->viewConverter()->viewToDocument(pixelPos);
q->proxyObject->emitDocumentMousePositionChanged(documentPos);
q->proxyObject->emitCanvasMousePositionChanged(pointerPos);
}
#include
void KoCanvasControllerWidget::Private::activate()
{
QWidget *parent = q;
while (parent->parentWidget()) {
parent = parent->parentWidget();
}
KoCanvasSupervisor *observerProvider = dynamic_cast(parent);
if (!observerProvider) {
return;
}
Q_FOREACH (KoCanvasObserverBase *docker, observerProvider->canvasObservers()) {
KoCanvasObserverBase *observer = dynamic_cast(docker);
if (observer) {
observer->setObservedCanvas(q->canvas());
}
}
}
void KoCanvasControllerWidget::Private::unsetCanvas()
{
QWidget *parent = q;
while (parent->parentWidget()) {
parent = parent->parentWidget();
}
KoCanvasSupervisor *observerProvider = dynamic_cast(parent);
if (!observerProvider) {
return;
}
Q_FOREACH (KoCanvasObserverBase *docker, observerProvider->canvasObservers()) {
KoCanvasObserverBase *observer = dynamic_cast(docker);
if (observer && observer->observedCanvas() == q->canvas()) {
observer->unsetObservedCanvas();
}
}
}
////////////
KoCanvasControllerWidget::KoCanvasControllerWidget(KActionCollection * actionCollection, QWidget *parent)
: QAbstractScrollArea(parent)
, KoCanvasController(actionCollection)
, d(new Private(this))
{
// We need to set this as QDeclarativeView sets them a bit differnt from QAbstractScrollArea
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
// And then our own Viewport
d->viewportWidget = new Viewport(this);
setViewport(d->viewportWidget);
d->viewportWidget->setFocusPolicy(Qt::NoFocus);
setFocusPolicy(Qt::NoFocus);
setFrameStyle(0);
//setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
setAutoFillBackground(false);
/*
Fixes: apps starting at zero zoom.
Details: Since the document is set on the mainwindow before loading commences the inial show/layout can choose
to set the document to be very small, even to be zero pixels tall. Setting a sane minimum size on the
widget means we no loger get rounding errors in zooming and we no longer end up with zero-zoom.
Note: KoPage apps should probably startup with a sane document size; for Krita that's impossible
*/
setMinimumSize(QSize(50, 50));
setMouseTracking(true);
connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(updateCanvasOffsetX()));
connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(updateCanvasOffsetY()));
connect(d->viewportWidget, SIGNAL(sizeChanged()), this, SLOT(updateCanvasOffsetX()));
connect(proxyObject, SIGNAL(moveDocumentOffset(const QPoint&)), d->viewportWidget, SLOT(documentOffsetMoved(const QPoint&)));
}
KoCanvasControllerWidget::~KoCanvasControllerWidget()
{
d->unsetCanvas();
delete d;
}
void KoCanvasControllerWidget::activate()
{
d->activate();
}
void KoCanvasControllerWidget::scrollContentsBy(int dx, int dy)
{
Q_UNUSED(dx);
Q_UNUSED(dy);
d->setDocumentOffset();
}
QSize KoCanvasControllerWidget::viewportSize() const
{
return viewport()->size();
}
void KoCanvasControllerWidget::setDrawShadow(bool drawShadow)
{
d->viewportWidget->setDrawShadow(drawShadow);
}
void KoCanvasControllerWidget::resizeEvent(QResizeEvent *resizeEvent)
{
proxyObject->emitSizeChanged(resizeEvent->size());
// XXX: When resizing, keep the area we're looking at now in the
// center of the resized view.
d->resetScrollBars();
d->setDocumentOffset();
}
void KoCanvasControllerWidget::setCanvas(KoCanvasBase *canvas)
{
Q_ASSERT(canvas); // param is not null
if (d->canvas) {
d->unsetCanvas();
proxyObject->emitCanvasRemoved(this);
canvas->setCanvasController(0);
d->canvas->canvasWidget()->removeEventFilter(this);
}
canvas->setCanvasController(this);
d->canvas = canvas;
changeCanvasWidget(canvas->canvasWidget());
proxyObject->emitCanvasSet(this);
QTimer::singleShot(0, this, SLOT(activate()));
setPreferredCenterFractionX(0);
setPreferredCenterFractionY(0);
}
KoCanvasBase* KoCanvasControllerWidget::canvas() const
{
return d->canvas;
}
void KoCanvasControllerWidget::changeCanvasWidget(QWidget *widget)
{
if (d->viewportWidget->canvas()) {
widget->setCursor(d->viewportWidget->canvas()->cursor());
d->viewportWidget->canvas()->removeEventFilter(this);
}
d->viewportWidget->setCanvas(widget);
setFocusProxy(d->canvas->canvasWidget());
}
int KoCanvasControllerWidget::visibleHeight() const
{
if (d->canvas == 0)
return 0;
QWidget *canvasWidget = canvas()->canvasWidget();
int height1;
if (canvasWidget == 0)
height1 = viewport()->height();
else
height1 = qMin(viewport()->height(), canvasWidget->height());
int height2 = height();
return qMin(height1, height2);
}
int KoCanvasControllerWidget::visibleWidth() const
{
if (d->canvas == 0)
return 0;
QWidget *canvasWidget = canvas()->canvasWidget();
int width1;
if (canvasWidget == 0)
width1 = viewport()->width();
else
width1 = qMin(viewport()->width(), canvasWidget->width());
int width2 = width();
return qMin(width1, width2);
}
int KoCanvasControllerWidget::canvasOffsetX() const
{
int offset = -horizontalScrollBar()->value();
if (d->canvas) {
offset += d->canvas->canvasWidget()->x() + frameWidth();
}
return offset;
}
int KoCanvasControllerWidget::canvasOffsetY() const
{
int offset = -verticalScrollBar()->value();
if (d->canvas) {
offset += d->canvas->canvasWidget()->y() + frameWidth();
}
return offset;
}
void KoCanvasControllerWidget::updateCanvasOffsetX()
{
proxyObject->emitCanvasOffsetXChanged(canvasOffsetX());
if (d->ignoreScrollSignals)
return;
setPreferredCenterFractionX((horizontalScrollBar()->value()
+ viewport()->width() / 2.0) / documentSize().width());
}
void KoCanvasControllerWidget::updateCanvasOffsetY()
{
proxyObject->emitCanvasOffsetYChanged(canvasOffsetY());
if (d->ignoreScrollSignals)
return;
setPreferredCenterFractionY((verticalScrollBar()->value()
+ verticalScrollBar()->pageStep() / 2.0) / documentSize().height());
}
void KoCanvasControllerWidget::ensureVisible(KoShape *shape)
{
Q_ASSERT(shape);
ensureVisible(d->canvas->viewConverter()->documentToView(shape->boundingRect()));
}
void KoCanvasControllerWidget::ensureVisible(const QRectF &rect, bool smooth)
{
QRect currentVisible(-canvasOffsetX(), -canvasOffsetY(), visibleWidth(), visibleHeight());
QRect viewRect = rect.toRect();
viewRect.translate(d->canvas->documentOrigin());
if (!viewRect.isValid() || currentVisible.contains(viewRect))
return; // its visible. Nothing to do.
// if we move, we move a little more so the amount of times we have to move is less.
int jumpWidth = smooth ? 0 : currentVisible.width() / 5;
int jumpHeight = smooth ? 0 : currentVisible.height() / 5;
if (!smooth && viewRect.width() + jumpWidth > currentVisible.width())
jumpWidth = 0;
if (!smooth && viewRect.height() + jumpHeight > currentVisible.height())
jumpHeight = 0;
int horizontalMove = 0;
if (currentVisible.width() <= viewRect.width()) // center view
horizontalMove = viewRect.center().x() - currentVisible.center().x();
else if (currentVisible.x() > viewRect.x()) // move left
horizontalMove = viewRect.x() - currentVisible.x() - jumpWidth;
else if (currentVisible.right() < viewRect.right()) // move right
horizontalMove = viewRect.right() - qMax(0, currentVisible.right() - jumpWidth);
int verticalMove = 0;
if (currentVisible.height() <= viewRect.height()) // center view
verticalMove = viewRect.center().y() - currentVisible.center().y();
if (currentVisible.y() > viewRect.y()) // move up
verticalMove = viewRect.y() - currentVisible.y() - jumpHeight;
else if (currentVisible.bottom() < viewRect.bottom()) // move down
verticalMove = viewRect.bottom() - qMax(0, currentVisible.bottom() - jumpHeight);
pan(QPoint(horizontalMove, verticalMove));
}
void KoCanvasControllerWidget::recenterPreferred()
{
const bool oldIgnoreScrollSignals = d->ignoreScrollSignals;
d->ignoreScrollSignals = true;
QPointF center = preferredCenter();
// convert into a viewport based point
center.rx() += d->canvas->canvasWidget()->x() + frameWidth();
center.ry() += d->canvas->canvasWidget()->y() + frameWidth();
// scroll to a new center point
QPointF topLeft = center - 0.5 * QPointF(viewport()->width(), viewport()->height());
setScrollBarValue(topLeft.toPoint());
d->ignoreScrollSignals = oldIgnoreScrollSignals;
}
void KoCanvasControllerWidget::zoomIn(const QPoint ¢er)
{
zoomBy(center, sqrt(2.0));
}
void KoCanvasControllerWidget::zoomOut(const QPoint ¢er)
{
zoomBy(center, sqrt(0.5));
}
void KoCanvasControllerWidget::zoomBy(const QPoint ¢er, qreal zoom)
{
setPreferredCenterFractionX(1.0 * center.x() / documentSize().width());
setPreferredCenterFractionY(1.0 * center.y() / documentSize().height());
const bool oldIgnoreScrollSignals = d->ignoreScrollSignals;
d->ignoreScrollSignals = true;
proxyObject->emitZoomRelative(zoom, preferredCenter());
d->ignoreScrollSignals = oldIgnoreScrollSignals;
}
void KoCanvasControllerWidget::zoomTo(const QRect &viewRect)
{
qreal scale;
if (1.0 * viewport()->width() / viewRect.width() > 1.0 * viewport()->height() / viewRect.height())
scale = 1.0 * viewport()->height() / viewRect.height();
else
scale = 1.0 * viewport()->width() / viewRect.width();
zoomBy(viewRect.center(), scale);
}
void KoCanvasControllerWidget::updateDocumentSize(const QSize &sz, bool recalculateCenter)
{
// Don't update if the document-size didn't changed to prevent infinite loops and unneeded updates.
if (KoCanvasController::documentSize() == sz)
return;
if (!recalculateCenter) {
// assume the distance from the top stays equal and recalculate the center.
setPreferredCenterFractionX(documentSize().width() * preferredCenterFractionX() / sz.width());
setPreferredCenterFractionY(documentSize().height() * preferredCenterFractionY() / sz.height());
}
const bool oldIgnoreScrollSignals = d->ignoreScrollSignals;
d->ignoreScrollSignals = true;
KoCanvasController::setDocumentSize(sz);
d->viewportWidget->setDocumentSize(sz);
d->resetScrollBars();
// Always emit the new offset.
updateCanvasOffsetX();
updateCanvasOffsetY();
d->ignoreScrollSignals = oldIgnoreScrollSignals;
}
void KoCanvasControllerWidget::setZoomWithWheel(bool zoom)
{
d->zoomWithWheel = zoom;
}
void KoCanvasControllerWidget::setVastScrolling(qreal factor)
{
d->vastScrollingFactor = factor;
}
+QPointF KoCanvasControllerWidget::currentCursorPosition() const
+{
+ QWidget *canvasWidget = d->canvas->canvasWidget();
+ const KoViewConverter *converter = d->canvas->viewConverter();
+ return converter->viewToDocument(canvasWidget->mapFromGlobal(QCursor::pos()) + d->canvas->canvasController()->documentOffset() - canvasWidget->pos());
+}
+
void KoCanvasControllerWidget::pan(const QPoint &distance)
{
QPoint sourcePoint = scrollBarValue();
setScrollBarValue(sourcePoint + distance);
}
void KoCanvasControllerWidget::setPreferredCenter(const QPointF &viewPoint)
{
setPreferredCenterFractionX(viewPoint.x() / documentSize().width());
setPreferredCenterFractionY(viewPoint.y() / documentSize().height());
recenterPreferred();
}
QPointF KoCanvasControllerWidget::preferredCenter() const
{
QPointF center;
center.setX(preferredCenterFractionX() * documentSize().width());
center.setY(preferredCenterFractionY() * documentSize().height());
return center;
}
void KoCanvasControllerWidget::paintEvent(QPaintEvent *event)
{
QPainter gc(viewport());
d->viewportWidget->handlePaintEvent(gc, event);
}
void KoCanvasControllerWidget::dragEnterEvent(QDragEnterEvent *event)
{
d->viewportWidget->handleDragEnterEvent(event);
}
void KoCanvasControllerWidget::dropEvent(QDropEvent *event)
{
d->viewportWidget->handleDropEvent(event);
}
void KoCanvasControllerWidget::dragMoveEvent(QDragMoveEvent *event)
{
d->viewportWidget->handleDragMoveEvent(event);
}
void KoCanvasControllerWidget::dragLeaveEvent(QDragLeaveEvent *event)
{
d->viewportWidget->handleDragLeaveEvent(event);
}
void KoCanvasControllerWidget::wheelEvent(QWheelEvent *event)
{
if (d->zoomWithWheel != ((event->modifiers() & Qt::ControlModifier) == Qt::ControlModifier)) {
const qreal zoomCoeff = event->delta() > 0 ? sqrt(2.0) : sqrt(0.5);
zoomRelativeToPoint(event->pos(), zoomCoeff);
event->accept();
} else
QAbstractScrollArea::wheelEvent(event);
}
void KoCanvasControllerWidget::zoomRelativeToPoint(const QPoint &widgetPoint, qreal zoomCoeff)
{
const QPoint offset = scrollBarValue();
const QPoint mousePos(widgetPoint + offset);
const bool oldIgnoreScrollSignals = d->ignoreScrollSignals;
d->ignoreScrollSignals = true;
proxyObject->emitZoomRelative(zoomCoeff, mousePos);
d->ignoreScrollSignals = oldIgnoreScrollSignals;
}
bool KoCanvasControllerWidget::focusNextPrevChild(bool)
{
// we always return false meaning the canvas takes keyboard focus, but never gives it away.
return false;
}
void KoCanvasControllerWidget::setMargin(int margin)
{
KoCanvasController::setMargin(margin);
Q_ASSERT(d->viewportWidget);
d->viewportWidget->setMargin(margin);
}
QPoint KoCanvasControllerWidget::scrollBarValue() const
{
QScrollBar * hBar = horizontalScrollBar();
QScrollBar * vBar = verticalScrollBar();
return QPoint(hBar->value(), vBar->value());
}
void KoCanvasControllerWidget::setScrollBarValue(const QPoint &value)
{
QScrollBar * hBar = horizontalScrollBar();
QScrollBar * vBar = verticalScrollBar();
hBar->setValue(value.x());
vBar->setValue(value.y());
}
KoCanvasControllerWidget::Private *KoCanvasControllerWidget::priv()
{
return d;
}
//have to include this because of Q_PRIVATE_SLOT
#include "moc_KoCanvasControllerWidget.cpp"
diff --git a/libs/flake/KoCanvasControllerWidget.h b/libs/flake/KoCanvasControllerWidget.h
index f8f3bf1da7..68d9d80f2f 100644
--- a/libs/flake/KoCanvasControllerWidget.h
+++ b/libs/flake/KoCanvasControllerWidget.h
@@ -1,182 +1,184 @@
/* This file is part of the KDE project
* Copyright (C) 2006, 2008 Thomas Zander
* Copyright (C) 2007-2010 Boudewijn Rempt
* Copyright (C) 2007-2008 C. Boemann
* Copyright (C) 2006-2007 Jan Hambrecht
* Copyright (C) 2009 Thorsten Zachmann
*
* 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.
*/
#ifndef KOCANVASCONTROLLERWIDGET_H
#define KOCANVASCONTROLLERWIDGET_H
#include "kritaflake_export.h"
#include
#include
#include "KoCanvasController.h"
class KoShape;
class KoCanvasBase;
/**
* KoCanvasController implementation for QWidget based canvases
*/
class KRITAFLAKE_EXPORT KoCanvasControllerWidget : public QAbstractScrollArea, public KoCanvasController
{
Q_OBJECT
public:
/**
* Constructor.
* @param parent the parent this widget will belong to
*/
explicit KoCanvasControllerWidget(KActionCollection * actionCollection, QWidget *parent = 0);
virtual ~KoCanvasControllerWidget();
/**
* Reimplemented from QAbstractScrollArea.
*/
void scrollContentsBy(int dx, int dy);
virtual QSize viewportSize() const;
/// Reimplemented from KoCanvasController
/**
* Activate this canvascontroller
*/
virtual void activate();
virtual void setDrawShadow(bool drawShadow);
virtual void setCanvas(KoCanvasBase *canvas);
virtual KoCanvasBase *canvas() const;
/**
* Change the actual canvas widget used by the current canvas. This allows the canvas widget
* to be changed while keeping the current KoCanvasBase canvas and its associated resources as
* they are. This might be used, for example, to switch from a QWidget to a QOpenGLWidget canvas.
* @param widget the new canvas widget.
*/
virtual void changeCanvasWidget(QWidget *widget);
virtual int visibleHeight() const;
virtual int visibleWidth() const;
virtual int canvasOffsetX() const;
virtual int canvasOffsetY() const;
virtual void ensureVisible(const QRectF &rect, bool smooth = false);
virtual void ensureVisible(KoShape *shape);
/**
* will cause the toolOptionWidgetsChanged to be emitted and all
* listeners to be updated to the new widget.
*
* FIXME: This doesn't belong her and it does an
* inherits("KoView") so it too much tied to komain
*
* @param widgets the map of widgets
*/
void setToolOptionWidgets(const QList > &widgets);
virtual void zoomIn(const QPoint ¢er);
virtual void zoomOut(const QPoint ¢er);
virtual void zoomBy(const QPoint ¢er, qreal zoom);
virtual void zoomTo(const QRect &rect);
/**
* Zoom document keeping point \p widgetPoint unchanged
* \param widgetPoint sticky point in widget pixels
*/
virtual void zoomRelativeToPoint(const QPoint &widgetPoint, qreal zoomCoeff);
virtual void recenterPreferred();
virtual void setPreferredCenter(const QPointF &viewPoint);
/// Returns the currently set preferred center point in view coordinates (pixels)
virtual QPointF preferredCenter() const;
virtual void pan(const QPoint &distance);
virtual void setMargin(int margin);
virtual QPoint scrollBarValue() const;
/**
* Used by KisCanvasController to correct the scrollbars position
* after the rotation.
*/
virtual void setScrollBarValue(const QPoint &value);
virtual void updateDocumentSize(const QSize &sz, bool recalculateCenter = true);
/**
* Set mouse wheel to zoom behaviour
* @param zoom if true wheel will zoom instead of scroll, control modifier will scroll
*/
void setZoomWithWheel(bool zoom);
virtual void setVastScrolling(qreal factor);
+ QPointF currentCursorPosition() const override;
+
/**
* \internal
*/
class Private;
KoCanvasControllerWidget::Private *priv();
private Q_SLOTS:
/// Called by the horizontal scrollbar when its value changes
void updateCanvasOffsetX();
/// Called by the vertical scrollbar when its value changes
void updateCanvasOffsetY();
protected:
friend class KisZoomAndPanTest;
/// reimplemented from QWidget
virtual void paintEvent(QPaintEvent *event);
/// reimplemented from QWidget
virtual void resizeEvent(QResizeEvent *resizeEvent);
/// reimplemented from QWidget
virtual void dragEnterEvent(QDragEnterEvent *event);
/// reimplemented from QWidget
virtual void dropEvent(QDropEvent *event);
/// reimplemented from QWidget
virtual void dragMoveEvent(QDragMoveEvent *event);
/// reimplemented from QWidget
virtual void dragLeaveEvent(QDragLeaveEvent *event);
/// reimplemented from QWidget
virtual void wheelEvent(QWheelEvent *event);
/// reimplemented from QWidget
virtual bool focusNextPrevChild(bool next);
private:
Q_PRIVATE_SLOT(d, void activate())
Private * const d;
};
#endif
diff --git a/libs/flake/KoCanvasControllerWidgetViewport_p.cpp b/libs/flake/KoCanvasControllerWidgetViewport_p.cpp
index edb24195b7..7cc545349e 100644
--- a/libs/flake/KoCanvasControllerWidgetViewport_p.cpp
+++ b/libs/flake/KoCanvasControllerWidgetViewport_p.cpp
@@ -1,384 +1,373 @@
/* This file is part of the KDE project
*
* Copyright (C) 2006-2007, 2009 Thomas Zander
* Copyright (C) 2006 Thorsten Zachmann
* Copyright (C) 2007-2010 Boudewijn Rempt
*
* 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.
*/
#include "KoCanvasControllerWidgetViewport_p.h"
#include
#include
#include
#include
#include
#include
#include
#include "KoShape.h"
#include "KoShape_p.h"
#include "KoShapeFactoryBase.h" // for the SHAPE mimetypes
#include "KoShapeRegistry.h"
#include "KoShapeController.h"
#include "KoShapeManager.h"
#include "KoSelection.h"
#include "KoCanvasBase.h"
#include "KoShapeLayer.h"
-#include "KoShapePaste.h"
#include "KoShapePaintingContext.h"
#include "KoToolProxy.h"
#include "KoCanvasControllerWidget.h"
#include "KoViewConverter.h"
// ********** Viewport **********
Viewport::Viewport(KoCanvasControllerWidget *parent)
: QWidget(parent)
, m_draggedShape(0)
, m_drawShadow(false)
, m_canvas(0)
, m_documentOffset(QPoint(0, 0))
, m_margin(0)
{
setAutoFillBackground(true);
setAcceptDrops(true);
setMouseTracking(true);
m_parent = parent;
}
void Viewport::setCanvas(QWidget *canvas)
{
if (m_canvas) {
m_canvas->hide();
delete m_canvas;
}
m_canvas = canvas;
if (!canvas) return;
m_canvas->setParent(this);
m_canvas->show();
if (!m_canvas->minimumSize().isNull()) {
m_documentSize = m_canvas->minimumSize();
}
resetLayout();
}
void Viewport::setDocumentSize(const QSize &size)
{
m_documentSize = size;
resetLayout();
}
void Viewport::documentOffsetMoved(const QPoint &pt)
{
m_documentOffset = pt;
resetLayout();
}
void Viewport::setDrawShadow(bool drawShadow)
{
m_drawShadow = drawShadow;
}
-
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.
- if (!(m_parent->canvas() && m_parent->canvas()->canvasWidget()))
+ if (!(m_parent->canvas() && m_parent->canvas()->canvasWidget())) {
+ event->ignore();
return;
+ }
// 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()))
+ if (activeLayer && (!activeLayer->isEditable() || activeLayer->isGeometryProtected())) {
+ event->ignore();
return;
+ }
const QMimeData *data = event->mimeData();
if (data->hasFormat(SHAPETEMPLATE_MIMETYPE) ||
data->hasFormat(SHAPEID_MIMETYPE)) {
QByteArray itemData;
bool isTemplate = true;
if (data->hasFormat(SHAPETEMPLATE_MIMETYPE))
itemData = data->data(SHAPETEMPLATE_MIMETYPE);
else {
isTemplate = false;
itemData = data->data(SHAPEID_MIMETYPE);
}
QDataStream dataStream(&itemData, QIODevice::ReadOnly);
QString id;
dataStream >> id;
QString properties;
if (isTemplate)
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)
KoShapeFactoryBase *factory = KoShapeRegistry::instance()->value(id);
if (! factory) {
warnFlake << "Application requested a shape that is not registered '" <<
id << "', Ignoring";
event->ignore();
return;
}
event->setDropAction(Qt::CopyAction);
event->accept();
if (isTemplate) {
KoProperties props;
props.load(properties);
m_draggedShape = factory->createShape(&props, m_parent->canvas()->shapeController()->resourceManager());
} else
m_draggedShape = factory->createDefaultShape(m_parent->canvas()->shapeController()->resourceManager());
Q_ASSERT(m_draggedShape);
if (!m_draggedShape) return;
if (m_draggedShape->shapeId().isEmpty())
m_draggedShape->setShapeId(factory->id());
m_draggedShape->setZIndex(KoShapePrivate::MaxZIndex);
m_draggedShape->setAbsolutePosition(correctPosition(event->pos()));
m_parent->canvas()->shapeManager()->addShape(m_draggedShape);
- }
- 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 shapes = paste.pastedShapes();
- if (shapes.count() == 1) {
- m_draggedShape = shapes.first();
- m_draggedShape->setZIndex(KoShapePrivate::MaxZIndex);
- event->setDropAction(Qt::CopyAction);
- }
- event->accept();
- }
} else {
event->ignore();
}
}
void Viewport::handleDropEvent(QDropEvent *event)
{
if (!m_draggedShape) {
m_parent->canvas()->toolProxy()->dropEvent(event, correctPosition(event->pos()));
return;
}
repaint(m_draggedShape);
m_parent->canvas()->shapeManager()->remove(m_draggedShape); // remove it to not interfere with z-index calc.
m_draggedShape->setPosition(QPointF(0, 0)); // always save position.
QPointF newPos = correctPosition(event->pos());
m_parent->canvas()->clipToDocument(m_draggedShape, newPos); // ensure the shape is dropped inside the document.
m_draggedShape->setAbsolutePosition(newPos);
KUndo2Command * cmd = m_parent->canvas()->shapeController()->addShape(m_draggedShape);
if (cmd) {
m_parent->canvas()->addCommand(cmd);
KoSelection *selection = m_parent->canvas()->shapeManager()->selection();
// repaint selection before selecting newly create shape
Q_FOREACH (KoShape * shape, selection->selectedShapes())
shape->update();
selection->deselectAll();
selection->select(m_draggedShape);
} else
delete m_draggedShape;
m_draggedShape = 0;
}
QPointF Viewport::correctPosition(const QPoint &point) const
{
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());
correctedPos += m_documentOffset;
return m_parent->canvas()->viewToDocument(correctedPos);
}
void Viewport::handleDragMoveEvent(QDragMoveEvent *event)
{
if (!m_draggedShape) {
m_parent->canvas()->toolProxy()->dragMoveEvent(event, correctPosition(event->pos()));
return;
}
m_draggedShape->update();
repaint(m_draggedShape);
m_draggedShape->setAbsolutePosition(correctPosition(event->pos()));
m_draggedShape->update();
repaint(m_draggedShape);
}
void Viewport::repaint(KoShape *shape)
{
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.
rect.moveLeft(rect.left() + canvasWidget->x() - m_documentOffset.x());
rect.moveTop(rect.top() + canvasWidget->y() - m_documentOffset.y());
rect.adjust(-2, -2, 2, 2); // adjust for antialias
update(rect);
}
void Viewport::handleDragLeaveEvent(QDragLeaveEvent *event)
{
if (m_draggedShape) {
repaint(m_draggedShape);
m_parent->canvas()->shapeManager()->remove(m_draggedShape);
delete m_draggedShape;
m_draggedShape = 0;
} else {
m_parent->canvas()->toolProxy()->dragLeaveEvent(event);
}
}
void Viewport::handlePaintEvent(QPainter &painter, QPaintEvent *event)
{
Q_UNUSED(event);
// Draw the shadow around the canvas.
if (m_parent->canvas() && m_parent->canvas()->canvasWidget() && m_drawShadow) {
QWidget *canvas = m_parent->canvas()->canvasWidget();
painter.setPen(QPen(Qt::black, 0));
QRect rect(canvas->x(), canvas->y(), canvas->width(), canvas->height());
rect.adjust(-1, -1, 0, 0);
painter.drawRect(rect);
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);
}
if (m_draggedShape) {
const KoViewConverter *vc = m_parent->canvas()->viewConverter();
painter.save();
QWidget *canvasWidget = m_parent->canvas()->canvasWidget();
Q_ASSERT(canvasWidget); // since we should not allow drag if there is not.
painter.translate(canvasWidget->x() - m_documentOffset.x(),
canvasWidget->y() - m_documentOffset.y());
QPointF offset = vc->documentToView(m_draggedShape->position());
painter.setOpacity(0.6);
painter.translate(offset.x(), offset.y());
painter.setRenderHint(QPainter::Antialiasing);
KoShapePaintingContext paintContext; //FIXME
m_draggedShape->paint(painter, *vc, paintContext);
painter.restore();
}
}
void Viewport::resetLayout()
{
// Determine the area we have to show
QRect viewRect(m_documentOffset, size());
const int viewH = viewRect.height();
const int viewW = viewRect.width();
const int docH = m_documentSize.height();
const int docW = m_documentSize.width();
int moveX = 0;
int moveY = 0;
int resizeW = viewW;
int resizeH = viewH;
// debugFlake <<"viewH:" << viewH << endl
// << "docH: " << docH << endl
// << "viewW: " << viewW << endl
// << "docW: " << docW << endl;
if (viewH == docH && viewW == docW) {
// Do nothing
resizeW = docW;
resizeH = docH;
} else if (viewH > docH && viewW > docW) {
// Show entire canvas centered
moveX = (viewW - docW) / 2;
moveY = (viewH - docH) / 2;
resizeW = docW;
resizeH = docH;
} else if (viewW > docW) {
// Center canvas horizontally
moveX = (viewW - docW) / 2;
resizeW = docW;
int marginTop = m_margin - m_documentOffset.y();
int marginBottom = viewH - (m_documentSize.height() - m_documentOffset.y());
if (marginTop > 0) moveY = marginTop;
if (marginTop > 0) resizeH = viewH - marginTop;
if (marginBottom > 0) resizeH = viewH - marginBottom;
} else if (viewH > docH) {
// Center canvas vertically
moveY = (viewH - docH) / 2;
resizeH = docH;
int marginLeft = m_margin - m_documentOffset.x();
int marginRight = viewW - (m_documentSize.width() - m_documentOffset.x());
if (marginLeft > 0) moveX = marginLeft;
if (marginLeft > 0) resizeW = viewW - marginLeft;
if (marginRight > 0) resizeW = viewW - marginRight;
} else {
// Take care of the margin around the canvas
int marginTop = m_margin - m_documentOffset.y();
int marginLeft = m_margin - m_documentOffset.x();
int marginRight = viewW - (m_documentSize.width() - m_documentOffset.x());
int marginBottom = viewH - (m_documentSize.height() - m_documentOffset.y());
if (marginTop > 0) moveY = marginTop;
if (marginLeft > 0) moveX = marginLeft;
if (marginTop > 0) resizeH = viewH - marginTop;
if (marginLeft > 0) resizeW = viewW - marginLeft;
if (marginRight > 0) resizeW = viewW - marginRight;
if (marginBottom > 0) resizeH = viewH - marginBottom;
}
if (m_parent->canvasMode() == KoCanvasController::AlignTop) {
// have up to m_margin pixels at top.
moveY = qMin(m_margin, moveY);
}
if (m_canvas) {
QRect geom;
if (m_parent->canvasMode() == KoCanvasController::Infinite)
geom = QRect(0, 0, viewW, viewH);
else
geom = QRect(moveX, moveY, resizeW, resizeH);
if (m_canvas->geometry() != geom) {
m_canvas->setGeometry(geom);
m_canvas->update();
}
}
if (m_drawShadow) {
update();
}
emit sizeChanged();
#if 0
debugFlake <<"View port geom:" << geometry();
if (m_canvas)
debugFlake <<"Canvas widget geom:" << m_canvas->geometry();
#endif
}
diff --git a/libs/flake/KoDocumentResourceManager.cpp b/libs/flake/KoDocumentResourceManager.cpp
index 7fad6b6597..15c791ed51 100644
--- a/libs/flake/KoDocumentResourceManager.cpp
+++ b/libs/flake/KoDocumentResourceManager.cpp
@@ -1,222 +1,201 @@
/*
Copyright (c) 2006 Boudewijn Rempt (boud@valdyas.org)
Copyright (C) 2007, 2010 Thomas Zander
Copyright (c) 2008 Carlos Licea
Copyright (c) 2011 Jan Hambrecht
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.
*/
#include "KoDocumentResourceManager.h"
#include
#include
#include
#include "KoShape.h"
#include "KoShapeController.h"
#include "KoResourceManager_p.h"
class Q_DECL_HIDDEN KoDocumentResourceManager::Private
{
public:
KoResourceManager manager;
};
KoDocumentResourceManager::KoDocumentResourceManager(QObject *parent)
: QObject(parent),
d(new Private())
{
connect(&d->manager, &KoResourceManager::resourceChanged,
this, &KoDocumentResourceManager::resourceChanged);
}
KoDocumentResourceManager::~KoDocumentResourceManager()
{
delete d;
}
void KoDocumentResourceManager::setResource(int key, const QVariant &value)
{
d->manager.setResource(key, value);
}
QVariant KoDocumentResourceManager::resource(int key) const
{
return d->manager.resource(key);
}
void KoDocumentResourceManager::setResource(int key, const KoColor &color)
{
QVariant v;
v.setValue(color);
setResource(key, v);
}
void KoDocumentResourceManager::setResource(int key, KoShape *shape)
{
QVariant v;
v.setValue(shape);
setResource(key, v);
}
void KoDocumentResourceManager::setResource(int key, const KoUnit &unit)
{
QVariant v;
v.setValue(unit);
setResource(key, v);
}
KoColor KoDocumentResourceManager::koColorResource(int key) const
{
return d->manager.koColorResource(key);
}
bool KoDocumentResourceManager::boolResource(int key) const
{
return d->manager.boolResource(key);
}
int KoDocumentResourceManager::intResource(int key) const
{
return d->manager.intResource(key);
}
QString KoDocumentResourceManager::stringResource(int key) const
{
return d->manager.stringResource(key);
}
QSizeF KoDocumentResourceManager::sizeResource(int key) const
{
return d->manager.sizeResource(key);
}
bool KoDocumentResourceManager::hasResource(int key) const
{
return d->manager.hasResource(key);
}
void KoDocumentResourceManager::clearResource(int key)
{
d->manager.clearResource(key);
}
KUndo2Stack *KoDocumentResourceManager::undoStack() const
{
if (!hasResource(UndoStack))
return 0;
return static_cast(resource(UndoStack).value());
}
void KoDocumentResourceManager::setHandleRadius(int handleRadius)
{
// do not allow arbitrary small handles
if (handleRadius < 5)
handleRadius = 5;
setResource(HandleRadius, QVariant(handleRadius));
}
int KoDocumentResourceManager::handleRadius() const
{
if (hasResource(HandleRadius))
return intResource(HandleRadius);
return 5; // default value (and is used just about everywhere)
}
void KoDocumentResourceManager::setGrabSensitivity(int grabSensitivity)
{
// do not allow arbitrary small grab sensitivity
if (grabSensitivity < 5)
grabSensitivity = 5;
setResource(GrabSensitivity, QVariant(grabSensitivity));
}
int KoDocumentResourceManager::grabSensitivity() const
{
if (hasResource(GrabSensitivity))
return intResource(GrabSensitivity);
return 5; // default value (and is used just about everywhere)
}
-void KoDocumentResourceManager::setPasteOffset(qreal pasteOffset)
-{
- setResource(PasteOffset, QVariant(pasteOffset));
-}
-
-qreal KoDocumentResourceManager::pasteOffset() const
-{
- return resource(PasteOffset).toDouble();
-}
-
-void KoDocumentResourceManager::enablePasteAtCursor(bool enable)
-{
- setResource(PasteAtCursor, QVariant(enable));
-}
-
-bool KoDocumentResourceManager::pasteAtCursor() const
-{
- return resource(PasteAtCursor).toBool();
-}
-
-
void KoDocumentResourceManager::setUndoStack(KUndo2Stack *undoStack)
{
QVariant variant;
variant.setValue(undoStack);
setResource(UndoStack, variant);
}
KoImageCollection *KoDocumentResourceManager::imageCollection() const
{
if (!hasResource(ImageCollection))
return 0;
return static_cast(resource(ImageCollection).value());
}
void KoDocumentResourceManager::setImageCollection(KoImageCollection *ic)
{
QVariant variant;
variant.setValue(ic);
setResource(ImageCollection, variant);
}
KoDocumentBase *KoDocumentResourceManager::odfDocument() const
{
if (!hasResource(OdfDocument))
return 0;
return static_cast(resource(OdfDocument).value());
}
void KoDocumentResourceManager::setOdfDocument(KoDocumentBase *currentDocument)
{
QVariant variant;
variant.setValue(currentDocument);
setResource(OdfDocument, variant);
}
KoShapeController *KoDocumentResourceManager::shapeController() const
{
if (!hasResource(ShapeController))
return 0;
return resource(ShapeController).value();
}
void KoDocumentResourceManager::setShapeController(KoShapeController *shapeController)
{
QVariant variant;
variant.setValue(shapeController);
setResource(ShapeController, variant);
}
diff --git a/libs/flake/KoDocumentResourceManager.h b/libs/flake/KoDocumentResourceManager.h
index d3a468f5ab..b24c06f23e 100644
--- a/libs/flake/KoDocumentResourceManager.h
+++ b/libs/flake/KoDocumentResourceManager.h
@@ -1,269 +1,253 @@
/*
Copyright (c) 2006 Boudewijn Rempt (boud@valdyas.org)
Copyright (C) 2007, 2009, 2010 Thomas Zander
Copyright (c) 2008 Carlos Licea
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.
*/
#ifndef KO_DOCUMENTRESOURCEMANAGER_H
#define KO_DOCUMENTRESOURCEMANAGER_H
#include
#include "kritaflake_export.h"
class KoShape;
class KUndo2Stack;
class KoImageCollection;
class KoDocumentBase;
class KoShapeController;
class KoColor;
class KoUnit;
class QVariant;
class QSizeF;
/**
* The KoResourceManager contains a set of per-canvas or per-document
* properties, like current foreground color, current background
* color and more. All tools belonging to the current canvas are
* notified when a Resource changes (is set).
*
* The properties come from the KoDocumentResourceManager::DocumentResource
* See KoShapeController::resourceManager
*
* The manager can contain all sorts of variable types and there are accessors
* for the most common ones. All variables are always stored inside a QVariant
* instance internally and you can always just use the resource() method to get
* that directly.
* The way to store arbitairy data objects that are stored as pointers you can use
* the following code snippets;
* @code
* QVariant variant;
* variant.setValue(textShapeData->document());
* resourceManager->setResource(KoText::CurrentTextDocument, variant);
* // and get it out again.
* QVariant var = resourceManager->resource(KoText::CurrentTextDocument);
* document = static_cast(var.value());
* @endcode
*/
class KRITAFLAKE_EXPORT KoDocumentResourceManager : public QObject
{
Q_OBJECT
public:
/**
* This enum holds identifiers to the resources that can be stored in here.
*/
enum DocumentResource {
UndoStack, ///< The document-wide undo stack (KUndo2Stack)
ImageCollection, ///< The KoImageCollection for the document
OdfDocument, ///< The document this canvas shows (KoDocumentBase)
- PasteOffset, ///< Application wide paste offset
- PasteAtCursor, ///< Application wide paste at cursor setting
HandleRadius, ///< The handle radius used for drawing handles of any kind
GrabSensitivity, ///< The grab sensitivity used for grabbing handles of any kind
MarkerCollection, ///< The collection holding all markers
ShapeController, ///< The KoShapeController for the document
KarbonStart = 1000, ///< Base number for Karbon specific values.
KexiStart = 2000, ///< Base number for Kexi specific values.
FlowStart = 3000, ///< Base number for Flow specific values.
PlanStart = 4000, ///< Base number for Plan specific values.
StageStart = 5000, ///< Base number for Stage specific values.
KritaStart = 6000, ///< Base number for Krita specific values.
SheetsStart = 7000, ///< Base number for Sheets specific values.
WordsStart = 8000, ///< Base number for Words specific values.
KoPageAppStart = 9000, ///< Base number for KoPageApp specific values.
KoTextStart = 10000 ///< Base number for KoText specific values.
};
/**
* Constructor.
* @param parent the parent QObject, used for memory management.
*/
explicit KoDocumentResourceManager(QObject *parent = 0);
~KoDocumentResourceManager();
/**
* Set a resource of any type.
* @param key the integer key
* @param value the new value for the key.
* @see KoDocumentResourceManager::DocumentResource
*/
void setResource(int key, const QVariant &value);
/**
* Set a resource of type KoColor.
* @param key the integer key
* @param color the new value for the key.
* @see KoDocumentResourceManager::DocumentResource
*/
void setResource(int key, const KoColor &color);
/**
* Set a resource of type KoShape*.
* @param key the integer key
* @param id the new value for the key.
* @see KoDocumentResourceManager::DocumentResource
*/
void setResource(int key, KoShape *shape);
/**
* Set a resource of type KoUnit
* @param key the integer key
* @param id the new value for the key.
* @see KoDocumentResourceManager::DocumentResource
*/
void setResource(int key, const KoUnit &unit);
/**
* Returns a qvariant containing the specified resource or a standard one if the
* specified resource does not exist.
* @param key the key
* @see KoDocumentResourceManager::DocumentResource
*/
QVariant resource(int key) const;
/**
* Return the resource determined by param key as a boolean.
* @param key the indentifying key for the resource
* @see KoDocumentResourceManager::DocumentResource
*/
bool boolResource(int key) const;
/**
* Return the resource determined by param key as an integer.
* @param key the indentifying key for the resource
* @see KoDocumentResourceManager::DocumentResource
*/
int intResource(int key) const;
/**
* Return the resource determined by param key as a KoColor.
* @param key the indentifying key for the resource
* @see KoDocumentResourceManager::DocumentResource
*/
KoColor koColorResource(int key) const;
/**
* Return the resource determined by param key as a pointer to a KoShape.
* @param key the indentifying key for the resource
* @see KoDocumentResourceManager::DocumentResource
*/
KoShape *koShapeResource(int key) const;
/**
* Return the resource determined by param key as a QString .
* @param key the indentifying key for the resource
* @see KoDocumentResourceManager::DocumentResource
*/
QString stringResource(int key) const;
/**
* Return the resource determined by param key as a QSizeF.
* @param key the indentifying key for the resource
* @see KoDocumentResourceManager::DocumentResource
*/
QSizeF sizeResource(int key) const;
/**
* Return the resource determined by param key as a KoUnit.
* @param key the indentifying key for the resource
* @see KoDocumentResourceManager::DocumentResource
*/
KoUnit unitResource(int key) const;
/**
* Returns true if there is a resource set with the requested key.
* @param key the indentifying key for the resource
* @see KoDocumentResourceManager::DocumentResource
*/
bool hasResource(int key) const;
/**
* Remove the resource with @p key from the provider.
* @param key the key that will be used to remove the resource
* There will be a signal emitted with a variable that will return true on QVariable::isNull();
* @see KoDocumentResourceManager::DocumentResource
*/
void clearResource(int key);
/**
* Tools that provide a handle for controlling the content that the tool can edit can
* use this property to alter the radius that a circular handle should have on screen.
* @param handleSize the radius in pixels.
*/
void setHandleRadius(int handleSize);
/// Returns the actual handle radius
int handleRadius() const;
/**
* Tools that are used to grab handles or similar with the mouse
* should use this value to determine if the mouse is near enough
* @param grabSensitivity the grab sensitivity in pixels
*/
void setGrabSensitivity(int grabSensitivity);
/// Returns the actual grab sensitivity
int grabSensitivity() const;
- /**
- * Offset used for pasting shapes to a document.
- */
- void setPasteOffset(qreal pasteOffset);
- /// Returns the current paste offset
- qreal pasteOffset() const;
-
- /**
- * Enables/disables pasting shape at cursor position
- */
- void enablePasteAtCursor(bool enable);
- /// Returns current state of paste at cursor setting
- bool pasteAtCursor() const;
-
KUndo2Stack *undoStack() const;
void setUndoStack(KUndo2Stack *undoStack);
KoImageCollection *imageCollection() const;
void setImageCollection(KoImageCollection *ic);
KoDocumentBase *odfDocument() const;
void setOdfDocument(KoDocumentBase *currentDocument);
KoShapeController *shapeController() const;
void setShapeController(KoShapeController *shapeController);
Q_SIGNALS:
/**
* This signal is emitted every time a resource is set that is either
* new or different from the previous set value.
* @param key the indentifying key for the resource
* @param value the variants new value.
* @see KoDocumentResourceManager::DocumentResource
*/
void resourceChanged(int key, const QVariant &value);
private:
KoDocumentResourceManager(const KoDocumentResourceManager&);
KoDocumentResourceManager& operator=(const KoDocumentResourceManager&);
class Private;
Private *const d;
};
#endif
diff --git a/libs/flake/KoDrag.cpp b/libs/flake/KoDrag.cpp
index 2f82798c27..08d5ccd328 100644
--- a/libs/flake/KoDrag.cpp
+++ b/libs/flake/KoDrag.cpp
@@ -1,149 +1,112 @@
/* This file is part of the KDE project
* Copyright (C) 2007-2008 Thorsten Zachmann
* Copyright (C) 2009 Thomas Zander
*
* 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.
*/
#include "KoDrag.h"
#include "KoDragOdfSaveHelper.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "KoShapeSavingContext.h"
+#include
+#include
+#include
+
+
class KoDragPrivate {
public:
KoDragPrivate() : mimeData(0) { }
~KoDragPrivate() { delete mimeData; }
QMimeData *mimeData;
};
KoDrag::KoDrag()
: d(new KoDragPrivate())
{
}
KoDrag::~KoDrag()
{
delete d;
}
-bool KoDrag::setOdf(const char *mimeType, KoDragOdfSaveHelper &helper)
+bool KoDrag::setSvg(const QList originalShapes)
{
- struct Finally {
- Finally(KoStore *s) : store(s) { }
- ~Finally() {
- delete store;
- }
- KoStore *store;
- };
-
- QBuffer buffer;
- KoStore *store = KoStore::createStore(&buffer, KoStore::Write, mimeType);
- Finally finally(store); // delete store when we exit this scope
- Q_ASSERT(store);
- Q_ASSERT(!store->bad());
-
- KoOdfWriteStore odfStore(store);
- KoEmbeddedDocumentSaver embeddedSaver;
-
- KoXmlWriter *manifestWriter = odfStore.manifestWriter(mimeType);
- KoXmlWriter *contentWriter = odfStore.contentWriter();
-
- if (!contentWriter) {
- return false;
- }
-
- KoGenStyles mainStyles;
- KoXmlWriter *bodyWriter = odfStore.bodyWriter();
- KoShapeSavingContext *context = helper.context(bodyWriter, mainStyles, embeddedSaver);
+ QRectF boundingRect;
+ QList shapes;
- if (!helper.writeBody()) {
- return false;
+ Q_FOREACH (KoShape *shape, originalShapes) {
+ boundingRect |= shape->boundingRect();
+ shapes.append(shape->cloneShape());
}
- mainStyles.saveOdfStyles(KoGenStyles::DocumentAutomaticStyles, contentWriter);
+ qSort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex);
- odfStore.closeContentWriter();
-
- //add manifest line for content.xml
- manifestWriter->addManifestEntry("content.xml", "text/xml");
-
-
- if (!mainStyles.saveOdfStylesDotXml(store, manifestWriter)) {
- return false;
- }
-
- if (!context->saveDataCenter(store, manifestWriter)) {
- debugFlake << "save data centers failed";
- return false;
- }
+ QBuffer buffer;
+ QLatin1String mimeType("image/svg+xml");
- // Save embedded objects
- KoDocumentBase::SavingContext documentContext(odfStore, embeddedSaver);
- if (!embeddedSaver.saveEmbeddedDocuments(documentContext)) {
- debugFlake << "save embedded documents failed";
- return false;
- }
+ buffer.open(QIODevice::WriteOnly);
- // Write out manifest file
- if (!odfStore.closeManifestWriter()) {
- return false;
- }
+ const QSizeF pageSize(boundingRect.right(), boundingRect.bottom());
+ SvgWriter writer(shapes, pageSize);
+ writer.save(buffer);
- delete store; // make sure the buffer if fully flushed.
- finally.store = 0;
- setData(mimeType, buffer.buffer());
+ buffer.close();
+ qDeleteAll(shapes);
+ setData(mimeType, buffer.data());
return true;
}
void KoDrag::setData(const QString &mimeType, const QByteArray &data)
{
if (d->mimeData == 0) {
d->mimeData = new QMimeData();
}
d->mimeData->setData(mimeType, data);
}
void KoDrag::addToClipboard()
{
if (d->mimeData) {
QApplication::clipboard()->setMimeData(d->mimeData);
d->mimeData = 0;
}
}
QMimeData * KoDrag::mimeData()
{
QMimeData *mimeData = d->mimeData;
d->mimeData = 0;
return mimeData;
}
diff --git a/libs/flake/KoDrag.h b/libs/flake/KoDrag.h
index 8ef1c26b0f..a5a8823305 100644
--- a/libs/flake/KoDrag.h
+++ b/libs/flake/KoDrag.h
@@ -1,78 +1,77 @@
/* This file is part of the KDE project
* Copyright (C) 2007 Thorsten Zachmann
*
* 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.
*/
#ifndef KODRAG_H
#define KODRAG_H
#include "kritaflake_export.h"
+#include
+
class QMimeData;
class QString;
class QByteArray;
class KoDragOdfSaveHelper;
class KoDragPrivate;
+class KoShape;
/**
* Class for simplifying adding a odf to the clip board
*
* For saving the odf a KoDragOdfSaveHelper class is used.
* It implements the writing of the body of the document. The
* setOdf takes care of saving styles and all the other
* common stuff.
*/
class KRITAFLAKE_EXPORT KoDrag
{
public:
KoDrag();
~KoDrag();
+
/**
- * Set odf mime type
- *
- * This calls helper.writeBody();
- *
- * @param mimeType used for creating the odf document
- * @param helper helper for saving the body of the odf document
+ * Load SVG data into the current mime data
*/
- bool setOdf(const char *mimeType, KoDragOdfSaveHelper &helper);
+ bool setSvg(const QList shapes);
/**
* Add additional mimeTypes
*/
void setData(const QString &mimeType, const QByteArray &data);
/**
* Add the mimeData to the clipboard
*/
void addToClipboard();
/**
* Get the mime data
*
* This transfers the ownership of the mimeData to the caller
*
* This function is for use in automated tests
*/
QMimeData *mimeData();
private:
KoDragPrivate * const d;
};
#endif /* KODRAG_H */
diff --git a/libs/flake/KoMarker.cpp b/libs/flake/KoMarker.cpp
index f041b710d9..0c51d9144e 100644
--- a/libs/flake/KoMarker.cpp
+++ b/libs/flake/KoMarker.cpp
@@ -1,421 +1,417 @@
/* This file is part of the KDE project
Copyright (C) 2011 Thorsten Zachmann
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.
*/
#include "KoMarker.h"
#include
#include
#include
#include
#include "KoPathShape.h"
#include "KoPathShapeLoader.h"
#include "KoShapeLoadingContext.h"
#include "KoShapeSavingContext.h"
#include "KoOdfWorkaround.h"
#include "KoShapePainter.h"
#include "KoViewConverter.h"
#include
#include
#include
#include
#include
#include
#include
#include "kis_global.h"
#include "kis_algebra_2d.h"
class Q_DECL_HIDDEN KoMarker::Private
{
public:
Private()
: coordinateSystem(StrokeWidth),
referenceSize(3,3),
hasAutoOrientation(false),
explicitOrientation(0)
{}
~Private() {
qDeleteAll(shapes);
}
bool operator==(const KoMarker::Private &other) const
{
// WARNING: comparison of shapes is extremely fuzzy! Don't
// trust it in life-critical cases!
return name == other.name &&
coordinateSystem == other.coordinateSystem &&
referencePoint == other.referencePoint &&
referenceSize == other.referenceSize &&
hasAutoOrientation == other.hasAutoOrientation &&
explicitOrientation == other.explicitOrientation &&
compareShapesTo(other.shapes);
}
Private(const Private &rhs)
: name(rhs.name),
coordinateSystem(rhs.coordinateSystem),
referencePoint(rhs.referencePoint),
referenceSize(rhs.referenceSize),
hasAutoOrientation(rhs.hasAutoOrientation),
explicitOrientation(rhs.explicitOrientation)
{
Q_FOREACH (KoShape *shape, rhs.shapes) {
shapes << shape->cloneShape();
}
}
QString name;
MarkerCoordinateSystem coordinateSystem;
QPointF referencePoint;
QSizeF referenceSize;
bool hasAutoOrientation;
qreal explicitOrientation;
QList shapes;
QScopedPointer shapePainter;
bool compareShapesTo(const QList other) const {
if (shapes.size() != other.size()) return false;
for (int i = 0; i < shapes.size(); i++) {
if (shapes[i]->outline() != other[i]->outline() ||
shapes[i]->absoluteTransformation(0) != other[i]->absoluteTransformation(0)) {
return false;
}
}
return true;
}
QTransform markerTransform(qreal strokeWidth, qreal nodeAngle, const QPointF &pos = QPointF()) {
const QTransform translate = QTransform::fromTranslate(-referencePoint.x(), -referencePoint.y());
QTransform t = translate;
if (coordinateSystem == StrokeWidth) {
t *= QTransform::fromScale(strokeWidth, strokeWidth);
}
const qreal angle = hasAutoOrientation ? nodeAngle : explicitOrientation;
if (angle != 0.0) {
QTransform r;
r.rotateRadians(angle);
t *= r;
}
t *= QTransform::fromTranslate(pos.x(), pos.y());
return t;
}
};
KoMarker::KoMarker()
: d(new Private())
{
}
KoMarker::~KoMarker()
{
delete d;
}
QString KoMarker::name() const
{
return d->name;
}
KoMarker::KoMarker(const KoMarker &rhs)
: QSharedData(rhs),
d(new Private(*rhs.d))
{
}
bool KoMarker::operator==(const KoMarker &other) const
{
return *d == *other.d;
}
void KoMarker::setCoordinateSystem(KoMarker::MarkerCoordinateSystem value)
{
d->coordinateSystem = value;
}
KoMarker::MarkerCoordinateSystem KoMarker::coordinateSystem() const
{
return d->coordinateSystem;
}
KoMarker::MarkerCoordinateSystem KoMarker::coordinateSystemFromString(const QString &value)
{
MarkerCoordinateSystem result = StrokeWidth;
if (value == "userSpaceOnUse") {
result = UserSpaceOnUse;
}
return result;
}
QString KoMarker::coordinateSystemToString(KoMarker::MarkerCoordinateSystem value)
{
return
value == StrokeWidth ?
"strokeWidth" :
"userSpaceOnUse";
}
void KoMarker::setReferencePoint(const QPointF &value)
{
d->referencePoint = value;
}
QPointF KoMarker::referencePoint() const
{
return d->referencePoint;
}
void KoMarker::setReferenceSize(const QSizeF &size)
{
d->referenceSize = size;
}
QSizeF KoMarker::referenceSize() const
{
return d->referenceSize;
}
bool KoMarker::hasAutoOtientation() const
{
return d->hasAutoOrientation;
}
void KoMarker::setAutoOrientation(bool value)
{
d->hasAutoOrientation = value;
}
qreal KoMarker::explicitOrientation() const
{
return d->explicitOrientation;
}
void KoMarker::setExplicitOrientation(qreal value)
{
d->explicitOrientation = value;
}
void KoMarker::setShapes(const QList &shapes)
{
d->shapes = shapes;
if (d->shapePainter) {
d->shapePainter->setShapes(shapes);
}
}
QList KoMarker::shapes() const
{
return d->shapes;
}
void KoMarker::paintAtPosition(QPainter *painter, const QPointF &pos, qreal strokeWidth, qreal nodeAngle)
{
QTransform oldTransform = painter->transform();
KoViewConverter converter;
if (!d->shapePainter) {
d->shapePainter.reset(new KoShapePainter());
d->shapePainter->setShapes(d->shapes);
}
painter->setTransform(d->markerTransform(strokeWidth, nodeAngle, pos), true);
d->shapePainter->paint(*painter, converter);
painter->setTransform(oldTransform);
}
qreal KoMarker::maxInset(qreal strokeWidth) const
{
QRectF shapesBounds = boundingRect(strokeWidth, 0.0); // normalized to 0,0
qreal result = 0.0;
result = qMax(KisAlgebra2D::norm(shapesBounds.topLeft()), result);
result = qMax(KisAlgebra2D::norm(shapesBounds.topRight()), result);
result = qMax(KisAlgebra2D::norm(shapesBounds.bottomLeft()), result);
result = qMax(KisAlgebra2D::norm(shapesBounds.bottomRight()), result);
if (d->coordinateSystem == StrokeWidth) {
result *= strokeWidth;
}
return result;
}
QRectF KoMarker::boundingRect(qreal strokeWidth, qreal nodeAngle) const
{
- QRectF shapesBounds;
-
- Q_FOREACH (KoShape *shape, d->shapes) {
- shapesBounds |= shape->boundingRect();
- }
+ QRectF shapesBounds = KoShape::boundingRect(d->shapes);
const QTransform t = d->markerTransform(strokeWidth, nodeAngle);
if (!t.isIdentity()) {
shapesBounds = t.mapRect(shapesBounds);
}
return shapesBounds;
}
QPainterPath KoMarker::outline(qreal strokeWidth, qreal nodeAngle) const
{
QPainterPath outline;
Q_FOREACH (KoShape *shape, d->shapes) {
outline |= shape->absoluteTransformation(0).map(shape->outline());
}
const QTransform t = d->markerTransform(strokeWidth, nodeAngle);
if (!t.isIdentity()) {
outline = t.map(outline);
}
return outline;
}
void KoMarker::drawPreview(QPainter *painter, const QRectF &previewRect, const QPen &pen, KoFlake::MarkerPosition position)
{
const QRectF outlineRect = outline(pen.widthF(), 0).boundingRect(); // normalized to 0,0
QPointF marker;
QPointF start;
QPointF end;
if (position == KoFlake::StartMarker) {
marker = QPointF(-outlineRect.left() + previewRect.left(), previewRect.center().y());
start = marker;
end = QPointF(previewRect.right(), start.y());
} else if (position == KoFlake::MidMarker) {
start = QPointF(previewRect.left(), previewRect.center().y());
marker = QPointF(-outlineRect.center().x() + previewRect.center().x(), start.y());
end = QPointF(previewRect.right(), start.y());
} else if (position == KoFlake::EndMarker) {
start = QPointF(previewRect.left(), previewRect.center().y());
marker = QPointF(-outlineRect.right() + previewRect.right(), start.y());
end = marker;
}
painter->save();
painter->setPen(pen);
painter->setClipRect(previewRect);
painter->drawLine(start, end);
paintAtPosition(painter, marker, pen.widthF(), 0);
painter->restore();
}
void KoMarker::applyShapeStroke(KoShape *parentShape, KoShapeStroke *stroke, const QPointF &pos, qreal strokeWidth, qreal nodeAngle)
{
const QGradient *originalGradient = stroke->lineBrush().gradient();
if (!originalGradient) {
QList linearizedShapes = KoShape::linearizeSubtree(d->shapes);
Q_FOREACH(KoShape *shape, linearizedShapes) {
// update the stroke
KoShapeStrokeSP shapeStroke = shape->stroke() ?
qSharedPointerDynamicCast(shape->stroke()) :
KoShapeStrokeSP();
if (shapeStroke) {
shapeStroke = toQShared(new KoShapeStroke(*shapeStroke));
shapeStroke->setLineBrush(QBrush());
shapeStroke->setColor(stroke->color());
shape->setStroke(shapeStroke);
}
// update the background
if (shape->background()) {
QSharedPointer bg(new KoColorBackground(stroke->color()));
shape->setBackground(bg);
}
}
} else {
QScopedPointer g(KoFlake::cloneGradient(originalGradient));
KIS_ASSERT_RECOVER_RETURN(g);
const QTransform markerTransformInverted =
d->markerTransform(strokeWidth, nodeAngle, pos).inverted();
QTransform gradientToUser;
// Unwrap the gradient to work in global mode
if (g->coordinateMode() == QGradient::ObjectBoundingMode) {
QRectF boundingRect =
parentShape ?
parentShape->outline().boundingRect() :
this->boundingRect(strokeWidth, nodeAngle);
boundingRect = KisAlgebra2D::ensureRectNotSmaller(boundingRect, QSizeF(1.0, 1.0));
gradientToUser = QTransform(boundingRect.width(), 0, 0, boundingRect.height(),
boundingRect.x(), boundingRect.y());
g->setCoordinateMode(QGradient::LogicalMode);
}
QList linearizedShapes = KoShape::linearizeSubtree(d->shapes);
Q_FOREACH(KoShape *shape, linearizedShapes) {
// shape-unwinding transform
QTransform t = gradientToUser * markerTransformInverted * shape->absoluteTransformation(0).inverted();
// update the stroke
KoShapeStrokeSP shapeStroke = shape->stroke() ?
qSharedPointerDynamicCast(shape->stroke()) :
KoShapeStrokeSP();
if (shapeStroke) {
shapeStroke = toQShared(new KoShapeStroke(*shapeStroke));
QBrush brush(*g);
brush.setTransform(t);
shapeStroke->setLineBrush(brush);
shapeStroke->setColor(Qt::transparent);
shape->setStroke(shapeStroke);
}
// update the background
if (shape->background()) {
QSharedPointer bg(new KoGradientBackground(KoFlake::cloneGradient(g.data()), t));
shape->setBackground(bg);
}
}
}
}
diff --git a/libs/flake/KoSelection.cpp b/libs/flake/KoSelection.cpp
index 923560a528..c7c409570f 100644
--- a/libs/flake/KoSelection.cpp
+++ b/libs/flake/KoSelection.cpp
@@ -1,309 +1,304 @@
/* This file is part of the KDE project
Copyright (C) 2006 Boudewijn Rempt
Copyright (C) 2006 Thorsten Zachmann
Copyright (C) 2006 Jan Hambrecht
Copyright (C) 2006-2007,2009 Thomas Zander
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.
*/
#include "KoSelection.h"
#include "KoSelection_p.h"
#include "KoShapeContainer.h"
#include "KoShapeGroup.h"
#include "KoPointerEvent.h"
#include "KoShapePaintingContext.h"
#include "kis_algebra_2d.h"
#include "krita_container_utils.h"
#include
#include "kis_debug.h"
KoSelection::KoSelection()
: KoShape(new KoSelectionPrivate(this))
{
Q_D(KoSelection);
connect(&d->selectionChangedCompressor, SIGNAL(timeout()), SIGNAL(selectionChanged()));
}
KoSelection::~KoSelection()
{
}
void KoSelection::paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintcontext)
{
Q_UNUSED(painter);
Q_UNUSED(converter);
Q_UNUSED(paintcontext);
}
void KoSelection::setSize(const QSizeF &size)
{
Q_UNUSED(size);
qWarning() << "WARNING: KoSelection::setSize() should never be used!";
}
QSizeF KoSelection::size() const
{
return outlineRect().size();
}
QRectF KoSelection::outlineRect() const
{
Q_D(const KoSelection);
QPolygonF globalPolygon;
Q_FOREACH (KoShape *shape, d->selectedShapes) {
globalPolygon = globalPolygon.united(
shape->absoluteTransformation(0).map(QPolygonF(shape->outlineRect())));
}
const QPolygonF localPolygon = transformation().inverted().map(globalPolygon);
return localPolygon.boundingRect();
}
QRectF KoSelection::boundingRect() const
{
Q_D(const KoSelection);
-
- QRectF result;
- Q_FOREACH (KoShape *shape, d->selectedShapes) {
- result |= shape->boundingRect();
- }
- return result;
+ return KoShape::boundingRect(d->selectedShapes);
}
void KoSelection::select(KoShape *shape)
{
Q_D(KoSelection);
KIS_SAFE_ASSERT_RECOVER_RETURN(shape != this);
KIS_SAFE_ASSERT_RECOVER_RETURN(shape);
if (!shape->isSelectable() || !shape->isVisible(true)) {
return;
}
// check recursively
if (isSelected(shape)) {
return;
}
// find the topmost parent to select
while (KoShapeGroup *parentGroup = dynamic_cast(shape->parent())) {
shape = parentGroup;
}
d->selectedShapes << shape;
shape->addShapeChangeListener(this);
d->savedMatrices = d->fetchShapesMatrices();
if (d->selectedShapes.size() == 1) {
setTransformation(shape->absoluteTransformation(0));
} else {
setTransformation(QTransform());
}
d->selectionChangedCompressor.start();
}
void KoSelection::deselect(KoShape *shape)
{
Q_D(KoSelection);
if (!d->selectedShapes.contains(shape))
return;
d->selectedShapes.removeAll(shape);
shape->removeShapeChangeListener(this);
d->savedMatrices = d->fetchShapesMatrices();
if (d->selectedShapes.size() == 1) {
setTransformation(d->selectedShapes.first()->absoluteTransformation(0));
}
d->selectionChangedCompressor.start();
}
void KoSelection::deselectAll()
{
Q_D(KoSelection);
if (d->selectedShapes.isEmpty())
return;
Q_FOREACH (KoShape *shape, d->selectedShapes) {
shape->removeShapeChangeListener(this);
}
d->savedMatrices = d->fetchShapesMatrices();
// reset the transformation matrix of the selection
setTransformation(QTransform());
d->selectedShapes.clear();
d->selectionChangedCompressor.start();
}
int KoSelection::count() const
{
Q_D(const KoSelection);
return d->selectedShapes.size();
}
bool KoSelection::hitTest(const QPointF &position) const
{
Q_D(const KoSelection);
Q_FOREACH (KoShape *shape, d->selectedShapes) {
if (shape->hitTest(position)) return true;
}
return false;
}
const QList KoSelection::selectedShapes() const
{
Q_D(const KoSelection);
return d->selectedShapes;
}
const QList KoSelection::selectedEditableShapes() const
{
Q_D(const KoSelection);
QList shapes = selectedShapes();
KritaUtils::filterContainer (shapes, [](KoShape *shape) {
return shape->isEditable();
});
return shapes;
}
const QList KoSelection::selectedEditableShapesAndDelegates() const
{
QList shapes;
Q_FOREACH (KoShape *shape, selectedShapes()) {
QSet delegates = shape->toolDelegates();
if (delegates.isEmpty()) {
shapes.append(shape);
} else {
Q_FOREACH (KoShape *delegatedShape, delegates) {
shapes.append(delegatedShape);
}
}
}
return shapes;
}
bool KoSelection::isSelected(const KoShape *shape) const
{
Q_D(const KoSelection);
if (shape == this)
return true;
const KoShape *tmpShape = shape;
while (tmpShape && std::find(d->selectedShapes.begin(), d->selectedShapes.end(), tmpShape) == d->selectedShapes.end()/*d->selectedShapes.contains(tmpShape)*/) {
tmpShape = tmpShape->parent();
}
return tmpShape;
}
KoShape *KoSelection::firstSelectedShape() const
{
Q_D(const KoSelection);
return !d->selectedShapes.isEmpty() ? d->selectedShapes.first() : 0;
}
void KoSelection::setActiveLayer(KoShapeLayer *layer)
{
Q_D(KoSelection);
d->activeLayer = layer;
emit currentLayerChanged(layer);
}
KoShapeLayer* KoSelection::activeLayer() const
{
Q_D(const KoSelection);
return d->activeLayer;
}
void KoSelection::notifyShapeChanged(KoShape::ChangeType type, KoShape *shape)
{
Q_UNUSED(shape);
Q_D(KoSelection);
if (type >= KoShape::PositionChanged && type <= KoShape::GenericMatrixChange) {
QList matrices = d->fetchShapesMatrices();
QTransform newTransform;
if (d->checkMatricesConsistent(matrices, &newTransform)) {
d->savedMatrices = matrices;
setTransformation(newTransform);
} else {
d->savedMatrices = matrices;
setTransformation(QTransform());
}
}
}
void KoSelection::saveOdf(KoShapeSavingContext &) const
{
}
bool KoSelection::loadOdf(const KoXmlElement &, KoShapeLoadingContext &)
{
return true;
}
QList KoSelectionPrivate::fetchShapesMatrices() const
{
QList result;
Q_FOREACH (KoShape *shape, selectedShapes) {
result << shape->absoluteTransformation(0);
}
return result;
}
bool KoSelectionPrivate::checkMatricesConsistent(const QList &matrices, QTransform *newTransform)
{
Q_Q(KoSelection);
QTransform commonDiff;
bool haveCommonDiff = false;
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(matrices.size() == selectedShapes.size(), false);
for (int i = 0; i < matrices.size(); i++) {
QTransform t = savedMatrices[i];
QTransform diff = t.inverted() * matrices[i];
if (haveCommonDiff) {
if (!KisAlgebra2D::fuzzyMatrixCompare(commonDiff, diff, 1e-5)) {
return false;
}
} else {
commonDiff = diff;
}
}
*newTransform = q->transformation() * commonDiff;
return true;
}
diff --git a/libs/flake/KoShape.cpp b/libs/flake/KoShape.cpp
index d1bb8c4226..1c6062afb3 100644
--- a/libs/flake/KoShape.cpp
+++ b/libs/flake/KoShape.cpp
@@ -1,2398 +1,2420 @@
/* This file is part of the KDE project
Copyright (C) 2006 C. Boemann Rasmussen
Copyright (C) 2006-2010 Thomas Zander
Copyright (C) 2006-2010 Thorsten Zachmann
Copyright (C) 2007-2009,2011 Jan Hambrecht
CopyRight (C) 2010 Boudewijn Rempt
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.
*/
#include "KoShape.h"
#include "KoShape_p.h"
#include "KoShapeContainer.h"
#include "KoShapeLayer.h"
#include "KoShapeContainerModel.h"
#include "KoSelection.h"
#include "KoPointerEvent.h"
#include "KoInsets.h"
#include "KoShapeStrokeModel.h"
#include "KoShapeBackground.h"
#include "KoColorBackground.h"
#include "KoHatchBackground.h"
#include "KoGradientBackground.h"
#include "KoPatternBackground.h"
#include "KoShapeManager.h"
#include "KoShapeUserData.h"
#include "KoShapeApplicationData.h"
#include "KoShapeSavingContext.h"
#include "KoShapeLoadingContext.h"
#include "KoViewConverter.h"
#include "KoShapeStroke.h"
#include "KoShapeShadow.h"
#include "KoClipPath.h"
#include "KoPathShape.h"
#include "KoOdfWorkaround.h"
#include "KoFilterEffectStack.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kis_assert.h"
#include
#include "KoOdfGradientBackground.h"
#include
// KoShapePrivate
KoShapePrivate::KoShapePrivate(KoShape *shape)
: q_ptr(shape),
size(50, 50),
parent(0),
shadow(0),
border(0),
filterEffectStack(0),
transparency(0.0),
zIndex(0),
runThrough(0),
visible(true),
printable(true),
geometryProtected(false),
keepAspect(false),
selectable(true),
detectCollision(false),
protectContent(false),
textRunAroundSide(KoShape::BiggestRunAroundSide),
textRunAroundDistanceLeft(0.0),
textRunAroundDistanceTop(0.0),
textRunAroundDistanceRight(0.0),
textRunAroundDistanceBottom(0.0),
textRunAroundThreshold(0.0),
textRunAroundContour(KoShape::ContourFull)
{
connectors[KoConnectionPoint::TopConnectionPoint] = KoConnectionPoint::defaultConnectionPoint(KoConnectionPoint::TopConnectionPoint);
connectors[KoConnectionPoint::RightConnectionPoint] = KoConnectionPoint::defaultConnectionPoint(KoConnectionPoint::RightConnectionPoint);
connectors[KoConnectionPoint::BottomConnectionPoint] = KoConnectionPoint::defaultConnectionPoint(KoConnectionPoint::BottomConnectionPoint);
connectors[KoConnectionPoint::LeftConnectionPoint] = KoConnectionPoint::defaultConnectionPoint(KoConnectionPoint::LeftConnectionPoint);
connectors[KoConnectionPoint::FirstCustomConnectionPoint] = KoConnectionPoint(QPointF(0.5, 0.5), KoConnectionPoint::AllDirections, KoConnectionPoint::AlignCenter);
}
KoShapePrivate::KoShapePrivate(const KoShapePrivate &rhs, KoShape *q)
: q_ptr(q),
size(rhs.size),
shapeId(rhs.shapeId),
name(rhs.name),
localMatrix(rhs.localMatrix),
connectors(rhs.connectors),
parent(0), // to be initialized later
shapeManagers(), // to be initialized later
toolDelegates(), // FIXME: how to initialize them?
userData(rhs.userData ? rhs.userData->clone() : 0),
stroke(rhs.stroke),
fill(rhs.fill),
dependees(), // FIXME: how to initialize them?
shadow(0), // WARNING: not implemented in Krita
border(0), // WARNING: not implemented in Krita
clipPath(rhs.clipPath ? rhs.clipPath->clone() : 0),
clipMask(rhs.clipMask ? rhs.clipMask->clone() : 0),
additionalAttributes(rhs.additionalAttributes),
additionalStyleAttributes(rhs.additionalStyleAttributes),
filterEffectStack(0), // WARNING: not implemented in Krita
transparency(rhs.transparency),
hyperLink(rhs.hyperLink),
zIndex(rhs.zIndex),
runThrough(rhs.runThrough),
visible(rhs.visible),
printable(rhs.visible),
geometryProtected(rhs.geometryProtected),
keepAspect(rhs.keepAspect),
selectable(rhs.selectable),
detectCollision(rhs.detectCollision),
protectContent(rhs.protectContent),
textRunAroundSide(rhs.textRunAroundSide),
textRunAroundDistanceLeft(rhs.textRunAroundDistanceLeft),
textRunAroundDistanceTop(rhs.textRunAroundDistanceTop),
textRunAroundDistanceRight(rhs.textRunAroundDistanceRight),
textRunAroundDistanceBottom(rhs.textRunAroundDistanceBottom),
textRunAroundThreshold(rhs.textRunAroundThreshold),
textRunAroundContour(rhs.textRunAroundContour)
{
}
KoShapePrivate::~KoShapePrivate()
{
Q_Q(KoShape);
if (parent)
parent->removeShape(q);
Q_FOREACH (KoShapeManager *manager, shapeManagers) {
manager->remove(q);
}
if (shadow && !shadow->deref())
delete shadow;
if (filterEffectStack && !filterEffectStack->deref())
delete filterEffectStack;
}
void KoShapePrivate::shapeChanged(KoShape::ChangeType type)
{
Q_Q(KoShape);
if (parent)
parent->model()->childChanged(q, type);
q->shapeChanged(type);
Q_FOREACH (KoShape * shape, dependees) {
shape->shapeChanged(type, q);
}
Q_FOREACH (KoShape::ShapeChangeListener *listener, listeners) {
listener->notifyShapeChangedImpl(type, q);
}
}
void KoShapePrivate::updateStroke()
{
Q_Q(KoShape);
if (!stroke) return;
KoInsets insets;
stroke->strokeInsets(q, insets);
QSizeF inner = q->size();
// update left
q->update(QRectF(-insets.left, -insets.top, insets.left,
inner.height() + insets.top + insets.bottom));
// update top
q->update(QRectF(-insets.left, -insets.top,
inner.width() + insets.left + insets.right, insets.top));
// update right
q->update(QRectF(inner.width(), -insets.top, insets.right,
inner.height() + insets.top + insets.bottom));
// update bottom
q->update(QRectF(-insets.left, inner.height(),
inner.width() + insets.left + insets.right, insets.bottom));
}
void KoShapePrivate::addShapeManager(KoShapeManager *manager)
{
shapeManagers.insert(manager);
}
void KoShapePrivate::removeShapeManager(KoShapeManager *manager)
{
shapeManagers.remove(manager);
}
void KoShapePrivate::convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
{
switch(point.alignment) {
case KoConnectionPoint::AlignNone:
point.position = KoFlake::toRelative(point.position, shapeSize);
point.position.rx() = qBound(0.0, point.position.x(), 1.0);
point.position.ry() = qBound(0.0, point.position.y(), 1.0);
break;
case KoConnectionPoint::AlignRight:
point.position.rx() -= shapeSize.width();
case KoConnectionPoint::AlignLeft:
point.position.ry() = 0.5*shapeSize.height();
break;
case KoConnectionPoint::AlignBottom:
point.position.ry() -= shapeSize.height();
case KoConnectionPoint::AlignTop:
point.position.rx() = 0.5*shapeSize.width();
break;
case KoConnectionPoint::AlignTopLeft:
// nothing to do here
break;
case KoConnectionPoint::AlignTopRight:
point.position.rx() -= shapeSize.width();
break;
case KoConnectionPoint::AlignBottomLeft:
point.position.ry() -= shapeSize.height();
break;
case KoConnectionPoint::AlignBottomRight:
point.position.rx() -= shapeSize.width();
point.position.ry() -= shapeSize.height();
break;
case KoConnectionPoint::AlignCenter:
point.position.rx() -= 0.5 * shapeSize.width();
point.position.ry() -= 0.5 * shapeSize.height();
break;
}
}
void KoShapePrivate::convertToShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
{
switch(point.alignment) {
case KoConnectionPoint::AlignNone:
point.position = KoFlake::toAbsolute(point.position, shapeSize);
break;
case KoConnectionPoint::AlignRight:
point.position.rx() += shapeSize.width();
case KoConnectionPoint::AlignLeft:
point.position.ry() = 0.5*shapeSize.height();
break;
case KoConnectionPoint::AlignBottom:
point.position.ry() += shapeSize.height();
case KoConnectionPoint::AlignTop:
point.position.rx() = 0.5*shapeSize.width();
break;
case KoConnectionPoint::AlignTopLeft:
// nothing to do here
break;
case KoConnectionPoint::AlignTopRight:
point.position.rx() += shapeSize.width();
break;
case KoConnectionPoint::AlignBottomLeft:
point.position.ry() += shapeSize.height();
break;
case KoConnectionPoint::AlignBottomRight:
point.position.rx() += shapeSize.width();
point.position.ry() += shapeSize.height();
break;
case KoConnectionPoint::AlignCenter:
point.position.rx() += 0.5 * shapeSize.width();
point.position.ry() += 0.5 * shapeSize.height();
break;
}
}
// static
QString KoShapePrivate::getStyleProperty(const char *property, KoShapeLoadingContext &context)
{
KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
QString value;
if (styleStack.hasProperty(KoXmlNS::draw, property)) {
value = styleStack.property(KoXmlNS::draw, property);
}
return value;
}
// ======== KoShape
KoShape::KoShape()
: d_ptr(new KoShapePrivate(this))
{
notifyChanged();
}
KoShape::KoShape(KoShapePrivate *dd)
: d_ptr(dd)
{
}
KoShape::~KoShape()
{
Q_D(KoShape);
d->shapeChanged(Deleted);
delete d_ptr;
}
KoShape *KoShape::cloneShape() const
{
return 0;
}
void KoShape::scale(qreal sx, qreal sy)
{
Q_D(KoShape);
QPointF pos = position();
QTransform scaleMatrix;
scaleMatrix.translate(pos.x(), pos.y());
scaleMatrix.scale(sx, sy);
scaleMatrix.translate(-pos.x(), -pos.y());
d->localMatrix = d->localMatrix * scaleMatrix;
notifyChanged();
d->shapeChanged(ScaleChanged);
}
void KoShape::rotate(qreal angle)
{
Q_D(KoShape);
QPointF center = d->localMatrix.map(QPointF(0.5 * size().width(), 0.5 * size().height()));
QTransform rotateMatrix;
rotateMatrix.translate(center.x(), center.y());
rotateMatrix.rotate(angle);
rotateMatrix.translate(-center.x(), -center.y());
d->localMatrix = d->localMatrix * rotateMatrix;
notifyChanged();
d->shapeChanged(RotationChanged);
}
void KoShape::shear(qreal sx, qreal sy)
{
Q_D(KoShape);
QPointF pos = position();
QTransform shearMatrix;
shearMatrix.translate(pos.x(), pos.y());
shearMatrix.shear(sx, sy);
shearMatrix.translate(-pos.x(), -pos.y());
d->localMatrix = d->localMatrix * shearMatrix;
notifyChanged();
d->shapeChanged(ShearChanged);
}
void KoShape::setSize(const QSizeF &newSize)
{
Q_D(KoShape);
QSizeF oldSize(size());
// always set size, as d->size and size() may vary
d->size = newSize;
if (oldSize == newSize)
return;
notifyChanged();
d->shapeChanged(SizeChanged);
}
void KoShape::setPosition(const QPointF &newPosition)
{
Q_D(KoShape);
QPointF currentPos = position();
if (newPosition == currentPos)
return;
QTransform translateMatrix;
translateMatrix.translate(newPosition.x() - currentPos.x(), newPosition.y() - currentPos.y());
d->localMatrix = d->localMatrix * translateMatrix;
notifyChanged();
d->shapeChanged(PositionChanged);
}
bool KoShape::hitTest(const QPointF &position) const
{
Q_D(const KoShape);
if (d->parent && d->parent->isClipped(this) && !d->parent->hitTest(position))
return false;
QPointF point = absoluteTransformation(0).inverted().map(position);
QRectF bb(QPointF(), size());
if (d->stroke) {
KoInsets insets;
d->stroke->strokeInsets(this, insets);
bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
}
if (bb.contains(point))
return true;
// if there is no shadow we can as well just leave
if (! d->shadow)
return false;
// the shadow has an offset to the shape, so we simply
// check if the position minus the shadow offset hits the shape
point = absoluteTransformation(0).inverted().map(position - d->shadow->offset());
return bb.contains(point);
}
QRectF KoShape::boundingRect() const
{
Q_D(const KoShape);
QTransform transform = absoluteTransformation(0);
QRectF bb = outlineRect();
if (d->stroke) {
KoInsets insets;
d->stroke->strokeInsets(this, insets);
bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
}
bb = transform.mapRect(bb);
if (d->shadow) {
KoInsets insets;
d->shadow->insets(insets);
bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
}
if (d->filterEffectStack) {
QRectF clipRect = d->filterEffectStack->clipRectForBoundingRect(outlineRect());
bb |= transform.mapRect(clipRect);
}
return bb;
}
+QRectF KoShape::boundingRect(const QList &shapes)
+{
+ QRectF boundingRect;
+ Q_FOREACH (KoShape *shape, shapes) {
+ boundingRect |= shape->boundingRect();
+ }
+ return boundingRect;
+}
+
QTransform KoShape::absoluteTransformation(const KoViewConverter *converter) const
{
Q_D(const KoShape);
QTransform matrix;
// apply parents matrix to inherit any transformations done there.
KoShapeContainer * container = d->parent;
if (container) {
if (container->inheritsTransform(this)) {
// We do need to pass the converter here, otherwise the parent's
// translation is not inherited.
matrix = container->absoluteTransformation(converter);
} else {
QSizeF containerSize = container->size();
QPointF containerPos = container->absolutePosition() - QPointF(0.5 * containerSize.width(), 0.5 * containerSize.height());
if (converter)
containerPos = converter->documentToView(containerPos);
matrix.translate(containerPos.x(), containerPos.y());
}
}
if (converter) {
QPointF pos = d->localMatrix.map(QPointF());
QPointF trans = converter->documentToView(pos) - pos;
matrix.translate(trans.x(), trans.y());
}
return d->localMatrix * matrix;
}
void KoShape::applyAbsoluteTransformation(const QTransform &matrix)
{
QTransform globalMatrix = absoluteTransformation(0);
// the transformation is relative to the global coordinate system
// but we want to change the local matrix, so convert the matrix
// to be relative to the local coordinate system
QTransform transformMatrix = globalMatrix * matrix * globalMatrix.inverted();
applyTransformation(transformMatrix);
}
void KoShape::applyTransformation(const QTransform &matrix)
{
Q_D(KoShape);
d->localMatrix = matrix * d->localMatrix;
notifyChanged();
d->shapeChanged(GenericMatrixChange);
}
void KoShape::setTransformation(const QTransform &matrix)
{
Q_D(KoShape);
d->localMatrix = matrix;
notifyChanged();
d->shapeChanged(GenericMatrixChange);
}
QTransform KoShape::transformation() const
{
Q_D(const KoShape);
return d->localMatrix;
}
KoShape::ChildZOrderPolicy KoShape::childZOrderPolicy()
{
return ChildZDefault;
}
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
+ /**
+ * WARNING: Our definition of zIndex is not yet compatible with SVG2's
+ * definition. In SVG stacking context of groups with the same
+ * zIndex are **merged**, while in Krita the contents of groups
+ * is never merged. One group will always below than the other.
+ * Therefore, when zIndex of two groups inside the same parent
+ * coinside, the resulting painting order in Krita is
+ * **UNDEFINED**.
+ *
+ * To avoid this trouble we use KoShapeReorderCommand::mergeInShape()
+ * inside KoShapeCreateCommand.
+ */
+
// First sort according to runThrough which is sort of a master level
KoShape *parentShapeS1 = s1->parent();
KoShape *parentShapeS2 = s2->parent();
int runThrough1 = s1->runThrough();
int runThrough2 = s2->runThrough();
while (parentShapeS1) {
if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
runThrough1 = parentShapeS1->runThrough();
} else {
runThrough1 = runThrough1 + parentShapeS1->runThrough();
}
parentShapeS1 = parentShapeS1->parent();
}
while (parentShapeS2) {
if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) {
runThrough2 = parentShapeS2->runThrough();
} else {
runThrough2 = runThrough2 + parentShapeS2->runThrough();
}
parentShapeS2 = parentShapeS2->parent();
}
if (runThrough1 > runThrough2) {
return false;
}
if (runThrough1 < runThrough2) {
return true;
}
// If on the same runThrough level then the zIndex is all that matters.
//
// We basically walk up through the parents until we find a common base parent
// To do that we need two loops where the inner loop walks up through the parents
// of s2 every time we step up one parent level on s1
//
// We don't update the index value until after we have seen that it's not a common base
// That way we ensure that two children of a common base are sorted according to their respective
// z value
bool foundCommonParent = false;
int index1 = s1->zIndex();
int index2 = s2->zIndex();
parentShapeS1 = s1;
parentShapeS2 = s2;
while (parentShapeS1 && !foundCommonParent) {
parentShapeS2 = s2;
index2 = parentShapeS2->zIndex();
while (parentShapeS2) {
if (parentShapeS2 == parentShapeS1) {
foundCommonParent = true;
break;
}
if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) {
index2 = parentShapeS2->zIndex();
}
parentShapeS2 = parentShapeS2->parent();
}
if (!foundCommonParent) {
if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
index1 = parentShapeS1->zIndex();
}
parentShapeS1 = parentShapeS1->parent();
}
}
// If the one shape is a parent/child of the other then sort so.
if (s1 == parentShapeS2) {
return true;
}
if (s2 == parentShapeS1) {
return false;
}
// If we went that far then the z-Index is used for sorting.
return index1 < index2;
}
void KoShape::setParent(KoShapeContainer *parent)
{
Q_D(KoShape);
if (d->parent == parent) {
return;
}
KoShapeContainer *oldParent = d->parent;
d->parent = 0; // avoids recursive removing
if (oldParent) {
oldParent->shapeInterface()->removeShape(this);
}
KIS_SAFE_ASSERT_RECOVER_NOOP(parent != this);
if (parent && parent != this) {
d->parent = parent;
parent->shapeInterface()->addShape(this);
}
notifyChanged();
d->shapeChanged(ParentChanged);
}
int KoShape::zIndex() const
{
Q_D(const KoShape);
return d->zIndex;
}
void KoShape::update() const
{
Q_D(const KoShape);
if (!d->shapeManagers.empty()) {
QRectF rect(boundingRect());
Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
manager->update(rect, this, true);
}
}
}
void KoShape::update(const QRectF &rect) const
{
if (rect.isEmpty() && !rect.isNull()) {
return;
}
Q_D(const KoShape);
if (!d->shapeManagers.empty() && isVisible()) {
QRectF rc(absoluteTransformation(0).mapRect(rect));
Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
manager->update(rc);
}
}
}
QPainterPath KoShape::outline() const
{
QPainterPath path;
path.addRect(outlineRect());
return path;
}
QRectF KoShape::outlineRect() const
{
const QSizeF s = size();
return QRectF(QPointF(0, 0), QSizeF(qMax(s.width(), qreal(0.0001)),
qMax(s.height(), qreal(0.0001))));
}
QPainterPath KoShape::shadowOutline() const
{
Q_D(const KoShape);
if (d->fill) {
return outline();
}
return QPainterPath();
}
QPointF KoShape::absolutePosition(KoFlake::AnchorPosition anchor) const
{
const QRectF rc = outlineRect();
QPointF point = rc.topLeft();
bool valid = false;
QPointF anchoredPoint = KoFlake::anchorToPoint(anchor, rc, &valid);
if (valid) {
point = anchoredPoint;
}
return absoluteTransformation(0).map(point);
}
void KoShape::setAbsolutePosition(const QPointF &newPosition, KoFlake::AnchorPosition anchor)
{
Q_D(KoShape);
QPointF currentAbsPosition = absolutePosition(anchor);
QPointF translate = newPosition - currentAbsPosition;
QTransform translateMatrix;
translateMatrix.translate(translate.x(), translate.y());
applyAbsoluteTransformation(translateMatrix);
notifyChanged();
d->shapeChanged(PositionChanged);
}
void KoShape::copySettings(const KoShape *shape)
{
Q_D(KoShape);
d->size = shape->size();
d->connectors.clear();
Q_FOREACH (const KoConnectionPoint &point, shape->connectionPoints())
addConnectionPoint(point);
d->zIndex = shape->zIndex();
d->visible = shape->isVisible();
// Ensure printable is true by default
if (!d->visible)
d->printable = true;
else
d->printable = shape->isPrintable();
d->geometryProtected = shape->isGeometryProtected();
d->protectContent = shape->isContentProtected();
d->selectable = shape->isSelectable();
d->keepAspect = shape->keepAspectRatio();
d->localMatrix = shape->d_ptr->localMatrix;
}
void KoShape::notifyChanged()
{
Q_D(KoShape);
Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
manager->notifyShapeChanged(this);
}
}
void KoShape::setUserData(KoShapeUserData *userData)
{
Q_D(KoShape);
d->userData.reset(userData);
}
KoShapeUserData *KoShape::userData() const
{
Q_D(const KoShape);
return d->userData.data();
}
bool KoShape::hasTransparency() const
{
Q_D(const KoShape);
if (! d->fill)
return true;
else
return d->fill->hasTransparency() || d->transparency > 0.0;
}
void KoShape::setTransparency(qreal transparency)
{
Q_D(KoShape);
d->transparency = qBound(0.0, transparency, 1.0);
d->shapeChanged(TransparencyChanged);
notifyChanged();
}
qreal KoShape::transparency(bool recursive) const
{
Q_D(const KoShape);
if (!recursive || !parent()) {
return d->transparency;
} else {
const qreal parentOpacity = 1.0-parent()->transparency(recursive);
const qreal childOpacity = 1.0-d->transparency;
return 1.0-(parentOpacity*childOpacity);
}
}
KoInsets KoShape::strokeInsets() const
{
Q_D(const KoShape);
KoInsets answer;
if (d->stroke)
d->stroke->strokeInsets(this, answer);
return answer;
}
qreal KoShape::rotation() const
{
Q_D(const KoShape);
// try to extract the rotation angle out of the local matrix
// if it is a pure rotation matrix
// check if the matrix has shearing mixed in
if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
return std::numeric_limits::quiet_NaN();
// check if the matrix has scaling mixed in
if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
return std::numeric_limits::quiet_NaN();
// calculate the angle from the matrix elements
qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
if (angle < 0.0)
angle += 360.0;
return angle;
}
QSizeF KoShape::size() const
{
Q_D(const KoShape);
return d->size;
}
QPointF KoShape::position() const
{
Q_D(const KoShape);
QPointF center = outlineRect().center();
return d->localMatrix.map(center) - center;
}
int KoShape::addConnectionPoint(const KoConnectionPoint &point)
{
Q_D(KoShape);
// get next glue point id
int nextConnectionPointId = KoConnectionPoint::FirstCustomConnectionPoint;
if (d->connectors.size())
nextConnectionPointId = qMax(nextConnectionPointId, (--d->connectors.end()).key()+1);
KoConnectionPoint p = point;
d->convertFromShapeCoordinates(p, size());
d->connectors[nextConnectionPointId] = p;
return nextConnectionPointId;
}
bool KoShape::setConnectionPoint(int connectionPointId, const KoConnectionPoint &point)
{
Q_D(KoShape);
if (connectionPointId < 0)
return false;
const bool insertPoint = !hasConnectionPoint(connectionPointId);
switch(connectionPointId) {
case KoConnectionPoint::TopConnectionPoint:
case KoConnectionPoint::RightConnectionPoint:
case KoConnectionPoint::BottomConnectionPoint:
case KoConnectionPoint::LeftConnectionPoint:
{
KoConnectionPoint::PointId id = static_cast(connectionPointId);
d->connectors[id] = KoConnectionPoint::defaultConnectionPoint(id);
break;
}
default:
{
KoConnectionPoint p = point;
d->convertFromShapeCoordinates(p, size());
d->connectors[connectionPointId] = p;
break;
}
}
if(!insertPoint)
d->shapeChanged(ConnectionPointChanged);
return true;
}
bool KoShape::hasConnectionPoint(int connectionPointId) const
{
Q_D(const KoShape);
return d->connectors.contains(connectionPointId);
}
KoConnectionPoint KoShape::connectionPoint(int connectionPointId) const
{
Q_D(const KoShape);
KoConnectionPoint p = d->connectors.value(connectionPointId, KoConnectionPoint());
// convert glue point to shape coordinates
d->convertToShapeCoordinates(p, size());
return p;
}
KoConnectionPoints KoShape::connectionPoints() const
{
Q_D(const KoShape);
QSizeF s = size();
KoConnectionPoints points = d->connectors;
KoConnectionPoints::iterator point = points.begin();
KoConnectionPoints::iterator lastPoint = points.end();
// convert glue points to shape coordinates
for(; point != lastPoint; ++point) {
d->convertToShapeCoordinates(point.value(), s);
}
return points;
}
void KoShape::removeConnectionPoint(int connectionPointId)
{
Q_D(KoShape);
d->connectors.remove(connectionPointId);
d->shapeChanged(ConnectionPointChanged);
}
void KoShape::clearConnectionPoints()
{
Q_D(KoShape);
d->connectors.clear();
}
KoShape::TextRunAroundSide KoShape::textRunAroundSide() const
{
Q_D(const KoShape);
return d->textRunAroundSide;
}
void KoShape::setTextRunAroundSide(TextRunAroundSide side, RunThroughLevel runThrought)
{
Q_D(KoShape);
if (side == RunThrough) {
if (runThrought == Background) {
setRunThrough(-1);
} else {
setRunThrough(1);
}
} else {
setRunThrough(0);
}
if ( d->textRunAroundSide == side) {
return;
}
d->textRunAroundSide = side;
notifyChanged();
d->shapeChanged(TextRunAroundChanged);
}
qreal KoShape::textRunAroundDistanceTop() const
{
Q_D(const KoShape);
return d->textRunAroundDistanceTop;
}
void KoShape::setTextRunAroundDistanceTop(qreal distance)
{
Q_D(KoShape);
d->textRunAroundDistanceTop = distance;
}
qreal KoShape::textRunAroundDistanceLeft() const
{
Q_D(const KoShape);
return d->textRunAroundDistanceLeft;
}
void KoShape::setTextRunAroundDistanceLeft(qreal distance)
{
Q_D(KoShape);
d->textRunAroundDistanceLeft = distance;
}
qreal KoShape::textRunAroundDistanceRight() const
{
Q_D(const KoShape);
return d->textRunAroundDistanceRight;
}
void KoShape::setTextRunAroundDistanceRight(qreal distance)
{
Q_D(KoShape);
d->textRunAroundDistanceRight = distance;
}
qreal KoShape::textRunAroundDistanceBottom() const
{
Q_D(const KoShape);
return d->textRunAroundDistanceBottom;
}
void KoShape::setTextRunAroundDistanceBottom(qreal distance)
{
Q_D(KoShape);
d->textRunAroundDistanceBottom = distance;
}
qreal KoShape::textRunAroundThreshold() const
{
Q_D(const KoShape);
return d->textRunAroundThreshold;
}
void KoShape::setTextRunAroundThreshold(qreal threshold)
{
Q_D(KoShape);
d->textRunAroundThreshold = threshold;
}
KoShape::TextRunAroundContour KoShape::textRunAroundContour() const
{
Q_D(const KoShape);
return d->textRunAroundContour;
}
void KoShape::setTextRunAroundContour(KoShape::TextRunAroundContour contour)
{
Q_D(KoShape);
d->textRunAroundContour = contour;
}
void KoShape::setBackground(QSharedPointer fill)
{
Q_D(KoShape);
d->fill = fill;
d->shapeChanged(BackgroundChanged);
notifyChanged();
}
QSharedPointer KoShape::background() const
{
Q_D(const KoShape);
return d->fill;
}
void KoShape::setZIndex(int zIndex)
{
Q_D(KoShape);
if (d->zIndex == zIndex)
return;
d->zIndex = zIndex;
notifyChanged();
}
int KoShape::runThrough()
{
Q_D(const KoShape);
return d->runThrough;
}
void KoShape::setRunThrough(short int runThrough)
{
Q_D(KoShape);
d->runThrough = runThrough;
}
void KoShape::setVisible(bool on)
{
Q_D(KoShape);
int _on = (on ? 1 : 0);
if (d->visible == _on) return;
d->visible = _on;
}
bool KoShape::isVisible(bool recursive) const
{
Q_D(const KoShape);
if (! recursive)
return d->visible;
if (recursive && ! d->visible)
return false;
KoShapeContainer * parentShape = parent();
while (parentShape) {
if (! parentShape->isVisible())
return false;
parentShape = parentShape->parent();
}
return true;
}
void KoShape::setPrintable(bool on)
{
Q_D(KoShape);
d->printable = on;
}
bool KoShape::isPrintable() const
{
Q_D(const KoShape);
if (d->visible)
return d->printable;
else
return false;
}
void KoShape::setSelectable(bool selectable)
{
Q_D(KoShape);
d->selectable = selectable;
}
bool KoShape::isSelectable() const
{
Q_D(const KoShape);
return d->selectable;
}
void KoShape::setGeometryProtected(bool on)
{
Q_D(KoShape);
d->geometryProtected = on;
}
bool KoShape::isGeometryProtected() const
{
Q_D(const KoShape);
return d->geometryProtected;
}
void KoShape::setContentProtected(bool protect)
{
Q_D(KoShape);
d->protectContent = protect;
}
bool KoShape::isContentProtected() const
{
Q_D(const KoShape);
return d->protectContent;
}
KoShapeContainer *KoShape::parent() const
{
Q_D(const KoShape);
return d->parent;
}
void KoShape::setKeepAspectRatio(bool keepAspect)
{
Q_D(KoShape);
d->keepAspect = keepAspect;
d->shapeChanged(KeepAspectRatioChange);
notifyChanged();
}
bool KoShape::keepAspectRatio() const
{
Q_D(const KoShape);
return d->keepAspect;
}
QString KoShape::shapeId() const
{
Q_D(const KoShape);
return d->shapeId;
}
void KoShape::setShapeId(const QString &id)
{
Q_D(KoShape);
d->shapeId = id;
}
void KoShape::setCollisionDetection(bool detect)
{
Q_D(KoShape);
d->detectCollision = detect;
}
bool KoShape::collisionDetection()
{
Q_D(KoShape);
return d->detectCollision;
}
KoShapeStrokeModelSP KoShape::stroke() const
{
Q_D(const KoShape);
return d->stroke;
}
void KoShape::setStroke(KoShapeStrokeModelSP stroke)
{
Q_D(KoShape);
// TODO: check if it really updates stuff
d->updateStroke();
d->stroke = stroke;
d->updateStroke();
d->shapeChanged(StrokeChanged);
notifyChanged();
}
void KoShape::setShadow(KoShapeShadow *shadow)
{
Q_D(KoShape);
if (d->shadow)
d->shadow->deref();
d->shadow = shadow;
if (d->shadow) {
d->shadow->ref();
// TODO update changed area
}
d->shapeChanged(ShadowChanged);
notifyChanged();
}
KoShapeShadow *KoShape::shadow() const
{
Q_D(const KoShape);
return d->shadow;
}
void KoShape::setBorder(KoBorder *border)
{
Q_D(KoShape);
if (d->border) {
// The shape owns the border.
delete d->border;
}
d->border = border;
d->shapeChanged(BorderChanged);
notifyChanged();
}
KoBorder *KoShape::border() const
{
Q_D(const KoShape);
return d->border;
}
void KoShape::setClipPath(KoClipPath *clipPath)
{
Q_D(KoShape);
d->clipPath.reset(clipPath);
d->shapeChanged(ClipPathChanged);
notifyChanged();
}
KoClipPath * KoShape::clipPath() const
{
Q_D(const KoShape);
return d->clipPath.data();
}
void KoShape::setClipMask(KoClipMask *clipMask)
{
Q_D(KoShape);
d->clipMask.reset(clipMask);
}
KoClipMask* KoShape::clipMask() const
{
Q_D(const KoShape);
return d->clipMask.data();
}
QTransform KoShape::transform() const
{
Q_D(const KoShape);
return d->localMatrix;
}
QString KoShape::name() const
{
Q_D(const KoShape);
return d->name;
}
void KoShape::setName(const QString &name)
{
Q_D(KoShape);
d->name = name;
}
void KoShape::waitUntilReady(const KoViewConverter &converter, bool asynchronous) const
{
Q_UNUSED(converter);
Q_UNUSED(asynchronous);
}
bool KoShape::isEditable() const
{
Q_D(const KoShape);
if (!d->visible || d->geometryProtected)
return false;
if (d->parent && d->parent->isChildLocked(this))
return false;
return true;
}
// painting
void KoShape::paintBorder(QPainter &painter, const KoViewConverter &converter)
{
Q_UNUSED(converter);
KoBorder *bd = border();
if (!bd) {
return;
}
QRectF borderRect = QRectF(QPointF(0, 0), size());
// Paint the border.
bd->paint(painter, borderRect, KoBorder::PaintInsideLine);
}
// loading & saving methods
QString KoShape::saveStyle(KoGenStyle &style, KoShapeSavingContext &context) const
{
Q_D(const KoShape);
// and fill the style
KoShapeStrokeModelSP sm = stroke();
if (sm) {
sm->fillStyle(style, context);
}
else {
style.addProperty("draw:stroke", "none", KoGenStyle::GraphicType);
}
KoShapeShadow *s = shadow();
if (s)
s->fillStyle(style, context);
QSharedPointer bg = background();
if (bg) {
bg->fillStyle(style, context);
}
else {
style.addProperty("draw:fill", "none", KoGenStyle::GraphicType);
}
KoBorder *b = border();
if (b) {
b->saveOdf(style);
}
if (context.isSet(KoShapeSavingContext::AutoStyleInStyleXml)) {
style.setAutoStyleInStylesDotXml(true);
}
QString value;
if (isGeometryProtected()) {
value = "position size";
}
if (isContentProtected()) {
if (! value.isEmpty())
value += ' ';
value += "content";
}
if (!value.isEmpty()) {
style.addProperty("style:protect", value, KoGenStyle::GraphicType);
}
QMap::const_iterator it(d->additionalStyleAttributes.constBegin());
for (; it != d->additionalStyleAttributes.constEnd(); ++it) {
style.addProperty(it.key(), it.value());
}
if (parent() && parent()->isClipped(this)) {
/*
* In Calligra clipping is done using a parent shape which can be rotated, sheared etc
* and even non-square. So the ODF interoperability version we write here is really
* just a very simple version of that...
*/
qreal top = -position().y();
qreal left = -position().x();
qreal right = parent()->size().width() - size().width() - left;
qreal bottom = parent()->size().height() - size().height() - top;
style.addProperty("fo:clip", QString("rect(%1pt, %2pt, %3pt, %4pt)")
.arg(top, 10, 'f').arg(right, 10, 'f')
.arg(bottom, 10, 'f').arg(left, 10, 'f'), KoGenStyle::GraphicType);
}
QString wrap;
switch (textRunAroundSide()) {
case BiggestRunAroundSide:
wrap = "biggest";
break;
case LeftRunAroundSide:
wrap = "left";
break;
case RightRunAroundSide:
wrap = "right";
break;
case EnoughRunAroundSide:
wrap = "dynamic";
break;
case BothRunAroundSide:
wrap = "parallel";
break;
case NoRunAround:
wrap = "none";
break;
case RunThrough:
wrap = "run-through";
break;
}
style.addProperty("style:wrap", wrap, KoGenStyle::GraphicType);
switch (textRunAroundContour()) {
case ContourBox:
style.addProperty("style:wrap-contour", "false", KoGenStyle::GraphicType);
break;
case ContourFull:
style.addProperty("style:wrap-contour", "true", KoGenStyle::GraphicType);
style.addProperty("style:wrap-contour-mode", "full", KoGenStyle::GraphicType);
break;
case ContourOutside:
style.addProperty("style:wrap-contour", "true", KoGenStyle::GraphicType);
style.addProperty("style:wrap-contour-mode", "outside", KoGenStyle::GraphicType);
break;
}
style.addPropertyPt("style:wrap-dynamic-threshold", textRunAroundThreshold(), KoGenStyle::GraphicType);
if ((textRunAroundDistanceLeft() == textRunAroundDistanceRight())
&& (textRunAroundDistanceTop() == textRunAroundDistanceBottom())
&& (textRunAroundDistanceLeft() == textRunAroundDistanceTop())) {
style.addPropertyPt("fo:margin", textRunAroundDistanceLeft(), KoGenStyle::GraphicType);
} else {
style.addPropertyPt("fo:margin-left", textRunAroundDistanceLeft(), KoGenStyle::GraphicType);
style.addPropertyPt("fo:margin-top", textRunAroundDistanceTop(), KoGenStyle::GraphicType);
style.addPropertyPt("fo:margin-right", textRunAroundDistanceRight(), KoGenStyle::GraphicType);
style.addPropertyPt("fo:margin-bottom", textRunAroundDistanceBottom(), KoGenStyle::GraphicType);
}
return context.mainStyles().insert(style, context.isSet(KoShapeSavingContext::PresentationShape) ? "pr" : "gr");
}
void KoShape::loadStyle(const KoXmlElement &element, KoShapeLoadingContext &context)
{
Q_D(KoShape);
KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
styleStack.setTypeProperties("graphic");
d->fill.clear();
d->stroke.clear();
if (d->shadow && !d->shadow->deref()) {
delete d->shadow;
d->shadow = 0;
}
setBackground(loadOdfFill(context));
setStroke(loadOdfStroke(element, context));
setShadow(d->loadOdfShadow(context));
setBorder(d->loadOdfBorder(context));
QString protect(styleStack.property(KoXmlNS::style, "protect"));
setGeometryProtected(protect.contains("position") || protect.contains("size"));
setContentProtected(protect.contains("content"));
QString margin = styleStack.property(KoXmlNS::fo, "margin");
if (!margin.isEmpty()) {
setTextRunAroundDistanceLeft(KoUnit::parseValue(margin));
setTextRunAroundDistanceTop(KoUnit::parseValue(margin));
setTextRunAroundDistanceRight(KoUnit::parseValue(margin));
setTextRunAroundDistanceBottom(KoUnit::parseValue(margin));
}
margin = styleStack.property(KoXmlNS::fo, "margin-left");
if (!margin.isEmpty()) {
setTextRunAroundDistanceLeft(KoUnit::parseValue(margin));
}
margin = styleStack.property(KoXmlNS::fo, "margin-top");
if (!margin.isEmpty()) {
setTextRunAroundDistanceTop(KoUnit::parseValue(margin));
}
margin = styleStack.property(KoXmlNS::fo, "margin-right");
if (!margin.isEmpty()) {
setTextRunAroundDistanceRight(KoUnit::parseValue(margin));
}
margin = styleStack.property(KoXmlNS::fo, "margin-bottom");
if (!margin.isEmpty()) {
setTextRunAroundDistanceBottom(KoUnit::parseValue(margin));
}
QString wrap;
if (styleStack.hasProperty(KoXmlNS::style, "wrap")) {
wrap = styleStack.property(KoXmlNS::style, "wrap");
} else {
// no value given in the file, but guess biggest
wrap = "biggest";
}
if (wrap == "none") {
setTextRunAroundSide(KoShape::NoRunAround);
} else if (wrap == "run-through") {
QString runTrought = styleStack.property(KoXmlNS::style, "run-through", "background");
if (runTrought == "background") {
setTextRunAroundSide(KoShape::RunThrough, KoShape::Background);
} else {
setTextRunAroundSide(KoShape::RunThrough, KoShape::Foreground);
}
} else {
if (wrap == "biggest")
setTextRunAroundSide(KoShape::BiggestRunAroundSide);
else if (wrap == "left")
setTextRunAroundSide(KoShape::LeftRunAroundSide);
else if (wrap == "right")
setTextRunAroundSide(KoShape::RightRunAroundSide);
else if (wrap == "dynamic")
setTextRunAroundSide(KoShape::EnoughRunAroundSide);
else if (wrap == "parallel")
setTextRunAroundSide(KoShape::BothRunAroundSide);
}
if (styleStack.hasProperty(KoXmlNS::style, "wrap-dynamic-threshold")) {
QString wrapThreshold = styleStack.property(KoXmlNS::style, "wrap-dynamic-threshold");
if (!wrapThreshold.isEmpty()) {
setTextRunAroundThreshold(KoUnit::parseValue(wrapThreshold));
}
}
if (styleStack.property(KoXmlNS::style, "wrap-contour", "false") == "true") {
if (styleStack.property(KoXmlNS::style, "wrap-contour-mode", "full") == "full") {
setTextRunAroundContour(KoShape::ContourFull);
} else {
setTextRunAroundContour(KoShape::ContourOutside);
}
} else {
setTextRunAroundContour(KoShape::ContourBox);
}
}
bool KoShape::loadOdfAttributes(const KoXmlElement &element, KoShapeLoadingContext &context, int attributes)
{
if (attributes & OdfPosition) {
QPointF pos(position());
if (element.hasAttributeNS(KoXmlNS::svg, "x"))
pos.setX(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "x", QString())));
if (element.hasAttributeNS(KoXmlNS::svg, "y"))
pos.setY(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "y", QString())));
setPosition(pos);
}
if (attributes & OdfSize) {
QSizeF s(size());
if (element.hasAttributeNS(KoXmlNS::svg, "width"))
s.setWidth(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "width", QString())));
if (element.hasAttributeNS(KoXmlNS::svg, "height"))
s.setHeight(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "height", QString())));
setSize(s);
}
if (attributes & OdfLayer) {
if (element.hasAttributeNS(KoXmlNS::draw, "layer")) {
KoShapeLayer *layer = context.layer(element.attributeNS(KoXmlNS::draw, "layer"));
if (layer) {
setParent(layer);
}
}
}
if (attributes & OdfId) {
KoElementReference ref;
ref.loadOdf(element);
if (ref.isValid()) {
context.addShapeId(this, ref.toString());
}
}
if (attributes & OdfZIndex) {
if (element.hasAttributeNS(KoXmlNS::draw, "z-index")) {
setZIndex(element.attributeNS(KoXmlNS::draw, "z-index").toInt());
} else {
setZIndex(context.zIndex());
}
}
if (attributes & OdfName) {
if (element.hasAttributeNS(KoXmlNS::draw, "name")) {
setName(element.attributeNS(KoXmlNS::draw, "name"));
}
}
if (attributes & OdfStyle) {
KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
styleStack.save();
if (element.hasAttributeNS(KoXmlNS::draw, "style-name")) {
context.odfLoadingContext().fillStyleStack(element, KoXmlNS::draw, "style-name", "graphic");
}
if (element.hasAttributeNS(KoXmlNS::presentation, "style-name")) {
context.odfLoadingContext().fillStyleStack(element, KoXmlNS::presentation, "style-name", "presentation");
}
loadStyle(element, context);
styleStack.restore();
}
if (attributes & OdfTransformation) {
QString transform = element.attributeNS(KoXmlNS::draw, "transform", QString());
if (! transform.isEmpty())
applyAbsoluteTransformation(parseOdfTransform(transform));
}
if (attributes & OdfAdditionalAttributes) {
QSet additionalAttributeData = KoShapeLoadingContext::additionalAttributeData();
Q_FOREACH (const KoShapeLoadingContext::AdditionalAttributeData &attributeData, additionalAttributeData) {
if (element.hasAttributeNS(attributeData.ns, attributeData.tag)) {
QString value = element.attributeNS(attributeData.ns, attributeData.tag);
//debugFlake << "load additional attribute" << attributeData.tag << value;
setAdditionalAttribute(attributeData.name, value);
}
}
}
if (attributes & OdfCommonChildElements) {
// load glue points (connection points)
loadOdfGluePoints(element, context);
}
return true;
}
QSharedPointer KoShape::loadOdfFill(KoShapeLoadingContext &context) const
{
QString fill = KoShapePrivate::getStyleProperty("fill", context);
QSharedPointer bg;
if (fill == "solid") {
bg = QSharedPointer(new KoColorBackground());
}
else if (fill == "hatch") {
bg = QSharedPointer(new KoHatchBackground());
}
else if (fill == "gradient") {
QString styleName = KoShapePrivate::getStyleProperty("fill-gradient-name", context);
KoXmlElement *e = context.odfLoadingContext().stylesReader().drawStyles("gradient")[styleName];
QString style;
if (e) {
style = e->attributeNS(KoXmlNS::draw, "style", QString());
}
if ((style == "rectangular") || (style == "square")) {
bg = QSharedPointer(new KoOdfGradientBackground());
} else {
QGradient *gradient = new QLinearGradient();
gradient->setCoordinateMode(QGradient::ObjectBoundingMode);
bg = QSharedPointer(new KoGradientBackground(gradient));
}
} else if (fill == "bitmap") {
bg = QSharedPointer(new KoPatternBackground(context.imageCollection()));
#ifndef NWORKAROUND_ODF_BUGS
} else if (fill.isEmpty()) {
bg = QSharedPointer(KoOdfWorkaround::fixBackgroundColor(this, context));
return bg;
#endif
} else {
return QSharedPointer(0);
}
if (!bg->loadStyle(context.odfLoadingContext(), size())) {
return QSharedPointer(0);
}
return bg;
}
KoShapeStrokeModelSP KoShape::loadOdfStroke(const KoXmlElement &element, KoShapeLoadingContext &context) const
{
KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
KoOdfStylesReader &stylesReader = context.odfLoadingContext().stylesReader();
QString stroke = KoShapePrivate::getStyleProperty("stroke", context);
if (stroke == "solid" || stroke == "dash") {
QPen pen = KoOdfGraphicStyles::loadOdfStrokeStyle(styleStack, stroke, stylesReader);
QSharedPointer stroke(new KoShapeStroke());
if (styleStack.hasProperty(KoXmlNS::calligra, "stroke-gradient")) {
QString gradientName = styleStack.property(KoXmlNS::calligra, "stroke-gradient");
QBrush brush = KoOdfGraphicStyles::loadOdfGradientStyleByName(stylesReader, gradientName, size());
stroke->setLineBrush(brush);
} else {
stroke->setColor(pen.color());
}
#ifndef NWORKAROUND_ODF_BUGS
KoOdfWorkaround::fixPenWidth(pen, context);
#endif
stroke->setLineWidth(pen.widthF());
stroke->setJoinStyle(pen.joinStyle());
stroke->setLineStyle(pen.style(), pen.dashPattern());
stroke->setCapStyle(pen.capStyle());
return stroke;
#ifndef NWORKAROUND_ODF_BUGS
} else if (stroke.isEmpty()) {
QPen pen = KoOdfGraphicStyles::loadOdfStrokeStyle(styleStack, "solid", stylesReader);
if (KoOdfWorkaround::fixMissingStroke(pen, element, context, this)) {
QSharedPointer stroke(new KoShapeStroke());
#ifndef NWORKAROUND_ODF_BUGS
KoOdfWorkaround::fixPenWidth(pen, context);
#endif
stroke->setLineWidth(pen.widthF());
stroke->setJoinStyle(pen.joinStyle());
stroke->setLineStyle(pen.style(), pen.dashPattern());
stroke->setCapStyle(pen.capStyle());
stroke->setColor(pen.color());
return stroke;
}
#endif
}
return KoShapeStrokeModelSP();
}
KoShapeShadow *KoShapePrivate::loadOdfShadow(KoShapeLoadingContext &context) const
{
KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
QString shadowStyle = KoShapePrivate::getStyleProperty("shadow", context);
if (shadowStyle == "visible" || shadowStyle == "hidden") {
KoShapeShadow *shadow = new KoShapeShadow();
QColor shadowColor(styleStack.property(KoXmlNS::draw, "shadow-color"));
qreal offsetX = KoUnit::parseValue(styleStack.property(KoXmlNS::draw, "shadow-offset-x"));
qreal offsetY = KoUnit::parseValue(styleStack.property(KoXmlNS::draw, "shadow-offset-y"));
shadow->setOffset(QPointF(offsetX, offsetY));
qreal blur = KoUnit::parseValue(styleStack.property(KoXmlNS::calligra, "shadow-blur-radius"));
shadow->setBlur(blur);
QString opacity = styleStack.property(KoXmlNS::draw, "shadow-opacity");
if (! opacity.isEmpty() && opacity.right(1) == "%")
shadowColor.setAlphaF(opacity.left(opacity.length() - 1).toFloat() / 100.0);
shadow->setColor(shadowColor);
shadow->setVisible(shadowStyle == "visible");
return shadow;
}
return 0;
}
KoBorder *KoShapePrivate::loadOdfBorder(KoShapeLoadingContext &context) const
{
KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
KoBorder *border = new KoBorder();
if (border->loadOdf(styleStack)) {
return border;
}
delete border;
return 0;
}
void KoShape::loadOdfGluePoints(const KoXmlElement &element, KoShapeLoadingContext &context)
{
Q_D(KoShape);
KoXmlElement child;
bool hasCenterGluePoint = false;
forEachElement(child, element) {
if (child.namespaceURI() != KoXmlNS::draw)
continue;
if (child.localName() != "glue-point")
continue;
// NOTE: this uses draw:id, but apparently while ODF 1.2 has deprecated
// all use of draw:id for xml:id, it didn't specify that here, so it
// doesn't support xml:id (and so, maybe, shouldn't use KoElementReference.
const QString id = child.attributeNS(KoXmlNS::draw, "id", QString());
const int index = id.toInt();
// connection point in center should be default but odf doesn't support,
// in new shape, first custom point is in center, it's okay to replace that point
// with point from xml now, we'll add it back later
if(id.isEmpty() || index < KoConnectionPoint::FirstCustomConnectionPoint ||
(index != KoConnectionPoint::FirstCustomConnectionPoint && d->connectors.contains(index))) {
warnFlake << "glue-point with no or invalid id";
continue;
}
QString xStr = child.attributeNS(KoXmlNS::svg, "x", QString()).simplified();
QString yStr = child.attributeNS(KoXmlNS::svg, "y", QString()).simplified();
if(xStr.isEmpty() || yStr.isEmpty()) {
warnFlake << "glue-point with invald position";
continue;
}
KoConnectionPoint connector;
const QString align = child.attributeNS(KoXmlNS::draw, "align", QString());
if (align.isEmpty()) {
#ifndef NWORKAROUND_ODF_BUGS
KoOdfWorkaround::fixGluePointPosition(xStr, context);
KoOdfWorkaround::fixGluePointPosition(yStr, context);
#endif
if(!xStr.endsWith('%') || !yStr.endsWith('%')) {
warnFlake << "glue-point with invald position";
continue;
}
// x and y are relative to drawing object center
connector.position.setX(xStr.remove('%').toDouble()/100.0);
connector.position.setY(yStr.remove('%').toDouble()/100.0);
// convert position to be relative to top-left corner
connector.position += QPointF(0.5, 0.5);
connector.position.rx() = qBound(0.0, connector.position.x(), 1.0);
connector.position.ry() = qBound(0.0, connector.position.y(), 1.0);
} else {
// absolute distances to the edge specified by align
connector.position.setX(KoUnit::parseValue(xStr));
connector.position.setY(KoUnit::parseValue(yStr));
if (align == "top-left") {
connector.alignment = KoConnectionPoint::AlignTopLeft;
} else if (align == "top") {
connector.alignment = KoConnectionPoint::AlignTop;
} else if (align == "top-right") {
connector.alignment = KoConnectionPoint::AlignTopRight;
} else if (align == "left") {
connector.alignment = KoConnectionPoint::AlignLeft;
} else if (align == "center") {
connector.alignment = KoConnectionPoint::AlignCenter;
} else if (align == "right") {
connector.alignment = KoConnectionPoint::AlignRight;
} else if (align == "bottom-left") {
connector.alignment = KoConnectionPoint::AlignBottomLeft;
} else if (align == "bottom") {
connector.alignment = KoConnectionPoint::AlignBottom;
} else if (align == "bottom-right") {
connector.alignment = KoConnectionPoint::AlignBottomRight;
}
debugFlake << "using alignment" << align;
}
const QString escape = child.attributeNS(KoXmlNS::draw, "escape-direction", QString());
if (!escape.isEmpty()) {
if (escape == "horizontal") {
connector.escapeDirection = KoConnectionPoint::HorizontalDirections;
} else if (escape == "vertical") {
connector.escapeDirection = KoConnectionPoint::VerticalDirections;
} else if (escape == "left") {
connector.escapeDirection = KoConnectionPoint::LeftDirection;
} else if (escape == "right") {
connector.escapeDirection = KoConnectionPoint::RightDirection;
} else if (escape == "up") {
connector.escapeDirection = KoConnectionPoint::UpDirection;
} else if (escape == "down") {
connector.escapeDirection = KoConnectionPoint::DownDirection;
}
debugFlake << "using escape direction" << escape;
}
d->connectors[index] = connector;
debugFlake << "loaded glue-point" << index << "at position" << connector.position;
if (d->connectors[index].position == QPointF(0.5, 0.5)) {
hasCenterGluePoint = true;
debugFlake << "center glue-point found at id " << index;
}
}
if (!hasCenterGluePoint) {
d->connectors[d->connectors.count()] = KoConnectionPoint(QPointF(0.5, 0.5),
KoConnectionPoint::AllDirections, KoConnectionPoint::AlignCenter);
}
debugFlake << "shape has now" << d->connectors.count() << "glue-points";
}
void KoShape::loadOdfClipContour(const KoXmlElement &element, KoShapeLoadingContext &context, const QSizeF &scaleFactor)
{
Q_D(KoShape);
KoXmlElement child;
forEachElement(child, element) {
if (child.namespaceURI() != KoXmlNS::draw)
continue;
if (child.localName() != "contour-polygon")
continue;
debugFlake << "shape loads contour-polygon";
KoPathShape *ps = new KoPathShape();
ps->loadContourOdf(child, context, scaleFactor);
ps->setTransformation(transformation());
KoClipPath *clipPath = new KoClipPath({ps}, KoFlake::UserSpaceOnUse);
d->clipPath.reset(clipPath);
}
}
QTransform KoShape::parseOdfTransform(const QString &transform)
{
QTransform matrix;
// Split string for handling 1 transform statement at a time
QStringList subtransforms = transform.split(')', QString::SkipEmptyParts);
QStringList::ConstIterator it = subtransforms.constBegin();
QStringList::ConstIterator end = subtransforms.constEnd();
for (; it != end; ++it) {
QStringList subtransform = (*it).split('(', QString::SkipEmptyParts);
subtransform[0] = subtransform[0].trimmed().toLower();
subtransform[1] = subtransform[1].simplified();
QRegExp reg("[,( ]");
QStringList params = subtransform[1].split(reg, QString::SkipEmptyParts);
if (subtransform[0].startsWith(';') || subtransform[0].startsWith(','))
subtransform[0] = subtransform[0].right(subtransform[0].length() - 1);
QString cmd = subtransform[0].toLower();
if (cmd == "rotate") {
QTransform rotMatrix;
if (params.count() == 3) {
qreal x = KoUnit::parseValue(params[1]);
qreal y = KoUnit::parseValue(params[2]);
rotMatrix.translate(x, y);
// oo2 rotates by radians
rotMatrix.rotate(-params[0].toDouble()*180.0 / M_PI);
rotMatrix.translate(-x, -y);
} else {
// oo2 rotates by radians
rotMatrix.rotate(-params[0].toDouble()*180.0 / M_PI);
}
matrix = matrix * rotMatrix;
} else if (cmd == "translate") {
QTransform moveMatrix;
if (params.count() == 2) {
qreal x = KoUnit::parseValue(params[0]);
qreal y = KoUnit::parseValue(params[1]);
moveMatrix.translate(x, y);
} else // Spec : if only one param given, assume 2nd param to be 0
moveMatrix.translate(KoUnit::parseValue(params[0]) , 0);
matrix = matrix * moveMatrix;
} else if (cmd == "scale") {
QTransform scaleMatrix;
if (params.count() == 2)
scaleMatrix.scale(params[0].toDouble(), params[1].toDouble());
else // Spec : if only one param given, assume uniform scaling
scaleMatrix.scale(params[0].toDouble(), params[0].toDouble());
matrix = matrix * scaleMatrix;
} else if (cmd == "skewx") {
QPointF p = absolutePosition(KoFlake::TopLeft);
QTransform shearMatrix;
shearMatrix.translate(p.x(), p.y());
shearMatrix.shear(tan(-params[0].toDouble()), 0.0F);
shearMatrix.translate(-p.x(), -p.y());
matrix = matrix * shearMatrix;
} else if (cmd == "skewy") {
QPointF p = absolutePosition(KoFlake::TopLeft);
QTransform shearMatrix;
shearMatrix.translate(p.x(), p.y());
shearMatrix.shear(0.0F, tan(-params[0].toDouble()));
shearMatrix.translate(-p.x(), -p.y());
matrix = matrix * shearMatrix;
} else if (cmd == "matrix") {
QTransform m;
if (params.count() >= 6) {
m.setMatrix(params[0].toDouble(), params[1].toDouble(), 0,
params[2].toDouble(), params[3].toDouble(), 0,
KoUnit::parseValue(params[4]), KoUnit::parseValue(params[5]), 1);
}
matrix = matrix * m;
}
}
return matrix;
}
void KoShape::saveOdfAttributes(KoShapeSavingContext &context, int attributes) const
{
Q_D(const KoShape);
if (attributes & OdfStyle) {
KoGenStyle style;
// all items that should be written to 'draw:frame' and any other 'draw:' object that inherits this shape
if (context.isSet(KoShapeSavingContext::PresentationShape)) {
style = KoGenStyle(KoGenStyle::PresentationAutoStyle, "presentation");
context.xmlWriter().addAttribute("presentation:style-name", saveStyle(style, context));
} else {
style = KoGenStyle(KoGenStyle::GraphicAutoStyle, "graphic");
context.xmlWriter().addAttribute("draw:style-name", saveStyle(style, context));
}
}
if (attributes & OdfId) {
if (context.isSet(KoShapeSavingContext::DrawId)) {
KoElementReference ref = context.xmlid(this, "shape", KoElementReference::Counter);
ref.saveOdf(&context.xmlWriter(), KoElementReference::DrawId);
}
}
if (attributes & OdfName) {
if (! name().isEmpty())
context.xmlWriter().addAttribute("draw:name", name());
}
if (attributes & OdfLayer) {
KoShape *parent = d->parent;
while (parent) {
if (dynamic_cast(parent)) {
context.xmlWriter().addAttribute("draw:layer", parent->name());
break;
}
parent = parent->parent();
}
}
if (attributes & OdfZIndex && context.isSet(KoShapeSavingContext::ZIndex)) {
context.xmlWriter().addAttribute("draw:z-index", zIndex());
}
if (attributes & OdfSize) {
QSizeF s(size());
if (parent() && parent()->isClipped(this)) { // being clipped shrinks our visible size
// clipping in ODF is done using a combination of visual size and content cliprect.
// A picture of 10cm x 10cm displayed in a box of 2cm x 4cm will be scaled (out
// of proportion in this case). If we then add a fo:clip like;
// fo:clip="rect(2cm, 3cm, 4cm, 5cm)" (top, right, bottom, left)
// our original 10x10 is clipped to 2cm x 4cm and *then* fitted in that box.
// TODO do this properly by subtracting rects
s = parent()->size();
}
context.xmlWriter().addAttributePt("svg:width", s.width());
context.xmlWriter().addAttributePt("svg:height", s.height());
}
// The position is implicitly stored in the transformation matrix
// if the transformation is saved as well
if ((attributes & OdfPosition) && !(attributes & OdfTransformation)) {
const QPointF p(position() * context.shapeOffset(this));
context.xmlWriter().addAttributePt("svg:x", p.x());
context.xmlWriter().addAttributePt("svg:y", p.y());
}
if (attributes & OdfTransformation) {
QTransform matrix = absoluteTransformation(0) * context.shapeOffset(this);
if (! matrix.isIdentity()) {
if (qAbs(matrix.m11() - 1) < 1E-5 // 1
&& qAbs(matrix.m12()) < 1E-5 // 0
&& qAbs(matrix.m21()) < 1E-5 // 0
&& qAbs(matrix.m22() - 1) < 1E-5) { // 1
context.xmlWriter().addAttributePt("svg:x", matrix.dx());
context.xmlWriter().addAttributePt("svg:y", matrix.dy());
} else {
QString m = QString("matrix(%1 %2 %3 %4 %5pt %6pt)")
.arg(matrix.m11(), 0, 'f', 11)
.arg(matrix.m12(), 0, 'f', 11)
.arg(matrix.m21(), 0, 'f', 11)
.arg(matrix.m22(), 0, 'f', 11)
.arg(matrix.dx(), 0, 'f', 11)
.arg(matrix.dy(), 0, 'f', 11);
context.xmlWriter().addAttribute("draw:transform", m);
}
}
}
if (attributes & OdfViewbox) {
const QSizeF s(size());
QString viewBox = QString("0 0 %1 %2").arg(qRound(s.width())).arg(qRound(s.height()));
context.xmlWriter().addAttribute("svg:viewBox", viewBox);
}
if (attributes & OdfAdditionalAttributes) {
QMap::const_iterator it(d->additionalAttributes.constBegin());
for (; it != d->additionalAttributes.constEnd(); ++it) {
context.xmlWriter().addAttribute(it.key().toUtf8(), it.value());
}
}
}
void KoShape::saveOdfCommonChildElements(KoShapeSavingContext &context) const
{
Q_D(const KoShape);
// save glue points see ODF 9.2.19 Glue Points
if(d->connectors.count()) {
KoConnectionPoints::const_iterator cp = d->connectors.constBegin();
KoConnectionPoints::const_iterator lastCp = d->connectors.constEnd();
for(; cp != lastCp; ++cp) {
// do not save default glue points
if(cp.key() < 4)
continue;
context.xmlWriter().startElement("draw:glue-point");
context.xmlWriter().addAttribute("draw:id", QString("%1").arg(cp.key()));
if (cp.value().alignment == KoConnectionPoint::AlignNone) {
// convert to percent from center
const qreal x = cp.value().position.x() * 100.0 - 50.0;
const qreal y = cp.value().position.y() * 100.0 - 50.0;
context.xmlWriter().addAttribute("svg:x", QString("%1%").arg(x));
context.xmlWriter().addAttribute("svg:y", QString("%1%").arg(y));
} else {
context.xmlWriter().addAttributePt("svg:x", cp.value().position.x());
context.xmlWriter().addAttributePt("svg:y", cp.value().position.y());
}
QString escapeDirection;
switch(cp.value().escapeDirection) {
case KoConnectionPoint::HorizontalDirections:
escapeDirection = "horizontal";
break;
case KoConnectionPoint::VerticalDirections:
escapeDirection = "vertical";
break;
case KoConnectionPoint::LeftDirection:
escapeDirection = "left";
break;
case KoConnectionPoint::RightDirection:
escapeDirection = "right";
break;
case KoConnectionPoint::UpDirection:
escapeDirection = "up";
break;
case KoConnectionPoint::DownDirection:
escapeDirection = "down";
break;
default:
// fall through
break;
}
if(!escapeDirection.isEmpty()) {
context.xmlWriter().addAttribute("draw:escape-direction", escapeDirection);
}
QString alignment;
switch(cp.value().alignment) {
case KoConnectionPoint::AlignTopLeft:
alignment = "top-left";
break;
case KoConnectionPoint::AlignTop:
alignment = "top";
break;
case KoConnectionPoint::AlignTopRight:
alignment = "top-right";
break;
case KoConnectionPoint::AlignLeft:
alignment = "left";
break;
case KoConnectionPoint::AlignCenter:
alignment = "center";
break;
case KoConnectionPoint::AlignRight:
alignment = "right";
break;
case KoConnectionPoint::AlignBottomLeft:
alignment = "bottom-left";
break;
case KoConnectionPoint::AlignBottom:
alignment = "bottom";
break;
case KoConnectionPoint::AlignBottomRight:
alignment = "bottom-right";
break;
default:
// fall through
break;
}
if(!alignment.isEmpty()) {
context.xmlWriter().addAttribute("draw:align", alignment);
}
context.xmlWriter().endElement();
}
}
}
void KoShape::saveOdfClipContour(KoShapeSavingContext &context, const QSizeF &originalSize) const
{
Q_D(const KoShape);
debugFlake << "shape saves contour-polygon";
if (d->clipPath && !d->clipPath->clipPathShapes().isEmpty()) {
// This will loose data as odf can only save one set of contour wheras
// svg loading and at least karbon editing can produce more than one
// TODO, FIXME see if we can save more than one clipshape to odf
d->clipPath->clipPathShapes().first()->saveContourOdf(context, originalSize);
}
}
// end loading & saving methods
// static
void KoShape::applyConversion(QPainter &painter, const KoViewConverter &converter)
{
qreal zoomX, zoomY;
converter.zoom(&zoomX, &zoomY);
painter.scale(zoomX, zoomY);
}
KisHandlePainterHelper KoShape::createHandlePainterHelper(QPainter *painter, KoShape *shape, const KoViewConverter &converter, qreal handleRadius)
{
const QTransform originalPainterTransform = painter->transform();
painter->setTransform(shape->absoluteTransformation(&converter) * painter->transform());
KoShape::applyConversion(*painter, converter);
// move c-tor
return KisHandlePainterHelper(painter, originalPainterTransform, handleRadius);
}
QPointF KoShape::shapeToDocument(const QPointF &point) const
{
return absoluteTransformation(0).map(point);
}
QRectF KoShape::shapeToDocument(const QRectF &rect) const
{
return absoluteTransformation(0).mapRect(rect);
}
QPointF KoShape::documentToShape(const QPointF &point) const
{
return absoluteTransformation(0).inverted().map(point);
}
QRectF KoShape::documentToShape(const QRectF &rect) const
{
return absoluteTransformation(0).inverted().mapRect(rect);
}
bool KoShape::addDependee(KoShape *shape)
{
Q_D(KoShape);
if (! shape)
return false;
// refuse to establish a circular dependency
if (shape->hasDependee(this))
return false;
if (! d->dependees.contains(shape))
d->dependees.append(shape);
return true;
}
void KoShape::removeDependee(KoShape *shape)
{
Q_D(KoShape);
int index = d->dependees.indexOf(shape);
if (index >= 0)
d->dependees.removeAt(index);
}
bool KoShape::hasDependee(KoShape *shape) const
{
Q_D(const KoShape);
return d->dependees.contains(shape);
}
QList KoShape::dependees() const
{
Q_D(const KoShape);
return d->dependees;
}
void KoShape::shapeChanged(ChangeType type, KoShape *shape)
{
Q_UNUSED(type);
Q_UNUSED(shape);
}
KoSnapData KoShape::snapData() const
{
return KoSnapData();
}
void KoShape::setAdditionalAttribute(const QString &name, const QString &value)
{
Q_D(KoShape);
d->additionalAttributes.insert(name, value);
}
void KoShape::removeAdditionalAttribute(const QString &name)
{
Q_D(KoShape);
d->additionalAttributes.remove(name);
}
bool KoShape::hasAdditionalAttribute(const QString &name) const
{
Q_D(const KoShape);
return d->additionalAttributes.contains(name);
}
QString KoShape::additionalAttribute(const QString &name) const
{
Q_D(const KoShape);
return d->additionalAttributes.value(name);
}
void KoShape::setAdditionalStyleAttribute(const char *name, const QString &value)
{
Q_D(KoShape);
d->additionalStyleAttributes.insert(name, value);
}
void KoShape::removeAdditionalStyleAttribute(const char *name)
{
Q_D(KoShape);
d->additionalStyleAttributes.remove(name);
}
KoFilterEffectStack *KoShape::filterEffectStack() const
{
Q_D(const KoShape);
return d->filterEffectStack;
}
void KoShape::setFilterEffectStack(KoFilterEffectStack *filterEffectStack)
{
Q_D(KoShape);
if (d->filterEffectStack)
d->filterEffectStack->deref();
d->filterEffectStack = filterEffectStack;
if (d->filterEffectStack) {
d->filterEffectStack->ref();
}
notifyChanged();
}
QSet KoShape::toolDelegates() const
{
Q_D(const KoShape);
return d->toolDelegates;
}
void KoShape::setToolDelegates(const QSet &delegates)
{
Q_D(KoShape);
d->toolDelegates = delegates;
}
QString KoShape::hyperLink () const
{
Q_D(const KoShape);
return d->hyperLink;
}
void KoShape::setHyperLink(const QString &hyperLink)
{
Q_D(KoShape);
d->hyperLink = hyperLink;
}
KoShapePrivate *KoShape::priv()
{
Q_D(KoShape);
return d;
}
KoShape::ShapeChangeListener::~ShapeChangeListener()
{
Q_FOREACH(KoShape *shape, m_registeredShapes) {
shape->removeShapeChangeListener(this);
}
}
void KoShape::ShapeChangeListener::registerShape(KoShape *shape)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(!m_registeredShapes.contains(shape));
m_registeredShapes.append(shape);
}
void KoShape::ShapeChangeListener::unregisterShape(KoShape *shape)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(m_registeredShapes.contains(shape));
m_registeredShapes.removeAll(shape);
}
void KoShape::ShapeChangeListener::notifyShapeChangedImpl(KoShape::ChangeType type, KoShape *shape)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(m_registeredShapes.contains(shape));
notifyShapeChanged(type, shape);
if (type == KoShape::Deleted) {
unregisterShape(shape);
}
}
void KoShape::addShapeChangeListener(KoShape::ShapeChangeListener *listener)
{
Q_D(KoShape);
KIS_SAFE_ASSERT_RECOVER_RETURN(!d->listeners.contains(listener));
listener->registerShape(this);
d->listeners.append(listener);
}
void KoShape::removeShapeChangeListener(KoShape::ShapeChangeListener *listener)
{
Q_D(KoShape);
KIS_SAFE_ASSERT_RECOVER_RETURN(d->listeners.contains(listener));
d->listeners.removeAll(listener);
listener->unregisterShape(this);
}
QList KoShape::linearizeSubtree(const QList &shapes)
{
QList result;
Q_FOREACH (KoShape *shape, shapes) {
result << shape;
KoShapeContainer *container = dynamic_cast(shape);
if (container) {
result << linearizeSubtree(container->shapes());
}
}
return result;
}
diff --git a/libs/flake/KoShape.h b/libs/flake/KoShape.h
index 4e2699fbcb..bee698062c 100644
--- a/libs/flake/KoShape.h
+++ b/libs/flake/KoShape.h
@@ -1,1222 +1,1228 @@
/* This file is part of the KDE project
Copyright (C) 2006-2008 Thorsten Zachmann
Copyright (C) 2006, 2008 C. Boemann
Copyright (C) 2006-2010 Thomas Zander
Copyright (C) 2007-2009,2011 Jan Hambrecht
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.
*/
#ifndef KOSHAPE_H
#define KOSHAPE_H
#include "KoFlake.h"
#include "KoFlakeTypes.h"
#include "KoConnectionPoint.h"
#include
#include
#include
#include
#include "kritaflake_export.h"
class QPainter;
class QRectF;
class QPainterPath;
class QTransform;
class KoShapeContainer;
class KoShapeStrokeModel;
class KoShapeUserData;
class KoViewConverter;
class KoShapeApplicationData;
class KoShapeSavingContext;
class KoShapeLoadingContext;
class KoGenStyle;
class KoShapeShadow;
class KoShapePrivate;
class KoFilterEffectStack;
class KoSnapData;
class KoClipPath;
class KoClipMask;
class KoShapePaintingContext;
class KoShapeAnchor;
class KoBorder;
struct KoInsets;
class KoShapeBackground;
class KisHandlePainterHelper;
/**
*
* Base class for all flake shapes. Shapes extend this class
* to allow themselves to be manipulated. This class just represents
* a graphical shape in the document and can be manipulated by some default
* tools in this library.
*
* Due to the limited responsibility of this class, the extending object
* can have any data backend and is responsible for painting itself.
*
* We strongly suggest that any extending class will use a Model View
* Controller (MVC) design where the View part is all in this class, as well
* as the one that inherits from this one. This allows the data that rests
* in the model to be reused in different parts of the document. For example
* by having two flake objects that show that same data. Or each showing a section of it.
*
* The KoShape data is completely in postscript-points (pt) (see KoUnit
* for conversion methods to and from points).
* This image will explain the real-world use of the shape and its options.
*
* The Rotation center can be returned with absolutePosition()
*
* Flake objects can be created in three ways:
*
* - a simple new KoDerivedFlake(),
*
- through an associated tool,
*
- through a factory
*
*
* Shape interaction notifications
* We had several notification methods that allow your shape to be notified of changes in other
* shapes positions or rotation etc.
* - The most general is KoShape::shapeChanged().
* a virtual method that you can use to check various changed to your shape made by tools or otherwise.
* - for shape hierarchies the parent may receive a notification when a child was modified.
* This is done though KoShapeContainerModel::childChanged()
* - any shape that is at a similar position as another shape there is collision detection.
* You can register your shape to be sensitive to any changes like moving or whatever to
* other shapes that intersect yours.
* Such changes will then be notified to your shape using the method from (1) You should call
* KoShape::setCollisionDetection(bool) to enable this.
*
*/
class KRITAFLAKE_EXPORT KoShape
{
public:
/// Used by shapeChanged() to select which change was made
enum ChangeType {
PositionChanged, ///< used after a setPosition()
RotationChanged, ///< used after a setRotation()
ScaleChanged, ///< used after a scale()
ShearChanged, ///< used after a shear()
SizeChanged, ///< used after a setSize()
GenericMatrixChange, ///< used after the matrix was changed without knowing which property explicitly changed
KeepAspectRatioChange, ///< used after setKeepAspectRatio()
ParentChanged, ///< used after a setParent()
CollisionDetected, ///< used when another shape moved in our boundingrect
Deleted, ///< the shape was deleted
StrokeChanged, ///< the shapes stroke has changed
BackgroundChanged, ///< the shapes background has changed
ShadowChanged, ///< the shapes shadow has changed
BorderChanged, ///< the shapes border has changed
ParameterChanged, ///< the shapes parameter has changed (KoParameterShape only)
ContentChanged, ///< the content of the shape changed e.g. a new image inside a pixmap/text change inside a textshape
TextRunAroundChanged, ///< used after a setTextRunAroundSide()
ChildChanged, ///< a child of a container was changed/removed. This is propagated to all parents
ConnectionPointChanged, ///< a connection point has changed
ClipPathChanged, ///< the shapes clip path has changed
TransparencyChanged ///< the shapetransparency value has changed
};
/// The behavior text should do when intersecting this shape.
enum TextRunAroundSide {
BiggestRunAroundSide, ///< Run other text around the side that has the most space
LeftRunAroundSide, ///< Run other text around the left side of the frame
RightRunAroundSide, ///< Run other text around the right side of the frame
EnoughRunAroundSide, ///< Run other text dynamically around both sides of the shape, provided there is sufficient space left
BothRunAroundSide, ///< Run other text around both sides of the shape
NoRunAround, ///< The text will be completely avoiding the frame by keeping the horizontal space that this frame occupies blank.
RunThrough ///< The text will completely ignore the frame and layout as if it was not there
};
/// The behavior text should do when intersecting this shape.
enum TextRunAroundContour {
ContourBox, /// Run other text around a bounding rect of the outline
ContourFull, ///< Run other text around also on the inside
ContourOutside ///< Run other text around only on the outside
};
/**
* TODO
*/
enum RunThroughLevel {
Background,
Foreground
};
/**
* @brief Constructor
*/
KoShape();
/**
* @brief Destructor
*/
virtual ~KoShape();
/**
* @brief creates a deep copy of thie shape or shapes subtree
* @return a cloned shape
*/
virtual KoShape* cloneShape() const;
/**
* @brief Paint the shape
* The class extending this one is responsible for painting itself. Since we do not
* assume the shape is square the paint must also clear its background if it will draw
* something transparent on top.
* This can be done with a method like:
*
painter.fillRect(converter.normalToView(QRectF(QPointF(0.0,0.0), size())), background());
* Or equavalent for non-square objects.
* Do note that a shape's top-left is always at coordinate 0,0. Even if the shape itself is rotated
* or translated.
* @param painter used for painting the shape
* @param converter to convert between internal and view coordinates.
* @see applyConversion()
* @param paintcontext the painting context.
*/
virtual void paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintcontext) = 0;
/**
* @brief Paint the shape's border
* This is a helper function that could be called from the paint() method of all shapes.
* @param painter used for painting the shape
* @param converter to convert between internal and view coordinates.
* @see applyConversion()
*/
virtual void paintBorder(QPainter &painter, const KoViewConverter &converter);
/**
* Load a shape from odf
*
* @param context the KoShapeLoadingContext used for loading
* @param element element which represents the shape in odf
*
* @return false if loading failed
*/
virtual bool loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context) = 0;
/**
* @brief store the shape data as ODF XML.
* This is the method that will be called when saving a shape as a described in
* OpenDocument 9.2 Drawing Shapes.
* @see saveOdfAttributes
*/
virtual void saveOdf(KoShapeSavingContext &context) const = 0;
/**
* This method can be used while saving the shape as ODF to add the data
* stored on this shape to the current element.
*
* @param context the context for the current save.
* @param attributes a number of OdfAttribute items to state which attributes to save.
* @see saveOdf
*/
void saveOdfAttributes(KoShapeSavingContext &context, int attributes) const;
/**
* This method can be used while saving the shape as Odf to add common child elements
*
* The office:event-listeners and draw:glue-point are saved.
* @param context the context for the current save.
*/
void saveOdfCommonChildElements(KoShapeSavingContext &context) const;
/**
* This method can be used to save contour data from the clipPath()
*
* The draw:contour-polygon or draw:contour-path elements are saved.
* @param context the context for the current save.
* @param originalSize the original size of the unscaled image.
*/
void saveOdfClipContour(KoShapeSavingContext &context, const QSizeF &originalSize) const;
/**
* @brief Scale the shape using the zero-point which is the top-left corner.
* @see position()
*
* @param sx scale in x direction
* @param sy scale in y direction
*/
void scale(qreal sx, qreal sy);
/**
* @brief Rotate the shape (relative)
*
* The shape will be rotated from the current rotation using the center of the shape using the size()
*
* @param angle change the angle of rotation increasing it with 'angle' degrees
*/
void rotate(qreal angle);
/**
* Return the current rotation in degrees.
* It returns NaN if the shape has a shearing or scaling transformation applied.
*/
qreal rotation() const;
/**
* @brief Shear the shape
* The shape will be sheared using the zero-point which is the top-left corner.
* @see position()
*
* @param sx shear in x direction
* @param sy shear in y direction
*/
void shear(qreal sx, qreal sy);
/**
* @brief Resize the shape
*
* @param size the new size of the shape. This is different from scaling as
* scaling is a so called secondary operation which is comparable to zooming in
* instead of changing the size of the basic shape.
* Easiest example of this difference is that using this method will not distort the
* size of pattern-fills and strokes.
*/
virtual void setSize(const QSizeF &size);
/**
* @brief Get the size of the shape in pt.
*
* The size is in shape coordinates.
*
* @return the size of the shape as set by setSize()
*/
virtual QSizeF size() const;
/**
* @brief Set the position of the shape in pt
*
* @param position the new position of the shape
*/
virtual void setPosition(const QPointF &position);
/**
* @brief Get the position of the shape in pt
*
* @return the position of the shape
*/
QPointF position() const;
/**
* @brief Check if the shape is hit on position
* @param position the position where the user clicked.
* @return true when it hits.
*/
virtual bool hitTest(const QPointF &position) const;
/**
* @brief Get the bounding box of the shape
*
* This includes the line width and the shadow of the shape
*
* @return the bounding box of the shape
*/
virtual QRectF boundingRect() const;
+ /**
+ * Get the united bounding box of a group of shapes. This is a utility
+ * function used in many places in Krita.
+ */
+ static QRectF boundingRect(const QList &shapes);
+
/**
* @brief Add a connector point to the shape
*
* A connector is a place on the shape that allows a graphical connection to be made
* using a line, for example.
*
* @param point the connection point to add
* @return the id of the new connection point
*/
int addConnectionPoint(const KoConnectionPoint &point);
/**
* Sets data of connection point with specified id.
*
* The position of the connector is restricted to the bounding rectangle of the shape.
* When setting a default connection point, the new position is ignored, as these
* are fixed at their default position.
* The function will insert a new connection point if the specified id was not used
* before.
*
* @param connectionPointId the id of the connection point to set
* @param point the connection point data
* @return false if specified connection point id is invalid, else true
*/
bool setConnectionPoint(int connectionPointId, const KoConnectionPoint &point);
/// Checks if a connection point with the specified id exists
bool hasConnectionPoint(int connectionPointId) const;
/// Returns connection point with specified connection point id
KoConnectionPoint connectionPoint(int connectionPointId) const;
/**
* Return a list of the connection points that have been added to this shape.
* All the points are relative to the shape position, see absolutePosition().
* @return a list of the connectors that have been added to this shape.
*/
KoConnectionPoints connectionPoints() const;
/// Removes connection point with specified id
void removeConnectionPoint(int connectionPointId);
/// Removes all connection points
void clearConnectionPoints();
/**
* Return the side text should flow around this shape. This implements the ODF style:wrap
* attribute that specifies how text is displayed around a frame or graphic object.
*/
TextRunAroundSide textRunAroundSide() const;
/**
* Set the side text should flow around this shape.
* @param side the requested side
* @param runThrought run through the foreground or background or...
*/
void setTextRunAroundSide(TextRunAroundSide side, RunThroughLevel runThrough = Background);
/**
* The space between this shape's left edge and text that runs around this shape.
* @return the space around this shape to keep free from text
*/
qreal textRunAroundDistanceLeft() const;
/**
* Set the space between this shape's left edge and the text that run around this shape.
* @param distance the space around this shape to keep free from text
*/
void setTextRunAroundDistanceLeft(qreal distance);
/**
* The space between this shape's top edge and text that runs around this shape.
* @return the space around this shape to keep free from text
*/
qreal textRunAroundDistanceTop() const;
/**
* Set the space between this shape's top edge and the text that run around this shape.
* @param distance the space around this shape to keep free from text
*/
void setTextRunAroundDistanceTop(qreal distance);
/**
* The space between this shape's right edge and text that runs around this shape.
* @return the space around this shape to keep free from text
*/
qreal textRunAroundDistanceRight() const;
/**
* Set the space between this shape's right edge and the text that run around this shape.
* @param distance the space around this shape to keep free from text
*/
void setTextRunAroundDistanceRight(qreal distance);
/**
* The space between this shape's bottom edge and text that runs around this shape.
* @return the space around this shape to keep free from text
*/
qreal textRunAroundDistanceBottom() const;
/**
* Set the space between this shape's bottom edge and the text that run around this shape.
* @param distance the space around this shape to keep free from text
*/
void setTextRunAroundDistanceBottom(qreal distance);
/**
* Return the threshold above which text should flow around this shape.
* The text will not flow around the shape on a side unless the space available on that side
* is above this threshold. Only used when the text run around side is EnoughRunAroundSide.
* @return threshold the threshold
*/
qreal textRunAroundThreshold() const;
/**
* Set the threshold above which text should flow around this shape.
* The text will not flow around the shape on a side unless the space available on that side
* is above this threshold. Only used when the text run around side is EnoughRunAroundSide.
* @param threshold the new threshold
*/
void setTextRunAroundThreshold(qreal threshold);
/**
* Return the how tight text run around is done around this shape.
* @return the contour
*/
TextRunAroundContour textRunAroundContour() const;
/**
* Set how tight text run around is done around this shape.
* @param contour the new contour
*/
void setTextRunAroundContour(TextRunAroundContour contour);
/**
* Set the KoShapeAnchor
*/
void setAnchor(KoShapeAnchor *anchor);
/**
* Return the KoShapeAnchor, or 0
*/
KoShapeAnchor *anchor() const;
/**
* Set the minimum height of the shape.
* Currently it's not respected but only for informational purpose
* @param minimumShapeHeight the minimum height of the frame.
*/
void setMinimumHeight(qreal height);
/**
* Return the minimum height of the shape.
* @return the minimum height of the shape. Default is 0.0.
*/
qreal minimumHeight() const;
/**
* Set the background of the shape.
* A shape background can be a plain color, a gradient, a pattern, be fully transparent
* or have a complex fill.
* Setting such a background will allow the shape to be filled and will be able to tell
* if it is transparent or not.
* @param background the new shape background.
*/
void setBackground(QSharedPointer background);
/**
* return the brush used to paint te background of this shape with.
* A QBrush can have a plain color, be fully transparent or have a complex fill.
* setting such a brush will allow the shape to fill itself using that brush and
* will be able to tell if its transparent or not.
* @return the background-brush
*/
QSharedPointer background() const;
/**
* Returns true if there is some transparency, false if the shape is fully opaque.
* The default implementation will just return if the background has some transparency,
* you should override it and always return true if your shape is not square.
* @return if the shape is (partly) transparent.
*/
virtual bool hasTransparency() const;
/**
* Sets shape level transparency.
* @param transparency the new shape level transparency
*/
void setTransparency(qreal transparency);
/**
* Returns the shape level transparency.
* @param recursive when true takes the parents transparency into account
*/
qreal transparency(bool recursive=false) const;
/**
* Retrieve the z-coordinate of this shape.
* The zIndex property is used to determine which shape lies on top of other objects.
* An shape with a higher z-order is on top, and can obscure another shape.
* @return the z-index of this shape.
* @see setZIndex()
*/
int zIndex() const;
/**
* Set the z-coordinate of this shape.
* The zIndex property is used to determine which shape lies on top of other objects.
* An shape with a higher z-order is on top, and can obscure, another shape.
* Just like two objects having the same x or y coordinate will make them 'touch',
* so will two objects with the same z-index touch on the z plane. In layering the
* shape this, however, can cause a little confusion as one always has to be on top.
* The layering if two overlapping objects have the same index is implementation dependent
* and probably depends on the order in which they are added to the shape manager.
* @param zIndex the new z-index;
*/
void setZIndex(int zIndex);
/**
* Retrieve the run through property of this shape.
* The run through property is used to determine if the shape is behind, inside or before text.
* @return the run through of this shape.
*/
int runThrough();
/**
* Set the run through property of this shape.
* The run through property is used to determine if the shape is behind, inside or before text.
* @param runThrough the new run through;
*/
virtual void setRunThrough(short int runThrough);
/**
* Changes the Shape to be visible or invisible.
* Being visible means being painted, as well as being used for
* things like guidelines or searches.
* @param on when true; set the shape to be visible.
* @see setGeometryProtected(), setContentProtected(), setSelectable()
*/
void setVisible(bool on);
/**
* Returns current visibility state of this shape.
* Being visible means being painted, as well as being used for
* things like guidelines or searches.
* @param recursive when true, checks visibility recursively
* @return current visibility state of this shape.
* @see isGeometryProtected(), isContentProtected(), isSelectable()
*/
bool isVisible(bool recursive = false) const;
/**
* Changes the shape to be printable or not. The default is true.
*
* If a Shape's print flag is true, the shape will be printed. If
* false, the shape will not be printed. If a shape is not visible (@see isVisible),
* it isPrinted will return false, too.
*/
void setPrintable(bool on);
/**
* Returns the current printable state of this shape.
*
* A shape can be visible but not printable, not printable and not visible
* or visible and printable, but not invisible and still printable.
*
* @return current printable state of this shape.
*/
bool isPrintable() const;
/**
* Makes it possible for the user to select this shape.
* This parameter defaults to true.
* @param selectable when true; set the shape to be selectable by the user.
* @see setGeometryProtected(), setContentProtected(), setVisible()
*/
void setSelectable(bool selectable);
/**
* Returns if this shape can be selected by the user.
* @return true only when the object is selectable.
* @see isGeometryProtected(), isContentProtected(), isVisible()
*/
bool isSelectable() const;
/**
* Tells the shape to have its position/rotation and size protected from user-changes.
* The geometry being protected means the user can not change shape or position of the
* shape. This includes any matrix operation such as rotation.
* @param on when true; set the shape to have its geometry protected.
* @see setContentProtected(), setSelectable(), setVisible()
*/
void setGeometryProtected(bool on);
/**
* Returns current geometry protection state of this shape.
* The geometry being protected means the user can not change shape or position of the
* shape. This includes any matrix operation such as rotation.
* @return current geometry protection state of this shape.
* @see isContentProtected(), isSelectable(), isVisible()
*/
bool isGeometryProtected() const;
/**
* Marks the shape to have its content protected against editing.
* Content protection is a hint for tools to disallow the user editing the content.
* @param protect when true set the shapes content to be protected from user modification.
* @see setGeometryProtected(), setSelectable(), setVisible()
*/
void setContentProtected(bool protect);
/**
* Returns current content protection state of this shape.
* Content protection is a hint for tools to disallow the user editing the content.
* @return current content protection state of this shape.
* @see isGeometryProtected(), isSelectable(), isVisible()
*/
bool isContentProtected() const;
/**
* Returns the parent, or 0 if there is no parent.
* @return the parent, or 0 if there is no parent.
*/
KoShapeContainer *parent() const;
/**
* Set the parent of this shape.
* @param parent the new parent of this shape. Can be 0 if the shape has no parent anymore.
*/
void setParent(KoShapeContainer *parent);
/**
* Request a repaint to be queued.
* The repaint will be of the entire Shape, including its selection handles should this
* shape be selected.
*
This method will return immediately and only request a repaint. Successive calls
* will be merged into an appropriate repaint action.
*/
virtual void update() const;
/**
* Request a repaint to be queued.
* The repaint will be restricted to the parameters rectangle, which is expected to be
* in points (the internal coordinates system of KoShape) and it is expected to be
* normalized.
*
This method will return immediately and only request a repaint. Successive calls
* will be merged into an appropriate repaint action.
* @param rect the rectangle (in pt) to queue for repaint.
*/
virtual void update(const QRectF &rect) const;
/// Used by compareShapeZIndex() to order shapes
enum ChildZOrderPolicy {
ChildZDefault,
ChildZParentChild = ChildZDefault, ///< normal parent/child ordering
ChildZPassThrough ///< children are considered equal to this shape
};
/**
* Returns if during compareShapeZIndex() how this shape portrays the values
* of its children. The default behaviour is to let this shape's z values take
* the place of its childrens values, so you get a parent/child relationship.
* The children are naturally still ordered relatively to their z values
*
* But for special cases (like Calligra's TextShape) it can be overloaded to return
* ChildZPassThrough which means the children keep their own z values
* @returns the z order policy of this shape
*/
virtual ChildZOrderPolicy childZOrderPolicy();
/**
* This is a method used to sort a list using the STL sorting methods.
* @param s1 the first shape
* @param s2 the second shape
*/
static bool compareShapeZIndex(KoShape *s1, KoShape *s2);
/**
* returns the outline of the shape in the form of a path.
* The outline returned will always be relative to the position() of the shape, so
* moving the shape will not alter the result. The outline is used to draw the stroke
* on, for example.
* @returns the outline of the shape in the form of a path.
*/
virtual QPainterPath outline() const;
/**
* returns the outline of the shape in the form of a rect.
* The outlineRect returned will always be relative to the position() of the shape, so
* moving the shape will not alter the result. The outline is used to calculate
* the boundingRect.
* @returns the outline of the shape in the form of a rect.
*/
virtual QRectF outlineRect() const;
/**
* returns the outline of the shape in the form of a path for the use of painting a shadow.
*
* Normally this would be the same as outline() if there is a fill (background) set on the
* shape and empty if not. However, a shape could reimplement this to return an outline
* even if no fill is defined. A typical example of this would be the picture shape
* which has a picture but almost never a background.
*
* @returns the outline of the shape in the form of a path.
*/
virtual QPainterPath shadowOutline() const;
/**
* Returns the currently set stroke, or 0 if there is no stroke.
* @return the currently set stroke, or 0 if there is no stroke.
*/
KoShapeStrokeModelSP stroke() const;
/**
* Set a new stroke, removing the old one.
* @param stroke the new stroke, or 0 if there should be no stroke.
*/
void setStroke(KoShapeStrokeModelSP stroke);
/**
* Return the insets of the stroke.
* Convenience method for KoShapeStrokeModel::strokeInsets()
*/
KoInsets strokeInsets() const;
/// Sets the new shadow, removing the old one
void setShadow(KoShapeShadow *shadow);
/// Returns the currently set shadow or 0 if there is no shadow set
KoShapeShadow *shadow() const;
/// Sets the new border, removing the old one.
void setBorder(KoBorder *border);
/// Returns the currently set border or 0 if there is no border set
KoBorder *border() const;
/// Sets a new clip path, removing the old one
void setClipPath(KoClipPath *clipPath);
/// Returns the currently set clip path or 0 if there is no clip path set
KoClipPath * clipPath() const;
/// Sets a new clip mask, removing the old one. The mask is owned by the shape.
void setClipMask(KoClipMask *clipMask);
/// Returns the currently set clip mask or 0 if there is no clip mask set
KoClipMask* clipMask() const;
/**
* Setting the shape to keep its aspect-ratio has the effect that user-scaling will
* keep the width/hight ratio intact so as not to distort shapes that rely on that
* ratio.
* @param keepAspect the new value
*/
void setKeepAspectRatio(bool keepAspect);
/**
* Setting the shape to keep its aspect-ratio has the effect that user-scaling will
* keep the width/hight ratio intact so as not to distort shapes that rely on that
* ratio.
* @return whether to keep aspect ratio of this shape
*/
bool keepAspectRatio() const;
/**
* Return the position of this shape regardless of rotation/skew/scaling and regardless of
* this shape having a parent (being in a group) or not.
* @param anchor The place on the (unaltered) shape that you want the position of.
* @return the point that is the absolute, centered position of this shape.
*/
QPointF absolutePosition(KoFlake::AnchorPosition anchor = KoFlake::Center) const;
/**
* Move this shape to an absolute position where the end location will be the same
* regardless of the shape's rotation/skew/scaling and regardless of this shape having
* a parent (being in a group) or not.
* The newPosition is going to be the center of the shape.
* This has the convenient effect that:
shape->setAbsolutePosition(QPointF(0,0));
shape->rotate(45);
Will result in the same visual position of the shape as the opposite:
shape->rotate(45);
shape->setAbsolutePosition(QPointF(0,0));
* @param newPosition the new absolute center of the shape.
* @param anchor The place on the (unaltered) shape that you set the position of.
*/
void setAbsolutePosition(const QPointF &newPosition, KoFlake::AnchorPosition anchor = KoFlake::Center);
/**
* Set a data object on the shape to be used by an application.
* This is specifically useful when a shape is created in a plugin and that data from that
* shape should be accessible outside the plugin.
* @param userData the new user data, or 0 to delete the current one.
*/
void setUserData(KoShapeUserData *userData);
/**
* Return the current userData.
*/
KoShapeUserData *userData() const;
/**
* Return the Id of this shape, identifying the type of shape by the id of the factory.
* @see KoShapeFactoryBase::shapeId()
* @return the id of the shape-type
*/
QString shapeId() const;
/**
* Set the Id of this shape. A shapeFactory is expected to set the Id at creation
* so applications can find out what kind of shape this is.
* @see KoShapeFactoryBase::shapeId()
* @param id the ID from the factory that created this shape
*/
void setShapeId(const QString &id);
/**
* Create a matrix that describes all the transformations done on this shape.
*
* The absolute transformation is the combined transformation of this shape
* and all its parents and grandparents.
*
* @param converter if not null, this method uses the converter to mark the right
* offsets in the current view.
*/
QTransform absoluteTransformation(const KoViewConverter *converter) const;
/**
* Applies a transformation to this shape.
*
* The transformation given is relative to the global coordinate system, i.e. the document.
* This is a convenience function to apply a global transformation to this shape.
* @see applyTransformation
*
* @param matrix the transformation matrix to apply
*/
void applyAbsoluteTransformation(const QTransform &matrix);
/**
* Sets a new transformation matrix describing the local transformations on this shape.
* @param matrix the new transformation matrix
*/
void setTransformation(const QTransform &matrix);
/// Returns the shapes local transformation matrix
QTransform transformation() const;
/**
* Applies a transformation to this shape.
*
* The transformation given is relative to the shape coordinate system.
*
* @param matrix the transformation matrix to apply
*/
void applyTransformation(const QTransform &matrix);
/**
* Copy all the settings from the parameter shape and apply them to this shape.
* Settings like the position and rotation to visible and locked. The parent
* is a notable exclusion.
* @param shape the shape to use as original
*/
void copySettings(const KoShape *shape);
/**
* Convenience method that allows people implementing paint() to use the shape
* internal coordinate system directly to paint itself instead of considering the
* views zoom.
* @param painter the painter to alter the zoom level of.
* @param converter the converter for the current views zoom.
*/
static void applyConversion(QPainter &painter, const KoViewConverter &converter);
/**
* A convenience method that creates a handles helper with applying transformations at
* the same time. Please note that you shouldn't save/restore additionally. All the work
* on restoring original painter's transformations is done by the helper.
*/
static KisHandlePainterHelper createHandlePainterHelper(QPainter *painter, KoShape *shape, const KoViewConverter &converter, qreal handleRadius = 0.0);
/**
* @brief Transforms point from shape coordinates to document coordinates
* @param point in shape coordinates
* @return point in document coordinates
*/
QPointF shapeToDocument(const QPointF &point) const;
/**
* @brief Transforms rect from shape coordinates to document coordinates
* @param rect in shape coordinates
* @return rect in document coordinates
*/
QRectF shapeToDocument(const QRectF &rect) const;
/**
* @brief Transforms point from document coordinates to shape coordinates
* @param point in document coordinates
* @return point in shape coordinates
*/
QPointF documentToShape(const QPointF &point) const;
/**
* @brief Transform rect from document coordinates to shape coordinates
* @param rect in document coordinates
* @return rect in shape coordinates
*/
QRectF documentToShape(const QRectF &rect) const;
/**
* Returns the name of the shape.
* @return the shapes name
*/
QString name() const;
/**
* Sets the name of the shape.
* @param name the new shape name
*/
void setName(const QString &name);
/**
* Update the position of the shape in the tree of the KoShapeManager.
*/
void notifyChanged();
/**
* A shape can be in a state that it is doing processing data like loading or text layout.
* In this case it can be shown on screen probably partially but it should really not be printed
* until it is fully done processing.
* Warning! This method can be blocking for a long time
* @param asynchronous If set to true the processing will can take place in a different thread and the
* function will not block until the shape is finised.
* In case of printing Flake will call this method from a non-main thread and only
* start printing it when the in case of printing method returned.
* If set to false the processing needs to be done synchronously and will
* block until the result is finished.
*/
virtual void waitUntilReady(const KoViewConverter &converter, bool asynchronous = true) const;
/// checks recursively if the shape or one of its parents is not visible or locked
bool isEditable() const;
/**
* Adds a shape which depends on this shape.
* Making a shape dependent on this one means it will get shapeChanged() called
* on each update of this shape.
*
* If this shape already depends on the given shape, establishing the
* dependency is refused to prevent circular dependencies.
*
* @param shape the shape which depends on this shape
* @return true if dependency could be established, otherwise false
* @see removeDependee(), hasDependee()
*/
bool addDependee(KoShape *shape);
/**
* Removes as shape depending on this shape.
* @see addDependee(), hasDependee()
*/
void removeDependee(KoShape *shape);
/// Returns if the given shape is dependent on this shape
bool hasDependee(KoShape *shape) const;
/// Returns list of shapes depending on this shape
QList dependees() const;
/// Returns additional snap data the shape wants to have snapping to
virtual KoSnapData snapData() const;
/**
* Set additional attribute
*
* This can be used to attach additional attributes to a shape for attributes
* that are application specific like presentation:placeholder
*
* @param name The name of the attribute in the following form prefix:tag e.g. presentation:placeholder
* @param value The value of the attribute
*/
void setAdditionalAttribute(const QString &name, const QString &value);
/**
* Remove additional attribute
*
* @param name The name of the attribute in the following form prefix:tag e.g. presentation:placeholder
*/
void removeAdditionalAttribute(const QString &name);
/**
* Check if additional attribute is set
*
* @param name The name of the attribute in the following form prefix:tag e.g. presentation:placeholder
*
* @return true if there is a attribute with prefix:tag set, false otherwise
*/
bool hasAdditionalAttribute(const QString &name) const;
/**
* Get additional attribute
*
* @param name The name of the attribute in the following form prefix:tag e.g. presentation:placeholder
*
* @return The value of the attribute if it exists or a null string if not found.
*/
QString additionalAttribute(const QString &name) const;
void setAdditionalStyleAttribute(const char *name, const QString &value);
void removeAdditionalStyleAttribute(const char *name);
/**
* Returns the filter effect stack of the shape
*
* @return the list of filter effects applied on the shape when rendering.
*/
KoFilterEffectStack *filterEffectStack() const;
/// Sets the new filter effect stack, removing the old one
void setFilterEffectStack(KoFilterEffectStack *filterEffectStack);
/**
* Set the property collision detection.
* Setting this to true will result in calls to shapeChanged() with the CollisionDetected
* parameter whenever either this or another shape is moved/rotated etc and intersects this shape.
* @param detect if true detect collisions.
*/
void setCollisionDetection(bool detect);
/**
* get the property collision detection.
* @returns true if collision detection is on.
*/
bool collisionDetection();
/**
* Return the tool delegates for this shape.
* In Flake a shape being selected will cause the tool manager to make available all tools that
* can edit the selected shapes. In some cases selecting one shape should allow the tool to
* edit a related shape be available too. The tool delegates allows this to happen by taking
* all the shapes in the set into account on tool selection.
* Notice that if the set is non-empty 'this' shape is no longer looked at. You can choose
* to add itself to the set too.
*/
QSet toolDelegates() const;
/**
* Set the tool delegates.
* @param delegates the new delegates.
* @see toolDelegates()
*/
void setToolDelegates(const QSet &delegates);
/**
* Return the hyperlink for this shape.
*/
QString hyperLink () const;
/**
* Set hyperlink for this shape.
* @param hyperLink name.
*/
void setHyperLink(const QString &hyperLink);
/**
* \internal
* Returns the private object for use within the flake lib
*/
KoShapePrivate *priv();
public:
struct ShapeChangeListener {
virtual ~ShapeChangeListener();
virtual void notifyShapeChanged(ChangeType type, KoShape *shape) = 0;
private:
friend class KoShape;
friend class KoShapePrivate;
void registerShape(KoShape *shape);
void unregisterShape(KoShape *shape);
void notifyShapeChangedImpl(ChangeType type, KoShape *shape);
QList m_registeredShapes;
};
void addShapeChangeListener(ShapeChangeListener *listener);
void removeShapeChangeListener(ShapeChangeListener *listener);
public:
static QList linearizeSubtree(const QList &shapes);
protected:
/// constructor
KoShape(KoShapePrivate *);
/* ** loading saving helper methods */
/// attributes from ODF 1.1 chapter 9.2.15 Common Drawing Shape Attributes
enum OdfAttribute {
OdfTransformation = 1, ///< Store transformation information
OdfSize = 2, ///< Store size information
OdfPosition = 8, ///< Store position
OdfAdditionalAttributes = 4, ///< Store additional attributes of the shape
OdfCommonChildElements = 16, ///< Event actions and connection points
OdfLayer = 64, ///< Store layer name
OdfStyle = 128, ///< Store the style
OdfId = 256, ///< Store the unique ID
OdfName = 512, ///< Store the name of the shape
OdfZIndex = 1024, ///< Store the z-index
OdfViewbox = 2048, ///< Store the viewbox
/// A mask for all mandatory attributes
OdfMandatories = OdfLayer | OdfStyle | OdfId | OdfName | OdfZIndex,
/// A mask for geometry attributes
OdfGeometry = OdfPosition | OdfSize,
/// A mask for all the attributes
OdfAllAttributes = OdfTransformation | OdfGeometry | OdfAdditionalAttributes | OdfMandatories | OdfCommonChildElements
};
/**
* This method is used during loading of the shape to load common attributes
*
* @param context the KoShapeLoadingContext used for loading
* @param element element which represents the shape in odf
* @param attributes a number of OdfAttribute items to state which attributes to load.
*/
bool loadOdfAttributes(const KoXmlElement &element, KoShapeLoadingContext &context, int attributes);
/**
* Parses the transformation attribute from the given string
* @param transform the transform attribute string
* @return the resulting transformation matrix
*/
QTransform parseOdfTransform(const QString &transform);
/**
* @brief Saves the style used for the shape
*
* This method fills the given style object with the stroke and
* background properties and then adds the style to the context.
*
* @param style the style object to fill
* @param context used for saving
* @return the name of the style
* @see saveOdf
*/
virtual QString saveStyle(KoGenStyle &style, KoShapeSavingContext &context) const;
/**
* Loads the stroke and fill style from the given element.
*
* @param element the xml element to load the style from
* @param context the loading context used for loading
*/
virtual void loadStyle(const KoXmlElement &element, KoShapeLoadingContext &context);
/// Loads the stroke style
KoShapeStrokeModelSP loadOdfStroke(const KoXmlElement &element, KoShapeLoadingContext &context) const;
/// Loads the fill style
QSharedPointer loadOdfFill(KoShapeLoadingContext &context) const;
/// Loads the connection points
void loadOdfGluePoints(const KoXmlElement &element, KoShapeLoadingContext &context);
/// Loads the clip contour
void loadOdfClipContour(const KoXmlElement &element, KoShapeLoadingContext &context, const QSizeF &scaleFactor);
/* ** end loading saving */
/**
* A hook that allows inheriting classes to do something after a KoShape property changed
* This is called whenever the shape, position rotation or scale properties were altered.
* @param type an indicator which type was changed.
*/
virtual void shapeChanged(ChangeType type, KoShape *shape = 0);
/// return the current matrix that contains the rotation/scale/position of this shape
QTransform transform() const;
KoShapePrivate *d_ptr;
private:
Q_DECLARE_PRIVATE(KoShape)
};
Q_DECLARE_METATYPE(KoShape*)
#endif
diff --git a/libs/flake/KoShapeBasedDocumentBase.cpp b/libs/flake/KoShapeBasedDocumentBase.cpp
index fc13c9e9c0..6daef223de 100644
--- a/libs/flake/KoShapeBasedDocumentBase.cpp
+++ b/libs/flake/KoShapeBasedDocumentBase.cpp
@@ -1,82 +1,85 @@
/* This file is part of the KDE project
Copyright (C) 2006, 2010 Thomas Zander
Copyright (C) 2011 Jan Hambrecht
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.
*/
+#include
#include "KoShapeBasedDocumentBase.h"
#include "KoDocumentResourceManager.h"
#include "KoShapeRegistry.h"
#include "KoShapeFactoryBase.h"
#include
#include
#include
class KoShapeBasedDocumentBasePrivate
{
public:
KoShapeBasedDocumentBasePrivate()
: resourceManager(new KoDocumentResourceManager())
{
KoShapeRegistry *registry = KoShapeRegistry::instance();
foreach (const QString &id, registry->keys()) {
KoShapeFactoryBase *shapeFactory = registry->value(id);
shapeFactory->newDocumentResourceManager(resourceManager);
}
// read persistent application wide resources
KSharedConfigPtr config = KSharedConfig::openConfig();
- if (config->hasGroup("Misc")) {
- KConfigGroup miscGroup = config->group("Misc");
- const qreal pasteOffset = miscGroup.readEntry("CopyOffset", 10.0);
- resourceManager->setPasteOffset(pasteOffset);
- const bool pasteAtCursor = miscGroup.readEntry("PasteAtCursor", true);
- resourceManager->enablePasteAtCursor(pasteAtCursor);
- const uint grabSensitivity = miscGroup.readEntry("GrabSensitivity", 3);
- resourceManager->setGrabSensitivity(grabSensitivity);
- const uint handleRadius = miscGroup.readEntry("HandleRadius", 3);
- resourceManager->setHandleRadius(handleRadius);
- }
+ KConfigGroup miscGroup = config->group("Misc");
+ const uint grabSensitivity = miscGroup.readEntry("GrabSensitivity", 3);
+ resourceManager->setGrabSensitivity(grabSensitivity);
+ const uint handleRadius = miscGroup.readEntry("HandleRadius", 3);
+ resourceManager->setHandleRadius(handleRadius);
}
~KoShapeBasedDocumentBasePrivate()
{
delete resourceManager;
}
KoDocumentResourceManager *resourceManager;
};
KoShapeBasedDocumentBase::KoShapeBasedDocumentBase()
: d(new KoShapeBasedDocumentBasePrivate())
{
}
KoShapeBasedDocumentBase::~KoShapeBasedDocumentBase()
{
delete d;
}
void KoShapeBasedDocumentBase::shapesRemoved(const QList & /*shapes*/, KUndo2Command * /*command*/)
{
}
KoDocumentResourceManager *KoShapeBasedDocumentBase::resourceManager() const
{
return d->resourceManager;
}
+
+QRectF KoShapeBasedDocumentBase::documentRect() const
+{
+ const qreal pxToPt = 72.0 / pixelsPerInch();
+
+ QTransform t = QTransform::fromScale(pxToPt, pxToPt);
+ return t.mapRect(documentRectInPixels());
+}
diff --git a/libs/flake/KoShapeBasedDocumentBase.h b/libs/flake/KoShapeBasedDocumentBase.h
index b2f3565b03..95e6327d6d 100644
--- a/libs/flake/KoShapeBasedDocumentBase.h
+++ b/libs/flake/KoShapeBasedDocumentBase.h
@@ -1,85 +1,102 @@
/* This file is part of the KDE project
Copyright (C) 2006 Jan Hambrecht
Copyright (C) 2006, 2010 Thomas Zander
Copyright (C) 2008 C. Boemann
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.
*/
#ifndef KOSHAPEBASEDDOCUMENTBASE_H
#define KOSHAPEBASEDDOCUMENTBASE_H
#include "kritaflake_export.h"
#include
+class QRectF;
class KoShape;
class KoShapeBasedDocumentBasePrivate;
class KoDocumentResourceManager;
class KUndo2Command;
/**
* The KoShapeBasedDocumentBase is an abstract interface that the application's class
* that owns the shapes should implement. This tends to be the document.
* @see KoShapeDeleteCommand, KoShapeCreateCommand
*/
class KRITAFLAKE_EXPORT KoShapeBasedDocumentBase
{
public:
KoShapeBasedDocumentBase();
virtual ~KoShapeBasedDocumentBase();
/**
* Add a shape to the shape controller, allowing it to be seen and saved.
* The controller should add the shape to the ShapeManager instance(s) manually
* if the shape is one that should be currently shown on screen.
* @param shape the new shape
*/
virtual void addShape(KoShape *shape) = 0;
/**
* Remove a shape from the shape controllers control, allowing it to be deleted shortly after
* The controller should remove the shape from all the ShapeManager instance(s) manually
* @param shape the shape to remove
*/
virtual void removeShape(KoShape *shape) = 0;
/**
* This method gets called after the KoShapeDeleteCommand is executed
*
* This passes the KoShapeDeleteCommand as the command parameter. This makes it possible
* for applications that need to do something after the KoShapeDeleteCommand is done, e.g.
* adding one commands that need to be executed when a shape was deleted.
* The default implementation is empty.
* @param shapes The list of shapes that got removed.
* @param command The command that was used to remove the shapes from the document.
*/
virtual void shapesRemoved(const QList &shapes, KUndo2Command *command);
/**
* Return a pointer to the resource manager associated with the
* shape-set (typically a document). The resource manager contains
* document wide resources * such as variable managers, the image
* collection and others.
*/
virtual KoDocumentResourceManager *resourceManager() const;
+ /**
+ * The size of the document measured in rasterized pixels. This information is needed for loading
+ * SVG documents that use 'px' as the default unit.
+ */
+ virtual QRectF documentRectInPixels() const = 0;
+
+ /**
+ * The size of the document measured in 'pt'
+ */
+ QRectF documentRect() const;
+
+ /**
+ * Resolution of the rasterized representaiton of the document. Used to load SVG documents correctly.
+ */
+ virtual qreal pixelsPerInch() const = 0;
+
private:
KoShapeBasedDocumentBasePrivate * const d;
};
#endif
diff --git a/libs/flake/KoShapeController.cpp b/libs/flake/KoShapeController.cpp
index 7d4559247a..d2f692fd34 100644
--- a/libs/flake/KoShapeController.cpp
+++ b/libs/flake/KoShapeController.cpp
@@ -1,185 +1,200 @@
/* This file is part of the KDE project
*
* Copyright (C) 2006-2007, 2010 Thomas Zander
* Copyright (C) 2006-2008 Thorsten Zachmann
* Copyright (C) 2011 Jan Hambrecht
*
* 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.
*/
#include "KoShapeController.h"
#include "KoShapeBasedDocumentBase.h"
#include "KoShapeRegistry.h"
#include "KoDocumentResourceManager.h"
#include "KoShapeManager.h"
#include "KoShapeLayer.h"
#include "KoSelection.h"
#include "commands/KoShapeCreateCommand.h"
#include "commands/KoShapeDeleteCommand.h"
#include "commands/KoShapeConnectionChangeCommand.h"
#include "KoCanvasBase.h"
#include "KoShapeConfigWidgetBase.h"
#include "KoShapeFactoryBase.h"
#include "KoShape.h"
#include "KoConnectionShape.h"
#include
#include
#include
#include
class KoShapeController::Private
{
public:
Private()
: canvas(0),
shapeBasedDocument(0)
{
}
KoCanvasBase *canvas;
KoShapeBasedDocumentBase *shapeBasedDocument;
KUndo2Command* addShape(KoShape *shape, bool showDialog, KUndo2Command *parent) {
if (canvas) {
if (showDialog) {
KoShapeFactoryBase *factory = KoShapeRegistry::instance()->value(shape->shapeId());
Q_ASSERT(factory);
int z = 0;
Q_FOREACH (KoShape *sh, canvas->shapeManager()->shapes())
z = qMax(z, sh->zIndex());
shape->setZIndex(z + 1);
// show config dialog.
KPageDialog *dialog = new KPageDialog(canvas->canvasWidget());
dialog->setWindowTitle(i18n("%1 Options", factory->name()));
int pageCount = 0;
QList widgets;
Q_FOREACH (KoShapeConfigWidgetBase* panel, factory->createShapeOptionPanels()) {
if (! panel->showOnShapeCreate())
continue;
panel->open(shape);
panel->connect(panel, SIGNAL(accept()), dialog, SLOT(accept()));
widgets.append(panel);
panel->setResourceManager(canvas->resourceManager());
panel->setUnit(canvas->unit());
QString title = panel->windowTitle().isEmpty() ? panel->objectName() : panel->windowTitle();
dialog->addPage(panel, title);
pageCount ++;
}
if (pageCount > 0) {
if (pageCount > 1)
dialog->setFaceType(KPageDialog::Tabbed);
if (dialog->exec() != KPageDialog::Accepted) {
delete dialog;
return 0;
}
Q_FOREACH (KoShapeConfigWidgetBase *widget, widgets)
widget->save();
}
delete dialog;
}
// set the active layer as parent if there is not yet a parent.
if (!shape->parent()) {
shape->setParent(canvas->shapeManager()->selection()->activeLayer());
}
}
return new KoShapeCreateCommand(shapeBasedDocument, shape, parent);
}
void handleAttachedConnections(KoShape *shape, KUndo2Command *parentCmd) {
foreach (KoShape *dependee, shape->dependees()) {
KoConnectionShape *connection = dynamic_cast(dependee);
if (connection) {
if (shape == connection->firstShape()) {
new KoShapeConnectionChangeCommand(connection, KoConnectionShape::StartHandle,
shape, connection->firstConnectionId(), 0, -1, parentCmd);
} else if (shape == connection->secondShape()) {
new KoShapeConnectionChangeCommand(connection, KoConnectionShape::EndHandle,
shape, connection->secondConnectionId(), 0, -1, parentCmd);
}
}
}
}
};
KoShapeController::KoShapeController(KoCanvasBase *canvas, KoShapeBasedDocumentBase *shapeBasedDocument)
: d(new Private())
{
d->canvas = canvas;
d->shapeBasedDocument = shapeBasedDocument;
if (shapeBasedDocument) {
shapeBasedDocument->resourceManager()->setShapeController(this);
}
}
KoShapeController::~KoShapeController()
{
delete d;
}
KUndo2Command* KoShapeController::addShape(KoShape *shape, KUndo2Command *parent)
{
return d->addShape(shape, true, parent);
}
KUndo2Command* KoShapeController::addShapeDirect(KoShape *shape, KUndo2Command *parent)
{
return d->addShape(shape, false, parent);
}
KUndo2Command* KoShapeController::removeShape(KoShape *shape, KUndo2Command *parent)
{
KUndo2Command *cmd = new KoShapeDeleteCommand(d->shapeBasedDocument, shape, parent);
QList shapes;
shapes.append(shape);
d->shapeBasedDocument->shapesRemoved(shapes, cmd);
// detach shape from any attached connection shapes
d->handleAttachedConnections(shape, cmd);
return cmd;
}
KUndo2Command* KoShapeController::removeShapes(const QList &shapes, KUndo2Command *parent)
{
KUndo2Command *cmd = new KoShapeDeleteCommand(d->shapeBasedDocument, shapes, parent);
d->shapeBasedDocument->shapesRemoved(shapes, cmd);
foreach (KoShape *shape, shapes) {
d->handleAttachedConnections(shape, cmd);
}
return cmd;
}
void KoShapeController::setShapeControllerBase(KoShapeBasedDocumentBase *shapeBasedDocument)
{
d->shapeBasedDocument = shapeBasedDocument;
}
+QRectF KoShapeController::documentRectInPixels() const
+{
+ return d->shapeBasedDocument ? d->shapeBasedDocument->documentRectInPixels() : QRectF(0,0,1920,1080);
+}
+
+qreal KoShapeController::pixelsPerInch() const
+{
+ return d->shapeBasedDocument ? d->shapeBasedDocument->pixelsPerInch() : 72.0;
+}
+
+QRectF KoShapeController::documentRect() const
+{
+ return d->shapeBasedDocument ? d->shapeBasedDocument->documentRect() : documentRectInPixels();
+}
+
KoDocumentResourceManager *KoShapeController::resourceManager() const
{
if (!d->shapeBasedDocument)
return 0;
return d->shapeBasedDocument->resourceManager();
}
KoShapeBasedDocumentBase *KoShapeController::documentBase() const
{
return d->shapeBasedDocument;
}
diff --git a/libs/flake/KoShapeController.h b/libs/flake/KoShapeController.h
index 42f7d45068..377cae0dbe 100644
--- a/libs/flake/KoShapeController.h
+++ b/libs/flake/KoShapeController.h
@@ -1,135 +1,151 @@
/* This file is part of the KDE project
*
* Copyright (C) 2006-2007, 2010 Thomas Zander
* Copyright (C) 2006-2008 Thorsten Zachmann
*
* 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.
*/
#ifndef KOSHAPECONTROLLER_H
#define KOSHAPECONTROLLER_H
#include "kritaflake_export.h"
#include
#include
class KoCanvasBase;
class KoShape;
class KoShapeBasedDocumentBase;
class KUndo2Command;
class KoDocumentResourceManager;
/**
* Class used by tools to maintain the list of shapes.
* All applications have some sort of list of all shapes that belong to the document.
* The applications implement the KoShapeBasedDocumentBase interface (all pure virtuals)
* to add and remove shapes from the document. To ensure that an application can expect
* a certain protocol to be adhered to when adding/removing shapes, all tools use the API
* from this class for maintaining the list of shapes in the document. So no tool gets
* to access the application directly.
*/
class KRITAFLAKE_EXPORT KoShapeController
{
public:
/**
* Create a new Controller; typically not called by applications, only
* by the KonCanvasBase constructor.
* @param canvas the canvas this controller works for. The canvas can be 0
* @param shapeBasedDocument the application provided shapeBasedDocument that we can call.
*/
KoShapeController(KoCanvasBase *canvas, KoShapeBasedDocumentBase *shapeBasedDocument);
/// destructor
~KoShapeController();
/**
* @brief Add a shape to the document.
* If the shape has no parent, the active layer will become its parent.
*
* @param shape to add to the document
* @param parent the parent command if the resulting command is a compound undo command.
*
* @return command which will insert the shape into the document or 0 if the
* insertion was cancelled. The command is not yet executed.
*/
KUndo2Command* addShape(KoShape *shape, KUndo2Command *parent = 0);
/**
* @brief Add a shape to the document, skipping any dialogs or other user interaction.
*
* @param shape to add to the document
* @param parent the parent command if the resulting command is a compound undo command.
*
* @return command which will insert the shape into the document. The command is not yet executed.
*/
KUndo2Command* addShapeDirect(KoShape *shape, KUndo2Command *parent = 0);
/**
* @brief Remove a shape from the document.
*
* @param shape to remove from the document
* @param parent the parent command if the resulting command is a compound undo command.
*
* @return command which will remove the shape from the document.
* The command is not yet executed.
*/
KUndo2Command* removeShape(KoShape *shape, KUndo2Command *parent = 0);
/**
* Remove a shape from the document.
*
* @param shapes the set of shapes to remove from the document
* @param parent the parent command if the resulting command is a compound undo command.
*
* @return command which will remove the shape from the document.
* The command is not yet executed.
*/
KUndo2Command* removeShapes(const QList &shapes, KUndo2Command *parent = 0);
/**
* @brief Set the KoShapeBasedDocumentBase used to add/remove shapes.
*
* NOTE: only Sheets uses this method. Do not use it in your application. Sheets
* has to also call:
* KoToolManager::instance()->updateShapeControllerBase(shapeBasedDocument, canvas->canvasController());
*
* @param shapeBasedDocument the new shapeBasedDocument.
*/
void setShapeControllerBase(KoShapeBasedDocumentBase *shapeBasedDocument);
+ /**
+ * The size of the document measured in rasterized pixels. This information is needed for loading
+ * SVG documents that use 'px' as the default unit.
+ */
+ QRectF documentRectInPixels() const;
+
+ /**
+ * Resolution of the rasterized representaiton of the document. Used to load SVG documents correctly.
+ */
+ qreal pixelsPerInch() const;
+
+ /**
+ * Document rect measured in 'pt'
+ */
+ QRectF documentRect() const;
+
/**
* Return a pointer to the resource manager associated with the
* shape-set (typically a document). The resource manager contains
* document wide resources * such as variable managers, the image
* collection and others.
*/
KoDocumentResourceManager *resourceManager() const;
/**
* @brief Returns the KoShapeBasedDocumentBase used to add/remove shapes.
*
* @return the KoShapeBasedDocumentBase
*/
KoShapeBasedDocumentBase *documentBase() const;
private:
class Private;
Private * const d;
};
Q_DECLARE_METATYPE(KoShapeController *)
#endif
diff --git a/libs/flake/KoShapeGroup.cpp b/libs/flake/KoShapeGroup.cpp
index 816501ae6d..0d9b11ed24 100644
--- a/libs/flake/KoShapeGroup.cpp
+++ b/libs/flake/KoShapeGroup.cpp
@@ -1,289 +1,286 @@
/* This file is part of the KDE project
* Copyright (C) 2006 Thomas Zander
* Copyright (C) 2007 Jan Hambrecht
*
* 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.
*/
#include "KoShapeGroup.h"
#include "KoShapeContainerModel.h"
#include "KoShapeContainer_p.h"
#include "KoShapeLayer.h"
#include "SimpleShapeContainerModel.h"
#include "KoShapeSavingContext.h"
#include "KoShapeLoadingContext.h"
#include "KoXmlWriter.h"
#include "KoXmlReader.h"
#include "KoShapeRegistry.h"
#include "KoShapeStrokeModel.h"
#include "KoShapeShadow.h"
#include "KoInsets.h"
#include
#include
class ShapeGroupContainerModel : public SimpleShapeContainerModel
{
public:
ShapeGroupContainerModel(KoShapeGroup *group) : m_group(group) {}
~ShapeGroupContainerModel() override {}
ShapeGroupContainerModel(const ShapeGroupContainerModel &rhs, KoShapeGroup *group)
: SimpleShapeContainerModel(rhs),
m_group(group)
{
}
void add(KoShape *child) override
{
SimpleShapeContainerModel::add(child);
m_group->invalidateSizeCache();
}
void remove(KoShape *child) override
{
SimpleShapeContainerModel::remove(child);
m_group->invalidateSizeCache();
}
void childChanged(KoShape *shape, KoShape::ChangeType type) override
{
SimpleShapeContainerModel::childChanged(shape, type);
//debugFlake << type;
switch (type) {
case KoShape::PositionChanged:
case KoShape::RotationChanged:
case KoShape::ScaleChanged:
case KoShape::ShearChanged:
case KoShape::SizeChanged:
case KoShape::GenericMatrixChange:
case KoShape::ParameterChanged:
case KoShape::ClipPathChanged :
m_group->invalidateSizeCache();
break;
default:
break;
}
}
private: // members
KoShapeGroup * m_group;
};
class KoShapeGroupPrivate : public KoShapeContainerPrivate
{
public:
KoShapeGroupPrivate(KoShapeGroup *q)
: KoShapeContainerPrivate(q)
{
model = new ShapeGroupContainerModel(q);
}
KoShapeGroupPrivate(const KoShapeGroupPrivate &rhs, KoShapeGroup *q)
: KoShapeContainerPrivate(rhs, q)
{
ShapeGroupContainerModel *otherModel = dynamic_cast