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
* implements 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:
// proxy QObject: use this to connect to slots and signals.
QPointer 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);
/**
* compatibility with QAbstractScrollArea
*/
virtual void scrollContentsBy(int dx, int dy) = 0;
/**
* @return the size of the viewport
*/
virtual QSize viewportSize() const = 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;
/**
* Move the canvas up. This behaves the same as \sa pan() with a positive y coordinate.
*/
virtual void panUp() = 0;
/**
* Move the canvas down. This behaves the same as \sa pan() with a negative y coordinate.
*/
virtual void panDown() = 0;
/**
* Move the canvas to the left. This behaves the same as \sa pan() with a positive x coordinate.
*/
virtual void panLeft() = 0;
/**
* Move the canvas to the right. This behaves the same as \sa pan() with a negative x coordinate.
*/
virtual void panRight() = 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;
/**
* Update the range of scroll bars
*/
virtual void resetScrollBars() = 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
+ * Returns the action collection for the window
+ * @returns action collection for this window, can be 0
*/
- virtual KActionCollection* actionCollection() const;
+ 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)
{}
~KoDummyCanvasController() override
{}
void scrollContentsBy(int /*dx*/, int /*dy*/) override {}
QSize viewportSize() const override { return QSize(); }
void setCanvas(KoCanvasBase *canvas) override {Q_UNUSED(canvas)}
KoCanvasBase *canvas() const override {return 0;}
int visibleHeight() const override {return 0;}
int visibleWidth() const override {return 0;}
int canvasOffsetX() const override {return 0;}
int canvasOffsetY() const override {return 0;}
void ensureVisible(const QRectF &/*rect*/, bool /*smooth */ = false) override {}
void ensureVisible(KoShape *shape) override {Q_UNUSED(shape)}
void zoomIn(const QPoint &/*center*/) override {}
void zoomOut(const QPoint &/*center*/) override {}
void zoomBy(const QPoint &/*center*/, qreal /*zoom*/) override {}
void zoomTo(const QRect &/*rect*/) override {}
void recenterPreferred() override {}
void setPreferredCenter(const QPointF &/*viewPoint*/) override {}
QPointF preferredCenter() const override {return QPointF();}
void pan(const QPoint &/*distance*/) override {}
void panUp() override {}
void panDown() override {}
void panLeft() override {}
void panRight() override {}
QPoint scrollBarValue() const override {return QPoint();}
void setScrollBarValue(const QPoint &/*value*/) override {}
void resetScrollBars() override {}
void updateDocumentSize(const QSize &/*sz*/, bool /*recalculateCenter*/) override {}
void setZoomWithWheel(bool /*zoom*/) override {}
void setVastScrolling(qreal /*factor*/) override {}
QPointF currentCursorPosition() const override { return QPointF(); }
};
#endif
diff --git a/libs/flake/KoToolBase.cpp b/libs/flake/KoToolBase.cpp
index 431bf8d89e..7d1f94c20c 100644
--- a/libs/flake/KoToolBase.cpp
+++ b/libs/flake/KoToolBase.cpp
@@ -1,429 +1,373 @@
/* 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
#include "KoToolBase.h"
#include "KoToolBase_p.h"
#include "KoCanvasBase.h"
#include "KoPointerEvent.h"
#include "KoDocumentResourceManager.h"
#include "KoCanvasResourceProvider.h"
#include "KoViewConverter.h"
#include "KoShapeController.h"
#include "KoShapeControllerBase.h"
#include "KoToolSelection.h"
+#include "KoCanvasController.h"
#include
#include
#include
#include
#include
#include
KoToolBase::KoToolBase(KoCanvasBase *canvas)
: d_ptr(new KoToolBasePrivate(this, canvas))
{
Q_D(KoToolBase);
d->connectSignals();
}
KoToolBase::KoToolBase(KoToolBasePrivate &dd)
: d_ptr(&dd)
{
Q_D(KoToolBase);
d->connectSignals();
}
KoToolBase::~KoToolBase()
{
-// Enable this to easily generate action files for tools
-
- // if (actions().size() > 0) {
-
- // QDomDocument doc;
- // QDomElement e = doc.createElement("Actions");
- // e.setAttribute("name", toolId());
- // e.setAttribute("version", "2");
- // doc.appendChild(e);
-
- // Q_FOREACH (QAction *action, actions().values()) {
- // QDomElement a = doc.createElement("Action");
- // a.setAttribute("name", action->objectName());
-
- // // But seriously, XML is the worst format ever designed
- // auto addElement = [&](QString title, QString content) {
- // QDomElement newNode = doc.createElement(title);
- // QDomText newText = doc.createTextNode(content);
- // newNode.appendChild(newText);
- // a.appendChild(newNode);
- // };
-
- // addElement("icon", action->icon().name());
- // addElement("text", action->text());
- // addElement("whatsThis" , action->whatsThis());
- // addElement("toolTip" , action->toolTip());
- // addElement("iconText" , action->iconText());
- // addElement("shortcut" , action->shortcut().toString());
- // addElement("isCheckable" , QString((action->isChecked() ? "true" : "false")));
- // addElement("statusTip", action->statusTip());
- // e.appendChild(a);
- // }
- // QFile f(toolId() + ".action");
- // f.open(QFile::WriteOnly);
- // f.write(doc.toString().toUtf8());
- // f.close();
-
- // }
-
-// else {
-// debugFlake << "Tool" << toolId() << "has no actions";
-// }
qDeleteAll(d_ptr->optionWidgets);
delete d_ptr;
}
bool KoToolBase::isActivated() const
{
Q_D(const KoToolBase);
return d->isActivated;
}
void KoToolBase::activate(KoToolBase::ToolActivation toolActivation, const QSet &shapes)
{
Q_UNUSED(toolActivation);
Q_UNUSED(shapes);
Q_D(KoToolBase);
d->isActivated = true;
}
void KoToolBase::deactivate()
{
Q_D(KoToolBase);
d->isActivated = false;
}
void KoToolBase::canvasResourceChanged(int key, const QVariant & res)
{
Q_UNUSED(key);
Q_UNUSED(res);
}
void KoToolBase::documentResourceChanged(int key, const QVariant &res)
{
Q_UNUSED(key);
Q_UNUSED(res);
}
bool KoToolBase::wantsAutoScroll() const
{
return true;
}
void KoToolBase::mouseDoubleClickEvent(KoPointerEvent *event)
{
event->ignore();
}
void KoToolBase::mouseTripleClickEvent(KoPointerEvent *event)
{
event->ignore();
}
void KoToolBase::keyPressEvent(QKeyEvent *e)
{
e->ignore();
}
void KoToolBase::keyReleaseEvent(QKeyEvent *e)
{
e->ignore();
}
void KoToolBase::explicitUserStrokeEndRequest()
{
}
QVariant KoToolBase::inputMethodQuery(Qt::InputMethodQuery query, const KoViewConverter &) const
{
Q_D(const KoToolBase);
if (d->canvas->canvasWidget() == 0)
return QVariant();
switch (query) {
case Qt::ImMicroFocus:
return QRect(d->canvas->canvasWidget()->width() / 2, 0, 1, d->canvas->canvasWidget()->height());
case Qt::ImFont:
return d->canvas->canvasWidget()->font();
default:
return QVariant();
}
}
void KoToolBase::inputMethodEvent(QInputMethodEvent * event)
{
if (! event->commitString().isEmpty()) {
QKeyEvent ke(QEvent::KeyPress, -1, 0, event->commitString());
keyPressEvent(&ke);
}
event->accept();
}
void KoToolBase::customPressEvent(KoPointerEvent * event)
{
event->ignore();
}
void KoToolBase::customReleaseEvent(KoPointerEvent * event)
{
event->ignore();
}
void KoToolBase::customMoveEvent(KoPointerEvent * event)
{
event->ignore();
}
void KoToolBase::useCursor(const QCursor &cursor)
{
Q_D(KoToolBase);
d->currentCursor = cursor;
emit cursorChanged(d->currentCursor);
}
QList > KoToolBase::optionWidgets()
{
Q_D(KoToolBase);
if (d->optionWidgets.empty()) {
d->optionWidgets = createOptionWidgets();
}
return d->optionWidgets;
}
-void KoToolBase::addAction(const QString &name, QAction *action)
-{
- Q_D(KoToolBase);
- if (action->objectName().isEmpty()) {
- action->setObjectName(name);
- }
- d->actions.insert(name, action);
-}
-
-QHash KoToolBase::actions() const
-{
- Q_D(const KoToolBase);
- return d->actions;
-}
-
QAction *KoToolBase::action(const QString &name) const
{
Q_D(const KoToolBase);
- return d->actions.value(name);
+ return d->canvas->canvasController()->actionCollection()->action(name);
}
QWidget * KoToolBase::createOptionWidget()
{
return 0;
}
QList > KoToolBase::createOptionWidgets()
{
QList > ow;
if (QWidget *widget = createOptionWidget()) {
if (widget->objectName().isEmpty()) {
widget->setObjectName(toolId());
}
ow.append(widget);
}
return ow;
}
void KoToolBase::setToolId(const QString &id)
{
Q_D(KoToolBase);
d->toolId = id;
}
QString KoToolBase::toolId() const
{
Q_D(const KoToolBase);
return d->toolId;
}
QCursor KoToolBase::cursor() const
{
Q_D(const KoToolBase);
return d->currentCursor;
}
void KoToolBase::deleteSelection()
{
}
void KoToolBase::cut()
{
copy();
deleteSelection();
}
QMenu *KoToolBase::popupActionsMenu()
{
return 0;
}
KoCanvasBase * KoToolBase::canvas() const
{
Q_D(const KoToolBase);
return d->canvas;
}
void KoToolBase::setStatusText(const QString &statusText)
{
emit statusTextChanged(statusText);
}
uint KoToolBase::handleRadius() const
{
Q_D(const KoToolBase);
if(d->canvas->shapeController()->resourceManager())
{
return d->canvas->shapeController()->resourceManager()->handleRadius();
} else {
return 3;
}
}
uint KoToolBase::grabSensitivity() const
{
Q_D(const KoToolBase);
if(d->canvas->shapeController()->resourceManager())
{
return d->canvas->shapeController()->resourceManager()->grabSensitivity();
} else {
return 3;
}
}
QRectF KoToolBase::handleGrabRect(const QPointF &position) const
{
Q_D(const KoToolBase);
const KoViewConverter * converter = d->canvas->viewConverter();
uint handleSize = 2*grabSensitivity();
QRectF r = converter->viewToDocument(QRectF(0, 0, handleSize, handleSize));
r.moveCenter(position);
return r;
}
QRectF KoToolBase::handlePaintRect(const QPointF &position) const
{
Q_D(const KoToolBase);
const KoViewConverter * converter = d->canvas->viewConverter();
uint handleSize = 2*handleRadius();
QRectF r = converter->viewToDocument(QRectF(0, 0, handleSize, handleSize));
r.moveCenter(position);
return r;
}
void KoToolBase::setTextMode(bool value)
{
Q_D(KoToolBase);
d->isInTextMode=value;
}
bool KoToolBase::paste()
{
return false;
}
void KoToolBase::copy() const
{
}
void KoToolBase::dragMoveEvent(QDragMoveEvent *event, const QPointF &point)
{
Q_UNUSED(event);
Q_UNUSED(point);
}
void KoToolBase::dragLeaveEvent(QDragLeaveEvent *event)
{
Q_UNUSED(event);
}
void KoToolBase::dropEvent(QDropEvent *event, const QPointF &point)
{
Q_UNUSED(event);
Q_UNUSED(point);
}
bool KoToolBase::hasSelection()
{
KoToolSelection *sel = selection();
return (sel && sel->hasSelection());
}
KoToolSelection *KoToolBase::selection()
{
return 0;
}
void KoToolBase::repaintDecorations()
{
}
bool KoToolBase::isInTextMode() const
{
Q_D(const KoToolBase);
return d->isInTextMode;
}
void KoToolBase::requestUndoDuringStroke()
{
/**
* Default implementation just cancels the stroke
*/
requestStrokeCancellation();
}
void KoToolBase::requestStrokeCancellation()
{
}
void KoToolBase::requestStrokeEnd()
{
}
bool KoToolBase::maskSyntheticEvents() const
{
Q_D(const KoToolBase);
return d->maskSyntheticEvents;
}
void KoToolBase::setMaskSyntheticEvents(bool value)
{
Q_D(KoToolBase);
d->maskSyntheticEvents = value;
}
diff --git a/libs/flake/KoToolBase.h b/libs/flake/KoToolBase.h
index 3ba17a26c4..0afb6a641e 100644
--- a/libs/flake/KoToolBase.h
+++ b/libs/flake/KoToolBase.h
@@ -1,542 +1,523 @@
/* This file is part of the KDE project
* Copyright (C) 2006 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.
*/
#ifndef KOTOOLBASE_H
#define KOTOOLBASE_H
#include
#include
#include
#include
#include
#include "kritaflake_export.h"
class KoShape;
class KoCanvasBase;
class KoPointerEvent;
class KoViewConverter;
class KoToolSelection;
class KoToolBasePrivate;
class KoShapeControllerBase;
class QAction;
class QKeyEvent;
class QWidget;
class QCursor;
class QPainter;
class QString;
class QStringList;
class QRectF;
class QPointF;
class QInputMethodEvent;
class QDragMoveEvent;
class QDragLeaveEvent;
class QDropEvent;
class QTouchEvent;
class QMenu;
/**
* Abstract base class for all tools. Tools can create or manipulate
* flake shapes, canvas state or any other thing that a user may wish
* to do to his document or his view on a document with a pointing
* device.
*
* There exists an instance of every tool for every pointer device.
* These instances are managed by the toolmanager..
*/
class KRITAFLAKE_EXPORT KoToolBase : public QObject
{
Q_OBJECT
public:
/// Option for activate()
enum ToolActivation {
TemporaryActivation, ///< The tool is activated temporarily and works 'in-place' of another one.
DefaultActivation ///< The tool is activated normally and emitting 'done' goes to the defaultTool
};
/**
* Constructor, normally only called by the factory (see KoToolFactoryBase)
* @param canvas the canvas interface this tool will work for.
*/
explicit KoToolBase(KoCanvasBase *canvas);
~KoToolBase() override;
/**
* request a repaint of the decorations to be made. This triggers
* an update call on the canvas, but does not paint directly.
*/
virtual void repaintDecorations();
/**
* Return if dragging (moving with the mouse down) to the edge of a canvas should scroll the
* canvas (default is true).
* @return if this tool wants mouse events to cause scrolling of canvas.
*/
virtual bool wantsAutoScroll() const;
/**
* Called by the canvas to paint any decorations that the tool deems needed.
* The painter has the top left of the canvas as its origin.
* @param painter used for painting the shape
* @param converter to convert between internal and view coordinates.
*/
virtual void paint(QPainter &painter, const KoViewConverter &converter) = 0;
/**
* Return the option widgets for this tool. Create them if they
* do not exist yet. If the tool does not have an option widget,
* this method return an empty list. (After discussion with Thomas, who prefers
* the toolmanager to handle that case.)
*
* @see m_optionWidgets
*/
QList > optionWidgets();
- /**
- * Retrieves the entire collection of actions for the tool.
- */
- QHash actions() const;
-
/**
* Retrieve an action by name.
*/
QAction *action(const QString &name) const;
-
/**
* Called when (one of) the mouse or stylus buttons is pressed.
* Implementors should call event->ignore() if they do not actually use the event.
* @param event state and reason of this mouse or stylus press
*/
virtual void mousePressEvent(KoPointerEvent *event) = 0;
/**
* Called when (one of) the mouse or stylus buttons is double clicked.
* Implementors should call event->ignore() if they do not actually use the event.
* Default implementation ignores this event.
* @param event state and reason of this mouse or stylus press
*/
virtual void mouseDoubleClickEvent(KoPointerEvent *event);
/**
* Called when (one of) the mouse or stylus buttons is triple clicked.
* Implementors should call event->ignore() if they do not actually use the event.
* Default implementation ignores this event.
* @param event state and reason of this mouse or stylus press
*/
virtual void mouseTripleClickEvent(KoPointerEvent *event);
/**
* Called when the mouse or stylus moved over the canvas.
* Implementors should call event->ignore() if they do not actually use the event.
* @param event state and reason of this mouse or stylus move
*/
virtual void mouseMoveEvent(KoPointerEvent *event) = 0;
/**
* Called when (one of) the mouse or stylus buttons is released.
* Implementors should call event->ignore() if they do not actually use the event.
* @param event state and reason of this mouse or stylus release
*/
virtual void mouseReleaseEvent(KoPointerEvent *event) = 0;
/**
* Called when a key is pressed.
* Implementors should call event->ignore() if they do not actually use the event.
* Default implementation ignores this event.
* @param event state and reason of this key press
*/
virtual void keyPressEvent(QKeyEvent *event);
/**
* Called when a key is released
* Implementors should call event->ignore() if they do not actually use the event.
* Default implementation ignores this event.
* @param event state and reason of this key release
*/
virtual void keyReleaseEvent(QKeyEvent *event);
/**
* @brief explicitUserStrokeEndRequest is called by the input manager
* when the user presses Enter key or any equivalent. This callback
* comes before requestStrokeEnd(), which comes from a different source.
*/
virtual void explicitUserStrokeEndRequest();
/**
* This method is used to query a set of properties of the tool to be
* able to support complex input method operations as support for surrounding
* text and reconversions.
* Default implementation returns simple defaults, for tools that want to provide
* a more responsive text entry experience for CJK languages it would be good to reimplement.
* @param query specifies which property is queried.
* @param converter the view converter for the current canvas.
*/
virtual QVariant inputMethodQuery(Qt::InputMethodQuery query, const KoViewConverter &converter) const;
/**
* Text entry of complex text, like CJK, can be made more interactive if a tool
* implements this and the InputMethodQuery() methods.
* Reimplementing this only provides the user with a more responsive text experience, since the
* default implementation forwards the typed text as key pressed events.
* @param event the input method event.
*/
virtual void inputMethodEvent(QInputMethodEvent *event);
/**
* Called when (one of) a custom device buttons is pressed.
* Implementors should call event->ignore() if they do not actually use the event.
* @param event state and reason of this custom device press
*/
virtual void customPressEvent(KoPointerEvent *event);
/**
* Called when (one of) a custom device buttons is released.
* Implementors should call event->ignore() if they do not actually use the event.
* @param event state and reason of this custom device release
*/
virtual void customReleaseEvent(KoPointerEvent *event);
/**
* Called when a custom device moved over the canvas.
* Implementors should call event->ignore() if they do not actually use the event.
* @param event state and reason of this custom device move
*/
virtual void customMoveEvent(KoPointerEvent *event);
/**
* @return true if synthetic mouse events on the canvas should be eaten.
*
* For example, the guides tool should allow click and drag with touch,
* while the same touch events should be rejected by the freehand tool.
*
* These events are sent by the OS in Windows
*/
bool maskSyntheticEvents() const;
/**
* get the identifier code from the KoToolFactoryBase that created this tool.
* @return the toolId.
* @see KoToolFactoryBase::id()
*/
Q_INVOKABLE QString toolId() const;
/// return the last emitted cursor
QCursor cursor() const;
/**
* Returns the internal selection object of this tool.
* Each tool can have a selection which is private to that tool and the specified shape that it comes with.
* The default returns 0.
*/
virtual KoToolSelection *selection();
/**
* @returns true if the tool has selected data.
*/
virtual bool hasSelection();
/**
* copies the tools selection to the clipboard.
* The default implementation is empty to aid tools that don't have any selection.
* @see selection()
*/
virtual void copy() const;
/**
* Delete the tools selection.
* The default implementation is empty to aid tools that don't have any selection.
* @see selection()
*/
virtual void deleteSelection();
/**
* Cut the tools selection and copy it to the clipboard.
* The default implementation calls copy() and then deleteSelection()
* @see copy()
* @see deleteSelection()
*/
virtual void cut();
/**
* Paste the clipboard selection.
* A tool typically has one or more shapes selected and pasting should do something meaningful
* for this specific shape and tool combination. Inserting text in a text tool, for example.
* @return will return true if pasting succeeded. False if nothing happened.
*/
virtual bool paste();
/**
* Handle the dragMoveEvent
* A tool typically has one or more shapes selected and dropping into should do
* something meaningful for this specific shape and tool combination. For example
* dropping text in a text tool.
* The tool should Accept the event if it is meaningful; Default implementation does not.
*/
virtual void dragMoveEvent(QDragMoveEvent *event, const QPointF &point);
/**
* Handle the dragLeaveEvent
* Basically just a noticification that the drag is no long relevant
* The tool should Accept the event if it is meaningful; Default implementation does not.
*/
virtual void dragLeaveEvent(QDragLeaveEvent *event);
/**
* Handle the dropEvent
* A tool typically has one or more shapes selected and dropping into should do
* something meaningful for this specific shape and tool combination. For example
* dropping text in a text tool.
* The tool should Accept the event if it is meaningful; Default implementation does not.
*/
virtual void dropEvent(QDropEvent *event, const QPointF &point);
/**
* @return a menu with context-aware actions for the current selection. If
* the returned value is null, no context menu is shown.
*/
virtual QMenu* popupActionsMenu();
/// Returns the canvas the tool is working on
KoCanvasBase *canvas() const;
/**
* This method can be reimplemented in a subclass.
* @return returns true, if the tool is in text mode. that means, that there is
* any kind of textual input and all single key shortcuts should be eaten.
*/
bool isInTextMode() const;
public Q_SLOTS:
/**
* Called when the user requested undo while the stroke is
* active. If you tool supports undo of the part of its actions,
* override this method and do the needed work there.
*
* NOTE: Default implementation forwards this request to
* requestStrokeCancellation() method, so that the stroke
* would be cancelled.
*/
virtual void requestUndoDuringStroke();
/**
* Called when the user requested the cancellation of the current
* stroke. If you tool supports cancelling, override this method
* and do the needed work there
*/
virtual void requestStrokeCancellation();
/**
* Called when the image decided that the stroke should better be
* ended. If you tool supports long strokes, override this method
* and do the needed work there
*/
virtual void requestStrokeEnd();
/**
* This method is called when this tool instance is activated.
* For any main window there is only one tool active at a time, which then gets all
* user input. Switching between tools will call deactivate on one and activate on the
* new tool allowing the tool to flush items (like a selection)
* when it is not in use.
*
* There is one case where two tools are activated at the same. This is the case
* where one tool delegates work to another temporarily. For example, while shift is
* being held down. The second tool will get activated with temporary=true and
* it should emit done() when the state that activated it is ended.
*
One of the important tasks of activate is to call useCursor()
*
* @param shapes the set of shapes that are selected or suggested for editing by a
* selected shape for the tool to work on. Not all shapes will be meant for this
* tool.
* @param toolActivation if TemporaryActivation, this tool is only temporarily activated
* and should emit done when it is done.
* @see deactivate()
*/
virtual void activate(ToolActivation toolActivation, const QSet &shapes);
/**
* This method is called whenever this tool is no longer the
* active tool
* @see activate()
*/
virtual void deactivate();
/**
* This method is called whenever a property in the resource
* provider associated with the canvas this tool belongs to
* changes. An example is currently selected foreground color.
*/
virtual void canvasResourceChanged(int key, const QVariant &res);
/**
* This method is called whenever a property in the resource
* provider associated with the document this tool belongs to
* changes. An example is the handle radius
*/
virtual void documentResourceChanged(int key, const QVariant &res);
/**
* This method just relays the given text via the tools statusTextChanged signal.
* @param statusText the new status text
*/
void setStatusText(const QString &statusText);
Q_SIGNALS:
/**
* Emitted when this tool wants itself to be replaced by another tool.
*
* @param id the identification of the desired tool
* @see toolId(), KoToolFactoryBase::id()
*/
void activateTool(const QString &id);
/**
* Emitted when this tool wants itself to temporarily be replaced by another tool.
* For instance, a paint tool could desire to be
* temporarily replaced by a pan tool which could be temporarily
* replaced by a colorpicker.
* @param id the identification of the desired tool
*/
void activateTemporary(const QString &id);
/**
* Emitted when the tool has been temporarily activated and wants
* to notify the world that it's done.
*/
void done();
/**
* Emitted by useCursor() when the cursor to display on the canvas is changed.
* The KoToolManager should connect to this signal to handle cursors further.
*/
void cursorChanged(const QCursor &cursor);
/**
* A tool can have a selection that is copy-able, this signal is emitted when that status changes.
* @param hasSelection is true when the tool holds selected data.
*/
void selectionChanged(bool hasSelection);
/**
* Emitted when the tool wants to display a different status text
* @param statusText the new status text
*/
void statusTextChanged(const QString &statusText);
protected:
/**
* Classes inheriting from this one can call this method to signify which cursor
* the tool wants to display at this time. Logical place to call it is after an
* incoming event has been handled.
* @param cursor the new cursor.
*/
void useCursor(const QCursor &cursor);
/**
* Reimplement this if your tool actually has an option widget.
* Sets the option widget to 0 by default.
*/
virtual QWidget *createOptionWidget();
virtual QList > createOptionWidgets();
- /**
- * Add an action under the given name to the collection.
- *
- * Inserting an action under a name that is already used for another action will replace
- * the other action in the collection.
- *
- * @param name The name by which the action be retrieved again from the collection.
- * @param action The action to add.
- * @param readWrite set this to ReadOnlyAction to keep the action available on
- * read-only documents
- */
- void addAction(const QString &name, QAction *action);
-
/// Convenience function to get the current handle radius
uint handleRadius() const;
/// Convencience function to get the current grab sensitivity
uint grabSensitivity() const;
/**
* Returns a handle grab rect at the given position.
*
* The position is expected to be in document coordinates. The grab sensitivity
* canvas resource is used for the dimension of the rectangle.
*
* @return the handle rectangle in document coordinates
*/
QRectF handleGrabRect(const QPointF &position) const;
/**
* Returns a handle paint rect at the given position.
*
* The position is expected to be in document coordinates. The handle radius
* canvas resource is used for the dimension of the rectangle.
*
* @return the handle rectangle in document coordinates
*/
QRectF handlePaintRect(const QPointF &position) const;
/**
* You should set the text mode to true in subclasses, if this tool is in text input mode, eg if the users
* are able to type. If you don't set it, then single key shortcuts will get the key event and not this tool.
*/
void setTextMode(bool value);
/**
* Allows subclasses to specify whether synthetic mouse events should be accepted.
*/
void setMaskSyntheticEvents(bool value);
/**
* Returns true if activate() has been called (more times than deactivate :) )
*/
bool isActivated() const;
protected:
KoToolBase(KoToolBasePrivate &dd);
KoToolBasePrivate *d_ptr;
private:
friend class ToolHelper;
/**
* Set the identifier code from the KoToolFactoryBase that created this tool.
* @param id the identifier code
* @see KoToolFactoryBase::id()
*/
void setToolId(const QString &id);
KoToolBase();
KoToolBase(const KoToolBase&);
KoToolBase& operator=(const KoToolBase&);
Q_DECLARE_PRIVATE(KoToolBase)
};
#endif /* KOTOOL_H */
diff --git a/libs/flake/KoToolBase_p.h b/libs/flake/KoToolBase_p.h
index 91cf2b5089..4e60c8b0d0 100644
--- a/libs/flake/KoToolBase_p.h
+++ b/libs/flake/KoToolBase_p.h
@@ -1,89 +1,88 @@
/* This file is part of the KDE project
* Copyright (C) 2006-2010 Thomas Zander
* Copyright (C) 2010 KO GmbH
*
* 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 KOTOOLBASE_P_H
#define KOTOOLBASE_P_H
#include "KoDocumentResourceManager.h"
#include "KoCanvasResourceProvider.h"
#include "KoCanvasBase.h"
#include "KoShapeController.h"
#include
#include
#include
#include
#include // for the qt version check
class QAction;
class KoToolBase;
class KoToolBasePrivate
{
public:
KoToolBasePrivate(KoToolBase *qq, KoCanvasBase *canvas_)
: currentCursor(Qt::ArrowCursor),
q(qq),
canvas(canvas_),
isInTextMode(false),
isActivated(false)
{
}
~KoToolBasePrivate()
{
Q_FOREACH (QPointer optionWidget, optionWidgets) {
if (optionWidget) {
optionWidget->setParent(0);
delete optionWidget;
}
}
optionWidgets.clear();
}
void connectSignals()
{
if (canvas) { // in the case of KoToolManagers dummytool it can be zero :(
KoCanvasResourceProvider * crp = canvas->resourceManager();
Q_ASSERT_X(crp, "KoToolBase::KoToolBase", "No Canvas KoResourceManager");
if (crp)
q->connect(crp, SIGNAL(canvasResourceChanged(int, const QVariant &)),
SLOT(canvasResourceChanged(int, const QVariant &)));
// can be 0 in the case of Calligra Sheets
KoDocumentResourceManager *scrm = canvas->shapeController()->resourceManager();
if (scrm) {
q->connect(scrm, SIGNAL(resourceChanged(int, const QVariant &)),
SLOT(documentResourceChanged(int, const QVariant &)));
}
}
}
QList > optionWidgets; ///< the optionwidgets associated with this tool
QCursor currentCursor;
- QHash actions;
QString toolId;
KoToolBase *q;
KoCanvasBase *canvas; ///< the canvas interface this tool will work for.
bool isInTextMode;
bool maskSyntheticEvents{false}; ///< Whether this tool masks synthetic events
bool isActivated;
};
#endif
diff --git a/libs/flake/KoToolFactoryBase.cpp b/libs/flake/KoToolFactoryBase.cpp
index e037f2e480..63ff044007 100644
--- a/libs/flake/KoToolFactoryBase.cpp
+++ b/libs/flake/KoToolFactoryBase.cpp
@@ -1,122 +1,213 @@
/* This file is part of the KDE project
* Copyright (C) 2006 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 "KoToolFactoryBase.h"
#include "KoToolBase.h"
+#include
+
+#include
#include
+#include
+#include
class Q_DECL_HIDDEN KoToolFactoryBase::Private
{
public:
Private(const QString &i)
- : priority(100),
- id(i)
+ : priority(100),
+ id(i)
{
}
int priority;
QString section;
QString tooltip;
QString activationId;
QString iconName;
const QString id;
QKeySequence shortcut;
};
KoToolFactoryBase::KoToolFactoryBase(const QString &id)
- : d(new Private(id))
+ : d(new Private(id))
{
}
KoToolFactoryBase::~KoToolFactoryBase()
{
delete d;
}
+QList KoToolFactoryBase::createActions(KActionCollection *actionCollection)
+{
+// qDebug() << "creating actions for" << id();
+ QList toolActions;
+ Q_FOREACH(QAction *action, createActionsImpl()) {
+ if (action->objectName().isEmpty()) {
+ qWarning() << "Tool" << id() << "tries to add an action without a name";
+ continue;
+ }
+ QAction *existingAction = actionCollection->action(action->objectName());
+ if (existingAction) {
+// qDebug() << "\tFound existing action" << action->objectName() << existingAction->property("tool_action");
+ delete action;
+ action = existingAction;
+ }
+
+ QStringList tools;
+ if (action->property("tool_action").isValid()) {
+ tools = action->property("tool_action").toStringList();
+ }
+ tools << id();
+ action->setProperty("tool_action", tools);
+ if (!existingAction) {
+// qDebug() << "\tAdding new action" << action->objectName() << "Associated with" << tools;
+ actionCollection->addAction(action->objectName(), action);
+ }
+ toolActions << action;
+ }
+
+ // Enable this to easily generate action files for tools
+ #if 0
+ if (toolActions.size() > 0) {
+
+ QDomDocument doc;
+ QDomElement e = doc.createElement("Actions");
+ e.setAttribute("name", id);
+ e.setAttribute("version", "2");
+ doc.appendChild(e);
+
+ Q_FOREACH (QAction *action, toolActions) {
+ QDomElement a = doc.createElement("Action");
+ a.setAttribute("name", action->objectName());
+
+ // But seriously, XML is the worst format ever designed
+ auto addElement = [&](QString title, QString content) {
+ QDomElement newNode = doc.createElement(title);
+ QDomText newText = doc.createTextNode(content);
+ newNode.appendChild(newText);
+ a.appendChild(newNode);
+ };
+
+ addElement("icon", action->icon().name());
+ addElement("text", action->text());
+ addElement("whatsThis" , action->whatsThis());
+ addElement("toolTip" , action->toolTip());
+ addElement("iconText" , action->iconText());
+ addElement("shortcut" , action->shortcut().toString());
+ addElement("isCheckable" , QString((action->isChecked() ? "true" : "false")));
+ addElement("statusTip", action->statusTip());
+ e.appendChild(a);
+ }
+ QFile f(id()z + ".action");
+ f.open(QFile::WriteOnly);
+ f.write(doc.toString().toUtf8());
+ f.close();
+
+ }
+
+ else {
+ debugFlake << "Tool" << id() << "has no actions";
+ }
+#endif
+
+// qDebug() << "Generated actions for tool factory" << id();
+// Q_FOREACH(QAction *action, toolActions) {
+// qDebug() << "\taction:" << action->objectName() << "shortcut" << action->shortcuts() << "tools" << action->property("tool_action").toStringList();
+// }
+ return toolActions;
+}
+
QString KoToolFactoryBase::id() const
{
return d->id;
}
int KoToolFactoryBase::priority() const
{
return d->priority;
}
QString KoToolFactoryBase::section() const
{
return d->section;
}
QString KoToolFactoryBase::toolTip() const
{
return d->tooltip;
}
QString KoToolFactoryBase::iconName() const
{
return d->iconName;
}
QString KoToolFactoryBase::activationShapeId() const
{
return d->activationId;
}
QKeySequence KoToolFactoryBase::shortcut() const
{
return d->shortcut;
}
void KoToolFactoryBase::setActivationShapeId(const QString &activationShapeId)
{
d->activationId = activationShapeId;
}
void KoToolFactoryBase::setToolTip(const QString & tooltip)
{
d->tooltip = tooltip;
}
void KoToolFactoryBase::setSection(const QString & section)
{
d->section = section;
}
void KoToolFactoryBase::setIconName(const char *iconName)
{
d->iconName = QLatin1String(iconName);
}
void KoToolFactoryBase::setIconName(const QString &iconName)
{
d->iconName = iconName;
}
void KoToolFactoryBase::setPriority(int newPriority)
{
d->priority = newPriority;
}
void KoToolFactoryBase::setShortcut(const QKeySequence &shortcut)
{
d->shortcut = shortcut;
}
+
+QList KoToolFactoryBase::createActionsImpl()
+{
+ return QList();
+}
+
diff --git a/libs/flake/KoToolFactoryBase.h b/libs/flake/KoToolFactoryBase.h
index 3673bad272..db827364a8 100644
--- a/libs/flake/KoToolFactoryBase.h
+++ b/libs/flake/KoToolFactoryBase.h
@@ -1,240 +1,262 @@
/* This file is part of the KDE project
* Copyright (c) 2004 Boudewijn Rempt
* Copyright (C) 2006 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.
*/
#ifndef KO_TOOL_FACTORY_H
#define KO_TOOL_FACTORY_H
#include "kritaflake_export.h"
#include
+#include
class KoCanvasBase;
class KoToolBase;
class QKeySequence;
+class KActionCollection;
+class QAction;
/**
* A factory for KoToolBase objects.
*
* The baseclass for all tool plugins. Each plugin that ships a KoToolBase should also
* ship a factory. That factory will extend this class and set variable data like
* a toolTip and icon in the constructor of that extending class.
*
* An example usage would be:
* class MyToolFactory : public KoToolFactoryBase {
* public:
* MyToolFactory(const QStringList&)
* : KoToolFactoryBase("MyTool") {
* setToolTip(i18n("Create object"));
* setToolType("dynamic");
* setPriority(5);
* }
* ~MyToolFactory() {}
* KoToolBase *createTool(KoCanvasBase *canvas);
* };
* K_PLUGIN_FACTORY_WITH_JSON((MyToolFactoryFactory, "mytool.json", registerPlugin();)
*/
class KRITAFLAKE_EXPORT KoToolFactoryBase
{
public:
/**
* Create the new factory
* @param id a string that will be used internally for referencing the tool, for
* example for use by the KoToolBase::activateTemporary.
*/
explicit KoToolFactoryBase(const QString &id);
virtual ~KoToolFactoryBase();
+ /**
+ * Create the actions for this tool. Actions are unique per window, not per
+ * tool instance; tool instances are unique per view/canvas.
+ */
+ QList createActions(KActionCollection *actionCollection);
+
/**
* Instantiate a new tool
* @param canvas the canvas that the new tool will work on. Should be passed
* to the constructor of the tool.
* @return a new KoToolBase instance, or zero if the tool doesn't want to show up.
*/
virtual KoToolBase *createTool(KoCanvasBase *canvas) = 0;
/**
* return the id for the tool this factory creates.
* @return the id for the tool this factory creates.
*/
QString id() const;
/**
* Returns The priority of this tool in its section in the toolbox
* @return The priority of this tool.
*/
int priority() const;
/**
* returns the type of tool, used to group tools in the toolbox
* @return the type of tool
*/
QString section() const;
/**
* return a translated tooltip Text
* @return a translated tooltip Text
*/
QString toolTip() const;
/**
* return the basename of the icon for this tool
* @return the basename of the icon for this tool
*/
QString iconName() const;
/**
* Return the id of the shape we can process.
* This is the shape Id the tool we create is associated with. So a TextTool for a TextShape.
* In combination with the toolType the following situations can occur;
Type | shapeId | Result |
'main' |
Foo |
Tool will always be visible, but only active when shape with shapeId 'Foo' is in the selection. |
'main' |
'' |
Tool will always be visible, but only active when at least one shape is selected |
'main' |
'flake/always' |
Tool will always be visible and enabled. |
'main' |
'flake/edit' |
Tool will be visible no matter which shape is selected (if any), but only
be enabled when the current layer is editable. |
'dynamic' |
Foo |
Tool will only be visible when shape with shapeId 'Foo' is in the selection. |
'dynamic' |
'' |
Tool will always be visible. We recommend you don't use this one. |
"comma separated list of application names" |
see main type |
Similar to the 'main' item if the application name matches with the current application. Otherwise it's similar to 'dynamic', but segmented in its own section. If the list includes 'dynamic' it's even added to the dynamic section, when not matching the application name |
'other' |
any |
similar to the 'dynamic' items, but segmented in its own section. |
n/a |
/always |
An activation shape id ending with '/always' will make the tool always visible and enabled. |
* @see KoShapeFactoryBase::shapeId()
* @see setActivationShapeId()
* @return the id of a shape, or an empty string for all shapes.
*/
QString activationShapeId() const;
/**
* Return the default keyboard shortcut for activation of this tool (if
* the shape this tool belongs to is active).
*
* See KoToolManager for use.
*
* @return the shortcut
*/
QKeySequence shortcut() const;
/**
* Returns the main toolType
* Each tool has a toolType which it uses to be grouped in the toolbox.
* The predefined areas are main and dynamic. "main" tools are always
* shown.
*
* @see toolType()
* @see setToolType()
*/
static QString mainToolType() {
return "main";
}
/**
* Returns the navigation toolType
* Each tool has a toolType which it uses to be grouped in the toolbox.
* The predefined areas are main and dynamic. "navigation" tools are always
* shown and are for tools that change the settings of the canvas, zoom, pan...
*
* @see toolType()
* @see setToolType()
*/
static QString navigationToolType() {
return "navigation";
}
/**
* Returns the dynamic toolType
* Each tool has a toolType which it uses to be grouped in the toolbox.
* The predefined areas are main and dynamic. Dynamic tools are hidden
* until the shape they belong to is activated.
*
* @see toolType()
* @see setToolType()
*/
static QString dynamicToolType() {
return "dynamic";
}
/**
* Set the default shortcut for activation of this tool.
*/
void setShortcut(const QKeySequence & shortcut);
protected:
+
/**
* Set the tooltip to be used for this tool
* @param tooltip the tooltip
*/
void setToolTip(const QString &tooltip);
+
/**
* Set the toolType. used to group tools in the toolbox
* @param toolType the toolType
*/
void setSection(const QString §ion);
+
/**
* Set an icon to be used in the toolBox.
* @param iconName the basename (without extension) of the icon
*/
void setIconName(const char *iconName);
void setIconName(const QString &iconName);
+
/**
* Set the priority of this tool, as it is shown in the toolBox; lower number means
* it will be show more to the front of the list.
* @param newPriority the priority
*/
void setPriority(int newPriority);
+
/**
* Set the id of the shape we can process.
* This is the Id, as passed to the constructor of a KoShapeFactoryBase, that the tool
* we create is associated with. This means that if a KoTextShape is selected, then
* all tools that have its id set here will be added to the dynamic part of the toolbox.
* @param activationShapeId the Id of the shape
* @see activationShapeId()
*/
void setActivationShapeId(const QString &activationShapeId);
+ /**
+ * @brief createActionsImpl should be reimplemented if the tool needs any actions.
+ * The actions should have a valid objectName().
+ *
+ * @return the list of actions this tool wishes to be available.
+ */
+ virtual QList createActionsImpl();
+
private:
class Private;
Private * const d;
};
#endif
diff --git a/libs/flake/KoToolManager.cpp b/libs/flake/KoToolManager.cpp
index 4050652412..7a323c07a9 100644
--- a/libs/flake/KoToolManager.cpp
+++ b/libs/flake/KoToolManager.cpp
@@ -1,1009 +1,972 @@
/* This file is part of the KDE project
*
* Copyright (c) 2005-2010 Boudewijn Rempt
* Copyright (C) 2006-2008 Thomas Zander
* Copyright (C) 2006 Thorsten Zachmann
* Copyright (C) 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.
*/
// flake
#include "KoToolManager.h"
#include "KoToolManager_p.h"
#include "KoToolRegistry.h"
#include "KoToolProxy.h"
#include "KoToolProxy_p.h"
#include "KoSelection.h"
#include "KoCanvasController.h"
#include "KoCanvasControllerWidget.h"
#include "KoShape.h"
#include "KoShapeLayer.h"
#include "KoShapeRegistry.h"
#include "KoShapeManager.h"
#include "KoSelectedShapesProxy.h"
#include "KoCanvasBase.h"
#include "KoInputDeviceHandlerRegistry.h"
#include "KoInputDeviceHandlerEvent.h"
#include "KoPointerEvent.h"
#include "tools/KoCreateShapesTool.h"
#include "tools/KoZoomTool.h"
#include "kis_action_registry.h"
#include "KoToolFactoryBase.h"
#include "kis_assert.h"
#include
// Qt + kde
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
Q_GLOBAL_STATIC(KoToolManager, s_instance)
class CanvasData
{
public:
CanvasData(KoCanvasController *cc, const KoInputDevice &id)
- : activeTool(0),
- canvas(cc),
- inputDevice(id),
- dummyToolWidget(0),
- dummyToolLabel(0)
+ : activeTool(0),
+ canvas(cc),
+ inputDevice(id),
+ dummyToolWidget(0),
+ dummyToolLabel(0)
{
}
~CanvasData()
{
// the dummy tool widget does not necessarily have a parent and we create it, so we delete it.
delete dummyToolWidget;
}
void activateToolActions()
{
- disabledDisabledActions.clear();
- disabledActions.clear();
- disabledCanvasShortcuts.clear();
- // we do several things here
- // 1. enable the actions of the active tool
- // 2. disable conflicting actions
- // 3. replace conflicting actions in the action collection
- KActionCollection *canvasActionCollection = canvas->actionCollection();
-
- QHash toolActions = activeTool->actions();
- QHash::const_iterator it(toolActions.constBegin());
-
- for (; it != toolActions.constEnd(); ++it) {
- if (canvasActionCollection) {
-
- QString toolActionID = it.key();
- QAction *toolAction = it.value();
-
- QAction * action = qobject_cast(canvasActionCollection->action(it.key()));
- if (action) {
- canvasActionCollection->takeAction(action);
- if (action != it.value()) {
- if (action->isEnabled()) {
- action->setEnabled(false);
- disabledActions.append(action);
- } else {
- disabledDisabledActions.append(action);
- }
+ toolActions.clear();
+ disabledGlobalActions.clear();
+
+ KActionCollection *windowActionCollection = canvas->actionCollection();
+
+ if (!windowActionCollection) {
+ qWarning() << "We haven't got an action collection";
+ return;
+ }
+
+ QStringList globalActions;
+
+ QMap shortcutMap;
+
+ ////qDebug() << "................... activating tool" << activeToolId;
+
+ Q_FOREACH(QAction *action, windowActionCollection->actions()) {
+
+ //qDebug() << "Action" << action->objectName() << "shortcuts" << action->shortcuts();
+ if (action->property("tool_action").isValid()) {
+ QStringList tools = action->property("tool_action").toStringList();
+ //qDebug() << "\tassociated with" << tools;
+ if (tools.contains(activeToolId)) {
+ //qDebug() << "\t\tenabling";
+ action->setEnabled(true);
+ toolActions << action->objectName();
+ }
+ else {
+ action->setDisabled(true);
+ }
+ }
+ else {
+ globalActions << action->objectName();
+ }
+
+ Q_FOREACH(QKeySequence keySequence, action->shortcuts()) {
+ if (shortcutMap.contains(keySequence)) {
+ shortcutMap[keySequence].append(action->objectName());
+ }
+ else {
+ shortcutMap[keySequence] = QStringList() << action->objectName();
+ }
+ }
+ }
+
+ // Make sure the tool's actions override the global actions that aren't associated with the tool.
+ Q_FOREACH(const QKeySequence &k, shortcutMap.keys()) {
+ if (shortcutMap[k].size() > 1) {
+ QStringList actions = shortcutMap[k];
+ //qDebug() << k << actions;
+ bool toolActionFound = false;
+ Q_FOREACH(const QString &action, actions) {
+ if (toolActions.contains(action)) {
+ toolActionFound = true;
}
}
- Q_FOREACH (QAction *a, canvasActionCollection->actions()) {
- QAction *canvasAction = dynamic_cast(a);
- if (canvasAction && canvasAction->shortcut().toString() != "" && canvasAction->shortcut() == toolAction->shortcut()) {
- warnFlake << activeToolId << ": action" << toolActionID << "conflicts with canvas action" << canvasAction->objectName() << "shortcut:" << canvasAction->shortcut().toString();
- disabledCanvasShortcuts[canvasAction] = canvasAction->shortcut().toString();
- canvasAction->setShortcut(QKeySequence());
+ Q_FOREACH(const QString &action, actions) {
+ if (toolActionFound && globalActions.contains(action)) {
+ //qDebug() << "\tdisabling global action" << action;
+ windowActionCollection->action(action)->setEnabled(false);
+ disabledGlobalActions << action;
}
}
- canvasActionCollection->addAction(toolActionID, toolAction);
+ //qDebug() << k << shortcutMap[k];
}
- it.value()->setEnabled(true);
}
- canvasActionCollection->readSettings(); // The shortcuts might have been configured in the meantime.
+
+ windowActionCollection->readSettings(); // The shortcuts might have been configured in the meantime.
}
void deactivateToolActions()
{
-
if (!activeTool)
return;
- // disable actions of active tool
- Q_FOREACH (QAction *action, activeTool->actions()) {
- action->setEnabled(false);
- }
- // enable actions which where disabled on activating the active tool
- // and re-add them to the action collection
- KActionCollection *ac = canvas->actionCollection();
- Q_FOREACH (QPointer action, disabledDisabledActions) {
- if (action) {
- if (ac) {
- ac->addAction(action->objectName(), action);
- }
- }
- }
- disabledDisabledActions.clear();
+ //qDebug() << "............... deactivating previous tool because activating" << activeToolId;
- Q_FOREACH (QPointer action, disabledActions) {
- if (action) {
- action->setEnabled(true);
- if(ac) {
- ac->addAction(action->objectName(), action);
- }
- }
- }
- disabledActions.clear();
+ KActionCollection *windowActionCollection = canvas->actionCollection();
- QMap, QString>::const_iterator it(disabledCanvasShortcuts.constBegin());
- for (; it != disabledCanvasShortcuts.constEnd(); ++it) {
- QAction *action = it.key();
- QString shortcut = it.value();
- action->setShortcut(shortcut);
+ Q_FOREACH(const QString &action, toolActions) {
+ //qDebug() << "disabling" << action;
+ windowActionCollection->action(action)->setDisabled(true);
+ }
+ Q_FOREACH(const QString &action, disabledGlobalActions) {
+ //qDebug() << "enabling" << action;
+ windowActionCollection->action(action)->setEnabled(true);
}
- disabledCanvasShortcuts.clear();
}
KoToolBase *activeTool; // active Tool
QString activeToolId; // the id of the active Tool
QString activationShapeId; // the shape-type (KoShape::shapeId()) the activeTool 'belongs' to.
QHash allTools; // all the tools that are created for this canvas.
QStack stack; // stack of temporary tools
KoCanvasController *const canvas;
const KoInputDevice inputDevice;
QWidget *dummyToolWidget; // the widget shown in the toolDocker.
QLabel *dummyToolLabel;
- QList > disabledActions; ///< disabled conflicting actions
- QList > disabledDisabledActions; ///< disabled conflicting actions that were already disabled
- QMap, QString> disabledCanvasShortcuts; ///< Shortcuts that were temporarily removed from canvas actions because the tool overrides
+ QStringList toolActions;
+ QStringList disabledGlobalActions;
};
// ******** KoToolManager **********
KoToolManager::KoToolManager()
: QObject(),
- d(new Private(this))
+ d(new Private(this))
{
connect(QApplication::instance(), SIGNAL(focusChanged(QWidget*,QWidget*)),
this, SLOT(movedFocus(QWidget*,QWidget*)));
}
KoToolManager::~KoToolManager()
{
delete d;
}
QList KoToolManager::toolActionList() const
{
QList answer;
answer.reserve(d->tools.count());
Q_FOREACH (ToolHelper *tool, d->tools) {
if (tool->id() == KoCreateShapesTool_ID)
continue; // don't show this one.
answer.append(tool->toolAction());
}
return answer;
}
void KoToolManager::requestToolActivation(KoCanvasController * controller)
{
if (d->canvasses.contains(controller)) {
QString activeToolId = d->canvasses.value(controller).first()->activeToolId;
Q_FOREACH (ToolHelper * th, d->tools) {
if (th->id() == activeToolId) {
d->toolActivated(th);
break;
}
}
}
}
KoInputDevice KoToolManager::currentInputDevice() const
{
return d->inputDevice;
}
void KoToolManager::registerToolActions(KActionCollection *ac, KoCanvasController *controller)
{
Q_ASSERT(controller);
Q_ASSERT(ac);
d->setup();
if (!d->canvasses.contains(controller)) {
return;
}
- // Actions available during the use of individual tools
- CanvasData *cd = d->canvasses.value(controller).first();
- Q_FOREACH (KoToolBase *tool, cd->allTools) {
- QHash actions = tool->actions();
- QHash::const_iterator action(actions.constBegin());
- for (; action != actions.constEnd(); ++action) {
- if (!ac->action(action.key()))
- ac->addAction(action.key(), action.value());
- }
- }
-
// Actions used to switch tools via shortcuts
Q_FOREACH (ToolHelper * th, d->tools) {
if (ac->action(th->id())) {
continue;
}
ShortcutToolAction* action = th->createShortcutToolAction(ac);
ac->addCategorizedAction(th->id(), action, "tool-shortcuts");
}
}
void KoToolManager::addController(KoCanvasController *controller)
{
Q_ASSERT(controller);
if (d->canvasses.contains(controller))
return;
d->setup();
d->attachCanvas(controller);
connect(controller->proxyObject, SIGNAL(destroyed(QObject*)), this, SLOT(attemptCanvasControllerRemoval(QObject*)));
connect(controller->proxyObject, SIGNAL(canvasRemoved(KoCanvasController*)), this, SLOT(detachCanvas(KoCanvasController*)));
connect(controller->proxyObject, SIGNAL(canvasSet(KoCanvasController*)), this, SLOT(attachCanvas(KoCanvasController*)));
}
void KoToolManager::removeCanvasController(KoCanvasController *controller)
{
Q_ASSERT(controller);
disconnect(controller->proxyObject, SIGNAL(canvasRemoved(KoCanvasController*)), this, SLOT(detachCanvas(KoCanvasController*)));
disconnect(controller->proxyObject, SIGNAL(canvasSet(KoCanvasController*)), this, SLOT(attachCanvas(KoCanvasController*)));
d->detachCanvas(controller);
}
void KoToolManager::attemptCanvasControllerRemoval(QObject* controller)
{
KoCanvasControllerProxyObject* controllerActual = qobject_cast(controller);
if (controllerActual) {
removeCanvasController(controllerActual->canvasController());
}
}
void KoToolManager::switchToolRequested(const QString & id)
{
Q_ASSERT(d->canvasData);
if (!d->canvasData) return;
while (!d->canvasData->stack.isEmpty()) // switching means to flush the stack
d->canvasData->stack.pop();
d->switchTool(id, false);
}
void KoToolManager::switchInputDeviceRequested(const KoInputDevice &id)
{
if (!d->canvasData) return;
d->switchInputDevice(id);
}
void KoToolManager::switchToolTemporaryRequested(const QString &id)
{
d->switchTool(id, true);
}
void KoToolManager::switchBackRequested()
{
if (!d->canvasData) return;
if (d->canvasData->stack.isEmpty()) {
// default to changing to the interactionTool
d->switchTool(KoInteractionTool_ID, false);
return;
}
d->switchTool(d->canvasData->stack.pop(), false);
}
KoCreateShapesTool * KoToolManager::shapeCreatorTool(KoCanvasBase *canvas) const
{
Q_ASSERT(canvas);
Q_FOREACH (KoCanvasController *controller, d->canvasses.keys()) {
if (controller->canvas() == canvas) {
KoCreateShapesTool *createTool = dynamic_cast
- (d->canvasData->allTools.value(KoCreateShapesTool_ID));
+ (d->canvasData->allTools.value(KoCreateShapesTool_ID));
Q_ASSERT(createTool /* ID changed? */);
return createTool;
}
}
Q_ASSERT(0); // this should not happen
return 0;
}
KoToolBase *KoToolManager::toolById(KoCanvasBase *canvas, const QString &id) const
{
Q_ASSERT(canvas);
Q_FOREACH (KoCanvasController *controller, d->canvasses.keys()) {
if (controller->canvas() == canvas)
return d->canvasData->allTools.value(id);
}
return 0;
}
KoCanvasController *KoToolManager::activeCanvasController() const
{
if (! d->canvasData) return 0;
return d->canvasData->canvas;
}
QString KoToolManager::preferredToolForSelection(const QList &shapes)
{
QSet shapeTypes;
Q_FOREACH (KoShape *shape, shapes) {
shapeTypes << shape->shapeId();
}
//KritaUtils::makeContainerUnique(types);
QString toolType = KoInteractionTool_ID;
int prio = INT_MAX;
Q_FOREACH (ToolHelper *helper, d->tools) {
if (helper->id() == KoCreateShapesTool_ID) continue;
if (helper->priority() >= prio)
continue;
bool toolWillWork = false;
foreach (const QString &type, shapeTypes) {
if (helper->activationShapeId().split(',').contains(type)) {
toolWillWork = true;
break;
}
}
if (toolWillWork) {
toolType = helper->id();
prio = helper->priority();
}
}
return toolType;
}
QPair KoToolManager::createTools(KoCanvasController *controller, ToolHelper *tool)
{
// XXX: maybe this method should go into the private class?
QHash origHash;
if (d->canvasses.contains(controller)) {
origHash = d->canvasses.value(controller).first()->allTools;
}
if (origHash.contains(tool->id())) {
return QPair(tool->id(), origHash.value(tool->id()));
}
debugFlake << "Creating tool" << tool->id() << ". Activated on:" << tool->activationShapeId() << ", prio:" << tool->priority();
KoToolBase *tl = tool->createTool(controller->canvas());
if (tl) {
d->uniqueToolIds.insert(tl, tool->uniqueId());
-
tl->setObjectName(tool->id());
-
- Q_FOREACH (QAction *action, tl->actions()) {
- action->setEnabled(false);
- }
-
}
KoZoomTool *zoomTool = dynamic_cast(tl);
if (zoomTool) {
zoomTool->setCanvasController(controller);
}
return QPair(tool->id(), tl);
}
-// NOT IMPLEMENTED
-void KoToolManager::updateToolShortcuts()
-{
- // auto actionRegistry = KisActionRegistry::instance();
- // foreach (KoToolBase *t, allTools) {
- // for (auto it = t->actions().constBegin();
- // it != t->actions().constEnd();
- // ++it;) {
- // actionRegistry->updateShortcut(it.key(), it.value());
- // }
- // }
-}
-
void KoToolManager::initializeCurrentToolForCanvas()
{
d->postSwitchTool(false);
}
KoToolManager* KoToolManager::instance()
{
return s_instance;
}
QString KoToolManager::activeToolId() const
{
if (!d->canvasData) return QString();
return d->canvasData->activeToolId;
}
KoToolManager::Private *KoToolManager::priv()
{
return d;
}
/**** KoToolManager::Private ****/
KoToolManager::Private::Private(KoToolManager *qq)
: q(qq),
- canvasData(0),
- layerExplicitlyDisabled(false)
+ canvasData(0),
+ layerExplicitlyDisabled(false)
{
}
KoToolManager::Private::~Private()
{
qDeleteAll(tools);
}
// helper method.
CanvasData *KoToolManager::Private::createCanvasData(KoCanvasController *controller, const KoInputDevice &device)
{
QHash toolsHash;
Q_FOREACH (ToolHelper *tool, tools) {
QPair toolPair = q->createTools(controller, tool);
if (toolPair.second) { // only if a real tool was created
toolsHash.insert(toolPair.first, toolPair.second);
}
}
KoCreateShapesTool *createShapesTool = dynamic_cast(toolsHash.value(KoCreateShapesTool_ID));
KIS_ASSERT(createShapesTool);
QString id = KoShapeRegistry::instance()->keys()[0];
createShapesTool->setShapeId(id);
CanvasData *cd = new CanvasData(controller, device);
cd->allTools = toolsHash;
return cd;
}
void KoToolManager::Private::setup()
{
if (tools.size() > 0)
return;
KoShapeRegistry::instance();
KoToolRegistry *registry = KoToolRegistry::instance();
Q_FOREACH (const QString & id, registry->keys()) {
ToolHelper *t = new ToolHelper(registry->value(id));
tools.append(t);
}
// connect to all tools so we can hear their button-clicks
Q_FOREACH (ToolHelper *tool, tools)
connect(tool, SIGNAL(toolActivated(ToolHelper*)), q, SLOT(toolActivated(ToolHelper*)));
// load pluggable input devices
KoInputDeviceHandlerRegistry::instance();
}
void KoToolManager::Private::connectActiveTool()
{
if (canvasData->activeTool) {
connect(canvasData->activeTool, SIGNAL(cursorChanged(QCursor)),
q, SLOT(updateCursor(QCursor)));
connect(canvasData->activeTool, SIGNAL(activateTool(QString)),
q, SLOT(switchToolRequested(QString)));
connect(canvasData->activeTool, SIGNAL(activateTemporary(QString)),
q, SLOT(switchToolTemporaryRequested(QString)));
connect(canvasData->activeTool, SIGNAL(done()), q, SLOT(switchBackRequested()));
connect(canvasData->activeTool, SIGNAL(statusTextChanged(QString)),
q, SIGNAL(changedStatusText(QString)));
}
// we expect the tool to emit a cursor on activation.
updateCursor(Qt::ForbiddenCursor);
}
void KoToolManager::Private::disconnectActiveTool()
{
if (canvasData->activeTool) {
canvasData->deactivateToolActions();
// repaint the decorations before we deactivate the tool as it might deleted
// data needed for the repaint
emit q->aboutToChangeTool(canvasData->canvas);
canvasData->activeTool->deactivate();
disconnect(canvasData->activeTool, SIGNAL(cursorChanged(QCursor)),
q, SLOT(updateCursor(QCursor)));
disconnect(canvasData->activeTool, SIGNAL(activateTool(QString)),
q, SLOT(switchToolRequested(QString)));
disconnect(canvasData->activeTool, SIGNAL(activateTemporary(QString)),
q, SLOT(switchToolTemporaryRequested(QString)));
disconnect(canvasData->activeTool, SIGNAL(done()), q, SLOT(switchBackRequested()));
disconnect(canvasData->activeTool, SIGNAL(statusTextChanged(QString)),
q, SIGNAL(changedStatusText(QString)));
}
// emit a empty status text to clear status text from last active tool
emit q->changedStatusText(QString());
}
void KoToolManager::Private::switchTool(KoToolBase *tool, bool temporary)
{
Q_ASSERT(tool);
if (canvasData == 0)
return;
if (canvasData->activeTool == tool && tool->toolId() != KoInteractionTool_ID)
return;
disconnectActiveTool();
canvasData->activeTool = tool;
connectActiveTool();
postSwitchTool(temporary);
}
void KoToolManager::Private::switchTool(const QString &id, bool temporary)
{
Q_ASSERT(canvasData);
if (!canvasData) return;
if (canvasData->activeTool && temporary)
canvasData->stack.push(canvasData->activeToolId);
canvasData->activeToolId = id;
KoToolBase *tool = canvasData->allTools.value(id);
if (! tool) {
return;
}
Q_FOREACH (ToolHelper *th, tools) {
if (th->id() == id) {
canvasData->activationShapeId = th->activationShapeId();
break;
}
}
switchTool(tool, temporary);
}
void KoToolManager::Private::postSwitchTool(bool temporary)
{
#ifndef NDEBUG
int canvasCount = 1;
Q_FOREACH (QList list, canvasses) {
bool first = true;
Q_FOREACH (CanvasData *data, list) {
if (first) {
debugFlake << "Canvas" << canvasCount++;
}
debugFlake << " +- Tool:" << data->activeToolId << (data == canvasData ? " *" : "");
first = false;
}
}
#endif
Q_ASSERT(canvasData);
if (!canvasData) return;
KoToolBase::ToolActivation toolActivation;
if (temporary)
toolActivation = KoToolBase::TemporaryActivation;
else
toolActivation = KoToolBase::DefaultActivation;
QSet shapesToOperateOn;
if (canvasData->activeTool
&& canvasData->activeTool->canvas()
&& canvasData->activeTool->canvas()->shapeManager()) {
KoSelection *selection = canvasData->activeTool->canvas()->shapeManager()->selection();
Q_ASSERT(selection);
shapesToOperateOn = QSet::fromList(selection->selectedEditableShapesAndDelegates());
}
if (canvasData->canvas->canvas()) {
// Caller of postSwitchTool expect this to be called to update the selected tool
updateToolForProxy();
canvasData->activeTool->activate(toolActivation, shapesToOperateOn);
KoCanvasBase *canvas = canvasData->canvas->canvas();
canvas->updateInputMethodInfo();
} else {
canvasData->activeTool->activate(toolActivation, shapesToOperateOn);
}
QList > optionWidgetList = canvasData->activeTool->optionWidgets();
if (optionWidgetList.empty()) { // no option widget.
QWidget *toolWidget;
QString title;
Q_FOREACH (ToolHelper *tool, tools) {
if (tool->id() == canvasData->activeTool->toolId()) {
title = tool->toolTip();
break;
}
}
toolWidget = canvasData->dummyToolWidget;
if (toolWidget == 0) {
toolWidget = new QWidget();
toolWidget->setObjectName("DummyToolWidget");
QVBoxLayout *layout = new QVBoxLayout(toolWidget);
layout->setMargin(3);
canvasData->dummyToolLabel = new QLabel(toolWidget);
layout->addWidget(canvasData->dummyToolLabel);
layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding));
toolWidget->setLayout(layout);
canvasData->dummyToolWidget = toolWidget;
}
canvasData->dummyToolLabel->setText(i18n("Active tool: %1", title));
optionWidgetList.append(toolWidget);
}
// Activate the actions for the currently active tool
canvasData->activateToolActions();
emit q->changedTool(canvasData->canvas, uniqueToolIds.value(canvasData->activeTool));
emit q->toolOptionWidgetsChanged(canvasData->canvas, optionWidgetList);
}
void KoToolManager::Private::switchCanvasData(CanvasData *cd)
{
Q_ASSERT(cd);
KoCanvasBase *oldCanvas = 0;
KoInputDevice oldInputDevice;
if (canvasData) {
oldCanvas = canvasData->canvas->canvas();
oldInputDevice = canvasData->inputDevice;
if (canvasData->activeTool) {
disconnectActiveTool();
}
KoToolProxy *proxy = proxies.value(oldCanvas);
Q_ASSERT(proxy);
proxy->setActiveTool(0);
}
canvasData = cd;
inputDevice = canvasData->inputDevice;
if (canvasData->activeTool) {
connectActiveTool();
postSwitchTool(false);
}
if (oldInputDevice != canvasData->inputDevice) {
emit q->inputDeviceChanged(canvasData->inputDevice);
}
if (oldCanvas != canvasData->canvas->canvas()) {
emit q->changedCanvas(canvasData->canvas->canvas());
}
}
void KoToolManager::Private::toolActivated(ToolHelper *tool)
{
Q_ASSERT(tool);
Q_ASSERT(canvasData);
if (!canvasData) return;
KoToolBase *t = canvasData->allTools.value(tool->id());
Q_ASSERT(t);
canvasData->activeToolId = tool->id();
canvasData->activationShapeId = tool->activationShapeId();
switchTool(t, false);
}
void KoToolManager::Private::detachCanvas(KoCanvasController *controller)
{
Q_ASSERT(controller);
// check if we are removing the active canvas controller
if (canvasData && canvasData->canvas == controller) {
KoCanvasController *newCanvas = 0;
// try to find another canvas controller beside the one we are removing
Q_FOREACH (KoCanvasController* canvas, canvasses.keys()) {
if (canvas != controller) {
// yay found one
newCanvas = canvas;
break;
}
}
if (newCanvas) {
switchCanvasData(canvasses.value(newCanvas).first());
} else {
emit q->toolOptionWidgetsChanged(controller, QList >());
// as a last resort just set a blank one
canvasData = 0;
}
}
KoToolProxy *proxy = proxies.value(controller->canvas());
if (proxy)
proxy->setActiveTool(0);
QList tools;
Q_FOREACH (CanvasData *canvasData, canvasses.value(controller)) {
Q_FOREACH (KoToolBase *tool, canvasData->allTools) {
if (! tools.contains(tool)) {
tools.append(tool);
}
}
delete canvasData;
}
Q_FOREACH (KoToolBase *tool, tools) {
uniqueToolIds.remove(tool);
delete tool;
}
canvasses.remove(controller);
emit q->changedCanvas(canvasData ? canvasData->canvas->canvas() : 0);
}
void KoToolManager::Private::attachCanvas(KoCanvasController *controller)
{
Q_ASSERT(controller);
CanvasData *cd = createCanvasData(controller, KoInputDevice::mouse());
// switch to new canvas as the active one.
switchCanvasData(cd);
inputDevice = cd->inputDevice;
QList canvasses_;
canvasses_.append(cd);
canvasses[controller] = canvasses_;
KoToolProxy *tp = proxies[controller->canvas()];
if (tp)
tp->priv()->setCanvasController(controller);
if (cd->activeTool == 0) {
// no active tool, so we activate the highest priority main tool
int highestPriority = INT_MAX;
ToolHelper * helper = 0;
Q_FOREACH (ToolHelper * th, tools) {
if (th->section() == KoToolFactoryBase::mainToolType()) {
if (th->priority() < highestPriority) {
highestPriority = qMin(highestPriority, th->priority());
helper = th;
}
}
}
if (helper)
toolActivated(helper);
}
Connector *connector = new Connector(controller->canvas()->shapeManager());
connect(connector, SIGNAL(selectionChanged(QList)), q,
SLOT(selectionChanged(QList)));
connect(controller->canvas()->selectedShapesProxy(),
SIGNAL(currentLayerChanged(const KoShapeLayer*)),
q, SLOT(currentLayerChanged(const KoShapeLayer*)));
emit q->changedCanvas(canvasData ? canvasData->canvas->canvas() : 0);
}
void KoToolManager::Private::movedFocus(QWidget *from, QWidget *to)
{
Q_UNUSED(from);
// no canvas anyway or no focus set anyway?
if (!canvasData || to == 0) {
return;
}
// Check if this app is about QWidget-based KoCanvasControllerWidget canvasses
// XXX: Focus handling for non-qwidget based canvases!
KoCanvasControllerWidget *canvasControllerWidget = dynamic_cast(canvasData->canvas);
if (!canvasControllerWidget) {
return;
}
// canvasWidget is set as focusproxy for KoCanvasControllerWidget,
// so all focus checks are to be done against canvasWidget objects
// focus returned to current canvas?
if (to == canvasData->canvas->canvas()->canvasWidget()) {
// nothing to do
return;
}
// if the 'to' is one of our canvasWidgets, then switch.
// for code simplicity the current canvas will be checked again,
// but would have been caught already in the lines above, so no issue
KoCanvasController *newCanvas = 0;
Q_FOREACH (KoCanvasController* canvas, canvasses.keys()) {
if (canvas->canvas()->canvasWidget() == to) {
newCanvas = canvas;
break;
}
}
// none of our canvasWidgets got focus?
if (newCanvas == 0) {
return;
}
// switch to canvasdata matching inputdevice used last with this app instance
Q_FOREACH (CanvasData *data, canvasses.value(newCanvas)) {
if (data->inputDevice == inputDevice) {
switchCanvasData(data);
return;
}
}
// if no such inputDevice for this canvas, then simply fallback to first one
switchCanvasData(canvasses.value(newCanvas).first());
}
void KoToolManager::Private::updateCursor(const QCursor &cursor)
{
Q_ASSERT(canvasData);
Q_ASSERT(canvasData->canvas);
Q_ASSERT(canvasData->canvas->canvas());
canvasData->canvas->canvas()->setCursor(cursor);
}
void KoToolManager::Private::selectionChanged(const QList &shapes)
{
QList types;
Q_FOREACH (KoShape *shape, shapes) {
QSet delegates = shape->toolDelegates();
if (delegates.isEmpty()) { // no delegates, just the orig shape
delegates << shape;
}
foreach (KoShape *shape2, delegates) {
Q_ASSERT(shape2);
if (! types.contains(shape2->shapeId())) {
types.append(shape2->shapeId());
}
}
}
// check if there is still a shape selected the active tool can work on
// there needs to be at least one shape for a tool without an activationShapeId
// to work
// if not change the current tool to the default tool
const QStringList activationShapeIds = canvasData->activationShapeId.split(',');
if (!(canvasData->activationShapeId.isNull() && shapes.size() > 0)
- && !activationShapeIds.contains("flake/always")
- && !activationShapeIds.contains("flake/edit")) {
+ && !activationShapeIds.contains("flake/always")
+ && !activationShapeIds.contains("flake/edit")) {
bool currentToolWorks = false;
foreach (const QString &type, types) {
if (activationShapeIds.contains(type)) {
currentToolWorks = true;
break;
}
}
if (!currentToolWorks) {
switchTool(KoInteractionTool_ID, false);
}
}
emit q->toolCodesSelected(types);
}
void KoToolManager::Private::currentLayerChanged(const KoShapeLayer *layer)
{
emit q->currentLayerChanged(canvasData->canvas, layer);
layerExplicitlyDisabled = layer && !layer->isShapeEditable();
updateToolForProxy();
debugFlake << "Layer changed to" << layer << "explicitly disabled:" << layerExplicitlyDisabled;
}
void KoToolManager::Private::updateToolForProxy()
{
KoToolProxy *proxy = proxies.value(canvasData->canvas->canvas());
if(!proxy) return;
bool canUseTool = !layerExplicitlyDisabled || canvasData->activationShapeId.endsWith(QLatin1String("/always"));
proxy->setActiveTool(canUseTool ? canvasData->activeTool : 0);
}
void KoToolManager::Private::switchInputDevice(const KoInputDevice &device)
{
Q_ASSERT(canvasData);
if (!canvasData) return;
if (inputDevice == device) return;
if (inputDevice.isMouse() && device.isMouse()) return;
if (device.isMouse() && !inputDevice.isMouse()) {
// we never switch back to mouse from a tablet input device, so the user can use the
// mouse to edit the settings for a tool activated by a tablet. See bugs
// https://bugs.kde.org/show_bug.cgi?id=283130 and https://bugs.kde.org/show_bug.cgi?id=285501.
// We do continue to switch between tablet devices, thought.
return;
}
QList items = canvasses[canvasData->canvas];
- // disable all actions for all tools in the all canvasdata objects for this canvas.
- Q_FOREACH (CanvasData *cd, items) {
- Q_FOREACH (KoToolBase* tool, cd->allTools) {
- Q_FOREACH (QAction * action, tool->actions()) {
- action->setEnabled(false);
- }
- }
- }
-
// search for a canvasdata object for the current input device
Q_FOREACH (CanvasData *cd, items) {
if (cd->inputDevice == device) {
switchCanvasData(cd);
if (!canvasData->activeTool) {
switchTool(KoInteractionTool_ID, false);
}
return;
}
}
// still here? That means we need to create a new CanvasData instance with the current InputDevice.
CanvasData *cd = createCanvasData(canvasData->canvas, device);
// switch to new canvas as the active one.
QString oldTool = canvasData->activeToolId;
items.append(cd);
canvasses[cd->canvas] = items;
switchCanvasData(cd);
q->switchToolRequested(oldTool);
}
void KoToolManager::Private::registerToolProxy(KoToolProxy *proxy, KoCanvasBase *canvas)
{
proxies.insert(canvas, proxy);
Q_FOREACH (KoCanvasController *controller, canvasses.keys()) {
if (controller->canvas() == canvas) {
proxy->priv()->setCanvasController(controller);
break;
}
}
}
//have to include this because of Q_PRIVATE_SLOT
#include "moc_KoToolManager.cpp"
diff --git a/libs/flake/KoToolManager.h b/libs/flake/KoToolManager.h
index 417e11fdbd..f99dc52d34 100644
--- a/libs/flake/KoToolManager.h
+++ b/libs/flake/KoToolManager.h
@@ -1,338 +1,335 @@
/* This file is part of the KDE project
* Copyright (c) 2005-2006 Boudewijn Rempt
* Copyright (C) 2006, 2008 Thomas Zander
* Copyright (C) 2006 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 KO_TOOL_MANAGER
#define KO_TOOL_MANAGER
#include "KoInputDevice.h"
#include "kritaflake_export.h"
#include
#include
class KoCanvasController;
class KoShapeControllerBase;
class KoToolFactoryBase;
class KoCanvasBase;
class KoToolBase;
class KoCreateShapesTool;
class KActionCollection;
class KoShape;
class KoInputDeviceHandlerEvent;
class KoShapeLayer;
class ToolHelper;
class QKeySequence;
class QCursor;
/**
* This class serves as a QAction-like control object for activation of a tool.
*
* It allows to implement a custom UI to control the activation of tools.
* See KoToolBox & KoModeBox in the kowidgets library.
*
* KoToolAction objects are indirectly owned by the KoToolManager singleton
* and live until the end of its lifetime.
*/
class KRITAFLAKE_EXPORT KoToolAction : public QObject
{
Q_OBJECT
public:
// toolHelper takes over ownership, and those live till the end of KoToolManager.
explicit KoToolAction(ToolHelper *toolHelper);
~KoToolAction() override;
public:
QString id() const; ///< The id of the tool
QString iconText() const; ///< The icontext of the tool
QString toolTip() const; ///< The tooltip of the tool
QString iconName() const; ///< The icon name of the tool
QKeySequence shortcut() const; ///< The shortcut to activate the tool
QString section() const; ///< The section the tool wants to be in.
int priority() const; ///< Lower number (higher priority) means coming first in the section.
int buttonGroupId() const; ///< A unique ID for this tool as passed by changedTool(), >= 0
QString visibilityCode() const; ///< This tool should become visible when we emit this string in toolCodesSelected()
public Q_SLOTS:
void trigger(); ///< Request the activation of the tool
Q_SIGNALS:
void changed(); ///< Emitted when a property changes (shortcut ATM)
private:
friend class ToolHelper;
class Private;
Private *const d;
};
/**
* This class manages the activation and deactivation of tools for
* each input device.
*
* Managing the active tool and switching tool based on various variables.
*
* The state of the toolbox will be the same for all views in the process so practically
* you can say we have one toolbox per application instance (process). Implementation
* does not allow one widget to be in more then one view, so we just make sure the toolbox
* is hidden in not-in-focus views.
*
* The ToolManager is a singleton and will manage all views in all applications that
* are loaded in this process. This means you will have to register and unregister your view.
* When creating your new view you should use a KoCanvasController() and register that
* with the ToolManager like this:
@code
MyGuiWidget::MyGuiWidget() {
m_canvasController = new KoCanvasController(this);
m_canvasController->setCanvas(m_canvas);
KoToolManager::instance()->addControllers(m_canvasController));
}
MyGuiWidget::~MyGuiWidget() {
KoToolManager::instance()->removeCanvasController(m_canvasController);
}
@endcode
*
* For a new view that extends KoView all you need to do is implement KoView::createToolBox()
*
* KoToolManager also keeps track of the current tool based on a
complex set of conditions and heuristics:
- there is one active tool per KoCanvasController (and there is one KoCanvasController
per view, because this is a class with scrollbars and a zoomlevel and so on)
- for every pointing device (determined by the unique id of tablet,
or 0 for mice -- you may have more than one mouse attached, but
Qt cannot distinguish between them, there is an associated tool.
- depending on things like tablet leave/enter proximity, incoming
mouse or tablet events and a little timer (that gets stopped when
we know what is what), the active pointing device is determined,
and the active tool is set accordingly.
Nota bene: if you use KoToolManager and register your canvases with
it you no longer have to manually implement methods to route mouse,
tablet, key or wheel events to the active tool. In fact, it's no
longer interesting to you which tool is active; you can safely
route the paint event through KoToolProxy::paint().
(The reason the input events are handled completely by the
toolmanager and the paint events not is that, generally speaking,
it's okay if the tools get the input events first, but you want to
paint your shapes or other canvas stuff first and only then paint
the tool stuff.)
*/
class KRITAFLAKE_EXPORT KoToolManager : public QObject
{
Q_OBJECT
public:
KoToolManager();
/// Return the toolmanager singleton
static KoToolManager* instance();
~KoToolManager() override;
/**
* Register actions for switching to tools at the actionCollection parameter.
* The actions will have the text / shortcut as stated by the toolFactory.
* If the application calls this in their KoView extending class they will have all the benefits
* from allowing this in the menus and to allow the use to configure the shortcuts used.
* @param ac the actionCollection that will be the parent of the actions.
* @param controller tools registered with this controller will have all their actions added as well.
*/
void registerToolActions(KActionCollection *ac, KoCanvasController *controller);
/**
* Register a new canvas controller
* @param controller the view controller that this toolmanager will manage the tools for
*/
void addController(KoCanvasController *controller);
/**
* Remove a set of controllers
* When the controller is no longer used it should be removed so all tools can be
* deleted and stop eating memory.
* @param controller the controller that is removed
*/
void removeCanvasController(KoCanvasController *controller);
/**
* Attempt to remove a controller.
* This is automatically called when a controller's proxy object is deleted, and
* it ensures that the controller is, in fact, removed, even if the creator forgot
* to do so.
* @param controller the proxy object of the controller to be removed
*/
Q_SLOT void attemptCanvasControllerRemoval(QObject *controller);
/// @return the active canvas controller
KoCanvasController *activeCanvasController() const;
/**
* Return the tool that is able to create shapes for this param canvas.
* This is typically used by the KoShapeSelector to set which shape to create next.
* @param canvas the canvas that is a child of a previously registered controller
* who's tool you want.
* @see addController()
*/
KoCreateShapesTool *shapeCreatorTool(KoCanvasBase *canvas) const;
/**
* Returns the tool for the given tool id. The tool may be 0
* @param canvas the canvas that is a child of a previously registered controller
* who's tool you want.
* @see addController()
*/
KoToolBase *toolById(KoCanvasBase *canvas, const QString &id) const;
/// @return the currently active pointing device
KoInputDevice currentInputDevice() const;
/**
* For the list of shapes find out which tool is the highest priority tool that can handle it.
* @returns the toolId for the shapes.
* @param shapes a list of shapes, a selection for example, that is used to look for the tool.
*/
QString preferredToolForSelection(const QList &shapes);
/**
* Returns the list of toolActions for the current tools.
* @returns lists of toolActions for the current tools.
*/
QList toolActionList() const;
- /// Update the internal shortcuts of each tool. (Activation shortcuts are exposed already.)
- void updateToolShortcuts();
-
/// Request tool activation for the given canvas controller
void requestToolActivation(KoCanvasController *controller);
/// Returns the toolId of the currently active tool
QString activeToolId() const;
void initializeCurrentToolForCanvas();
class Private;
/**
* \internal return the private object for the toolmanager.
*/
KoToolManager::Private *priv();
public Q_SLOTS:
/**
* Request switching tool
* @param id the id of the tool
*/
void switchToolRequested(const QString &id);
/**
* Request change input device
* @param id the id of the input device
*/
void switchInputDeviceRequested(const KoInputDevice &id);
/**
* Request for temporary switching the tools.
* This switch can be later reverted with switchBackRequested().
* @param id the id of the tool
*
* @see switchBackRequested()
*/
void switchToolTemporaryRequested(const QString &id);
/**
* Switches back to the original tool after the temporary switch
* has been done. It the user changed the tool manually on the way,
* then it switches to the interaction tool
*/
void switchBackRequested();
Q_SIGNALS:
/**
* Emitted when a new tool is going to override the current tool
* @param canvas the currently active canvas.
*/
void aboutToChangeTool(KoCanvasController *canvas);
/**
* Emitted when a new tool was selected or became active.
* @param canvas the currently active canvas.
* @param uniqueToolId a random but unique code for the new tool.
*/
void changedTool(KoCanvasController *canvas, int uniqueToolId);
/**
* Emitted after the selection changed to state which unique shape-types are now
* in the selection.
* @param canvas the currently active canvas.
* @param types a list of string that are the shape types of the selected objects.
*/
void toolCodesSelected(const QList &types);
/**
* Emitted after the current layer changed either its properties or to a new layer.
* @param canvas the currently active canvas.
* @param layer the layer that is selected.
*/
void currentLayerChanged(const KoCanvasController *canvas, const KoShapeLayer *layer);
/**
* Every time a new input device gets used by a tool, this event is emitted.
* @param device the new input device that the user picked up.
*/
void inputDeviceChanged(const KoInputDevice &device);
/**
* Emitted whenever the active canvas changed.
* @param canvas the new activated canvas (might be 0)
*/
void changedCanvas(const KoCanvasBase *canvas);
/**
* Emitted whenever the active tool changes the status text.
* @param statusText the new status text
*/
void changedStatusText(const QString &statusText);
/**
* emitted whenever a new tool is dynamically added for the given canvas
*/
void addedTool(KoToolAction *toolAction, KoCanvasController *canvas);
/**
* Emit the new tool option widgets to be used with this canvas.
*/
void toolOptionWidgetsChanged(KoCanvasController *controller, const QList > &widgets);
private:
KoToolManager(const KoToolManager&);
KoToolManager operator=(const KoToolManager&);
Q_PRIVATE_SLOT(d, void toolActivated(ToolHelper *tool))
Q_PRIVATE_SLOT(d, void detachCanvas(KoCanvasController *controller))
Q_PRIVATE_SLOT(d, void attachCanvas(KoCanvasController *controller))
Q_PRIVATE_SLOT(d, void movedFocus(QWidget *from, QWidget *to))
Q_PRIVATE_SLOT(d, void updateCursor(const QCursor &cursor))
Q_PRIVATE_SLOT(d, void selectionChanged(const QList &shapes))
Q_PRIVATE_SLOT(d, void currentLayerChanged(const KoShapeLayer *layer))
QPair createTools(KoCanvasController *controller, ToolHelper *tool);
Private *const d;
};
#endif
diff --git a/libs/flake/tools/KoPathTool.cpp b/libs/flake/tools/KoPathTool.cpp
index 07e62e7b15..20ba9d5955 100644
--- a/libs/flake/tools/KoPathTool.cpp
+++ b/libs/flake/tools/KoPathTool.cpp
@@ -1,1313 +1,1303 @@
/* This file is part of the KDE project
* Copyright (C) 2006-2012 Jan Hambrecht
* Copyright (C) 2006,2007 Thorsten Zachmann
* Copyright (C) 2007, 2010 Thomas Zander
* Copyright (C) 2007 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 "KoPathTool.h"
#include "KoToolBase_p.h"
#include "KoPathShape_p.h"
#include "KoPathToolHandle.h"
#include "KoCanvasBase.h"
#include "KoShapeManager.h"
#include "KoSelectedShapesProxy.h"
#include "KoDocumentResourceManager.h"
#include "KoViewConverter.h"
#include "KoSelection.h"
#include "KoPointerEvent.h"
#include "commands/KoPathPointTypeCommand.h"
#include "commands/KoPathPointInsertCommand.h"
#include "commands/KoPathPointRemoveCommand.h"
#include "commands/KoPathSegmentTypeCommand.h"
#include "commands/KoPathBreakAtPointCommand.h"
#include "commands/KoPathSegmentBreakCommand.h"
#include "commands/KoParameterToPathCommand.h"
#include "commands/KoSubpathJoinCommand.h"
#include
#include
#include
#include "KoParameterShape.h"
#include
#include "KoPathPoint.h"
#include "KoPathPointRubberSelectStrategy.h"
#include "KoPathSegmentChangeStrategy.h"
#include "KoPathConnectionPointStrategy.h"
#include "KoParameterChangeStrategy.h"
#include "PathToolOptionWidget.h"
#include "KoConnectionShape.h"
#include "KoSnapGuide.h"
#include "KoShapeController.h"
#include "kis_action_registry.h"
#include
#include
#include "kis_command_utils.h"
#include "kis_pointer_utils.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
static const unsigned char needle_bits[] = {
0x00, 0x00, 0x10, 0x00, 0x20, 0x00, 0x60, 0x00, 0xc0, 0x00, 0xc0, 0x01,
0x80, 0x03, 0x80, 0x07, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x3e, 0x00, 0x7e,
0x00, 0x7c, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x00
};
static const unsigned char needle_move_bits[] = {
0x00, 0x00, 0x10, 0x00, 0x20, 0x00, 0x60, 0x00, 0xc0, 0x00, 0xc0, 0x01,
0x80, 0x03, 0x80, 0x07, 0x10, 0x0f, 0x38, 0x1f, 0x54, 0x3e, 0xfe, 0x7e,
0x54, 0x7c, 0x38, 0x1c, 0x10, 0x18, 0x00, 0x00
};
// helper function to calculate the squared distance between two points
qreal squaredDistance(const QPointF& p1, const QPointF &p2)
{
qreal dx = p1.x()-p2.x();
qreal dy = p1.y()-p2.y();
return dx*dx + dy*dy;
}
struct KoPathTool::PathSegment {
PathSegment()
: path(0), segmentStart(0), positionOnSegment(0)
{
}
bool isValid() {
return path && segmentStart;
}
KoPathShape *path;
KoPathPoint *segmentStart;
qreal positionOnSegment;
};
KoPathTool::KoPathTool(KoCanvasBase *canvas)
: KoToolBase(canvas)
, m_pointSelection(this)
, m_activeHandle(0)
, m_handleRadius(3)
, m_activeSegment(0)
, m_currentStrategy(0)
, m_activatedTemporarily(false)
{
- QActionGroup *points = new QActionGroup(this);
+ m_points = new QActionGroup(this);
// m_pointTypeGroup->setExclusive(true);
- KisActionRegistry *actionRegistry = KisActionRegistry::instance();
- m_actionPathPointCorner = actionRegistry->makeQAction("pathpoint-corner", this);
- addAction("pathpoint-corner", m_actionPathPointCorner);
+
+ m_actionPathPointCorner = action("pathpoint-corner");
m_actionPathPointCorner->setData(KoPathPointTypeCommand::Corner);
- points->addAction(m_actionPathPointCorner);
+ m_points->addAction(m_actionPathPointCorner);
- m_actionPathPointSmooth = actionRegistry->makeQAction("pathpoint-smooth", this);
- addAction("pathpoint-smooth", m_actionPathPointSmooth);
+ m_actionPathPointSmooth = action("pathpoint-smooth");
m_actionPathPointSmooth->setData(KoPathPointTypeCommand::Smooth);
- points->addAction(m_actionPathPointSmooth);
+ m_points->addAction(m_actionPathPointSmooth);
- m_actionPathPointSymmetric = actionRegistry->makeQAction("pathpoint-symmetric", this);
- addAction("pathpoint-symmetric", m_actionPathPointSymmetric);
+ m_actionPathPointSymmetric = action("pathpoint-symmetric");
m_actionPathPointSymmetric->setData(KoPathPointTypeCommand::Symmetric);
- points->addAction(m_actionPathPointSymmetric);
-
- m_actionCurvePoint = actionRegistry->makeQAction("pathpoint-curve", this);
- addAction("pathpoint-curve", m_actionCurvePoint);
- connect(m_actionCurvePoint, SIGNAL(triggered()), this, SLOT(pointToCurve()));
-
- m_actionLinePoint = actionRegistry->makeQAction("pathpoint-line", this);
- addAction("pathpoint-line", m_actionLinePoint);
- connect(m_actionLinePoint, SIGNAL(triggered()), this, SLOT(pointToLine()));
-
- m_actionLineSegment = actionRegistry->makeQAction("pathsegment-line", this);
- addAction("pathsegment-line", m_actionLineSegment);
- connect(m_actionLineSegment, SIGNAL(triggered()), this, SLOT(segmentToLine()));
-
- m_actionCurveSegment = actionRegistry->makeQAction("pathsegment-curve", this);
- addAction("pathsegment-curve", m_actionCurveSegment);
- connect(m_actionCurveSegment, SIGNAL(triggered()), this, SLOT(segmentToCurve()));
-
- m_actionAddPoint = actionRegistry->makeQAction("pathpoint-insert", this);
- addAction("pathpoint-insert", m_actionAddPoint);
- connect(m_actionAddPoint, SIGNAL(triggered()), this, SLOT(insertPoints()));
-
- m_actionRemovePoint = actionRegistry->makeQAction("pathpoint-remove", this);
- addAction("pathpoint-remove", m_actionRemovePoint);
- connect(m_actionRemovePoint, SIGNAL(triggered()), this, SLOT(removePoints()));
-
- m_actionBreakPoint = actionRegistry->makeQAction("path-break-point", this);
- addAction("path-break-point", m_actionBreakPoint);
- connect(m_actionBreakPoint, SIGNAL(triggered()), this, SLOT(breakAtPoint()));
-
- m_actionBreakSegment = actionRegistry->makeQAction("path-break-segment", this);
- addAction("path-break-segment", m_actionBreakSegment);
- connect(m_actionBreakSegment, SIGNAL(triggered()), this, SLOT(breakAtSegment()));
-
- m_actionJoinSegment = actionRegistry->makeQAction("pathpoint-join", this);
- addAction("pathpoint-join", m_actionJoinSegment);
- connect(m_actionJoinSegment, SIGNAL(triggered()), this, SLOT(joinPoints()));
-
- m_actionMergePoints = actionRegistry->makeQAction("pathpoint-merge", this);
- addAction("pathpoint-merge", m_actionMergePoints);
- connect(m_actionMergePoints, SIGNAL(triggered()), this, SLOT(mergePoints()));
-
- m_actionConvertToPath = actionRegistry->makeQAction("convert-to-path", this);
- addAction("convert-to-path", m_actionConvertToPath);
- connect(m_actionConvertToPath, SIGNAL(triggered()), this, SLOT(convertToPath()));
+ m_points->addAction(m_actionPathPointSymmetric);
+
+ m_actionCurvePoint = action("pathpoint-curve");
+ m_actionLinePoint = action("pathpoint-line");
+ m_actionLineSegment = action("pathsegment-line");
+ m_actionCurveSegment = action("pathsegment-curve");
+ m_actionAddPoint = action("pathpoint-insert");
+ m_actionRemovePoint = action("pathpoint-remove");
+ m_actionBreakPoint = action("path-break-point");
+ m_actionBreakSegment = action("path-break-segment");
+ m_actionJoinSegment = action("pathpoint-join");
+ m_actionMergePoints = action("pathpoint-merge");
+ m_actionConvertToPath = action("convert-to-path");
m_contextMenu.reset(new QMenu());
-
- connect(points, SIGNAL(triggered(QAction*)), this, SLOT(pointTypeChanged(QAction*)));
- connect(&m_pointSelection, SIGNAL(selectionChanged()), this, SLOT(pointSelectionChanged()));
-
QBitmap b = QBitmap::fromData(QSize(16, 16), needle_bits);
QBitmap m = b.createHeuristicMask(false);
m_selectCursor = QCursor(b, m, 2, 0);
b = QBitmap::fromData(QSize(16, 16), needle_move_bits);
m = b.createHeuristicMask(false);
m_moveCursor = QCursor(b, m, 2, 0);
}
KoPathTool::~KoPathTool()
{
delete m_activeHandle;
delete m_activeSegment;
delete m_currentStrategy;
}
QList > KoPathTool::createOptionWidgets()
{
QList > list;
PathToolOptionWidget * toolOptions = new PathToolOptionWidget(this);
connect(this, SIGNAL(typeChanged(int)), toolOptions, SLOT(setSelectionType(int)));
connect(this, SIGNAL(singleShapeChanged(KoPathShape*)), toolOptions, SLOT(setCurrentShape(KoPathShape*)));
connect(toolOptions, SIGNAL(sigRequestUpdateActions()), this, SLOT(updateActions()));
updateOptionsWidget();
toolOptions->setWindowTitle(i18n("Edit Shape"));
list.append(toolOptions);
return list;
}
void KoPathTool::pointTypeChanged(QAction *type)
{
Q_D(KoToolBase);
if (m_pointSelection.hasSelection()) {
QList selectedPoints = m_pointSelection.selectedPointsData();
KUndo2Command *initialConversionCommand = createPointToCurveCommand(selectedPoints);
// conversion should happen before the c-tor
// of KoPathPointTypeCommand is executed!
if (initialConversionCommand) {
initialConversionCommand->redo();
}
KUndo2Command *command =
new KoPathPointTypeCommand(selectedPoints,
static_cast(type->data().toInt()));
if (initialConversionCommand) {
using namespace KisCommandUtils;
CompositeCommand *parent = new CompositeCommand();
parent->setText(command->text());
parent->addCommand(new SkipFirstRedoWrapper(initialConversionCommand));
parent->addCommand(command);
command = parent;
}
d->canvas->addCommand(command);
}
}
void KoPathTool::insertPoints()
{
Q_D(KoToolBase);
QList segments(m_pointSelection.selectedSegmentsData());
if (segments.size() == 1) {
qreal positionInSegment = 0.5;
if (m_activeSegment && m_activeSegment->isValid()) {
positionInSegment = m_activeSegment->positionOnSegment;
}
KoPathPointInsertCommand *cmd = new KoPathPointInsertCommand(segments, positionInSegment);
d->canvas->addCommand(cmd);
// TODO: this construction is dangerous. The canvas can remove the command right after
// it has been added to it!
m_pointSelection.clear();
foreach (KoPathPoint * p, cmd->insertedPoints()) {
m_pointSelection.add(p, false);
}
}
}
void KoPathTool::removePoints()
{
Q_D(KoToolBase);
if (m_pointSelection.size() > 0) {
KUndo2Command *cmd = KoPathPointRemoveCommand::createCommand(m_pointSelection.selectedPointsData(), d->canvas->shapeController());
PointHandle *pointHandle = dynamic_cast(m_activeHandle);
if (pointHandle && m_pointSelection.contains(pointHandle->activePoint())) {
delete m_activeHandle;
m_activeHandle = 0;
}
clearActivePointSelectionReferences();
d->canvas->addCommand(cmd);
}
}
void KoPathTool::pointToLine()
{
Q_D(KoToolBase);
if (m_pointSelection.hasSelection()) {
QList selectedPoints = m_pointSelection.selectedPointsData();
QList pointToChange;
QList::const_iterator it(selectedPoints.constBegin());
for (; it != selectedPoints.constEnd(); ++it) {
KoPathPoint *point = it->pathShape->pointByIndex(it->pointIndex);
if (point && (point->activeControlPoint1() || point->activeControlPoint2()))
pointToChange.append(*it);
}
if (! pointToChange.isEmpty()) {
d->canvas->addCommand(new KoPathPointTypeCommand(pointToChange, KoPathPointTypeCommand::Line));
}
}
}
void KoPathTool::pointToCurve()
{
Q_D(KoToolBase);
if (m_pointSelection.hasSelection()) {
QList selectedPoints = m_pointSelection.selectedPointsData();
KUndo2Command *command = createPointToCurveCommand(selectedPoints);
if (command) {
d->canvas->addCommand(command);
}
}
}
KUndo2Command* KoPathTool::createPointToCurveCommand(const QList &points)
{
KUndo2Command *command = 0;
QList pointToChange;
QList::const_iterator it(points.constBegin());
for (; it != points.constEnd(); ++it) {
KoPathPoint *point = it->pathShape->pointByIndex(it->pointIndex);
if (point && (! point->activeControlPoint1() || ! point->activeControlPoint2()))
pointToChange.append(*it);
}
if (!pointToChange.isEmpty()) {
command = new KoPathPointTypeCommand(pointToChange, KoPathPointTypeCommand::Curve);
}
return command;
}
void KoPathTool::segmentToLine()
{
Q_D(KoToolBase);
if (m_pointSelection.size() > 1) {
QList segments(m_pointSelection.selectedSegmentsData());
if (segments.size() > 0) {
d->canvas->addCommand(new KoPathSegmentTypeCommand(segments, KoPathSegmentTypeCommand::Line));
}
}
}
void KoPathTool::segmentToCurve()
{
Q_D(KoToolBase);
if (m_pointSelection.size() > 1) {
QList segments(m_pointSelection.selectedSegmentsData());
if (segments.size() > 0) {
d->canvas->addCommand(new KoPathSegmentTypeCommand(segments, KoPathSegmentTypeCommand::Curve));
}
}
}
void KoPathTool::convertToPath()
{
Q_D(KoToolBase);
KoSelection *selection = canvas()->selectedShapesProxy()->selection();
QList parameterShapes;
Q_FOREACH (KoShape *shape, m_pointSelection.selectedShapes()) {
KoParameterShape * parameteric = dynamic_cast(shape);
if (parameteric && parameteric->isParametricShape()) {
parameterShapes.append(parameteric);
}
}
if (!parameterShapes.isEmpty()) {
d->canvas->addCommand(new KoParameterToPathCommand(parameterShapes));
}
QList textShapes;
Q_FOREACH (KoShape *shape, selection->selectedEditableShapes()) {
if (KoSvgTextShape *text = dynamic_cast(shape)) {
textShapes.append(text);
}
}
if (!textShapes.isEmpty()) {
KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Convert to Path")); // TODO: reuse the text from KoParameterToPathCommand
const QList oldSelectedShapes = implicitCastList(textShapes);
new KoKeepShapesSelectedCommand(oldSelectedShapes, {}, canvas()->selectedShapesProxy(),
KisCommandUtils::FlipFlopCommand::State::INITIALIZING, cmd);
QList newSelectedShapes;
Q_FOREACH (KoSvgTextShape *shape, textShapes) {
const QPainterPath path = shape->textOutline();
if (path.isEmpty()) continue;
KoPathShape *pathShape = KoPathShape::createShapeFromPainterPath(path);
pathShape->setBackground(shape->background());
pathShape->setStroke(shape->stroke());
pathShape->setZIndex(shape->zIndex());
pathShape->setTransformation(shape->transformation());
KoShapeContainer *parent = shape->parent();
canvas()->shapeController()->addShapeDirect(pathShape, parent, cmd);
newSelectedShapes << pathShape;
}
canvas()->shapeController()->removeShapes(oldSelectedShapes, cmd);
new KoKeepShapesSelectedCommand({}, newSelectedShapes, canvas()->selectedShapesProxy(),
KisCommandUtils::FlipFlopCommand::State::FINALIZING, cmd);
canvas()->addCommand(cmd);
}
updateOptionsWidget();
}
namespace {
bool checkCanJoinToPoints(const KoPathPointData & pd1, const KoPathPointData & pd2)
{
const KoPathPointIndex & index1 = pd1.pointIndex;
const KoPathPointIndex & index2 = pd2.pointIndex;
KoPathShape *path1 = pd1.pathShape;
KoPathShape *path2 = pd2.pathShape;
// check if subpaths are already closed
if (path1->isClosedSubpath(index1.first) || path2->isClosedSubpath(index2.first))
return false;
// check if first point is an endpoint
if (index1.second != 0 && index1.second != path1->subpathPointCount(index1.first)-1)
return false;
// check if second point is an endpoint
if (index2.second != 0 && index2.second != path2->subpathPointCount(index2.first)-1)
return false;
return true;
}
}
void KoPathTool::mergePointsImpl(bool doJoin)
{
Q_D(KoToolBase);
if (m_pointSelection.size() != 2)
return;
QList pointData = m_pointSelection.selectedPointsData();
if (pointData.size() != 2) return;
const KoPathPointData & pd1 = pointData.at(0);
const KoPathPointData & pd2 = pointData.at(1);
if (!checkCanJoinToPoints(pd1, pd2)) {
return;
}
clearActivePointSelectionReferences();
KUndo2Command *cmd = 0;
if (doJoin) {
cmd = new KoMultiPathPointJoinCommand(pd1, pd2, d->canvas->shapeController()->documentBase(), d->canvas->shapeManager()->selection());
} else {
cmd = new KoMultiPathPointMergeCommand(pd1, pd2, d->canvas->shapeController()->documentBase(), d->canvas->shapeManager()->selection());
}
d->canvas->addCommand(cmd);
}
void KoPathTool::joinPoints()
{
mergePointsImpl(true);
}
void KoPathTool::mergePoints()
{
mergePointsImpl(false);
}
void KoPathTool::breakAtPoint()
{
Q_D(KoToolBase);
if (m_pointSelection.hasSelection()) {
d->canvas->addCommand(new KoPathBreakAtPointCommand(m_pointSelection.selectedPointsData()));
}
}
void KoPathTool::breakAtSegment()
{
Q_D(KoToolBase);
// only try to break a segment when 2 points of the same object are selected
if (m_pointSelection.objectCount() == 1 && m_pointSelection.size() == 2) {
QList segments(m_pointSelection.selectedSegmentsData());
if (segments.size() == 1) {
d->canvas->addCommand(new KoPathSegmentBreakCommand(segments.at(0)));
}
}
}
void KoPathTool::paint(QPainter &painter, const KoViewConverter &converter)
{
Q_D(KoToolBase);
Q_FOREACH (KoPathShape *shape, m_pointSelection.selectedShapes()) {
KisHandlePainterHelper helper =
KoShape::createHandlePainterHelper(&painter, shape, converter, m_handleRadius);
helper.setHandleStyle(KisHandleStyle::primarySelection());
KoParameterShape * parameterShape = dynamic_cast(shape);
if (parameterShape && parameterShape->isParametricShape()) {
parameterShape->paintHandles(helper);
} else {
shape->paintPoints(helper);
}
if (!shape->stroke() || !shape->stroke()->isVisible()) {
helper.setHandleStyle(KisHandleStyle::secondarySelection());
helper.drawPath(shape->outline());
}
}
if (m_currentStrategy) {
painter.save();
m_currentStrategy->paint(painter, converter);
painter.restore();
}
m_pointSelection.paint(painter, converter, m_handleRadius);
if (m_activeHandle) {
if (m_activeHandle->check(m_pointSelection.selectedShapes())) {
m_activeHandle->paint(painter, converter, m_handleRadius);
} else {
delete m_activeHandle;
m_activeHandle = 0;
}
} else if (m_activeSegment && m_activeSegment->isValid()) {
KoPathShape *shape = m_activeSegment->path;
// if the stroke is invisible, then we already painted the outline of the shape!
if (shape->stroke() && shape->stroke()->isVisible()) {
KoPathPointIndex index = shape->pathPointIndex(m_activeSegment->segmentStart);
KoPathSegment segment = shape->segmentByIndex(index).toCubic();
KIS_SAFE_ASSERT_RECOVER_RETURN(segment.isValid());
KisHandlePainterHelper helper =
KoShape::createHandlePainterHelper(&painter, shape, converter, m_handleRadius);
helper.setHandleStyle(KisHandleStyle::secondarySelection());
QPainterPath path;
path.moveTo(segment.first()->point());
path.cubicTo(segment.first()->controlPoint2(),
segment.second()->controlPoint1(),
segment.second()->point());
helper.drawPath(path);
}
}
if (m_currentStrategy) {
painter.save();
KoShape::applyConversion(painter, converter);
d->canvas->snapGuide()->paint(painter, converter);
painter.restore();
}
}
void KoPathTool::repaintDecorations()
{
Q_FOREACH (KoShape *shape, m_pointSelection.selectedShapes()) {
repaint(shape->boundingRect());
}
m_pointSelection.repaint();
updateOptionsWidget();
}
void KoPathTool::mousePressEvent(KoPointerEvent *event)
{
// we are moving if we hit a point and use the left mouse button
event->ignore();
if (m_activeHandle) {
m_currentStrategy = m_activeHandle->handleMousePress(event);
event->accept();
} else {
if (event->button() & Qt::LeftButton) {
// check if we hit a path segment
if (m_activeSegment && m_activeSegment->isValid()) {
KoPathShape *shape = m_activeSegment->path;
KoPathPointIndex index = shape->pathPointIndex(m_activeSegment->segmentStart);
KoPathSegment segment = shape->segmentByIndex(index);
m_pointSelection.add(segment.first(), !(event->modifiers() & Qt::ShiftModifier));
m_pointSelection.add(segment.second(), false);
KoPathPointData data(shape, index);
m_currentStrategy = new KoPathSegmentChangeStrategy(this, event->point, data, m_activeSegment->positionOnSegment);
event->accept();
} else {
KoShapeManager *shapeManager = canvas()->shapeManager();
KoSelection *selection = shapeManager->selection();
KoShape *shape = shapeManager->shapeAt(event->point, KoFlake::ShapeOnTop);
if (shape && !selection->isSelected(shape)) {
if (!(event->modifiers() & Qt::ShiftModifier)) {
selection->deselectAll();
}
selection->select(shape);
} else {
KIS_ASSERT_RECOVER_RETURN(m_currentStrategy == 0);
m_currentStrategy = new KoPathPointRubberSelectStrategy(this, event->point);
event->accept();
}
}
}
}
}
void KoPathTool::mouseMoveEvent(KoPointerEvent *event)
{
if (event->button() & Qt::RightButton)
return;
if (m_currentStrategy) {
m_lastPoint = event->point;
m_currentStrategy->handleMouseMove(event->point, event->modifiers());
// repaint new handle positions
m_pointSelection.repaint();
if (m_activeHandle) {
m_activeHandle->repaint();
}
if (m_activeSegment) {
repaintSegment(m_activeSegment);
}
return;
}
if (m_activeSegment) {
KoPathPointIndex index = m_activeSegment->path->pathPointIndex(m_activeSegment->segmentStart);
KoPathSegment segment = m_activeSegment->path->segmentByIndex(index);
repaint(segment.boundingRect());
delete m_activeSegment;
m_activeSegment = 0;
}
Q_FOREACH (KoPathShape *shape, m_pointSelection.selectedShapes()) {
QRectF roi = handleGrabRect(shape->documentToShape(event->point));
KoParameterShape * parameterShape = dynamic_cast(shape);
if (parameterShape && parameterShape->isParametricShape()) {
int handleId = parameterShape->handleIdAt(roi);
if (handleId != -1) {
useCursor(m_moveCursor);
emit statusTextChanged(i18n("Drag to move handle."));
if (m_activeHandle)
m_activeHandle->repaint();
delete m_activeHandle;
if (KoConnectionShape * connectionShape = dynamic_cast(parameterShape)) {
//debugFlake << "handleId" << handleId;
m_activeHandle = new ConnectionHandle(this, connectionShape, handleId);
m_activeHandle->repaint();
return;
} else {
//debugFlake << "handleId" << handleId;
m_activeHandle = new ParameterHandle(this, parameterShape, handleId);
m_activeHandle->repaint();
return;
}
}
} else {
QList points = shape->pointsAt(roi);
if (! points.empty()) {
// find the nearest control point from all points within the roi
KoPathPoint * bestPoint = 0;
KoPathPoint::PointType bestPointType = KoPathPoint::Node;
qreal minDistance = HUGE_VAL;
Q_FOREACH (KoPathPoint *p, points) {
// the node point must be hit if the point is not selected yet
if (! m_pointSelection.contains(p) && ! roi.contains(p->point()))
continue;
// check for the control points first as otherwise it is no longer
// possible to change the control points when they are the same as the point
if (p->activeControlPoint1() && roi.contains(p->controlPoint1())) {
qreal dist = squaredDistance(roi.center(), p->controlPoint1());
if (dist < minDistance) {
bestPoint = p;
bestPointType = KoPathPoint::ControlPoint1;
minDistance = dist;
}
}
if (p->activeControlPoint2() && roi.contains(p->controlPoint2())) {
qreal dist = squaredDistance(roi.center(), p->controlPoint2());
if (dist < minDistance) {
bestPoint = p;
bestPointType = KoPathPoint::ControlPoint2;
minDistance = dist;
}
}
// check the node point at last
qreal dist = squaredDistance(roi.center(), p->point());
if (dist < minDistance) {
bestPoint = p;
bestPointType = KoPathPoint::Node;
minDistance = dist;
}
}
if (! bestPoint)
return;
useCursor(m_moveCursor);
if (bestPointType == KoPathPoint::Node)
emit statusTextChanged(i18n("Drag to move point. Shift click to change point type."));
else
emit statusTextChanged(i18n("Drag to move control point."));
PointHandle *prev = dynamic_cast(m_activeHandle);
if (prev && prev->activePoint() == bestPoint && prev->activePointType() == bestPointType)
return; // no change;
if (m_activeHandle)
m_activeHandle->repaint();
delete m_activeHandle;
m_activeHandle = new PointHandle(this, bestPoint, bestPointType);
m_activeHandle->repaint();
return;
}
}
}
useCursor(m_selectCursor);
if (m_activeHandle) {
m_activeHandle->repaint();
}
delete m_activeHandle;
m_activeHandle = 0;
PathSegment *hoveredSegment = segmentAtPoint(event->point);
if(hoveredSegment) {
useCursor(Qt::PointingHandCursor);
emit statusTextChanged(i18n("Drag to change curve directly. Double click to insert new path point."));
m_activeSegment = hoveredSegment;
repaintSegment(m_activeSegment);
} else {
uint selectedPointCount = m_pointSelection.size();
if (selectedPointCount == 0)
emit statusTextChanged(QString());
else if (selectedPointCount == 1)
emit statusTextChanged(i18n("Press B to break path at selected point."));
else
emit statusTextChanged(i18n("Press B to break path at selected segments."));
}
}
void KoPathTool::repaintSegment(PathSegment *pathSegment)
{
if (!pathSegment || !pathSegment->isValid()) return;
KoPathPointIndex index = pathSegment->path->pathPointIndex(pathSegment->segmentStart);
KoPathSegment segment = pathSegment->path->segmentByIndex(index);
repaint(segment.boundingRect());
}
void KoPathTool::mouseReleaseEvent(KoPointerEvent *event)
{
Q_D(KoToolBase);
if (m_currentStrategy) {
const bool hadNoSelection = !m_pointSelection.hasSelection();
m_currentStrategy->finishInteraction(event->modifiers());
KUndo2Command *command = m_currentStrategy->createCommand();
if (command)
d->canvas->addCommand(command);
if (hadNoSelection && dynamic_cast(m_currentStrategy)
&& !m_pointSelection.hasSelection()) {
// the click didn't do anything at all. Allow it to be used by others.
event->ignore();
}
delete m_currentStrategy;
m_currentStrategy = 0;
}
}
void KoPathTool::keyPressEvent(QKeyEvent *event)
{
if (m_currentStrategy) {
switch (event->key()) {
case Qt::Key_Control:
case Qt::Key_Alt:
case Qt::Key_Shift:
case Qt::Key_Meta:
if (! event->isAutoRepeat()) {
m_currentStrategy->handleMouseMove(m_lastPoint, event->modifiers());
}
break;
case Qt::Key_Escape:
m_currentStrategy->cancelInteraction();
delete m_currentStrategy;
m_currentStrategy = 0;
break;
default:
event->ignore();
return;
}
} else {
switch (event->key()) {
#ifndef NDEBUG
case Qt::Key_D:
if (m_pointSelection.objectCount() == 1) {
QList selectedPoints = m_pointSelection.selectedPointsData();
KoPathShapePrivate *p = static_cast(selectedPoints[0].pathShape->priv());
p->debugPath();
}
break;
#endif
case Qt::Key_B:
if (m_pointSelection.size() == 1)
breakAtPoint();
else if (m_pointSelection.size() >= 2)
breakAtSegment();
break;
default:
event->ignore();
return;
}
}
event->accept();
}
void KoPathTool::keyReleaseEvent(QKeyEvent *event)
{
if (m_currentStrategy) {
switch (event->key()) {
case Qt::Key_Control:
case Qt::Key_Alt:
case Qt::Key_Shift:
case Qt::Key_Meta:
if (! event->isAutoRepeat()) {
m_currentStrategy->handleMouseMove(m_lastPoint, Qt::NoModifier);
}
break;
default:
break;
}
}
event->accept();
}
void KoPathTool::mouseDoubleClickEvent(KoPointerEvent *event)
{
Q_D(KoToolBase);
event->ignore();
// check if we are doing something else at the moment
if (m_currentStrategy) return;
if (!m_activeHandle && m_activeSegment && m_activeSegment->isValid()) {
QList segments;
segments.append(
KoPathPointData(m_activeSegment->path,
m_activeSegment->path->pathPointIndex(m_activeSegment->segmentStart)));
KoPathPointInsertCommand *cmd = new KoPathPointInsertCommand(segments, m_activeSegment->positionOnSegment);
d->canvas->addCommand(cmd);
m_pointSelection.clear();
foreach (KoPathPoint * p, cmd->insertedPoints()) {
m_pointSelection.add(p, false);
}
updateActions();
event->accept();
} else if (!m_activeHandle && !m_activeSegment && m_activatedTemporarily) {
emit done();
event->accept();
} else if (!m_activeHandle && !m_activeSegment) {
KoShapeManager *shapeManager = canvas()->shapeManager();
KoSelection *selection = shapeManager->selection();
selection->deselectAll();
event->accept();
}
}
KoPathTool::PathSegment* KoPathTool::segmentAtPoint(const QPointF &point)
{
// the max allowed distance from a segment
const QRectF grabRoi = handleGrabRect(point);
const qreal distanceThreshold = 0.5 * KisAlgebra2D::maxDimension(grabRoi);
QScopedPointer segment(new PathSegment);
Q_FOREACH (KoPathShape *shape, m_pointSelection.selectedShapes()) {
KoParameterShape * parameterShape = dynamic_cast(shape);
if (parameterShape && parameterShape->isParametricShape())
continue;
// convert document point to shape coordinates
const QPointF p = shape->documentToShape(point);
// our region of interest, i.e. a region around our mouse position
const QRectF roi = shape->documentToShape(grabRoi);
qreal minDistance = std::numeric_limits::max();
// check all segments of this shape which intersect the region of interest
const QList segments = shape->segmentsAt(roi);
foreach (const KoPathSegment &s, segments) {
const qreal nearestPointParam = s.nearestPoint(p);
const QPointF nearestPoint = s.pointAt(nearestPointParam);
const qreal distance = kisDistance(p, nearestPoint);
// are we within the allowed distance ?
if (distance > distanceThreshold)
continue;
// are we closer to the last closest point ?
if (distance < minDistance) {
segment->path = shape;
segment->segmentStart = s.first();
segment->positionOnSegment = nearestPointParam;
}
}
}
if (!segment->isValid()) {
segment.reset();
}
return segment.take();
}
void KoPathTool::activate(ToolActivation activation, const QSet &shapes)
{
KoToolBase::activate(activation, shapes);
Q_D(KoToolBase);
m_activatedTemporarily = activation == TemporaryActivation;
// retrieve the actual global handle radius
m_handleRadius = handleRadius();
d->canvas->snapGuide()->reset();
useCursor(m_selectCursor);
m_canvasConnections.addConnection(d->canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged()));
m_canvasConnections.addConnection(d->canvas->selectedShapesProxy(), SIGNAL(selectionContentChanged()), this, SLOT(updateActions()));
m_shapeFillResourceConnector.connectToCanvas(d->canvas);
initializeWithShapes(shapes.toList());
+
+ connect(m_actionCurvePoint, SIGNAL(triggered()), this, SLOT(pointToCurve()), Qt::UniqueConnection);
+ connect(m_actionLinePoint, SIGNAL(triggered()), this, SLOT(pointToLine()), Qt::UniqueConnection);
+ connect(m_actionLineSegment, SIGNAL(triggered()), this, SLOT(segmentToLine()), Qt::UniqueConnection);
+ connect(m_actionCurveSegment, SIGNAL(triggered()), this, SLOT(segmentToCurve()), Qt::UniqueConnection);
+ connect(m_actionAddPoint, SIGNAL(triggered()), this, SLOT(insertPoints()), Qt::UniqueConnection);
+ connect(m_actionRemovePoint, SIGNAL(triggered()), this, SLOT(removePoints()), Qt::UniqueConnection);
+ connect(m_actionBreakPoint, SIGNAL(triggered()), this, SLOT(breakAtPoint()), Qt::UniqueConnection);
+ connect(m_actionBreakSegment, SIGNAL(triggered()), this, SLOT(breakAtSegment()), Qt::UniqueConnection);
+ connect(m_actionJoinSegment, SIGNAL(triggered()), this, SLOT(joinPoints()), Qt::UniqueConnection);
+ connect(m_actionMergePoints, SIGNAL(triggered()), this, SLOT(mergePoints()), Qt::UniqueConnection);
+ connect(m_actionConvertToPath, SIGNAL(triggered()), this, SLOT(convertToPath()), Qt::UniqueConnection);
+ connect(m_points, SIGNAL(triggered(QAction*)), this, SLOT(pointTypeChanged(QAction*)), Qt::UniqueConnection);
+ connect(&m_pointSelection, SIGNAL(selectionChanged()), this, SLOT(pointSelectionChanged()), Qt::UniqueConnection);
+
}
void KoPathTool::slotSelectionChanged()
{
Q_D(KoToolBase);
QList shapes =
d->canvas->selectedShapesProxy()->selection()->selectedEditableShapesAndDelegates();
initializeWithShapes(shapes);
}
void KoPathTool::notifyPathPointsChanged(KoPathShape *shape)
{
Q_UNUSED(shape);
// active handle and selection might have already become invalid, so just
// delete them without dereferencing anything...
delete m_activeHandle;
m_activeHandle = 0;
delete m_activeSegment;
m_activeSegment = 0;
}
void KoPathTool::clearActivePointSelectionReferences()
{
delete m_activeHandle;
m_activeHandle = 0;
delete m_activeSegment;
m_activeSegment = 0;
m_pointSelection.clear();
}
void KoPathTool::initializeWithShapes(const QList shapes)
{
QList selectedShapes;
Q_FOREACH (KoShape *shape, shapes) {
KoPathShape *pathShape = dynamic_cast(shape);
if (pathShape && pathShape->isShapeEditable()) {
selectedShapes.append(pathShape);
}
}
const QRectF oldBoundingRect =
KoShape::boundingRect(implicitCastList(m_pointSelection.selectedShapes()));
if (selectedShapes != m_pointSelection.selectedShapes()) {
clearActivePointSelectionReferences();
m_pointSelection.setSelectedShapes(selectedShapes);
repaintDecorations();
}
Q_FOREACH (KoPathShape *shape, selectedShapes) {
// as the tool is just in activation repaintDecorations does not yet get called
// so we need to use repaint of the tool and it is only needed to repaint the
// current canvas
repaint(shape->boundingRect());
}
repaint(oldBoundingRect);
updateOptionsWidget();
updateActions();
}
void KoPathTool::updateOptionsWidget()
{
PathToolOptionWidget::Types type;
QList selectedShapes = m_pointSelection.selectedShapes();
Q_FOREACH (KoPathShape *shape, selectedShapes) {
KoParameterShape * parameterShape = dynamic_cast(shape);
type |= parameterShape && parameterShape->isParametricShape() ?
PathToolOptionWidget::ParametricShape : PathToolOptionWidget::PlainPath;
}
emit singleShapeChanged(selectedShapes.size() == 1 ? selectedShapes.first() : 0);
emit typeChanged(type);
}
void KoPathTool::updateActions()
{
QList pointData = m_pointSelection.selectedPointsData();
bool canBreakAtPoint = false;
bool hasNonSmoothPoints = false;
bool hasNonSymmetricPoints = false;
bool hasNonSplitPoints = false;
bool hasNonLinePoints = false;
bool hasNonCurvePoints = false;
bool canJoinSubpaths = false;
if (!pointData.isEmpty()) {
Q_FOREACH (const KoPathPointData &pd, pointData) {
const int subpathIndex = pd.pointIndex.first;
const int pointIndex = pd.pointIndex.second;
canBreakAtPoint |= pd.pathShape->isClosedSubpath(subpathIndex) ||
(pointIndex > 0 && pointIndex < pd.pathShape->subpathPointCount(subpathIndex) - 1);
KoPathPoint *point = pd.pathShape->pointByIndex(pd.pointIndex);
hasNonSmoothPoints |= !(point->properties() & KoPathPoint::IsSmooth);
hasNonSymmetricPoints |= !(point->properties() & KoPathPoint::IsSymmetric);
hasNonSplitPoints |=
point->properties() & KoPathPoint::IsSymmetric ||
point->properties() & KoPathPoint::IsSmooth;
hasNonLinePoints |= point->activeControlPoint1() || point->activeControlPoint2();
hasNonCurvePoints |= !point->activeControlPoint1() && !point->activeControlPoint2();
}
if (pointData.size() == 2) {
const KoPathPointData & pd1 = pointData.at(0);
const KoPathPointData & pd2 = pointData.at(1);
canJoinSubpaths = checkCanJoinToPoints(pd1, pd2);
}
}
m_actionPathPointCorner->setEnabled(hasNonSplitPoints);
m_actionPathPointSmooth->setEnabled(hasNonSmoothPoints);
m_actionPathPointSymmetric->setEnabled(hasNonSymmetricPoints);
m_actionRemovePoint->setEnabled(!pointData.isEmpty());
m_actionBreakPoint->setEnabled(canBreakAtPoint);
m_actionCurvePoint->setEnabled(hasNonCurvePoints);
m_actionLinePoint->setEnabled(hasNonLinePoints);
m_actionJoinSegment->setEnabled(canJoinSubpaths);
m_actionMergePoints->setEnabled(canJoinSubpaths);
QList segments(m_pointSelection.selectedSegmentsData());
bool canSplitAtSegment = false;
bool canConvertSegmentToLine = false;
bool canConvertSegmentToCurve= false;
if (!segments.isEmpty()) {
canSplitAtSegment = segments.size() == 1;
bool hasLines = false;
bool hasCurves = false;
Q_FOREACH (const KoPathPointData &pd, segments) {
KoPathSegment segment = pd.pathShape->segmentByIndex(pd.pointIndex);
hasLines |= segment.degree() == 1;
hasCurves |= segment.degree() > 1;
}
canConvertSegmentToLine = !segments.isEmpty() && hasCurves;
canConvertSegmentToCurve= !segments.isEmpty() && hasLines;
}
m_actionAddPoint->setEnabled(canSplitAtSegment);
m_actionLineSegment->setEnabled(canConvertSegmentToLine);
m_actionCurveSegment->setEnabled(canConvertSegmentToCurve);
m_actionBreakSegment->setEnabled(canSplitAtSegment);
KoSelection *selection = canvas()->selectedShapesProxy()->selection();
bool haveConvertibleShapes = false;
Q_FOREACH (KoShape *shape, selection->selectedEditableShapes()) {
KoParameterShape * parameterShape = dynamic_cast(shape);
KoSvgTextShape *textShape = dynamic_cast(shape);
if (textShape ||
(parameterShape && parameterShape->isParametricShape())) {
haveConvertibleShapes = true;
break;
}
}
m_actionConvertToPath->setEnabled(haveConvertibleShapes);
}
void KoPathTool::deactivate()
{
Q_D(KoToolBase);
m_shapeFillResourceConnector.disconnect();
m_canvasConnections.clear();
m_pointSelection.clear();
m_pointSelection.setSelectedShapes(QList());
delete m_activeHandle;
m_activeHandle = 0;
delete m_activeSegment;
m_activeSegment = 0;
delete m_currentStrategy;
m_currentStrategy = 0;
d->canvas->snapGuide()->reset();
+ disconnect(m_actionCurvePoint, 0, this, 0);
+ disconnect(m_actionLinePoint, 0, this, 0);
+ disconnect(m_actionLineSegment, 0, this, 0);
+ disconnect(m_actionCurveSegment, 0, this, 0);
+ disconnect(m_actionAddPoint, 0, this, 0);
+ disconnect(m_actionRemovePoint, 0, this, 0);
+ disconnect(m_actionBreakPoint, 0, this, 0);
+ disconnect(m_actionBreakSegment, 0, this, 0);
+ disconnect(m_actionJoinSegment, 0, this, 0);
+ disconnect(m_actionMergePoints, 0, this, 0);
+ disconnect(m_actionConvertToPath, 0, this, 0);
+ disconnect(m_points, 0, this, 0);
+ disconnect(&m_pointSelection, 0, this, 0);
+
KoToolBase::deactivate();
}
void KoPathTool::documentResourceChanged(int key, const QVariant & res)
{
if (key == KoDocumentResourceManager::HandleRadius) {
int oldHandleRadius = m_handleRadius;
m_handleRadius = res.toUInt();
// repaint with the bigger of old and new handle radius
int maxRadius = qMax(m_handleRadius, oldHandleRadius);
Q_FOREACH (KoPathShape *shape, m_pointSelection.selectedShapes()) {
QRectF controlPointRect = shape->absoluteTransformation(0).map(shape->outline()).controlPointRect();
repaint(controlPointRect.adjusted(-maxRadius, -maxRadius, maxRadius, maxRadius));
}
}
}
void KoPathTool::pointSelectionChanged()
{
Q_D(KoToolBase);
updateActions();
d->canvas->snapGuide()->setIgnoredPathPoints(m_pointSelection.selectedPoints().toList());
emit selectionChanged(m_pointSelection.hasSelection());
}
void KoPathTool::repaint(const QRectF &repaintRect)
{
Q_D(KoToolBase);
//debugFlake <<"KoPathTool::repaint(" << repaintRect <<")" << m_handleRadius;
// widen border to take antialiasing into account
qreal radius = m_handleRadius + 1;
d->canvas->updateCanvas(repaintRect.adjusted(-radius, -radius, radius, radius));
}
namespace {
void addActionsGroupIfEnabled(QMenu *menu, QAction *a1, QAction *a2)
{
if (a1->isEnabled() || a2->isEnabled()) {
menu->addAction(a1);
menu->addAction(a2);
menu->addSeparator();
}
}
void addActionsGroupIfEnabled(QMenu *menu, QAction *a1, QAction *a2, QAction *a3)
{
if (a1->isEnabled() || a2->isEnabled()) {
menu->addAction(a1);
menu->addAction(a2);
menu->addAction(a3);
menu->addSeparator();
}
}
}
QMenu *KoPathTool::popupActionsMenu()
{
if (m_activeHandle) {
m_activeHandle->trySelectHandle();
}
if (m_activeSegment && m_activeSegment->isValid()) {
KoPathShape *shape = m_activeSegment->path;
KoPathSegment segment = shape->segmentByIndex(shape->pathPointIndex(m_activeSegment->segmentStart));
m_pointSelection.add(segment.first(), true);
m_pointSelection.add(segment.second(), false);
}
if (m_contextMenu) {
m_contextMenu->clear();
addActionsGroupIfEnabled(m_contextMenu.data(),
m_actionPathPointCorner,
m_actionPathPointSmooth,
m_actionPathPointSymmetric);
addActionsGroupIfEnabled(m_contextMenu.data(),
m_actionCurvePoint,
m_actionLinePoint);
addActionsGroupIfEnabled(m_contextMenu.data(),
m_actionAddPoint,
m_actionRemovePoint);
addActionsGroupIfEnabled(m_contextMenu.data(),
m_actionLineSegment,
m_actionCurveSegment);
addActionsGroupIfEnabled(m_contextMenu.data(),
m_actionBreakPoint,
m_actionBreakSegment);
addActionsGroupIfEnabled(m_contextMenu.data(),
m_actionJoinSegment,
m_actionMergePoints);
m_contextMenu->addAction(m_actionConvertToPath);
m_contextMenu->addSeparator();
}
return m_contextMenu.data();
}
void KoPathTool::deleteSelection()
{
removePoints();
}
KoToolSelection * KoPathTool::selection()
{
return &m_pointSelection;
}
void KoPathTool::requestUndoDuringStroke()
{
// noop!
}
void KoPathTool::requestStrokeCancellation()
{
explicitUserStrokeEndRequest();
}
void KoPathTool::requestStrokeEnd()
{
// noop!
}
void KoPathTool::explicitUserStrokeEndRequest()
{
if (m_activatedTemporarily) {
emit done();
}
}
diff --git a/libs/flake/tools/KoPathTool.h b/libs/flake/tools/KoPathTool.h
index 2ad56b0487..9e6757ed8b 100644
--- a/libs/flake/tools/KoPathTool.h
+++ b/libs/flake/tools/KoPathTool.h
@@ -1,161 +1,162 @@
/* This file is part of the KDE project
* Copyright (C) 2006-2012 Jan Hambrecht
* Copyright (C) 2006,2007 Thorsten Zachmann
* Copyright (C) 2007 Thomas Zander
* Copyright (C) 2007 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.
*/
#ifndef KOPATHTOOL_H
#define KOPATHTOOL_H
#include "KoPathShape.h"
#include "KoToolBase.h"
#include "KoPathToolSelection.h"
#include "kis_signal_auto_connection.h"
#include
#include
#include
+class QActionGroup;
class QButtonGroup;
class KoCanvasBase;
class KoInteractionStrategy;
class KoPathToolHandle;
class KoParameterShape;
class KUndo2Command;
class QAction;
class QMenu;
/// The tool for editing a KoPathShape or a KoParameterShape.
/// See KoCreatePathTool for code handling the initial path creation.
class KRITAFLAKE_EXPORT KoPathTool : public KoToolBase
{
Q_OBJECT
public:
explicit KoPathTool(KoCanvasBase *canvas);
~KoPathTool() override;
void paint(QPainter &painter, const KoViewConverter &converter) override;
void repaintDecorations() override;
void mousePressEvent(KoPointerEvent *event) override;
void mouseMoveEvent(KoPointerEvent *event) override;
void mouseReleaseEvent(KoPointerEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
void keyReleaseEvent(QKeyEvent *event) override;
void mouseDoubleClickEvent(KoPointerEvent *event) override;
void activate(ToolActivation activation, const QSet &shapes) override;
void deactivate() override;
void deleteSelection() override;
KoToolSelection* selection() override;
void requestUndoDuringStroke() override;
void requestStrokeCancellation() override;
void requestStrokeEnd() override;
void explicitUserStrokeEndRequest() override;
/// repaints the specified rect
void repaint(const QRectF &repaintRect);
QMenu* popupActionsMenu() override;
// for KoPathToolSelection
void notifyPathPointsChanged(KoPathShape *shape);
public Q_SLOTS:
void documentResourceChanged(int key, const QVariant & res) override;
Q_SIGNALS:
void typeChanged(int types);
void singleShapeChanged(KoPathShape* path);
protected:
/// reimplemented
QList > createOptionWidgets() override;
private:
struct PathSegment;
void updateOptionsWidget();
PathSegment* segmentAtPoint(const QPointF &point);
private Q_SLOTS:
void pointTypeChanged(QAction *type);
void insertPoints();
void removePoints();
void segmentToLine();
void segmentToCurve();
void convertToPath();
void joinPoints();
void mergePoints();
void breakAtPoint();
void breakAtSegment();
void pointSelectionChanged();
void updateActions();
void pointToLine();
void pointToCurve();
void slotSelectionChanged();
private:
void clearActivePointSelectionReferences();
void initializeWithShapes(const QList shapes);
KUndo2Command* createPointToCurveCommand(const QList &points);
void repaintSegment(PathSegment *pathSegment);
void mergePointsImpl(bool doJoin);
protected:
KoPathToolSelection m_pointSelection; ///< the point selection
QCursor m_selectCursor;
private:
KoPathToolHandle * m_activeHandle; ///< the currently active handle
int m_handleRadius; ///< the radius of the control point handles
uint m_grabSensitivity; ///< the grab sensitivity
QPointF m_lastPoint; ///< needed for interaction strategy
PathSegment *m_activeSegment;
// make a frind so that it can test private member/methods
friend class TestPathTool;
KoInteractionStrategy *m_currentStrategy; ///< the rubber selection strategy
QButtonGroup *m_pointTypeGroup;
-
+ QActionGroup *m_points;
QAction *m_actionPathPointCorner;
QAction *m_actionPathPointSmooth;
QAction *m_actionPathPointSymmetric;
QAction *m_actionCurvePoint;
QAction *m_actionLinePoint;
QAction *m_actionLineSegment;
QAction *m_actionCurveSegment;
QAction *m_actionAddPoint;
QAction *m_actionRemovePoint;
QAction *m_actionBreakPoint;
QAction *m_actionBreakSegment;
QAction *m_actionJoinSegment;
QAction *m_actionMergePoints;
QAction *m_actionConvertToPath;
QCursor m_moveCursor;
bool m_activatedTemporarily;
QScopedPointer m_contextMenu;
KisSignalAutoConnectionsStore m_canvasConnections;
KoShapeFillResourceConnector m_shapeFillResourceConnector;
Q_DECLARE_PRIVATE(KoToolBase)
};
#endif
diff --git a/libs/flake/tools/KoPathToolFactory.cpp b/libs/flake/tools/KoPathToolFactory.cpp
index aa82b36d16..f2e0527931 100644
--- a/libs/flake/tools/KoPathToolFactory.cpp
+++ b/libs/flake/tools/KoPathToolFactory.cpp
@@ -1,44 +1,66 @@
/* This file is part of the KDE project
* Copyright (C) 2006 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 "KoPathToolFactory.h"
#include "KoPathTool.h"
#include "KoPathShape.h"
+#include
#include
#include
KoPathToolFactory::KoPathToolFactory()
: KoToolFactoryBase("PathTool")
{
setToolTip(i18n("Edit Shapes Tool"));
setSection(mainToolType());
setIconName(koIconNameCStr("shape_handling"));
setPriority(2);
setActivationShapeId("flake/always,KoPathShape");
}
KoPathToolFactory::~KoPathToolFactory()
{
}
KoToolBase * KoPathToolFactory::createTool(KoCanvasBase *canvas)
{
return new KoPathTool(canvas);
}
+
+QList KoPathToolFactory::createActionsImpl()
+{
+ KisActionRegistry *actionRegistry = KisActionRegistry::instance();
+ QList actions;
+ actions << actionRegistry->makeQAction("pathpoint-corner");
+ actions << actionRegistry->makeQAction("pathpoint-smooth");
+ actions << actionRegistry->makeQAction("pathpoint-symmetric");
+ actions << actionRegistry->makeQAction("pathpoint-curve");
+ actions << actionRegistry->makeQAction("pathpoint-line");
+ actions << actionRegistry->makeQAction("pathsegment-line");
+ actions << actionRegistry->makeQAction("pathsegment-curve");
+ actions << actionRegistry->makeQAction("pathpoint-insert");
+ actions << actionRegistry->makeQAction("pathpoint-remove");
+ actions << actionRegistry->makeQAction("path-break-point");
+ actions << actionRegistry->makeQAction("path-break-segment");
+ actions << actionRegistry->makeQAction("pathpoint-join");
+ actions << actionRegistry->makeQAction("pathpoint-merge");
+ actions << actionRegistry->makeQAction("convert-to-path");
+ return actions;
+}
diff --git a/libs/flake/tools/KoPathToolFactory.h b/libs/flake/tools/KoPathToolFactory.h
index e5b6804e7c..55b72d3abb 100644
--- a/libs/flake/tools/KoPathToolFactory.h
+++ b/libs/flake/tools/KoPathToolFactory.h
@@ -1,35 +1,36 @@
/* This file is part of the KDE project
* Copyright (C) 2006 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 KOPATHTOOLFACTORY_H
#define KOPATHTOOLFACTORY_H
#include
/// Factory for the KoPathTool
class KoPathToolFactory : public KoToolFactoryBase
{
public:
KoPathToolFactory();
~KoPathToolFactory() override;
KoToolBase *createTool(KoCanvasBase *canvas) override;
+ QList createActionsImpl() override;
};
#endif
diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt
index b55f9c9d00..a86eaf94e8 100644
--- a/libs/ui/CMakeLists.txt
+++ b/libs/ui/CMakeLists.txt
@@ -1,592 +1,594 @@
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/qtlockedfile
${EXIV2_INCLUDE_DIR}
)
include_directories(SYSTEM
${EIGEN3_INCLUDE_DIR}
${OCIO_INCLUDE_DIR}
)
add_subdirectory( tests )
if (APPLE)
find_library(FOUNDATION_LIBRARY Foundation)
find_library(APPKIT_LIBRARY AppKit)
endif ()
set(kritaui_LIB_SRCS
canvas/kis_canvas_widget_base.cpp
canvas/kis_canvas2.cpp
canvas/kis_canvas_updates_compressor.cpp
canvas/kis_canvas_controller.cpp
canvas/kis_paintop_transformation_connector.cpp
canvas/kis_display_color_converter.cpp
canvas/kis_display_filter.cpp
canvas/kis_exposure_gamma_correction_interface.cpp
canvas/kis_tool_proxy.cpp
canvas/kis_canvas_decoration.cc
canvas/kis_coordinates_converter.cpp
canvas/kis_grid_manager.cpp
canvas/kis_grid_decoration.cpp
canvas/kis_grid_config.cpp
canvas/kis_prescaled_projection.cpp
canvas/kis_qpainter_canvas.cpp
canvas/kis_projection_backend.cpp
canvas/kis_update_info.cpp
canvas/kis_image_patch.cpp
canvas/kis_image_pyramid.cpp
canvas/kis_infinity_manager.cpp
canvas/kis_change_guides_command.cpp
canvas/kis_guides_decoration.cpp
canvas/kis_guides_manager.cpp
canvas/kis_guides_config.cpp
canvas/kis_snap_config.cpp
canvas/kis_snap_line_strategy.cpp
canvas/KisSnapPointStrategy.cpp
dialogs/kis_about_application.cpp
dialogs/kis_dlg_adj_layer_props.cc
dialogs/kis_dlg_adjustment_layer.cc
dialogs/kis_dlg_filter.cpp
dialogs/kis_dlg_generator_layer.cpp
dialogs/kis_dlg_file_layer.cpp
dialogs/kis_dlg_filter.cpp
dialogs/kis_dlg_stroke_selection_properties.cpp
dialogs/kis_dlg_image_properties.cc
dialogs/kis_dlg_layer_properties.cc
dialogs/kis_dlg_preferences.cc
dialogs/slider_and_spin_box_sync.cpp
dialogs/kis_dlg_blacklist_cleanup.cpp
dialogs/kis_dlg_layer_style.cpp
dialogs/kis_dlg_png_import.cpp
dialogs/kis_dlg_import_image_sequence.cpp
dialogs/kis_delayed_save_dialog.cpp
dialogs/KisSessionManagerDialog.cpp
dialogs/KisNewWindowLayoutDialog.cpp
flake/kis_node_dummies_graph.cpp
flake/kis_dummies_facade_base.cpp
flake/kis_dummies_facade.cpp
flake/kis_node_shapes_graph.cpp
flake/kis_node_shape.cpp
flake/kis_shape_controller.cpp
flake/kis_shape_layer.cc
flake/kis_shape_layer_canvas.cpp
flake/kis_shape_selection.cpp
flake/kis_shape_selection_canvas.cpp
flake/kis_shape_selection_model.cpp
flake/kis_take_all_shapes_command.cpp
brushhud/kis_uniform_paintop_property_widget.cpp
brushhud/kis_brush_hud.cpp
brushhud/kis_round_hud_button.cpp
brushhud/kis_dlg_brush_hud_config.cpp
brushhud/kis_brush_hud_properties_list.cpp
brushhud/kis_brush_hud_properties_config.cpp
kis_aspect_ratio_locker.cpp
kis_autogradient.cc
kis_bookmarked_configurations_editor.cc
kis_bookmarked_configurations_model.cc
kis_bookmarked_filter_configurations_model.cc
KisPaintopPropertiesBase.cpp
kis_canvas_resource_provider.cpp
kis_derived_resources.cpp
kis_categories_mapper.cpp
kis_categorized_list_model.cpp
kis_categorized_item_delegate.cpp
kis_clipboard.cc
kis_config.cc
kis_control_frame.cpp
kis_composite_ops_model.cc
kis_paint_ops_model.cpp
kis_cursor.cc
kis_cursor_cache.cpp
kis_custom_pattern.cc
kis_file_layer.cpp
kis_change_file_layer_command.h
kis_safe_document_loader.cpp
kis_splash_screen.cpp
kis_filter_manager.cc
kis_filters_model.cc
kis_histogram_view.cc
KisImageBarrierLockerWithFeedback.cpp
kis_image_manager.cc
kis_image_view_converter.cpp
kis_import_catcher.cc
kis_layer_manager.cc
kis_mask_manager.cc
kis_mimedata.cpp
kis_node_commands_adapter.cpp
kis_node_manager.cpp
kis_node_juggler_compressed.cpp
kis_node_selection_adapter.cpp
kis_node_insertion_adapter.cpp
KisNodeDisplayModeAdapter.cpp
kis_node_model.cpp
kis_node_filter_proxy_model.cpp
kis_model_index_converter_base.cpp
kis_model_index_converter.cpp
kis_model_index_converter_show_all.cpp
kis_painting_assistant.cc
kis_painting_assistants_decoration.cpp
KisDecorationsManager.cpp
kis_paintop_box.cc
kis_paintop_option.cpp
kis_paintop_options_model.cpp
kis_paintop_settings_widget.cpp
kis_popup_palette.cpp
kis_png_converter.cpp
kis_preference_set_registry.cpp
KisResourceServerProvider.cpp
KisResourceBundleServerProvider.cpp
KisSelectedShapesProxy.cpp
kis_selection_decoration.cc
kis_selection_manager.cc
KisSelectionActionsAdapter.cpp
kis_statusbar.cc
kis_zoom_manager.cc
kis_favorite_resource_manager.cpp
kis_workspace_resource.cpp
kis_action.cpp
kis_action_manager.cpp
KisActionPlugin.cpp
kis_canvas_controls_manager.cpp
kis_tooltip_manager.cpp
kis_multinode_property.cpp
kis_stopgradient_editor.cpp
KisWelcomePageWidget.cpp
kisexiv2/kis_exif_io.cpp
kisexiv2/kis_exiv2.cpp
kisexiv2/kis_iptc_io.cpp
kisexiv2/kis_xmp_io.cpp
opengl/kis_opengl.cpp
opengl/kis_opengl_canvas2.cpp
opengl/kis_opengl_canvas_debugger.cpp
opengl/kis_opengl_image_textures.cpp
opengl/kis_texture_tile.cpp
opengl/kis_opengl_shader_loader.cpp
opengl/kis_texture_tile_info_pool.cpp
opengl/KisOpenGLUpdateInfoBuilder.cpp
kis_fps_decoration.cpp
+
tool/KisToolChangesTracker.cpp
tool/KisToolChangesTrackerData.cpp
tool/kis_selection_tool_helper.cpp
tool/kis_selection_tool_config_widget_helper.cpp
tool/kis_rectangle_constraint_widget.cpp
tool/kis_shape_tool_helper.cpp
tool/kis_tool.cc
tool/kis_delegated_tool_policies.cpp
tool/kis_tool_freehand.cc
tool/kis_speed_smoother.cpp
tool/kis_painting_information_builder.cpp
tool/kis_stabilized_events_sampler.cpp
tool/kis_tool_freehand_helper.cpp
tool/kis_tool_multihand_helper.cpp
tool/kis_figure_painting_tool_helper.cpp
tool/kis_tool_paint.cc
tool/kis_tool_shape.cc
tool/kis_tool_ellipse_base.cpp
tool/kis_tool_rectangle_base.cpp
tool/kis_tool_polyline_base.cpp
tool/kis_tool_utils.cpp
tool/kis_resources_snapshot.cpp
tool/kis_smoothing_options.cpp
tool/KisStabilizerDelayedPaintHelper.cpp
tool/KisStrokeSpeedMonitor.cpp
tool/strokes/freehand_stroke.cpp
tool/strokes/KisStrokeEfficiencyMeasurer.cpp
tool/strokes/kis_painter_based_stroke_strategy.cpp
tool/strokes/kis_filter_stroke_strategy.cpp
tool/strokes/kis_color_picker_stroke_strategy.cpp
tool/strokes/KisFreehandStrokeInfo.cpp
tool/strokes/KisMaskedFreehandStrokePainter.cpp
tool/strokes/KisMaskingBrushRenderer.cpp
tool/strokes/KisMaskingBrushCompositeOpFactory.cpp
tool/strokes/move_stroke_strategy.cpp
-
+ tool/KisSelectionToolFactoryBase.cpp
+ tool/KisToolPaintFactoryBase.cpp
widgets/kis_cmb_composite.cc
widgets/kis_cmb_contour.cpp
widgets/kis_cmb_gradient.cpp
widgets/kis_paintop_list_widget.cpp
widgets/kis_cmb_idlist.cc
widgets/kis_color_space_selector.cc
widgets/kis_advanced_color_space_selector.cc
widgets/kis_cie_tongue_widget.cpp
widgets/kis_tone_curve_widget.cpp
widgets/kis_curve_widget.cpp
widgets/kis_custom_image_widget.cc
widgets/kis_image_from_clipboard_widget.cpp
widgets/kis_double_widget.cc
widgets/kis_filter_selector_widget.cc
widgets/kis_gradient_chooser.cc
widgets/kis_iconwidget.cc
widgets/kis_mask_widgets.cpp
widgets/kis_meta_data_merge_strategy_chooser_widget.cc
widgets/kis_multi_bool_filter_widget.cc
widgets/kis_multi_double_filter_widget.cc
widgets/kis_multi_integer_filter_widget.cc
widgets/kis_multipliers_double_slider_spinbox.cpp
widgets/kis_paintop_presets_popup.cpp
widgets/kis_tool_options_popup.cpp
widgets/kis_paintop_presets_chooser_popup.cpp
widgets/kis_paintop_presets_save.cpp
widgets/kis_paintop_preset_icon_library.cpp
widgets/kis_pattern_chooser.cc
widgets/kis_preset_chooser.cpp
widgets/kis_progress_widget.cpp
widgets/kis_selection_options.cc
widgets/kis_scratch_pad.cpp
widgets/kis_scratch_pad_event_filter.cpp
widgets/kis_preset_selector_strip.cpp
widgets/kis_slider_spin_box.cpp
widgets/KisSelectionPropertySlider.cpp
widgets/kis_size_group.cpp
widgets/kis_size_group_p.cpp
widgets/kis_wdg_generator.cpp
widgets/kis_workspace_chooser.cpp
widgets/kis_categorized_list_view.cpp
widgets/kis_widget_chooser.cpp
widgets/kis_tool_button.cpp
widgets/kis_floating_message.cpp
widgets/kis_lod_availability_widget.cpp
widgets/kis_color_label_selector_widget.cpp
widgets/kis_color_filter_combo.cpp
widgets/kis_elided_label.cpp
widgets/kis_stopgradient_slider_widget.cpp
widgets/kis_preset_live_preview_view.cpp
widgets/KisScreenColorPicker.cpp
widgets/KoDualColorButton.cpp
widgets/KoStrokeConfigWidget.cpp
widgets/KoFillConfigWidget.cpp
KisPaletteEditor.cpp
dialogs/KisDlgPaletteEditor.cpp
widgets/KisNewsWidget.cpp
widgets/KisGamutMaskToolbar.cpp
utils/kis_document_aware_spin_box_unit_manager.cpp
input/kis_input_manager.cpp
input/kis_input_manager_p.cpp
input/kis_extended_modifiers_mapper.cpp
input/kis_abstract_input_action.cpp
input/kis_tool_invocation_action.cpp
input/kis_pan_action.cpp
input/kis_alternate_invocation_action.cpp
input/kis_rotate_canvas_action.cpp
input/kis_zoom_action.cpp
input/kis_change_frame_action.cpp
input/kis_gamma_exposure_action.cpp
input/kis_show_palette_action.cpp
input/kis_change_primary_setting_action.cpp
input/kis_abstract_shortcut.cpp
input/kis_native_gesture_shortcut.cpp
input/kis_single_action_shortcut.cpp
input/kis_stroke_shortcut.cpp
input/kis_shortcut_matcher.cpp
input/kis_select_layer_action.cpp
input/KisQtWidgetsTweaker.cpp
input/KisInputActionGroup.cpp
operations/kis_operation.cpp
operations/kis_operation_configuration.cpp
operations/kis_operation_registry.cpp
operations/kis_operation_ui_factory.cpp
operations/kis_operation_ui_widget.cpp
operations/kis_filter_selection_operation.cpp
actions/kis_selection_action_factories.cpp
actions/KisPasteActionFactory.cpp
actions/KisTransformToolActivationCommand.cpp
input/kis_touch_shortcut.cpp
kis_document_undo_store.cpp
kis_gui_context_command.cpp
kis_gui_context_command_p.cpp
input/kis_tablet_debugger.cpp
input/kis_input_profile_manager.cpp
input/kis_input_profile.cpp
input/kis_shortcut_configuration.cpp
input/config/kis_input_configuration_page.cpp
input/config/kis_edit_profiles_dialog.cpp
input/config/kis_input_profile_model.cpp
input/config/kis_input_configuration_page_item.cpp
input/config/kis_action_shortcuts_model.cpp
input/config/kis_input_type_delegate.cpp
input/config/kis_input_mode_delegate.cpp
input/config/kis_input_button.cpp
input/config/kis_input_editor_delegate.cpp
input/config/kis_mouse_input_editor.cpp
input/config/kis_wheel_input_editor.cpp
input/config/kis_key_input_editor.cpp
processing/fill_processing_visitor.cpp
kis_asl_layer_style_serializer.cpp
kis_psd_layer_style_resource.cpp
canvas/kis_mirror_axis.cpp
kis_abstract_perspective_grid.cpp
KisApplication.cpp
KisAutoSaveRecoveryDialog.cpp
KisDetailsPane.cpp
KisDocument.cpp
KisCloneDocumentStroke.cpp
KisNodeDelegate.cpp
kis_node_view_visibility_delegate.cpp
KisNodeToolTip.cpp
KisNodeView.cpp
kis_node_view_color_scheme.cpp
KisImportExportFilter.cpp
KisFilterEntry.cpp
KisImportExportManager.cpp
KisImportExportUtils.cpp
kis_async_action_feedback.cpp
KisMainWindow.cpp
KisOpenPane.cpp
KisPart.cpp
KisPrintJob.cpp
KisTemplate.cpp
KisTemplateCreateDia.cpp
KisTemplateGroup.cpp
KisTemplates.cpp
KisTemplatesPane.cpp
KisTemplateTree.cpp
KisUndoActionsUpdateManager.cpp
KisView.cpp
thememanager.cpp
kis_mainwindow_observer.cpp
KisViewManager.cpp
kis_mirror_manager.cpp
qtlockedfile/qtlockedfile.cpp
qtsingleapplication/qtlocalpeer.cpp
qtsingleapplication/qtsingleapplication.cpp
KisResourceBundle.cpp
KisResourceBundleManifest.cpp
kis_md5_generator.cpp
KisApplicationArguments.cpp
KisNetworkAccessManager.cpp
KisMultiFeedRSSModel.cpp
KisRemoteFileFetcher.cpp
KisSaveGroupVisitor.cpp
KisWindowLayoutResource.cpp
KisWindowLayoutManager.cpp
KisSessionResource.cpp
KisReferenceImagesDecoration.cpp
KisReferenceImage.cpp
flake/KisReferenceImagesLayer.cpp
flake/KisReferenceImagesLayer.h
)
if(WIN32)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
input/wintab/kis_tablet_support_win.cpp
input/wintab/kis_screen_size_choice_dialog.cpp
qtlockedfile/qtlockedfile_win.cpp
input/wintab/kis_tablet_support_win8.cpp
opengl/kis_opengl_win.cpp
)
endif()
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
kis_animation_frame_cache.cpp
kis_animation_cache_populator.cpp
KisAsyncAnimationRendererBase.cpp
KisAsyncAnimationCacheRenderer.cpp
KisAsyncAnimationFramesSavingRenderer.cpp
dialogs/KisAsyncAnimationRenderDialogBase.cpp
dialogs/KisAsyncAnimationCacheRenderDialog.cpp
dialogs/KisAsyncAnimationFramesSaveDialog.cpp
canvas/kis_animation_player.cpp
kis_animation_importer.cpp
KisSyncedAudioPlayback.cpp
KisFrameDataSerializer.cpp
KisFrameCacheStore.cpp
KisFrameCacheSwapper.cpp
KisAbstractFrameCacheSwapper.cpp
KisInMemoryFrameCacheSwapper.cpp
input/wintab/drawpile_tablettester/tablettester.cpp
input/wintab/drawpile_tablettester/tablettest.cpp
)
if (UNIX)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
qtlockedfile/qtlockedfile_unix.cpp
)
if(NOT USE_QT_XCB)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
input/wintab/kis_tablet_support.cpp
)
endif()
if(NOT APPLE AND NOT USE_QT_XCB)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
input/wintab/qxcbconnection_xi2.cpp
input/wintab/qxcbconnection.cpp
input/wintab/kis_xi2_event_filter.cpp
)
endif()
endif()
if(APPLE)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
osx.mm
)
endif()
ki18n_wrap_ui(kritaui_LIB_SRCS
widgets/KoFillConfigWidget.ui
widgets/KoStrokeConfigWidget.ui
forms/wdgdlgpngimport.ui
forms/wdgfullscreensettings.ui
forms/wdgautogradient.ui
forms/wdggeneralsettings.ui
forms/wdgperformancesettings.ui
forms/wdggenerators.ui
forms/wdgbookmarkedconfigurationseditor.ui
forms/wdgapplyprofile.ui
forms/wdgcustompattern.ui
forms/wdglayerproperties.ui
forms/wdgcolorsettings.ui
forms/wdgtabletsettings.ui
forms/wdgcolorspaceselector.ui
forms/wdgcolorspaceselectoradvanced.ui
forms/wdgdisplaysettings.ui
forms/kis_previewwidgetbase.ui
forms/kis_matrix_widget.ui
forms/wdgselectionoptions.ui
forms/wdggeometryoptions.ui
forms/wdgnewimage.ui
forms/wdgimageproperties.ui
forms/wdgmaskfromselection.ui
forms/wdgmasksource.ui
forms/wdgfilterdialog.ui
forms/wdgmetadatamergestrategychooser.ui
forms/wdgpaintoppresets.ui
forms/wdgpaintopsettings.ui
forms/wdgdlggeneratorlayer.ui
forms/wdgdlgfilelayer.ui
forms/wdgfilterselector.ui
forms/wdgfilternodecreation.ui
forms/wdgmultipliersdoublesliderspinbox.ui
forms/wdgnodequerypatheditor.ui
forms/wdgpresetselectorstrip.ui
forms/wdgsavebrushpreset.ui
forms/wdgpreseticonlibrary.ui
forms/wdgdlgblacklistcleanup.ui
forms/wdgrectangleconstraints.ui
forms/wdgimportimagesequence.ui
forms/wdgstrokeselectionproperties.ui
forms/KisDetailsPaneBase.ui
forms/KisOpenPaneBase.ui
forms/wdgstopgradienteditor.ui
forms/wdgsessionmanager.ui
forms/wdgnewwindowlayout.ui
forms/KisWelcomePage.ui
forms/WdgDlgPaletteEditor.ui
forms/KisNewsPage.ui
forms/wdgGamutMaskToolbar.ui
brushhud/kis_dlg_brush_hud_config.ui
dialogs/kis_delayed_save_dialog.ui
input/config/kis_input_configuration_page.ui
input/config/kis_edit_profiles_dialog.ui
input/config/kis_input_configuration_page_item.ui
input/config/kis_mouse_input_editor.ui
input/config/kis_wheel_input_editor.ui
input/config/kis_key_input_editor.ui
layerstyles/wdgBevelAndEmboss.ui
layerstyles/wdgblendingoptions.ui
layerstyles/WdgColorOverlay.ui
layerstyles/wdgContour.ui
layerstyles/wdgdropshadow.ui
layerstyles/WdgGradientOverlay.ui
layerstyles/wdgInnerGlow.ui
layerstyles/wdglayerstyles.ui
layerstyles/WdgPatternOverlay.ui
layerstyles/WdgSatin.ui
layerstyles/WdgStroke.ui
layerstyles/wdgstylesselector.ui
layerstyles/wdgTexture.ui
wdgsplash.ui
input/wintab/kis_screen_size_choice_dialog.ui
input/wintab/drawpile_tablettester/tablettest.ui
)
QT5_WRAP_CPP(kritaui_HEADERS_MOC KisNodePropertyAction_p.h)
add_library(kritaui SHARED ${kritaui_HEADERS_MOC} ${kritaui_LIB_SRCS} )
generate_export_header(kritaui BASE_NAME kritaui)
target_link_libraries(kritaui KF5::CoreAddons KF5::Completion KF5::I18n KF5::ItemViews Qt5::Network
kritaimpex kritacolor kritaimage kritalibbrush kritawidgets kritawidgetutils ${PNG_LIBRARIES} ${EXIV2_LIBRARIES}
)
if (HAVE_QT_MULTIMEDIA)
target_link_libraries(kritaui Qt5::Multimedia)
endif()
if (NOT WIN32 AND NOT APPLE)
target_link_libraries(kritaui ${X11_X11_LIB}
${X11_Xinput_LIB}
${XCB_LIBRARIES})
endif()
if(APPLE)
target_link_libraries(kritaui ${FOUNDATION_LIBRARY})
target_link_libraries(kritaui ${APPKIT_LIBRARY})
endif ()
target_link_libraries(kritaui ${OPENEXR_LIBRARIES})
# Add VSync disable workaround
if(NOT WIN32 AND NOT APPLE)
target_link_libraries(kritaui ${CMAKE_DL_LIBS} Qt5::X11Extras)
endif()
if(X11_FOUND)
target_link_libraries(kritaui Qt5::X11Extras ${X11_LIBRARIES})
endif()
target_include_directories(kritaui
PUBLIC
$
$
$
$
$
$
$
)
set_target_properties(kritaui
PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION}
)
install(TARGETS kritaui ${INSTALL_TARGETS_DEFAULT_ARGS})
if (APPLE)
install(FILES osx.stylesheet DESTINATION ${DATA_INSTALL_DIR}/krita)
endif ()
diff --git a/libs/ui/KisMainWindow.cpp b/libs/ui/KisMainWindow.cpp
index 87e3ca96d3..8fa82fa3e7 100644
--- a/libs/ui/KisMainWindow.cpp
+++ b/libs/ui/KisMainWindow.cpp
@@ -1,2663 +1,2665 @@
/* This file is part of the KDE project
Copyright (C) 1998, 1999 Torben Weis
Copyright (C) 2000-2006 David Faure
Copyright (C) 2007, 2009 Thomas zander
Copyright (C) 2010 Benjamin Port
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 "KisMainWindow.h"
#include
// qt includes
#include
#include
#include
#include
#include
#include
#include