diff --git a/krita/pics/svg/dark_warning.svg b/krita/pics/svg/dark_warning.svg
new file mode 100644
index 0000000000..fe07e782d7
--- /dev/null
+++ b/krita/pics/svg/dark_warning.svg
@@ -0,0 +1,145 @@
+
+
+
+
diff --git a/krita/pics/svg/light_warning.svg b/krita/pics/svg/light_warning.svg
new file mode 100644
index 0000000000..c9e7efd599
--- /dev/null
+++ b/krita/pics/svg/light_warning.svg
@@ -0,0 +1,145 @@
+
+
+
+
diff --git a/krita/pics/svg/svg-icons.qrc b/krita/pics/svg/svg-icons.qrc
index 312f78b568..d93584993c 100644
--- a/krita/pics/svg/svg-icons.qrc
+++ b/krita/pics/svg/svg-icons.qrc
@@ -1,150 +1,152 @@
broken-preset.svgzdark_addblankframe.svgdark_addcolor.svgdark_addduplicateframe.svgdark_deletekeyframe.svgdark_docker_lock_a.svgdark_docker_lock_b.svgdark_layer-locked.svgdark_layer-unlocked.svgdark_nextframe.svgdark_nextkeyframe.svgdark_lastframe.svgdark_prevkeyframe.svgdark_firstframe.svgdark_pallete_librarysvg.svgdark_passthrough-disabled.svgdark_passthrough-enabled.svgdark_prevframe.svgdark_selection-mode_ants.svgdark_selection-mode_invisible.svgdark_selection-mode_mask.svgdark_transparency-disabled.svgdark_transparency-enabled.svgdark_trim-to-image.svg
+ dark_warning.svgdelete.svgzlayer-style-disabled.svglayer-style-enabled.svglight_addblankframe.svglight_addcolor.svglight_addduplicateframe.svglight_deletekeyframe.svglight_docker_lock_a.svglight_docker_lock_b.svglight_layer-locked.svglight_layer-unlocked.svglight_nextframe.svglight_pallete_library.svglight_passthrough-disabled.svgzlight_passthrough-enabled.svgzlight_prevframe.svglight_nextkeyframe.svglight_lastframe.svglight_prevkeyframe.svglight_firstframe.svglight_selection-mode_ants.svglight_selection-mode_invisible.svglight_selection-mode_mask.svglight_timeline_keyframe.svglight_transparency-disabled.svglight_transparency-enabled.svglight_trim-to-image.svg
+ light_warning.svgpaintop_presets_disabled.svgzpaintop_settings_01.svgzselection-info.svgselection-mode_invisible.svgsvg-icons.qrctransparency-locked.svgtransparency-unlocked.svgworkspace-chooser.svglight_lazyframeOn.svglight_lazyframeOff.svgdark_lazyframeOn.svgdark_lazyframeOff.svgdark_mirror-view.svglight_mirror-view.svgdark_rotation-reset.svglight_rotation-reset.svglight_smoothing-basic.svglight_smoothing-no.svglight_smoothing-stabilizer.svglight_smoothing-weighted.svgdark_smoothing-basic.svgdark_smoothing-no.svgdark_smoothing-stabilizer.svgdark_smoothing-weighted.svglight_merge-layer-below.svgdark_merge-layer-below.svglight_rotate-canvas-left.svglight_rotate-canvas-right.svgdark_rotate-canvas-left.svgdark_rotate-canvas-right.svglight_gmic.svgdark_gmic.svglight_split-layer.svgdark_split-layer.svglight_color-to-alpha.svgdark_color-to-alpha.svglight_preset-switcher.svgdark_preset-switcher.svgdark_animation_play.svgdark_animation_stop.svgdark_dropframe.svgdark_droppedframes.svglight_animation_play.svglight_animation_stop.svglight_dropframe.svglight_droppedframes.svgdark_landscape.svgdark_portrait.svglight_landscape.svglight_portrait.svgdark_interpolation_constant.svgdark_interpolation_linear.svgdark_interpolation_bezier.svgdark_interpolation_sharp.svgdark_interpolation_smooth.svglight_interpolation_bezier.svglight_interpolation_constant.svglight_interpolation_linear.svglight_interpolation_sharp.svglight_interpolation_smooth.svgdark_audio-none.svgdark_audio-volume-high.svgdark_audio-volume-mute.svgdark_keyframe-add.svgdark_keyframe-remove.svgdark_zoom-fit.svgdark_zoom-horizontal.svgdark_zoom-vertical.svglight_audio-none.svglight_audio-volume-high.svglight_audio-volume-mute.svglight_keyframe-add.svglight_keyframe-remove.svglight_zoom-fit.svglight_zoom-horizontal.svglight_zoom-vertical.svgdark_showColoring.svgdark_showMarks.svgdark_showColoringOff.svgdark_showMarksOff.svgdark_updateColorize.svglight_showColoring.svglight_showMarks.svglight_showColoringOff.svglight_showMarksOff.svglight_updateColorize.svg
diff --git a/krita/pics/tools/SVG/16/dark_krita_tool_smart_patch.svg b/krita/pics/tools/SVG/16/dark_krita_tool_smart_patch.svg
index 749fb5a986..979467e945 100644
--- a/krita/pics/tools/SVG/16/dark_krita_tool_smart_patch.svg
+++ b/krita/pics/tools/SVG/16/dark_krita_tool_smart_patch.svg
@@ -1,102 +1,69 @@
\ No newline at end of file
+ sodipodi:nodetypes="ccccc" />
\ No newline at end of file
diff --git a/krita/pics/tools/SVG/16/light_krita_tool_smart_patch.svg b/krita/pics/tools/SVG/16/light_krita_tool_smart_patch.svg
index 5371092ed9..d909e83f61 100644
--- a/krita/pics/tools/SVG/16/light_krita_tool_smart_patch.svg
+++ b/krita/pics/tools/SVG/16/light_krita_tool_smart_patch.svg
@@ -1,102 +1,69 @@
\ No newline at end of file
+ sodipodi:nodetypes="ccccc" />
\ No newline at end of file
diff --git a/libs/flake/KoToolBase.cpp b/libs/flake/KoToolBase.cpp
index 8452ea12cd..0853b242f0 100644
--- a/libs/flake/KoToolBase.cpp
+++ b/libs/flake/KoToolBase.cpp
@@ -1,424 +1,429 @@
/* 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 "KoCanvasResourceManager.h"
#include "KoViewConverter.h"
#include "KoShapeController.h"
#include "KoShapeBasedDocumentBase.h"
#include "KoToolSelection.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 {
// qDebug() << "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);
}
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 cancells 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 dd7c45252d..e02735c23c 100644
--- a/libs/flake/KoToolBase.h
+++ b/libs/flake/KoToolBase.h
@@ -1,536 +1,544 @@
/* 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 KoShapeBasedDocumentBase;
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 reimplemnt.
* @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 currect 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 actived
* 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/KoToolProxy.cpp b/libs/flake/KoToolProxy.cpp
index e2772e324c..a697913637 100644
--- a/libs/flake/KoToolProxy.cpp
+++ b/libs/flake/KoToolProxy.cpp
@@ -1,475 +1,513 @@
/* This file is part of the KDE project
* Copyright (C) 2006-2007 Thomas Zander
* Copyright (c) 2006-2011 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 "KoToolProxy.h"
#include "KoToolProxy_p.h"
#include
#include
#include
-#include
#include
+#include
+#include
#include
#include
#include
#include
#include "KoToolBase.h"
#include "KoPointerEvent.h"
#include "KoInputDevice.h"
#include "KoToolManager_p.h"
#include "KoToolSelection.h"
#include "KoCanvasBase.h"
#include "KoCanvasController.h"
#include "KoShapeManager.h"
#include "KoSelection.h"
#include "KoShapeLayer.h"
#include "KoShapeRegistry.h"
#include "KoShapeController.h"
#include "KoOdf.h"
#include "KoViewConverter.h"
#include "KoShapeFactoryBase.h"
KoToolProxyPrivate::KoToolProxyPrivate(KoToolProxy *p)
: activeTool(0),
tabletPressed(false),
hasSelection(false),
controller(0),
parent(p)
{
scrollTimer.setInterval(100);
mouseLeaveWorkaround = false;
+ multiClickCount = 0;
}
void KoToolProxyPrivate::timeout() // Auto scroll the canvas
{
Q_ASSERT(controller);
QPoint offset = QPoint(controller->canvasOffsetX(), controller->canvasOffsetY());
QPoint origin = controller->canvas()->documentOrigin();
QPoint viewPoint = widgetScrollPoint - origin - offset;
QRectF mouseArea(viewPoint, QSizeF(10, 10));
mouseArea.setTopLeft(mouseArea.center());
controller->ensureVisible(mouseArea, true);
QPoint newOffset = QPoint(controller->canvasOffsetX(), controller->canvasOffsetY());
QPoint moved = offset - newOffset;
if (moved.isNull())
return;
widgetScrollPoint += moved;
QPointF documentPoint = parent->widgetToDocument(widgetScrollPoint);
QMouseEvent event(QEvent::MouseMove, widgetScrollPoint, Qt::LeftButton, Qt::LeftButton, 0);
KoPointerEvent ev(&event, documentPoint);
activeTool->mouseMoveEvent(&ev);
}
void KoToolProxyPrivate::checkAutoScroll(const KoPointerEvent &event)
{
if (controller == 0) return;
if (!activeTool) return;
if (!activeTool->wantsAutoScroll()) return;
if (!event.isAccepted()) return;
if (event.buttons() != Qt::LeftButton) return;
widgetScrollPoint = event.pos();
if (! scrollTimer.isActive())
scrollTimer.start();
}
void KoToolProxyPrivate::selectionChanged(bool newSelection)
{
if (hasSelection == newSelection)
return;
hasSelection = newSelection;
emit parent->selectionChanged(hasSelection);
}
bool KoToolProxyPrivate::isActiveLayerEditable()
{
if (!activeTool)
return false;
KoShapeManager * shapeManager = activeTool->canvas()->shapeManager();
KoShapeLayer * activeLayer = shapeManager->selection()->activeLayer();
if (activeLayer && !activeLayer->isEditable())
return false;
return true;
}
KoToolProxy::KoToolProxy(KoCanvasBase *canvas, QObject *parent)
: QObject(parent),
d(new KoToolProxyPrivate(this))
{
KoToolManager::instance()->priv()->registerToolProxy(this, canvas);
connect(&d->scrollTimer, SIGNAL(timeout()), this, SLOT(timeout()));
}
KoToolProxy::~KoToolProxy()
{
delete d;
}
void KoToolProxy::paint(QPainter &painter, const KoViewConverter &converter)
{
if (d->activeTool) d->activeTool->paint(painter, converter);
}
void KoToolProxy::repaintDecorations()
{
if (d->activeTool) d->activeTool->repaintDecorations();
}
QPointF KoToolProxy::widgetToDocument(const QPointF &widgetPoint) const
{
QPoint offset = QPoint(d->controller->canvasOffsetX(), d->controller->canvasOffsetY());
QPoint origin = d->controller->canvas()->documentOrigin();
QPointF viewPoint = widgetPoint.toPoint() - QPointF(origin - offset);
return d->controller->canvas()->viewConverter()->viewToDocument(viewPoint);
}
KoCanvasBase* KoToolProxy::canvas() const
{
return d->controller->canvas();
}
void KoToolProxy::tabletEvent(QTabletEvent *event, const QPointF &point)
{
// We get these events exclusively from KisToolProxy - accept them
event->accept();
KoInputDevice id(event->device(), event->pointerType(), event->uniqueId());
KoToolManager::instance()->priv()->switchInputDevice(id);
KoPointerEvent ev(event, point);
switch (event->type()) {
case QEvent::TabletPress:
ev.setTabletButton(Qt::LeftButton);
if (!d->tabletPressed && d->activeTool)
d->activeTool->mousePressEvent(&ev);
d->tabletPressed = true;
break;
case QEvent::TabletRelease:
ev.setTabletButton(Qt::LeftButton);
d->tabletPressed = false;
d->scrollTimer.stop();
if (d->activeTool)
d->activeTool->mouseReleaseEvent(&ev);
break;
case QEvent::TabletMove:
if (d->tabletPressed)
ev.setTabletButton(Qt::LeftButton);
if (d->activeTool)
d->activeTool->mouseMoveEvent(&ev);
d->checkAutoScroll(ev);
default:
; // ignore the rest.
}
d->mouseLeaveWorkaround = true;
}
void KoToolProxy::mousePressEvent(KoPointerEvent *ev)
{
d->mouseLeaveWorkaround = false;
KoInputDevice id;
KoToolManager::instance()->priv()->switchInputDevice(id);
d->mouseDownPoint = ev->pos();
- if (d->tabletPressed) { // refuse to send a press unless there was a release first.
+ if (d->tabletPressed) // refuse to send a press unless there was a release first.
return;
+
+ QPointF globalPoint = ev->globalPos();
+ if (d->multiClickGlobalPoint != globalPoint) {
+ if (qAbs(globalPoint.x() - d->multiClickGlobalPoint.x()) > 5||
+ qAbs(globalPoint.y() - d->multiClickGlobalPoint.y()) > 5) {
+ d->multiClickCount = 0;
+ }
+ d->multiClickGlobalPoint = globalPoint;
+ }
+
+ if (d->multiClickCount && d->multiClickTimeStamp.elapsed() < QApplication::doubleClickInterval()) {
+ // One more multiclick;
+ d->multiClickCount++;
+ } else {
+ d->multiClickTimeStamp.start();
+ d->multiClickCount = 1;
+ }
+
+ if (d->activeTool) {
+ switch (d->multiClickCount) {
+ case 0:
+ case 1:
+ d->activeTool->mousePressEvent(ev);
+ break;
+ case 2:
+ d->activeTool->mouseDoubleClickEvent(ev);
+ break;
+ case 3:
+ default:
+ d->activeTool->mouseTripleClickEvent(ev);
+ break;
+ }
+ } else {
+ d->multiClickCount = 0;
+ ev->ignore();
}
}
void KoToolProxy::mousePressEvent(QMouseEvent *event, const QPointF &point)
{
KoPointerEvent ev(event, point);
mousePressEvent(&ev);
}
void KoToolProxy::mouseDoubleClickEvent(QMouseEvent *event, const QPointF &point)
{
KoPointerEvent ev(event, point);
mouseDoubleClickEvent(&ev);
}
void KoToolProxy::mouseDoubleClickEvent(KoPointerEvent *event)
{
- d->activeTool->mouseDoubleClickEvent(event);
+ // let us handle it as any other mousepress (where we then detect multi clicks
+ mousePressEvent(event);
}
void KoToolProxy::mouseMoveEvent(QMouseEvent *event, const QPointF &point)
{
KoPointerEvent ev(event, point);
mouseMoveEvent(&ev);
}
void KoToolProxy::mouseMoveEvent(KoPointerEvent *event)
{
if (d->mouseLeaveWorkaround) {
d->mouseLeaveWorkaround = false;
return;
}
KoInputDevice id;
KoToolManager::instance()->priv()->switchInputDevice(id);
if (d->activeTool == 0) {
event->ignore();
return;
}
d->activeTool->mouseMoveEvent(event);
d->checkAutoScroll(*event);
}
void KoToolProxy::mouseReleaseEvent(QMouseEvent *event, const QPointF &point)
{
KoPointerEvent ev(event, point);
mouseReleaseEvent(&ev);
}
void KoToolProxy::mouseReleaseEvent(KoPointerEvent* event)
{
d->mouseLeaveWorkaround = false;
KoInputDevice id;
KoToolManager::instance()->priv()->switchInputDevice(id);
d->scrollTimer.stop();
if (d->activeTool) {
d->activeTool->mouseReleaseEvent(event);
} else {
event->ignore();
}
}
void KoToolProxy::keyPressEvent(QKeyEvent *event)
{
if (d->activeTool)
d->activeTool->keyPressEvent(event);
else
event->ignore();
}
void KoToolProxy::keyReleaseEvent(QKeyEvent *event)
{
if (d->activeTool)
d->activeTool->keyReleaseEvent(event);
else
event->ignore();
}
void KoToolProxy::explicitUserStrokeEndRequest()
{
if (d->activeTool) {
d->activeTool->explicitUserStrokeEndRequest();
}
}
QVariant KoToolProxy::inputMethodQuery(Qt::InputMethodQuery query, const KoViewConverter &converter) const
{
if (d->activeTool)
return d->activeTool->inputMethodQuery(query, converter);
return QVariant();
}
void KoToolProxy::inputMethodEvent(QInputMethodEvent *event)
{
if (d->activeTool) d->activeTool->inputMethodEvent(event);
}
QMenu *KoToolProxy::popupActionsMenu()
{
return d->activeTool ? d->activeTool->popupActionsMenu() : 0;
}
void KoToolProxy::setActiveTool(KoToolBase *tool)
{
if (d->activeTool)
disconnect(d->activeTool, SIGNAL(selectionChanged(bool)), this, SLOT(selectionChanged(bool)));
d->activeTool = tool;
if (tool) {
connect(d->activeTool, SIGNAL(selectionChanged(bool)), this, SLOT(selectionChanged(bool)));
d->selectionChanged(hasSelection());
emit toolChanged(tool->toolId());
}
}
void KoToolProxyPrivate::setCanvasController(KoCanvasController *c)
{
controller = c;
}
QHash KoToolProxy::actions() const
{
return d->activeTool ? d->activeTool->actions() : QHash();
}
bool KoToolProxy::hasSelection() const
{
return d->activeTool ? d->activeTool->hasSelection() : false;
}
void KoToolProxy::cut()
{
if (d->activeTool && d->isActiveLayerEditable())
d->activeTool->cut();
}
void KoToolProxy::copy() const
{
if (d->activeTool)
d->activeTool->copy();
}
bool KoToolProxy::paste()
{
bool success = false;
KoCanvasBase *canvas = d->controller->canvas();
if (d->activeTool && d->isActiveLayerEditable()) {
success = d->activeTool->paste();
}
if (!success) {
const QMimeData *data = QApplication::clipboard()->mimeData();
QList imageList;
QImage image = QApplication::clipboard()->image();
if (!image.isNull()) {
imageList << image;
}
// QT5TODO: figure out how to download data synchronously, which is deprecated in frameworks.
else if (data->hasUrls()) {
QList urls = QApplication::clipboard()->mimeData()->urls();
foreach (const QUrl &url, urls) {
QImage image;
image.load(url.toLocalFile());
if (!image.isNull()) {
imageList << image;
}
}
}
KoShapeFactoryBase *factory = KoShapeRegistry::instance()->value("PictureShape");
QWidget *canvasWidget = canvas->canvasWidget();
const KoViewConverter *converter = canvas->viewConverter();
if (imageList.length() > 0 && factory && canvasWidget) {
KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Paste Image"));
QList pastedShapes;
Q_FOREACH (const QImage &image, imageList) {
if (!image.isNull()) {
QPointF p = converter->viewToDocument(canvasWidget->mapFromGlobal(QCursor::pos()) + canvas->canvasController()->documentOffset()- canvasWidget->pos());
KoProperties params;
params.setProperty("qimage", image);
KoShape *shape = factory->createShape(¶ms, canvas->shapeController()->resourceManager());
shape->setPosition(p);
pastedShapes << shape;
success = true;
}
}
if (!pastedShapes.isEmpty()) {
// add shape to the document
canvas->shapeController()->addShapesDirect(pastedShapes, cmd);
canvas->addCommand(cmd);
}
}
}
return success;
}
void KoToolProxy::dragMoveEvent(QDragMoveEvent *event, const QPointF &point)
{
if (d->activeTool)
d->activeTool->dragMoveEvent(event, point);
}
void KoToolProxy::dragLeaveEvent(QDragLeaveEvent *event)
{
if (d->activeTool)
d->activeTool->dragLeaveEvent(event);
}
void KoToolProxy::dropEvent(QDropEvent *event, const QPointF &point)
{
if (d->activeTool)
d->activeTool->dropEvent(event, point);
}
void KoToolProxy::deleteSelection()
{
if (d->activeTool)
d->activeTool->deleteSelection();
}
void KoToolProxy::processEvent(QEvent *e) const
{
if(e->type()==QEvent::ShortcutOverride
&& d->activeTool
&& d->activeTool->isInTextMode()
&& (static_cast(e)->modifiers()==Qt::NoModifier ||
static_cast(e)->modifiers()==Qt::ShiftModifier)) {
e->accept();
}
}
void KoToolProxy::requestUndoDuringStroke()
{
if (d->activeTool) {
d->activeTool->requestUndoDuringStroke();
}
}
void KoToolProxy::requestStrokeCancellation()
{
if (d->activeTool) {
d->activeTool->requestStrokeCancellation();
}
}
void KoToolProxy::requestStrokeEnd()
{
if (d->activeTool) {
d->activeTool->requestStrokeEnd();
}
}
KoToolProxyPrivate *KoToolProxy::priv()
{
return d;
}
//have to include this because of Q_PRIVATE_SLOT
#include "moc_KoToolProxy.cpp"
diff --git a/libs/flake/KoToolProxy_p.h b/libs/flake/KoToolProxy_p.h
index 3bf8db60d3..06eddf297d 100644
--- a/libs/flake/KoToolProxy_p.h
+++ b/libs/flake/KoToolProxy_p.h
@@ -1,63 +1,68 @@
/* This file is part of the KDE project
* Copyright (C) 2006-2010 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 KOTOOLPROXYPRIVATE_P
#define KOTOOLPROXYPRIVATE_P
#include
-#include
+#include
+#include
class KoPointerEvent;
class KoToolBase;
class KoCanvasController;
class KoToolProxy;
class KoToolProxyPrivate
{
public:
explicit KoToolProxyPrivate(KoToolProxy *p);
void timeout(); // Auto scroll the canvas
void checkAutoScroll(const KoPointerEvent &event);
void selectionChanged(bool newSelection);
bool isActiveLayerEditable();
/// the toolManager tells us which KoCanvasController this toolProxy is working for.
void setCanvasController(KoCanvasController *controller);
KoToolBase *activeTool;
bool tabletPressed;
bool hasSelection;
QTimer scrollTimer;
QPoint widgetScrollPoint;
KoCanvasController *controller;
KoToolProxy *parent;
// used to determine if the mouse-release is after a drag or a simple click
QPoint mouseDownPoint;
// up until at least 4.3.0 we get a mouse move event when the tablet leaves the canvas.
bool mouseLeaveWorkaround;
+ // for multi clicking (double click or triple click) we need the following
+ int multiClickCount;
+ QPointF multiClickGlobalPoint;
+ QTime multiClickTimeStamp;
};
#endif
diff --git a/libs/image/kis_properties_configuration.cc b/libs/image/kis_properties_configuration.cc
index ade808b394..9b9e682770 100644
--- a/libs/image/kis_properties_configuration.cc
+++ b/libs/image/kis_properties_configuration.cc
@@ -1,328 +1,331 @@
/*
* Copyright (c) 2006 Boudewijn Rempt
* Copyright (c) 2007,2010 Cyrille Berger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_properties_configuration.h"
#include
#include
#include
#include "kis_image.h"
#include "kis_transaction.h"
#include "kis_undo_adapter.h"
#include "kis_painter.h"
#include "kis_selection.h"
#include "KoID.h"
#include "kis_types.h"
#include
#include
struct Q_DECL_HIDDEN KisPropertiesConfiguration::Private {
QMap properties;
QStringList notSavedProperties;
};
KisPropertiesConfiguration::KisPropertiesConfiguration() : d(new Private)
{
}
KisPropertiesConfiguration::~KisPropertiesConfiguration()
{
delete d;
}
KisPropertiesConfiguration::KisPropertiesConfiguration(const KisPropertiesConfiguration& rhs)
: KisSerializableConfiguration(rhs)
, d(new Private(*rhs.d))
{
}
KisPropertiesConfiguration &KisPropertiesConfiguration::operator=(const KisPropertiesConfiguration &rhs)
{
if (&rhs != this) {
*d = *rhs.d;
}
return *this;
}
bool KisPropertiesConfiguration::fromXML(const QString & xml, bool clear)
{
if (clear) {
clearProperties();
}
QDomDocument doc;
bool retval = doc.setContent(xml);
if (retval) {
QDomElement e = doc.documentElement();
fromXML(e);
}
return retval;
}
void KisPropertiesConfiguration::fromXML(const QDomElement& e)
{
QDomNode n = e.firstChild();
while (!n.isNull()) {
// We don't nest elements in filter configuration. For now...
QDomElement e = n.toElement();
if (!e.isNull()) {
if (e.tagName() == "param") {
// If the file contains the new type parameter introduced in Krita act on it
// Else invoke old behaviour
if(e.attributes().contains("type"))
{
QString type = e.attribute("type");
QString name = e.attribute("name");
QString value = e.text();
if(type == "bytearray")
{
d->properties[name] = QVariant(QByteArray::fromBase64(value.toLatin1()));
}
else
d->properties[name] = value;
}
else
d->properties[e.attribute("name")] = QVariant(e.text());
}
}
n = n.nextSibling();
}
//dump();
}
void KisPropertiesConfiguration::toXML(QDomDocument& doc, QDomElement& root) const
{
QMap::Iterator it;
for (it = d->properties.begin(); it != d->properties.end(); ++it) {
if(d->notSavedProperties.contains(it.key())) {
continue;
}
QDomElement e = doc.createElement("param");
e.setAttribute("name", QString(it.key().toLatin1()));
QString type = "string";
QVariant v = it.value();
QDomText text;
if (v.type() == QVariant::UserType && v.userType() == qMetaTypeId()) {
text = doc.createCDATASection(v.value().toString());
} else if (v.type() == QVariant::UserType && v.userType() == qMetaTypeId()) {
QDomDocument doc = QDomDocument("color");
QDomElement root = doc.createElement("color");
doc.appendChild(root);
v.value().toXML(doc, root);
text = doc.createCDATASection(doc.toString());
type = "color";
} else if(v.type() == QVariant::String ) {
text = doc.createCDATASection(v.toString()); // XXX: Unittest this!
type = "string";
} else if(v.type() == QVariant::ByteArray ) {
text = doc.createTextNode(QString::fromLatin1(v.toByteArray().toBase64())); // Arbitrary Data
type = "bytearray";
} else {
text = doc.createTextNode(v.toString());
type = "internal";
}
e.setAttribute("type", type);
e.appendChild(text);
root.appendChild(e);
}
}
QString KisPropertiesConfiguration::toXML() const
{
QDomDocument doc = QDomDocument("params");
QDomElement root = doc.createElement("params");
doc.appendChild(root);
toXML(doc, root);
return doc.toString();
}
bool KisPropertiesConfiguration::hasProperty(const QString& name) const
{
return d->properties.contains(name);
}
void KisPropertiesConfiguration::setProperty(const QString & name, const QVariant & value)
{
if (d->properties.find(name) == d->properties.end()) {
d->properties.insert(name, value);
} else {
d->properties[name] = value;
}
}
bool KisPropertiesConfiguration::getProperty(const QString & name, QVariant & value) const
{
if (d->properties.find(name) == d->properties.end()) {
return false;
} else {
value = d->properties[name];
return true;
}
}
QVariant KisPropertiesConfiguration::getProperty(const QString & name) const
{
if (d->properties.find(name) == d->properties.end()) {
return QVariant();
} else {
return d->properties[name];
}
}
int KisPropertiesConfiguration::getInt(const QString & name, int def) const
{
QVariant v = getProperty(name);
if (v.isValid())
return v.toInt();
else
return def;
}
double KisPropertiesConfiguration::getDouble(const QString & name, double def) const
{
QVariant v = getProperty(name);
if (v.isValid())
return v.toDouble();
else
return def;
}
float KisPropertiesConfiguration::getFloat(const QString & name, float def) const
{
QVariant v = getProperty(name);
if (v.isValid())
return (float)v.toDouble();
else
return def;
}
bool KisPropertiesConfiguration::getBool(const QString & name, bool def) const
{
QVariant v = getProperty(name);
if (v.isValid())
return v.toBool();
else
return def;
}
QString KisPropertiesConfiguration::getString(const QString & name, const QString & def) const
{
QVariant v = getProperty(name);
if (v.isValid())
return v.toString();
else
return def;
}
KisCubicCurve KisPropertiesConfiguration::getCubicCurve(const QString & name, const KisCubicCurve & curve) const
{
QVariant v = getProperty(name);
if (v.isValid()) {
if (v.type() == QVariant::UserType && v.userType() == qMetaTypeId()) {
return v.value();
} else {
KisCubicCurve c;
c.fromString(v.toString());
return c;
}
} else
return curve;
}
KoColor KisPropertiesConfiguration::getColor(const QString& name, const KoColor& color) const
{
QVariant v = getProperty(name);
if (v.isValid()) {
if (v.type() == QVariant::UserType && v.userType() == qMetaTypeId()) {
return v.value();
} else {
QDomDocument doc;
doc.setContent(v.toString());
QDomElement e = doc.documentElement().firstChild().toElement();
- return KoColor::fromXML(e, Integer16BitsColorDepthID.id());
+ bool ok;
+ KoColor c = KoColor::fromXML(e, Integer16BitsColorDepthID.id(), &ok);
+ if (ok) {
+ return c;
+ }
}
- } else {
- return color;
}
+ return color;
}
void KisPropertiesConfiguration::dump() const
{
QMap::Iterator it;
for (it = d->properties.begin(); it != d->properties.end(); ++it) {
dbgKrita << it.key() << " = " << it.value();
}
}
void KisPropertiesConfiguration::clearProperties()
{
d->properties.clear();
}
void KisPropertiesConfiguration::setPropertyNotSaved(const QString& name)
{
d->notSavedProperties.append(name);
}
QMap KisPropertiesConfiguration::getProperties() const
{
return d->properties;
}
void KisPropertiesConfiguration::removeProperty(const QString & name)
{
d->properties.remove(name);
}
// --- factory ---
struct Q_DECL_HIDDEN KisPropertiesConfigurationFactory::Private {
};
KisPropertiesConfigurationFactory::KisPropertiesConfigurationFactory() : d(new Private)
{
}
KisPropertiesConfigurationFactory::~KisPropertiesConfigurationFactory()
{
delete d;
}
KisSerializableConfigurationSP KisPropertiesConfigurationFactory::createDefault()
{
return new KisPropertiesConfiguration();
}
KisSerializableConfigurationSP KisPropertiesConfigurationFactory::create(const QDomElement& e)
{
KisPropertiesConfigurationSP pc = new KisPropertiesConfiguration();
pc->fromXML(e);
return pc;
}
diff --git a/libs/libkis/Palette.cpp b/libs/libkis/Palette.cpp
index ca02e02aea..2117e6e4a8 100644
--- a/libs/libkis/Palette.cpp
+++ b/libs/libkis/Palette.cpp
@@ -1,107 +1,113 @@
/*
* Copyright (c) 2017 Wolthera van Hövell tot Westerflier
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "Palette.h"
#include
#include
struct Palette::Private {
KoColorSet *palette {0};
};
Palette::Palette(Resource *resource): d(new Private()) {
d->palette = dynamic_cast(resource->resource());
}
Palette::~Palette()
{
delete d;
}
int Palette::numberOfEntries() const
{
if (!d->palette) return 0;
return d->palette->nColors();
}
int Palette::columnCount()
{
if (!d->palette) return 0;
return d->palette->columnCount();
}
void Palette::setColumnCount(int columns)
{
if (d->palette)
d->palette->setColumnCount(columns);
}
QString Palette::comment()
{
if (!d->palette) return "";
return d->palette->comment();
}
QStringList Palette::groupNames()
{
if (!d->palette) return QStringList();
return d->palette->getGroupNames();
}
bool Palette::addGroup(QString name)
{
if (!d->palette) return false;
return d->palette->addGroup(name);
}
bool Palette::removeGroup(QString name, bool keepColors)
{
if (!d->palette) return false;
return d->palette->removeGroup(name, keepColors);
}
+int Palette::colorsCountTotal()
+{
+ if (!d->palette) return 0;
+ return d->palette->nColors();
+}
+
int Palette::colorsCountGroup(QString name)
{
if (!d->palette) return 0;
return d->palette->nColorsGroup(name);
}
KoColorSetEntry Palette::colorSetEntryByIndex(int index)
{
if (!d->palette) return KoColorSetEntry();
return d->palette->getColorGlobal(index);
}
KoColorSetEntry Palette::colorSetEntryFromGroup(int index, const QString &groupName)
{
if (!d->palette) return KoColorSetEntry();
return d->palette->getColorGroup(index, groupName);
}
ManagedColor *Palette::colorForEntry(KoColorSetEntry entry)
{
if (!d->palette) return 0;
ManagedColor *color = new ManagedColor(entry.color);
return color;
}
KoColorSet *Palette::colorSet()
{
return d->palette;
}
diff --git a/libs/libkis/Palette.h b/libs/libkis/Palette.h
index cf7332f19c..fef32bf1f5 100644
--- a/libs/libkis/Palette.h
+++ b/libs/libkis/Palette.h
@@ -1,130 +1,136 @@
/*
* Copyright (c) 2017 Wolthera van Hövell tot Westerflier
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef LIBKIS_PALETTE_H
#define LIBKIS_PALETTE_H
#include
#include "kritalibkis_export.h"
#include "libkis.h"
#include "Resource.h"
#include "KoColorSet.h"
class ManagedColor;
/**
* @brief The Palette class
* Palette is a resource object that stores organised color data.
* It's purpose is to allow artists to save colors and store them.
*
* An example for printing all the palettes and the entries:
*
* @code
import sys
from krita import *
resources = Application.resources("palette")
for (k, v) in resources.items():
print(k)
palette = Palette(v)
for x in range(palette.numberOfEntries()):
entry = palette.colorSetEntryByIndex(x)
c = palette.colorForEntry(entry);
print(x, entry.name, entry.id, entry.spotColor, c.toQString())
* @endcode
*/
class KRITALIBKIS_EXPORT Palette : public QObject
{
public:
Palette(Resource *resource);
~Palette() override;
/**
* @brief numberOfEntries
* @return
*/
int numberOfEntries() const;
/**
* @brief columnCount
* @return the amount of columns this palette is set to use.
*/
int columnCount();
/**
* @brief setColumnCount
* Set the amount of columns this palette should use.
*/
void setColumnCount(int columns);
/**
* @brief comment
* @return the comment or description associated with the palette.
*/
QString comment();
//setcomment
/**
* @brief groupNames
* @return the list of group names. This is list is in the order these groups are in the file.
*/
QStringList groupNames();
/**
* @brief addGroup
* @param name of the new group
* @return whether adding the group was succesful.
*/
bool addGroup(QString name);
/**
* @brief removeGroup
* @param name the name of the group to remove.
* @param keepColors whether or not to delete all the colors inside, or to move them to the default group.
* @return
*/
bool removeGroup(QString name, bool keepColors = true);
+
+ /**
+ * @brief colorsCountTotal
+ * @return the total amount of entries in the whole group
+ */
+ int colorsCountTotal();
/**
* @brief colorsCountGroup
* @param name of the group to check. Empty is the default group.
* @return the amount of colors within that group.
*/
int colorsCountGroup(QString name);
KoColorSetEntry colorSetEntryByIndex(int index);
KoColorSetEntry colorSetEntryFromGroup(int index, const QString &groupName);
ManagedColor *colorForEntry(KoColorSetEntry entry);
//getcolorgroup
//Add
//Remove
//Insert
private:
friend class PaletteView;
struct Private;
Private *const d;
/**
* @brief colorSet
* @return gives qa KoColorSet object back
*/
KoColorSet *colorSet();
};
#endif // LIBKIS_PALETTE_H
diff --git a/libs/pigment/KoColor.cpp b/libs/pigment/KoColor.cpp
index b5973762b0..5ab7e9e9a3 100644
--- a/libs/pigment/KoColor.cpp
+++ b/libs/pigment/KoColor.cpp
@@ -1,336 +1,345 @@
/*
* Copyright (c) 2005 Boudewijn Rempt
* Copyright (C) 2007 Thomas Zander
* Copyright (C) 2007 Cyrille Berger
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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 "KoColor.h"
#include
#include
#include "DebugPigment.h"
#include "KoColorModelStandardIds.h"
#include "KoColorProfile.h"
#include "KoColorSpace.h"
#include "KoColorSpaceRegistry.h"
#include "KoChannelInfo.h"
class Q_DECL_HIDDEN KoColor::Private
{
public:
Private() : data(0), colorSpace(0) {}
~Private() {
delete [] data;
}
quint8 * data;
const KoColorSpace * colorSpace;
};
KoColor::KoColor()
: d(new Private())
{
d->colorSpace = KoColorSpaceRegistry::instance()->rgb16(0);
d->data = new quint8[d->colorSpace->pixelSize()];
d->colorSpace->fromQColor(Qt::black, d->data);
d->colorSpace->setOpacity(d->data, OPACITY_OPAQUE_U8, 1);
}
KoColor::KoColor(const KoColorSpace * colorSpace)
: d(new Private())
{
Q_ASSERT(colorSpace);
d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace);
d->data = new quint8[d->colorSpace->pixelSize()];
memset(d->data, 0, d->colorSpace->pixelSize());
}
KoColor::~KoColor()
{
delete d;
}
KoColor::KoColor(const QColor & color, const KoColorSpace * colorSpace)
: d(new Private())
{
Q_ASSERT(color.isValid());
Q_ASSERT(colorSpace);
d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace);
d->data = new quint8[colorSpace->pixelSize()];
memset(d->data, 0, d->colorSpace->pixelSize());
d->colorSpace->fromQColor(color, d->data);
}
KoColor::KoColor(const quint8 * data, const KoColorSpace * colorSpace)
: d(new Private())
{
Q_ASSERT(colorSpace);
Q_ASSERT(data);
d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace);
d->data = new quint8[colorSpace->pixelSize()];
memset(d->data, 0, d->colorSpace->pixelSize());
memmove(d->data, data, colorSpace->pixelSize());
}
KoColor::KoColor(const KoColor &src, const KoColorSpace * colorSpace)
: d(new Private())
{
Q_ASSERT(colorSpace);
d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace);
d->data = new quint8[colorSpace->pixelSize()];
memset(d->data, 0, d->colorSpace->pixelSize());
src.colorSpace()->convertPixelsTo(src.d->data, d->data, colorSpace, 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
}
KoColor::KoColor(const KoColor & rhs)
: d(new Private())
{
d->colorSpace = rhs.colorSpace();
Q_ASSERT(*d->colorSpace == *KoColorSpaceRegistry::instance()->permanentColorspace(d->colorSpace));
if (d->colorSpace && rhs.d->data) {
d->data = new quint8[d->colorSpace->pixelSize()];
memcpy(d->data, rhs.d->data, d->colorSpace->pixelSize());
}
}
KoColor & KoColor::operator=(const KoColor & rhs)
{
if (this == &rhs) return *this;
delete [] d->data;
d->data = 0;
d->colorSpace = rhs.colorSpace();
if (rhs.d->colorSpace && rhs.d->data) {
Q_ASSERT(d->colorSpace == KoColorSpaceRegistry::instance()->permanentColorspace(d->colorSpace)); // here we want to do a check on pointer, since d->colorSpace is supposed to already be a permanent one
d->data = new quint8[d->colorSpace->pixelSize()];
memcpy(d->data, rhs.d->data, d->colorSpace->pixelSize());
}
return * this;
}
bool KoColor::operator==(const KoColor &other) const
{
if (*colorSpace() != *other.colorSpace())
return false;
return memcmp(d->data, other.d->data, d->colorSpace->pixelSize()) == 0;
}
void KoColor::convertTo(const KoColorSpace * cs, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
{
//dbgPigment <<"Our colormodel:" << d->colorSpace->id().name()
// << ", new colormodel: " << cs->id().name() << "\n";
if (*d->colorSpace == *cs)
return;
quint8 * data = new quint8[cs->pixelSize()];
memset(data, 0, cs->pixelSize());
d->colorSpace->convertPixelsTo(d->data, data, cs, 1, renderingIntent, conversionFlags);
delete [] d->data;
d->data = data;
d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(cs);
}
void KoColor::convertTo(const KoColorSpace * cs)
{
convertTo(cs,
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags());
}
void KoColor::setProfile(const KoColorProfile *profile)
{
const KoColorSpace *dstColorSpace =
KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile);
if (!dstColorSpace) return;
d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(dstColorSpace);
}
void KoColor::setColor(const quint8 * data, const KoColorSpace * colorSpace)
{
Q_ASSERT(data);
Q_ASSERT(colorSpace);
if(d->colorSpace->pixelSize() != colorSpace->pixelSize())
{
delete [] d->data;
d->data = new quint8[colorSpace->pixelSize()];
}
memcpy(d->data, data, colorSpace->pixelSize());
d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace);
}
// To save the user the trouble of doing color->colorSpace()->toQColor(color->data(), &c, &a, profile
void KoColor::toQColor(QColor *c) const
{
Q_ASSERT(c);
if (d->colorSpace && d->data) {
d->colorSpace->toQColor(d->data, c);
}
}
QColor KoColor::toQColor() const
{
QColor c;
toQColor(&c);
return c;
}
void KoColor::fromQColor(const QColor& c) const
{
if (d->colorSpace && d->data) {
d->colorSpace->fromQColor(c, d->data);
}
}
#ifndef NDEBUG
void KoColor::dump() const
{
dbgPigment <<"KoColor (" << this <<")," << d->colorSpace->id() <<"";
QList channels = d->colorSpace->channels();
QList::const_iterator begin = channels.constBegin();
QList::const_iterator end = channels.constEnd();
for (QList::const_iterator it = begin; it != end; ++it) {
KoChannelInfo * ch = (*it);
// XXX: setNum always takes a byte.
if (ch->size() == sizeof(quint8)) {
// Byte
dbgPigment <<"Channel (byte):" << ch->name() <<":" << QString().setNum(d->data[ch->pos()]) <<"";
} else if (ch->size() == sizeof(quint16)) {
// Short (may also by an nvidia half)
dbgPigment <<"Channel (short):" << ch->name() <<":" << QString().setNum(*((const quint16 *)(d->data+ch->pos()))) <<"";
} else if (ch->size() == sizeof(quint32)) {
// Integer (may also be float... Find out how to distinguish these!)
dbgPigment <<"Channel (int):" << ch->name() <<":" << QString().setNum(*((const quint32 *)(d->data+ch->pos()))) <<"";
}
}
}
#endif
void KoColor::fromKoColor(const KoColor& src)
{
src.colorSpace()->convertPixelsTo(src.d->data, d->data, colorSpace(), 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
}
const KoColorProfile *KoColor::profile() const
{
return d->colorSpace->profile();
}
quint8 * KoColor::data()
{
return d->data;
}
const quint8 * KoColor::data() const
{
return d->data;
}
const KoColorSpace * KoColor::colorSpace() const
{
return d->colorSpace;
}
void KoColor::toXML(QDomDocument& doc, QDomElement& colorElt) const
{
d->colorSpace->colorToXML(d->data, doc, colorElt);
}
void KoColor::setOpacity(quint8 alpha)
{
d->colorSpace->setOpacity(d->data, alpha, 1);
}
void KoColor::setOpacity(qreal alpha)
{
d->colorSpace->setOpacity(d->data, alpha, 1);
}
quint8 KoColor::opacityU8() const
{
return d->colorSpace->opacityU8(d->data);
}
qreal KoColor::opacityF() const
{
return d->colorSpace->opacityF(d->data);
}
-KoColor KoColor::fromXML(const QDomElement& elt, const QString & bitDepthId)
+KoColor KoColor::fromXML(const QDomElement& elt, const QString& bitDepthId)
{
+ bool ok;
+ return fromXML(elt, bitDepthId, &ok);
+}
+
+KoColor KoColor::fromXML(const QDomElement& elt, const QString& bitDepthId, bool* ok)
+{
+ *ok = true;
QString modelId;
if (elt.tagName() == "CMYK") {
modelId = CMYKAColorModelID.id();
} else if (elt.tagName() == "RGB") {
modelId = RGBAColorModelID.id();
} else if (elt.tagName() == "sRGB") {
modelId = RGBAColorModelID.id();
} else if (elt.tagName() == "Lab") {
modelId = LABAColorModelID.id();
} else if (elt.tagName() == "XYZ") {
modelId = XYZAColorModelID.id();
} else if (elt.tagName() == "Gray") {
modelId = GrayAColorModelID.id();
} else if (elt.tagName() == "YCbCr") {
modelId = YCbCrAColorModelID.id();
}
QString profileName;
if (elt.tagName() != "sRGB") {
profileName = elt.attribute("space", "");
if (!KoColorSpaceRegistry::instance()->profileByName(profileName)) {
profileName.clear();
}
}
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace(modelId, bitDepthId, profileName);
if (cs == 0) {
QList list = KoColorSpaceRegistry::instance()->colorDepthList(modelId, KoColorSpaceRegistry::AllColorSpaces);
if (!list.empty()) {
cs = KoColorSpaceRegistry::instance()->colorSpace(modelId, list[0].id(), profileName);
}
}
if (cs) {
KoColor c(cs);
+ // TODO: Provide a way for colorFromXML() to notify the caller if parsing failed. Currently it returns default values on failure.
cs->colorFromXML(c.data(), elt);
return c;
} else {
+ *ok = false;
return KoColor();
}
}
QString KoColor::toQString(const KoColor &color)
{
QStringList ls;
Q_FOREACH (KoChannelInfo *channel, KoChannelInfo::displayOrderSorted(color.colorSpace()->channels())) {
int realIndex = KoChannelInfo::displayPositionToChannelIndex(channel->displayPosition(), color.colorSpace()->channels());
ls << channel->name();
ls << color.colorSpace()->channelValueText(color.data(), realIndex);
}
return ls.join(" ");
}
diff --git a/libs/pigment/KoColor.h b/libs/pigment/KoColor.h
index 214c20a137..24c4d28ef6 100644
--- a/libs/pigment/KoColor.h
+++ b/libs/pigment/KoColor.h
@@ -1,179 +1,193 @@
/*
* Copyright (c) 2005 Boudewijn Rempt
* Copyright (C) 2007 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 KOCOLOR_H
#define KOCOLOR_H
#include
#include
#include "kritapigment_export.h"
#include "KoColorConversionTransformation.h"
#include
class QDomDocument;
class QDomElement;
class KoColorProfile;
class KoColorSpace;
/**
* A KoColor describes a color in a certain colorspace. The color is stored in a buffer
* that can be manipulated by the function of the color space.
*/
class KRITAPIGMENT_EXPORT KoColor : public boost::equality_comparable
{
public:
/// Create an empty KoColor. It will be valid, but also black and transparent
KoColor();
~KoColor();
/// Create a null KoColor. It will be valid, but all channels will be set to 0
explicit KoColor(const KoColorSpace * colorSpace);
/// Create a KoColor from a QColor. The QColor is immediately converted to native. The QColor
/// is assumed to have the current monitor profile.
KoColor(const QColor & color, const KoColorSpace * colorSpace);
/// Create a KoColor using a native color strategy. The data is copied.
KoColor(const quint8 * data, const KoColorSpace * colorSpace);
/// Create a KoColor by converting src into another colorspace
KoColor(const KoColor &src, const KoColorSpace * colorSpace);
/// Copy constructor -- deep copies the colors.
KoColor(const KoColor & rhs);
/**
* assignment operator to copy the data from the param color into this one.
* @param other the color we are going to copy
* @return this color
*/
KoColor &operator=(const KoColor &other);
bool operator==(const KoColor &other) const;
/// return the current colorSpace
const KoColorSpace * colorSpace() const;
/// return the current profile
const KoColorProfile *profile() const;
/// Convert this KoColor to the specified colorspace. If the specified colorspace is the
/// same as the original colorspace, do nothing. Returns the converted KoColor.
void convertTo(const KoColorSpace * cs,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags);
void convertTo(const KoColorSpace * cs);
/// assign new profile without converting pixel data
void setProfile(const KoColorProfile *profile);
-
/// Replace the existing color data, and colorspace with the specified data.
/// The data is copied.
void setColor(const quint8 * data, const KoColorSpace * colorSpace = 0);
/// Convert the color from src and replace the value of the current color with the converted data.
/// Don't convert the color if src and this have the same colorspace.
void fromKoColor(const KoColor& src);
/// a convenience method for the above.
void toQColor(QColor *c) const;
/// a convenience method for the above.
QColor toQColor() const;
/**
* Convenient function to set the opacity of the color.
*/
void setOpacity(quint8 alpha);
void setOpacity(qreal alpha);
/**
* Convenient function that return the opacity of the color
*/
quint8 opacityU8() const;
qreal opacityF() const;
/// Convenient function for converting from a QColor
void fromQColor(const QColor& c) const;
/**
* @return the buffer associated with this color object to be used with the
* transformation object created by the color space of this KoColor
* or to copy to a different buffer from the same color space
*/
quint8 * data();
/**
* @return the buffer associated with this color object to be used with the
* transformation object created by the color space of this KoColor
* or to copy to a different buffer from the same color space
*/
const quint8 * data() const;
/**
* Serialize this color following Create's swatch color specification available
* at http://create.freedesktop.org/wiki/index.php/Swatches_-_colour_file_format
*
* This function doesn't create the element but rather the ,
* , ... elements. It is assumed that colorElt is the
* element.
*
* @param colorElt root element for the serialization, it is assumed that this
* element is
* @param doc is the document containing colorElt
*/
void toXML(QDomDocument& doc, QDomElement& colorElt) const;
/**
* Unserialize a color following Create's swatch color specification available
* at http://create.freedesktop.org/wiki/index.php/Swatches_-_colour_file_format
*
* @param elt the element to unserialize (, , )
* @param bitDepthId the bit depth is unspecified by the spec, this allow to select
* a preferred bit depth for creating the KoColor object (if that
* bit depth isn't available, this function will randomly select
* an other bit depth)
* @return the unserialize color, or an empty color object if the function failed
* to unserialize the color
*/
static KoColor fromXML(const QDomElement& elt, const QString & bitDepthId);
+ /**
+ * Unserialize a color following Create's swatch color specification available
+ * at http://create.freedesktop.org/wiki/index.php/Swatches_-_colour_file_format
+ *
+ * @param elt the element to unserialize (, , )
+ * @param bitDepthId the bit depth is unspecified by the spec, this allow to select
+ * a preferred bit depth for creating the KoColor object (if that
+ * bit depth isn't available, this function will randomly select
+ * an other bit depth)
+ * @param ok If a an error occurs, *ok is set to false; otherwise it's set to true
+ * @return the unserialize color, or an empty color object if the function failed
+ * to unserialize the color
+ */
+ static KoColor fromXML(const QDomElement& elt, const QString & bitDepthId, bool* ok);
+
/**
* @brief toQString create a user-visible string of the channel names and the channel values
* @param color the color to create the string from
* @return a string that can be used to display the values of this color to the user.
*/
static QString toQString(const KoColor &color);
#ifndef NODEBUG
/// use qDebug calls to print internal info
void dump() const;
#endif
private:
class Private;
Private * const d;
};
Q_DECLARE_METATYPE(KoColor)
#endif
diff --git a/libs/ui/tool/kis_tool.cc b/libs/ui/tool/kis_tool.cc
index 4329369b86..191ba27918 100644
--- a/libs/ui/tool/kis_tool.cc
+++ b/libs/ui/tool/kis_tool.cc
@@ -1,690 +1,695 @@
/*
* Copyright (c) 2006, 2010 Boudewijn Rempt
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_tool.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kis_node_manager.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "opengl/kis_opengl_canvas2.h"
#include "kis_canvas_resource_provider.h"
#include "canvas/kis_canvas2.h"
#include "kis_coordinates_converter.h"
#include "filter/kis_filter_configuration.h"
#include "kis_config.h"
#include "kis_config_notifier.h"
#include "kis_cursor.h"
#include
#include
#include "kis_resources_snapshot.h"
#include
#include "kis_action_registry.h"
#include "kis_tool_utils.h"
struct Q_DECL_HIDDEN KisTool::Private {
QCursor cursor; // the cursor that should be shown on tool activation.
// From the canvas resources
KoPattern* currentPattern{0};
KoAbstractGradient* currentGradient{0};
KoColor currentFgColor;
KoColor currentBgColor;
float currentExposure{1.0};
KisFilterConfigurationSP currentGenerator;
QWidget* optionWidget{0};
ToolMode m_mode{HOVER_MODE};
bool m_isActive{false};
};
KisTool::KisTool(KoCanvasBase * canvas, const QCursor & cursor)
: KoToolBase(canvas)
, d(new Private)
{
d->cursor = cursor;
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(resetCursorStyle()));
connect(this, SIGNAL(isActiveChanged()), SLOT(resetCursorStyle()));
KActionCollection *collection = this->canvas()->canvasController()->actionCollection();
if (!collection->action("toggle_fg_bg")) {
QAction *toggleFgBg = KisActionRegistry::instance()->makeQAction("toggle_fg_bg", collection);
collection->addAction("toggle_fg_bg", toggleFgBg);
}
if (!collection->action("reset_fg_bg")) {
QAction *toggleFgBg = KisActionRegistry::instance()->makeQAction("reset_fg_bg", collection);
collection->addAction("reset_fg_bg", toggleFgBg);
}
addAction("toggle_fg_bg", dynamic_cast(collection->action("toggle_fg_bg")));
addAction("reset_fg_bg", dynamic_cast(collection->action("reset_fg_bg")));
}
KisTool::~KisTool()
{
delete d;
}
void KisTool::activate(ToolActivation activation, const QSet &shapes)
{
KoToolBase::activate(activation, shapes);
resetCursorStyle();
if (!canvas()) return;
if (!canvas()->resourceManager()) return;
d->currentFgColor = canvas()->resourceManager()->resource(KoCanvasResourceManager::ForegroundColor).value();
d->currentBgColor = canvas()->resourceManager()->resource(KoCanvasResourceManager::BackgroundColor).value();
if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::CurrentPattern)) {
d->currentPattern = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPattern).value();
}
if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::CurrentGradient)) {
d->currentGradient = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentGradient).value();
}
KisPaintOpPresetSP preset = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value();
if (preset && preset->settings()) {
preset->settings()->activate();
}
if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::HdrExposure)) {
d->currentExposure = static_cast(canvas()->resourceManager()->resource(KisCanvasResourceProvider::HdrExposure).toDouble());
}
if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::CurrentGeneratorConfiguration)) {
d->currentGenerator = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentGeneratorConfiguration).value();
}
connect(action("toggle_fg_bg"), SIGNAL(triggered()), SLOT(slotToggleFgBg()), Qt::UniqueConnection);
connect(action("reset_fg_bg"), SIGNAL(triggered()), SLOT(slotResetFgBg()), Qt::UniqueConnection);
connect(image(), SIGNAL(sigUndoDuringStrokeRequested()), SLOT(requestUndoDuringStroke()), Qt::UniqueConnection);
connect(image(), SIGNAL(sigStrokeCancellationRequested()), SLOT(requestStrokeCancellation()), Qt::UniqueConnection);
connect(image(), SIGNAL(sigStrokeEndRequested()), SLOT(requestStrokeEnd()), Qt::UniqueConnection);
d->m_isActive = true;
emit isActiveChanged();
}
void KisTool::deactivate()
{
bool result = true;
result &= disconnect(image().data(), SIGNAL(sigUndoDuringStrokeRequested()), this, 0);
result &= disconnect(image().data(), SIGNAL(sigStrokeCancellationRequested()), this, 0);
result &= disconnect(image().data(), SIGNAL(sigStrokeEndRequested()), this, 0);
result &= disconnect(action("toggle_fg_bg"), 0, this, 0);
result &= disconnect(action("reset_fg_bg"), 0, this, 0);
if (!result) {
warnKrita << "WARNING: KisTool::deactivate() failed to disconnect"
<< "some signal connections. Your actions might be executed twice!";
}
d->m_isActive = false;
emit isActiveChanged();
KoToolBase::deactivate();
}
void KisTool::canvasResourceChanged(int key, const QVariant & v)
{
switch (key) {
case(KoCanvasResourceManager::ForegroundColor):
d->currentFgColor = v.value();
break;
case(KoCanvasResourceManager::BackgroundColor):
d->currentBgColor = v.value();
break;
case(KisCanvasResourceProvider::CurrentPattern):
d->currentPattern = static_cast(v.value());
break;
case(KisCanvasResourceProvider::CurrentGradient):
d->currentGradient = static_cast(v.value());
break;
case(KisCanvasResourceProvider::HdrExposure):
d->currentExposure = static_cast(v.toDouble());
break;
case(KisCanvasResourceProvider::CurrentGeneratorConfiguration):
d->currentGenerator = static_cast(v.value());
break;
case(KisCanvasResourceProvider::CurrentPaintOpPreset):
emit statusTextChanged(v.value()->name());
break;
case(KisCanvasResourceProvider::CurrentKritaNode):
resetCursorStyle();
break;
default:
break; // Do nothing
};
}
void KisTool::updateSettingsViews()
{
}
QPointF KisTool::widgetCenterInWidgetPixels()
{
KisCanvas2 *kritaCanvas = dynamic_cast(canvas());
Q_ASSERT(kritaCanvas);
const KisCoordinatesConverter *converter = kritaCanvas->coordinatesConverter();
return converter->flakeToWidget(converter->flakeCenterPoint());
}
QPointF KisTool::convertDocumentToWidget(const QPointF& pt)
{
KisCanvas2 *kritaCanvas = dynamic_cast(canvas());
Q_ASSERT(kritaCanvas);
return kritaCanvas->coordinatesConverter()->documentToWidget(pt);
}
QPointF KisTool::convertToPixelCoord(KoPointerEvent *e)
{
if (!image())
return e->point;
return image()->documentToPixel(e->point);
}
QPointF KisTool::convertToPixelCoord(const QPointF& pt)
{
if (!image())
return pt;
return image()->documentToPixel(pt);
}
QPointF KisTool::convertToPixelCoordAndSnap(KoPointerEvent *e, const QPointF &offset, bool useModifiers)
{
if (!image())
return e->point;
KoSnapGuide *snapGuide = canvas()->snapGuide();
QPointF pos = snapGuide->snap(e->point, offset, useModifiers ? e->modifiers() : Qt::NoModifier);
return image()->documentToPixel(pos);
}
QPointF KisTool::convertToPixelCoordAndSnap(const QPointF& pt, const QPointF &offset)
{
if (!image())
return pt;
KoSnapGuide *snapGuide = canvas()->snapGuide();
QPointF pos = snapGuide->snap(pt, offset, Qt::NoModifier);
return image()->documentToPixel(pos);
}
QPoint KisTool::convertToIntPixelCoord(KoPointerEvent *e)
{
if (!image())
return e->point.toPoint();
return image()->documentToIntPixel(e->point);
}
QPointF KisTool::viewToPixel(const QPointF &viewCoord) const
{
if (!image())
return viewCoord;
return image()->documentToPixel(canvas()->viewConverter()->viewToDocument(viewCoord));
}
QRectF KisTool::convertToPt(const QRectF &rect)
{
if (!image())
return rect;
QRectF r;
//We add 1 in the following to the extreme coords because a pixel always has size
r.setCoords(int(rect.left()) / image()->xRes(), int(rect.top()) / image()->yRes(),
int(1 + rect.right()) / image()->xRes(), int(1 + rect.bottom()) / image()->yRes());
return r;
}
QPointF KisTool::pixelToView(const QPoint &pixelCoord) const
{
if (!image())
return pixelCoord;
QPointF documentCoord = image()->pixelToDocument(pixelCoord);
return canvas()->viewConverter()->documentToView(documentCoord);
}
QPointF KisTool::pixelToView(const QPointF &pixelCoord) const
{
if (!image())
return pixelCoord;
QPointF documentCoord = image()->pixelToDocument(pixelCoord);
return canvas()->viewConverter()->documentToView(documentCoord);
}
QRectF KisTool::pixelToView(const QRectF &pixelRect) const
{
if (!image())
return pixelRect;
QPointF topLeft = pixelToView(pixelRect.topLeft());
QPointF bottomRight = pixelToView(pixelRect.bottomRight());
return QRectF(topLeft, bottomRight);
}
QPainterPath KisTool::pixelToView(const QPainterPath &pixelPolygon) const
{
QTransform matrix;
qreal zoomX, zoomY;
canvas()->viewConverter()->zoom(&zoomX, &zoomY);
matrix.scale(zoomX/image()->xRes(), zoomY/ image()->yRes());
return matrix.map(pixelPolygon);
}
QPolygonF KisTool::pixelToView(const QPolygonF &pixelPath) const
{
QTransform matrix;
qreal zoomX, zoomY;
canvas()->viewConverter()->zoom(&zoomX, &zoomY);
matrix.scale(zoomX/image()->xRes(), zoomY/ image()->yRes());
return matrix.map(pixelPath);
}
void KisTool::updateCanvasPixelRect(const QRectF &pixelRect)
{
canvas()->updateCanvas(convertToPt(pixelRect));
}
void KisTool::updateCanvasViewRect(const QRectF &viewRect)
{
canvas()->updateCanvas(canvas()->viewConverter()->viewToDocument(viewRect));
}
KisImageWSP KisTool::image() const
{
// For now, krita tools only work in krita, not for a krita shape. Krita shapes are for 2.1
KisCanvas2 * kisCanvas = dynamic_cast(canvas());
if (kisCanvas) {
return kisCanvas->currentImage();
}
return 0;
}
QCursor KisTool::cursor() const
{
return d->cursor;
}
void KisTool::notifyModified() const
{
if (image()) {
image()->setModified();
}
}
KoPattern * KisTool::currentPattern()
{
return d->currentPattern;
}
KoAbstractGradient * KisTool::currentGradient()
{
return d->currentGradient;
}
KisPaintOpPresetSP KisTool::currentPaintOpPreset()
{
return canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value();
}
KisNodeSP KisTool::currentNode() const
{
KisNodeSP node = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentKritaNode).value();
return node;
}
KisNodeList KisTool::selectedNodes() const
{
KisCanvas2 * kiscanvas = static_cast(canvas());
KisViewManager* viewManager = kiscanvas->viewManager();
return viewManager->nodeManager()->selectedNodes();
}
KoColor KisTool::currentFgColor()
{
return d->currentFgColor;
}
KoColor KisTool::currentBgColor()
{
return d->currentBgColor;
}
KisImageWSP KisTool::currentImage()
{
return image();
}
KisFilterConfigurationSP KisTool::currentGenerator()
{
return d->currentGenerator;
}
void KisTool::setMode(ToolMode mode) {
d->m_mode = mode;
}
KisTool::ToolMode KisTool::mode() const {
return d->m_mode;
}
void KisTool::setCursor(const QCursor &cursor)
{
d->cursor = cursor;
}
KisTool::AlternateAction KisTool::actionToAlternateAction(ToolAction action) {
KIS_ASSERT_RECOVER_RETURN_VALUE(action != Primary, Secondary);
return (AlternateAction)action;
}
void KisTool::activatePrimaryAction()
{
resetCursorStyle();
}
void KisTool::deactivatePrimaryAction()
{
resetCursorStyle();
}
void KisTool::beginPrimaryAction(KoPointerEvent *event)
{
Q_UNUSED(event);
}
void KisTool::beginPrimaryDoubleClickAction(KoPointerEvent *event)
{
beginPrimaryAction(event);
}
void KisTool::continuePrimaryAction(KoPointerEvent *event)
{
Q_UNUSED(event);
}
void KisTool::endPrimaryAction(KoPointerEvent *event)
{
Q_UNUSED(event);
}
bool KisTool::primaryActionSupportsHiResEvents() const
{
return false;
}
void KisTool::activateAlternateAction(AlternateAction action)
{
Q_UNUSED(action);
}
void KisTool::deactivateAlternateAction(AlternateAction action)
{
Q_UNUSED(action);
}
void KisTool::beginAlternateAction(KoPointerEvent *event, AlternateAction action)
{
Q_UNUSED(event);
Q_UNUSED(action);
}
void KisTool::beginAlternateDoubleClickAction(KoPointerEvent *event, AlternateAction action)
{
beginAlternateAction(event, action);
}
void KisTool::continueAlternateAction(KoPointerEvent *event, AlternateAction action)
{
Q_UNUSED(event);
Q_UNUSED(action);
}
void KisTool::endAlternateAction(KoPointerEvent *event, AlternateAction action)
{
Q_UNUSED(event);
Q_UNUSED(action);
}
void KisTool::mouseDoubleClickEvent(KoPointerEvent *event)
{
Q_UNUSED(event);
}
+void KisTool::mouseTripleClickEvent(KoPointerEvent *event)
+{
+ mouseDoubleClickEvent(event);
+}
+
void KisTool::mousePressEvent(KoPointerEvent *event)
{
Q_UNUSED(event);
}
void KisTool::mouseReleaseEvent(KoPointerEvent *event)
{
Q_UNUSED(event);
}
void KisTool::mouseMoveEvent(KoPointerEvent *event)
{
Q_UNUSED(event);
}
void KisTool::deleteSelection()
{
KisResourcesSnapshotSP resources =
new KisResourcesSnapshot(image(), currentNode(), this->canvas()->resourceManager());
if (!blockUntilOperationsFinished()) {
return;
}
if (!KisToolUtils::clearImage(image(), resources->currentNode(), resources->activeSelection())) {
KoToolBase::deleteSelection();
}
}
void KisTool::setupPaintAction(KisRecordedPaintAction* action)
{
action->setPaintColor(currentFgColor());
action->setBackgroundColor(currentBgColor());
}
QWidget* KisTool::createOptionWidget()
{
d->optionWidget = new QLabel(i18n("No options"));
d->optionWidget->setObjectName("SpecialSpacer");
return d->optionWidget;
}
#define NEAR_VAL -1000.0
#define FAR_VAL 1000.0
#define PROGRAM_VERTEX_ATTRIBUTE 0
void KisTool::paintToolOutline(QPainter* painter, const QPainterPath &path)
{
KisOpenGLCanvas2 *canvasWidget = dynamic_cast(canvas()->canvasWidget());
if (canvasWidget) {
painter->beginNativePainting();
canvasWidget->paintToolOutline(path);
painter->endNativePainting();
}
else {
painter->setCompositionMode(QPainter::RasterOp_SourceXorDestination);
painter->setPen(QColor(128, 255, 128));
painter->drawPath(path);
}
}
void KisTool::resetCursorStyle()
{
useCursor(d->cursor);
}
bool KisTool::overrideCursorIfNotEditable()
{
// override cursor for canvas iff this tool is active
// and we can't paint on the active layer
if (isActive()) {
KisNodeSP node = currentNode();
if (node && !node->isEditable()) {
canvas()->setCursor(Qt::ForbiddenCursor);
return true;
}
}
return false;
}
bool KisTool::blockUntilOperationsFinished()
{
KisCanvas2 * kiscanvas = static_cast(canvas());
KisViewManager* viewManager = kiscanvas->viewManager();
return viewManager->blockUntilOperationsFinished(image());
}
void KisTool::blockUntilOperationsFinishedForced()
{
KisCanvas2 * kiscanvas = static_cast(canvas());
KisViewManager* viewManager = kiscanvas->viewManager();
viewManager->blockUntilOperationsFinishedForced(image());
}
bool KisTool::isActive() const
{
return d->m_isActive;
}
void KisTool::slotToggleFgBg()
{
KoCanvasResourceManager* resourceManager = canvas()->resourceManager();
KoColor newFg = resourceManager->backgroundColor();
KoColor newBg = resourceManager->foregroundColor();
/**
* NOTE: Some of color selectors do not differentiate foreground
* and background colors, so if one wants them to end up
* being set up to foreground color, it should be set the
* last.
*/
resourceManager->setBackgroundColor(newBg);
resourceManager->setForegroundColor(newFg);
}
void KisTool::slotResetFgBg()
{
KoCanvasResourceManager* resourceManager = canvas()->resourceManager();
// see a comment in slotToggleFgBg()
resourceManager->setBackgroundColor(KoColor(Qt::white, KoColorSpaceRegistry::instance()->rgb8()));
resourceManager->setForegroundColor(KoColor(Qt::black, KoColorSpaceRegistry::instance()->rgb8()));
}
bool KisTool::nodeEditable()
{
KisNodeSP node = currentNode();
if (!node) {
return false;
}
bool nodeEditable = node->isEditable();
if (!nodeEditable) {
KisCanvas2 * kiscanvas = static_cast(canvas());
QString message;
if (!node->visible() && node->userLocked()) {
message = i18n("Layer is locked and invisible.");
} else if (node->userLocked()) {
message = i18n("Layer is locked.");
} else if(!node->visible()) {
message = i18n("Layer is invisible.");
} else {
message = i18n("Group not editable.");
}
kiscanvas->viewManager()->showFloatingMessage(message, KisIconUtils::loadIcon("object-locked"));
}
return nodeEditable;
}
bool KisTool::selectionEditable()
{
KisCanvas2 * kisCanvas = static_cast(canvas());
KisViewManager * view = kisCanvas->viewManager();
bool editable = view->selectionEditable();
if (!editable) {
KisCanvas2 * kiscanvas = static_cast(canvas());
kiscanvas->viewManager()->showFloatingMessage(i18n("Local selection is locked."), KisIconUtils::loadIcon("object-locked"));
}
return editable;
}
void KisTool::listenToModifiers(bool listen)
{
Q_UNUSED(listen);
}
bool KisTool::listeningToModifiers()
{
return false;
}
diff --git a/libs/ui/tool/kis_tool.h b/libs/ui/tool/kis_tool.h
index f6f4b0e93b..bec8e7abe8 100644
--- a/libs/ui/tool/kis_tool.h
+++ b/libs/ui/tool/kis_tool.h
@@ -1,324 +1,325 @@
/*
* Copyright (c) 2006 Boudewijn Rempt
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_TOOL_H_
#define KIS_TOOL_H_
#include
#include
#include
#include
#include
#include
#include
#ifdef __GNUC__
#define WARN_WRONG_MODE(_mode) warnKrita << "Unexpected tool event has come to" << __func__ << "while being mode" << _mode << "!"
#else
#define WARN_WRONG_MODE(_mode) warnKrita << "Unexpected tool event has come while being mode" << _mode << "!"
#endif
#define CHECK_MODE_SANITY_OR_RETURN(_mode) if (mode() != _mode) { WARN_WRONG_MODE(mode()); return; }
class KoCanvasBase;
class KoPattern;
class KoAbstractGradient;
class KisFilterConfiguration;
class QPainter;
class QPainterPath;
class QPolygonF;
class KisRecordedPaintAction;
/// Definitions of the toolgroups of Krita
static const QString TOOL_TYPE_SHAPE = "0 Krita/Shape"; // Geometric shapes like ellipses and lines
static const QString TOOL_TYPE_TRANSFORM = "2 Krita/Transform"; // Tools that transform the layer;
static const QString TOOL_TYPE_FILL = "3 Krita/Fill"; // Tools that fill parts of the canvas
static const QString TOOL_TYPE_VIEW = "4 Krita/View"; // Tools that affect the canvas: pan, zoom, etc.
static const QString TOOL_TYPE_SELECTION = "5 Krita/Select"; // Tools that select pixels
//activation id for Krita tools, Krita tools are always active and handle locked and invisible layers by themself
static const QString KRITA_TOOL_ACTIVATION_ID = "flake/always";
class KRITAUI_EXPORT KisTool
: public KoToolBase
{
Q_OBJECT
Q_PROPERTY(bool isActive READ isActive NOTIFY isActiveChanged)
public:
enum { FLAG_USES_CUSTOM_PRESET=0x01, FLAG_USES_CUSTOM_COMPOSITEOP=0x02, FLAG_USES_CUSTOM_SIZE=0x04 };
KisTool(KoCanvasBase * canvas, const QCursor & cursor);
~KisTool() override;
virtual int flags() const { return 0; }
void deleteSelection() override;
// KoToolBase Implementation.
public:
/**
* Called by KisToolProxy when the primary action of the tool is
* going to be started now, that is when all the modifiers are
* pressed and the only thing left is just to press the mouse
* button. On coming of this callback the tool is supposed to
* prepare the cursor and/or the outline to show the user shat is
* going to happen next
*/
virtual void activatePrimaryAction();
/**
* Called by KisToolProxy when the primary is no longer possible
* to be started now, e.g. when its modifiers and released. The
* tool is supposed revert all the preparetions it has doen in
* activatePrimaryAction().
*/
virtual void deactivatePrimaryAction();
/**
* Called by KisToolProxy when a primary action for the tool is
* started. The \p event stores the original event that
* started the stroke. The \p event is _accepted_ by default. If
* the tool decides to ignore this particular action (e.g. when
* the node is not editable), it should call event->ignore(). Then
* no further continuePrimaryAction() or endPrimaryAction() will
* be called until the next user action.
*/
virtual void beginPrimaryAction(KoPointerEvent *event);
/**
* Called by KisToolProxy when the primary action is in progress
* of pointer movement. If the tool has ignored the event in
* beginPrimaryAction(), this method will not be called.
*/
virtual void continuePrimaryAction(KoPointerEvent *event);
/**
* Called by KisToolProxy when the primary action is being
* finished, that is while mouseRelease or tabletRelease event.
* If the tool has ignored the event in beginPrimaryAction(), this
* method will not be called.
*/
virtual void endPrimaryAction(KoPointerEvent *event);
/**
* The same as beginPrimaryAction(), but called when the stroke is
* started by a double-click
*
* \see beginPrimaryAction()
*/
virtual void beginPrimaryDoubleClickAction(KoPointerEvent *event);
/**
* Returns true if the tool can handle (and wants to handle) a
* very tight flow of input events from the tablet
*/
virtual bool primaryActionSupportsHiResEvents() const;
enum ToolAction {
Primary,
AlternateChangeSize,
AlternatePickFgNode,
AlternatePickBgNode,
AlternatePickFgImage,
AlternatePickBgImage,
AlternateSecondary,
AlternateThird,
AlternateFourth,
AlternateFifth,
Alternate_NONE = 10000
};
// Technically users are allowed to configure this, but nobody ever would do that.
// So these can basically be thought of as aliases to ctrl+click, etc.
enum AlternateAction {
ChangeSize = AlternateChangeSize, // Default: Shift+Left click
PickFgNode = AlternatePickFgNode, // Default: Ctrl+Alt+Left click
PickBgNode = AlternatePickBgNode, // Default: Ctrl+Alt+Right click
PickFgImage = AlternatePickFgImage, // Default: Ctrl+Left click
PickBgImage = AlternatePickBgImage, // Default: Ctrl+Right click
Secondary = AlternateSecondary,
Third = AlternateThird,
Fourth = AlternateFourth,
Fifth = AlternateFifth,
NONE = 10000
};
static AlternateAction actionToAlternateAction(ToolAction action);
virtual void activateAlternateAction(AlternateAction action);
virtual void deactivateAlternateAction(AlternateAction action);
virtual void beginAlternateAction(KoPointerEvent *event, AlternateAction action);
virtual void continueAlternateAction(KoPointerEvent *event, AlternateAction action);
virtual void endAlternateAction(KoPointerEvent *event, AlternateAction action);
virtual void beginAlternateDoubleClickAction(KoPointerEvent *event, AlternateAction action);
void mousePressEvent(KoPointerEvent *event) override;
void mouseDoubleClickEvent(KoPointerEvent *event) override;
+ void mouseTripleClickEvent(KoPointerEvent *event) override;
void mouseReleaseEvent(KoPointerEvent *event) override;
void mouseMoveEvent(KoPointerEvent *event) override;
bool isActive() const;
public Q_SLOTS:
void activate(ToolActivation activation, const QSet &shapes) override;
void deactivate() override;
void canvasResourceChanged(int key, const QVariant & res) override;
// Implement this slot in case there are any widgets or properties which need
// to be updated after certain operations, to reflect the inner state correctly.
// At the moment this is used for smoothing options in the freehand brush, but
// this will likely be expanded.
virtual void updateSettingsViews();
Q_SIGNALS:
void isActiveChanged();
protected:
// conversion methods are also needed by the paint information builder
friend class KisToolPaintingInformationBuilder;
/// Convert from native (postscript points) to image pixel
/// coordinates.
QPointF convertToPixelCoord(KoPointerEvent *e);
QPointF convertToPixelCoord(const QPointF& pt);
QPointF convertToPixelCoordAndSnap(KoPointerEvent *e, const QPointF &offset = QPointF(), bool useModifiers = true);
QPointF convertToPixelCoordAndSnap(const QPointF& pt, const QPointF &offset = QPointF());
protected:
QPointF widgetCenterInWidgetPixels();
QPointF convertDocumentToWidget(const QPointF& pt);
/// Convert from native (postscript points) to integer image pixel
/// coordinates. This truncates the floating point components and
/// should be used in preference to QPointF::toPoint(), which rounds,
/// to ensure the cursor acts on the pixel it is visually over.
QPoint convertToIntPixelCoord(KoPointerEvent *e);
QRectF convertToPt(const QRectF &rect);
QPointF viewToPixel(const QPointF &viewCoord) const;
/// Convert an integer pixel coordinate into a view coordinate.
/// The view coordinate is at the centre of the pixel.
QPointF pixelToView(const QPoint &pixelCoord) const;
/// Convert a floating point pixel coordinate into a view coordinate.
QPointF pixelToView(const QPointF &pixelCoord) const;
/// Convert a pixel rectangle into a view rectangle.
QRectF pixelToView(const QRectF &pixelRect) const;
/// Convert a pixel path into a view path
QPainterPath pixelToView(const QPainterPath &pixelPath) const;
/// Convert a pixel polygon into a view path
QPolygonF pixelToView(const QPolygonF &pixelPolygon) const;
/// Update the canvas for the given rectangle in image pixel coordinates.
void updateCanvasPixelRect(const QRectF &pixelRect);
/// Update the canvas for the given rectangle in view coordinates.
void updateCanvasViewRect(const QRectF &viewRect);
QWidget* createOptionWidget() override;
/**
* To determine whether this tool will change its behavior when
* modifier keys are pressed
*/
virtual bool listeningToModifiers();
/**
* Request that this tool no longer listen to modifier keys
* (Responding to the request is optional)
*/
virtual void listenToModifiers(bool listen);
protected:
KisImageWSP image() const;
QCursor cursor() const;
/// Call this to set the document modified
void notifyModified() const;
KisImageWSP currentImage();
KoPattern* currentPattern();
KoAbstractGradient *currentGradient();
KisNodeSP currentNode() const;
KisNodeList selectedNodes() const;
KoColor currentFgColor();
KoColor currentBgColor();
KisPaintOpPresetSP currentPaintOpPreset();
KisFilterConfigurationSP currentGenerator();
virtual void setupPaintAction(KisRecordedPaintAction* action);
/// paint the path which is in view coordinates, default paint mode is XOR_MODE, BW_MODE is also possible
/// never apply transformations to the painter, they would be useless, if drawing in OpenGL mode. The coordinates in the path should be in view coordinates.
void paintToolOutline(QPainter * painter, const QPainterPath &path);
/// Checks checks if the current node is editable
bool nodeEditable();
/// Checks checks if the selection is editable, only applies to local selection as global selection is always editable
bool selectionEditable();
/// Override the cursor appropriately if current node is not editable
bool overrideCursorIfNotEditable();
bool blockUntilOperationsFinished();
void blockUntilOperationsFinishedForced();
protected:
enum ToolMode {
HOVER_MODE,
PAINT_MODE,
SECONDARY_PAINT_MODE,
MIRROR_AXIS_SETUP_MODE,
GESTURE_MODE,
PAN_MODE,
OTHER // not used now
};
virtual void setMode(ToolMode mode);
virtual ToolMode mode() const;
void setCursor(const QCursor &cursor);
protected Q_SLOTS:
/**
* Called whenever the configuration settings change.
*/
virtual void resetCursorStyle();
private Q_SLOTS:
void slotToggleFgBg();
void slotResetFgBg();
private:
struct Private;
Private* const d;
};
#endif // KIS_TOOL_H_
diff --git a/plugins/extensions/pykrita/plugin/plugins/palette_docker/palette_docker.py b/plugins/extensions/pykrita/plugin/plugins/palette_docker/palette_docker.py
index 27ec9ce094..7eb645642a 100644
--- a/plugins/extensions/pykrita/plugin/plugins/palette_docker/palette_docker.py
+++ b/plugins/extensions/pykrita/plugin/plugins/palette_docker/palette_docker.py
@@ -1,163 +1,143 @@
# Description: A Python based docker that allows you to edit KPL color palettes.
# By Wolthera
# Importing the relevant dependancies:
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
+from PyQt5.Qt import *
import math
from krita import *
-class palette_swatch_widget(QWidget):
- colorSelected = pyqtSignal(['ManagedColor'])
-
- def __init__(self, parent, color, mColor):
- super().__init__(parent)
- self.setMinimumHeight(12)
- self.setMinimumWidth(12)
- self.setSizePolicy(QSizePolicy().MinimumExpanding,QSizePolicy().MinimumExpanding)
-
- self.color = color
- self.mColor = mColor
- def paintEvent(self, event):
- painter = QPainter(self)
- painter.setBrush(self.color)
- painter.setPen(self.color)
- painter.drawRect(self.contentsRect())
-
- def mousePressEvent(self, event):
- print("Color selected: "+self.toolTip())
- self.colorSelected.emit(self.mColor)
-
- def sizeHint(self):
- return QSize(12,12);
-
-
class Palette_Docker(DockWidget):
#Init the docker
def __init__(self):
super().__init__()
# make base-widget and layout
widget = QWidget()
- layout = QVBoxLayout(self)
+ layout = QVBoxLayout()
+ buttonLayout = QHBoxLayout()
widget.setLayout(layout)
self.setWindowTitle("Python Palette Docker")
#Make a combobox and add palettes
self.cmb_palettes = QComboBox()
allPalettes = Application.resources("palette")
for palette_name in allPalettes:
self.cmb_palettes.addItem(palette_name)
self.currentPalette = Palette(allPalettes["Default"])
self.cmb_palettes.currentTextChanged.connect(self.slot_paletteChanged)
layout.addWidget(self.cmb_palettes) # add combobox to the layout
self.paletteView = PaletteView()
self.paletteView.setPalette(self.currentPalette)
layout.addWidget(self.paletteView)
self.paletteView.entrySelectedForeGround.connect(self.slot_swatchSelected)
- #self.palette_frame = QScrollArea()
- #self.palette_frame.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
- #self.palette_container = QWidget()
- #self.palette_frame.setContentsMargins(0,0,0,0)
- #self.palette_layout = QVBoxLayout()
- #self.palette_layout.setSpacing(0)
- #self.palette_layout.setContentsMargins(0,0,0,0)
- #self.palette_container.setLayout(self.palette_layout)
- #self.palette_frame.setWidget(self.palette_container)
- #layout.addWidget(self.palette_frame)
- #print("palette")
- #self.fill_palette_frame()
- self.setWidget(widget) # add widget to the docker
-
-
- def fill_palette_frame(self):
- for i in reversed(range(self.palette_layout.count())):
- self.palette_layout.itemAt(i).widget().setParent(None)
- columnCount = self.currentPalette.columnCount()
- groupNames = self.currentPalette.groupNames()
-
- self.palette_container.setMinimumHeight(0)
- self.palette_container.setContentsMargins(0, 0, 0, 0)
- self.palette_container.setSizePolicy(QSizePolicy().Ignored,QSizePolicy().MinimumExpanding)
-
- swatchSize = math.floor(self.width()/(columnCount+2))
- if swatchSize < 12:
- swatchSize = 12
-
- self.palette_container.setFixedWidth((columnCount+1)*swatchSize)
- gb_defaultGroup = QWidget()
- gb_defaultGroup.setLayout(QGridLayout())
- gb_defaultGroup.layout().setSpacing(0)
- colorCount = self.currentPalette.colorsCountGroup("")
- rowsForGroup = math.ceil(colorCount/(columnCount+1))
- gb_defaultGroup.setMinimumWidth(columnCount*swatchSize)
- gb_defaultGroup.setContentsMargins(0, 0, 0, 0)
- gb_defaultGroup.setMinimumHeight(rowsForGroup*swatchSize+gb_defaultGroup.style().pixelMetric(QStyle.PM_TitleBarHeight))
- for c in range(columnCount):
- gb_defaultGroup.layout().setColumnMinimumWidth(c, swatchSize)
-
- for i in range(colorCount):
- entry = self.currentPalette.colorSetEntryFromGroup(i, "")
- color = self.currentPalette.colorForEntry(entry);
- swatch = palette_swatch_widget(self, color.colorForCanvas(self.canvas()), color)
- swatch.setToolTip(entry.name)
- column = i % columnCount
- row = math.floor(i/columnCount)
- gb_defaultGroup.layout().addWidget(swatch, row, column)
- #print("palette swatch added "+entry.name+" "+str(column)+", "+str(row))
- swatch.colorSelected.connect(self.slot_swatchSelected)
- self.palette_layout.addWidget(gb_defaultGroup)
- self.palette_container.setMinimumHeight(self.palette_container.minimumHeight()+gb_defaultGroup.minimumHeight())
-
- for groupName in groupNames:
- gb_groupBox = QGroupBox()
- gb_groupBox.setTitle(groupName)
- gb_groupBox.setLayout(QGridLayout())
- gb_groupBox.setFlat(True)
- gb_groupBox.setFixedWidth((columnCount)*swatchSize)
- colorCount = self.currentPalette.colorsCountGroup(groupName)
- rowsForGroup = math.ceil(colorCount/(columnCount+1))
- for c in range(columnCount):
- gb_groupBox.layout().setColumnMinimumWidth(c, swatchSize)
- gb_groupBox.layout().setSpacing(0)
- gb_groupBox.setContentsMargins(0, 0, 0, 0)
-
- for i in range(colorCount):
- entry = self.currentPalette.colorSetEntryFromGroup(i, groupName)
- color = self.currentPalette.colorForEntry(entry)
- swatch = palette_swatch_widget(self, color.colorForCanvas(self.canvas()), color)
- swatch.setToolTip(entry.name)
- swatch.setFixedHeight(swatchSize)
- column = i % columnCount
- row = math.floor(i/columnCount)
- gb_groupBox.layout().addWidget(swatch, row, column)
- #print("palette swatch added "+entry.name+" "+str(column)+", "+str(row))
- #swatch.colorSelected.connect(self.slot_swatchSelected)
-
- self.palette_layout.addWidget(gb_groupBox)
- gb_groupBox.adjustSize()
- self.palette_container.setMinimumHeight(self.palette_container.minimumHeight()+gb_groupBox.height())
- self.palette_container.setMaximumHeight(self.palette_container.minimumHeight())
+ self.colorComboBox = QComboBox()
+ self.colorList = list()
+ buttonLayout.addWidget(self.colorComboBox)
+ self.addEntry = QPushButton()
+ self.addEntry.setText("A")
+ self.addEntry.setToolTip("Add Entry")
+ self.addEntry.clicked.connect(self.slot_add_entry)
+ buttonLayout.addWidget(self.addEntry)
+ self.addGroup = QPushButton()
+ self.addGroup.clicked.connect(self.slot_add_group)
+ self.addGroup.setText("G")
+ self.addGroup.setToolTip("Add Group")
+ buttonLayout.addWidget(self.addGroup)
+ self.removeEntry = QPushButton()
+ self.removeEntry.setText("R")
+ self.removeEntry.setToolTip("Remove Entry")
+ self.removeEntry.clicked.connect(self.slot_remove_entry)
+ buttonLayout.addWidget(self.removeEntry)
+ layout.addLayout(buttonLayout)
+ self.slot_fill_combobox()
+ self.setWidget(widget) # add widget to the docker
+
def slot_paletteChanged(self, name):
self.currentPalette = Palette(Application.resources("palette")[name])
self.paletteView.setPalette(self.currentPalette)
+ self.slot_fill_combobox()
#self.fill_palette_frame()
@pyqtSlot('KoColorSetEntry')
def slot_swatchSelected(self, entry):
print("entry "+entry.name)
if (self.canvas()) is not None:
if (self.canvas().view()) is not None:
+ name = entry.name
+ if len(entry.id)>0:
+ name = entry.id+" - "+entry.name
+ if len(name)>0:
+ if name in self.colorList:
+ self.colorComboBox.setCurrentIndex(self.colorList.index(name))
color = self.currentPalette.colorForEntry(entry)
self.canvas().view().setForeGroundColor(color)
+
+ def slot_fill_combobox(self):
+ if self.currentPalette is None:
+ pass
+ palette = self.currentPalette
+ self.colorComboBox.clear()
+ self.colorList.clear()
+ for i in range(palette.colorsCountTotal()):
+ entry = palette.colorSetEntryByIndex(i)
+ color = palette.colorForEntry(entry).colorForCanvas(self.canvas())
+ colorSquare = QPixmap(12, 12)
+ if entry.spotColor is True:
+ img = colorSquare.toImage()
+ circlePainter = QPainter()
+ img.fill(self.colorComboBox.palette().color(QPalette.Base))
+ circlePainter.begin(img)
+ brush = QBrush(Qt.SolidPattern)
+ brush.setColor(color)
+ circlePainter.setBrush(brush)
+ circlePainter.drawEllipse(0, 0, 11, 11)
+ circlePainter.end()
+ colorSquare = QPixmap.fromImage(img)
+ else:
+ colorSquare.fill(color)
+ name = entry.name
+ if len(entry.id)>0:
+ name = entry.id+" - "+entry.name
+ self.colorList.append(name)
+ self.colorComboBox.addItem(QIcon(colorSquare), name)
+ self.colorComboBox.setEditable(True)
+ self.colorComboBox.setInsertPolicy(QComboBox.NoInsert)
+ self.colorComboBox.completer().setCompletionMode(QCompleter.PopupCompletion)
+ self.colorComboBox.completer().setCaseSensitivity(False)
+ self.colorComboBox.completer().setFilterMode(Qt.MatchContains)
+ self.colorComboBox.currentIndexChanged.connect(self.slot_get_color_from_combobox)
+ def slot_get_color_from_combobox(self):
+ if self.currentPalette is not None:
+ entry = self.currentPalette.colorSetEntryByIndex(self.colorComboBox.currentIndex())
+ self.slot_swatchSelected(entry)
+
+ def slot_add_entry(self):
+ if (self.canvas()) is not None:
+ if (self.canvas().view()) is not None:
+ color = self.canvas().view().foreGroundColor()
+ succes = self.paletteView.addEntryWithDialog(color)
+ if succes is True:
+ self.slot_fill_combobox()
+
+ def slot_add_group(self):
+ succes = self.paletteView.addGroupWithDialog()
+ if succes is True:
+ self.slot_fill_combobox()
+
+ def slot_remove_entry(self):
+ succes = self.paletteView.removeSelectedEntryWithDialog()
+ if succes is True:
+ self.slot_fill_combobox()
+
def canvasChanged(self, canvas):
- #self.fill_palette_frame()
pass
#Add docker to the application :)
Application.addDockWidgetFactory(DockWidgetFactory("palette_docker", DockWidgetFactoryBase.DockRight, Palette_Docker))
diff --git a/plugins/extensions/pykrita/sip/CMakeLists.txt b/plugins/extensions/pykrita/sip/CMakeLists.txt
index beb7a1f6b7..994eb25a0c 100644
--- a/plugins/extensions/pykrita/sip/CMakeLists.txt
+++ b/plugins/extensions/pykrita/sip/CMakeLists.txt
@@ -1,27 +1,29 @@
include(SIPMacros)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../libkis)
message( ${SIP_VERSION} " - The version of SIP found expressed as a 6 digit hex number suitable for comparison as a string.")
message( ${SIP_VERSION_STR} " - The version of SIP found as a human readable string.")
message( ${SIP_EXECUTABLE} " - Path and filename of the SIP command line executable.")
message( ${SIP_INCLUDE_DIR} " - Directory holding the SIP C++ header file.")
message( ${SIP_DEFAULT_SIP_DIR} " - default SIP dir" )
set(SIP_INCLUDES
${SIP_DEFAULT_SIP_DIR}
${SIP_DEFAULT_SIP_DIR}/PyQt5
${PYQT_SIP_DIR_OVERRIDE}
./krita)
set(SIP_CONCAT_PARTS 1)
set(SIP_TAGS ALL WS_X11 ${PYQT5_VERSION_TAG})
set(SIP_EXTRA_OPTIONS -g -x PyKDE_QVector)
set(PYTHON_SITE_PACKAGES_INSTALL_DIR ${DATA_INSTALL_DIR}/krita/pykrita/)
+file(GLOB PYKRITA_KRITA_sip_files ./krita/*.sip)
+set(SIP_EXTRA_FILES_DEPEND ${PYKRITA_KRITA_sip_files})
add_sip_python_module(PyKrita.krita ./krita/kritamod.sip kritalibkis kritaui kritaimage kritalibbrush)
#install(FILES
# ./__init__.py
# DESTINATION ${PYTHON_SITE_PACKAGES_INSTALL_DIR})
diff --git a/plugins/extensions/pykrita/sip/krita/Palette.sip b/plugins/extensions/pykrita/sip/krita/Palette.sip
index a55cb6487d..7ae88cf501 100644
--- a/plugins/extensions/pykrita/sip/krita/Palette.sip
+++ b/plugins/extensions/pykrita/sip/krita/Palette.sip
@@ -1,36 +1,37 @@
struct KoColorSetEntry {
%TypeHeaderCode
#include "KoColorSet.h"
%End
public:
KoColorSetEntry();
QString name;
QString id;
bool spotColor;
bool operator==(const KoColorSetEntry& rhs) const;
};
class Palette : QObject
{
%TypeHeaderCode
#include "Palette.h"
%End
Palette(const Palette & __0);
public:
Palette(Resource *resource);
int numberOfEntries() const;
int columnCount();
void setColumnCount(int columns);
QString comment();
QStringList groupNames();
bool addGroup(QString name);
bool removeGroup(QString name, bool keepColors);
+ int colorsCountTotal();
int colorsCountGroup(QString name);
KoColorSetEntry colorSetEntryByIndex(int index);
KoColorSetEntry colorSetEntryFromGroup(int index, const QString &groupName);
ManagedColor *colorForEntry(KoColorSetEntry entry) /Factory/;
private:
};
diff --git a/plugins/flake/textshape/TextTool.cpp b/plugins/flake/textshape/TextTool.cpp
index 36038ce3a6..fc7218feb0 100644
--- a/plugins/flake/textshape/TextTool.cpp
+++ b/plugins/flake/textshape/TextTool.cpp
@@ -1,3142 +1,3164 @@
/* This file is part of the KDE project
* Copyright (C) 2006-2010 Thomas Zander
* Copyright (C) 2008 Thorsten Zachmann
* Copyright (C) 2008 Girish Ramakrishnan
* Copyright (C) 2008, 2012 Pierre Stirnweiss
* Copyright (C) 2009 KO GmbH
* Copyright (C) 2011 Mojtaba Shahi Senobari
* Copyright (C) 2014 Denis Kuplyakov
*
* 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 "TextTool.h"
#include "TextEditingPluginContainer.h"
#include "dialogs/SimpleCharacterWidget.h"
#include "dialogs/SimpleParagraphWidget.h"
#include "dialogs/SimpleTableWidget.h"
#include "dialogs/SimpleInsertWidget.h"
#include "dialogs/ParagraphSettingsDialog.h"
#include "dialogs/StyleManagerDialog.h"
#include "dialogs/InsertCharacter.h"
#include "dialogs/FontDia.h"
#include "dialogs/TableDialog.h"
#include "dialogs/SectionFormatDialog.h"
#include "dialogs/SectionsSplitDialog.h"
#include "dialogs/SimpleTableWidget.h"
#include "commands/AutoResizeCommand.h"
#include "commands/ChangeListLevelCommand.h"
#include "FontSizeAction.h"
#include "FontFamilyAction.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#include
#include
#include
#include
#include "kis_action_registry.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "AnnotationTextShape.h"
#define AnnotationShape_SHAPEID "AnnotationTextShapeID"
#include "KoShapeBasedDocumentBase.h"
#include
#include
#include
#include
class TextToolSelection : public KoToolSelection
{
public:
TextToolSelection(QWeakPointer editor)
: KoToolSelection(0)
, m_editor(editor)
{
}
bool hasSelection() override
{
if (!m_editor.isNull()) {
return m_editor.data()->hasSelection();
}
return false;
}
QWeakPointer m_editor;
};
static bool hit(const QKeySequence &input, KStandardShortcut::StandardShortcut shortcut)
{
foreach (const QKeySequence &ks, KStandardShortcut::shortcut(shortcut)) {
if (input == ks) {
return true;
}
}
return false;
}
TextTool::TextTool(KoCanvasBase *canvas)
: KoToolBase(canvas)
, m_textShape(0)
, m_textShapeData(0)
, m_changeTracker(0)
, m_allowActions(true)
, m_allowAddUndoCommand(true)
, m_allowResourceManagerUpdates(true)
, m_prevCursorPosition(-1)
, m_caretTimer(this)
, m_caretTimerState(true)
, m_currentCommand(0)
, m_currentCommandHasChildren(false)
, m_specialCharacterDocker(0)
, m_textTyping(false)
, m_textDeleting(false)
, m_editTipTimer(this)
, m_delayedEnsureVisible(false)
, m_toolSelection(0)
, m_tableDraggedOnce(false)
, m_tablePenMode(false)
, m_lastImMicroFocus(QRectF(0, 0, 0, 0))
, m_drag(0)
{
setTextMode(true);
createActions();
m_unit = canvas->resourceManager()->unitResource(KoCanvasResourceManager::Unit);
foreach (KoTextEditingPlugin *plugin, textEditingPluginContainer()->values()) {
connect(plugin, SIGNAL(startMacro(QString)),
this, SLOT(startMacro(QString)));
connect(plugin, SIGNAL(stopMacro()), this, SLOT(stopMacro()));
QHash actions = plugin->actions();
QHash::iterator i = actions.begin();
while (i != actions.end()) {
addAction(i.key(), i.value());
++i;
}
}
m_contextMenu.reset(new QMenu());
// setup the context list.
QSignalMapper *signalMapper = new QSignalMapper(this);
connect(signalMapper, SIGNAL(mapped(QString)), this, SLOT(startTextEditingPlugin(QString)));
m_contextMenu->addAction(this->action("format_font"));
foreach (const QString &key, KoTextEditingRegistry::instance()->keys()) {
KoTextEditingFactory *factory = KoTextEditingRegistry::instance()->value(key);
if (factory->showInMenu()) {
QAction *a = new QAction(factory->title(), this);
connect(a, SIGNAL(triggered()), signalMapper, SLOT(map()));
signalMapper->setMapping(a, factory->id());
m_contextMenu->addAction(a);
addAction(QString("apply_%1").arg(factory->id()), a);
}
}
connect(canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), this, SLOT(shapeAddedToCanvas()));
m_caretTimer.setInterval(500);
connect(&m_caretTimer, SIGNAL(timeout()), this, SLOT(blinkCaret()));
m_editTipTimer.setInterval(500);
m_editTipTimer.setSingleShot(true);
connect(&m_editTipTimer, SIGNAL(timeout()), this, SLOT(showEditTip()));
}
void TextTool::createActions()
{
bool useAdvancedText = !(canvas()->resourceManager()->intResource(KoCanvasResourceManager::ApplicationSpeciality)
& KoCanvasResourceManager::NoAdvancedText);
KisActionRegistry *actionRegistry = KisActionRegistry::instance();
// FIXME: find new icons for these
m_actionConfigureSection = actionRegistry->makeQAction("configure_section", this);
addAction("configure_section", m_actionConfigureSection);
connect(m_actionConfigureSection, SIGNAL(triggered(bool)), this, SLOT(configureSection()));
m_actionInsertSection = actionRegistry->makeQAction("insert_section", this);
addAction("insert_section", m_actionInsertSection);
connect(m_actionInsertSection, SIGNAL(triggered(bool)), this, SLOT(insertNewSection()));
m_actionSplitSections = actionRegistry->makeQAction("split_sections", this);
addAction("split_sections", m_actionSplitSections);
connect(m_actionSplitSections, SIGNAL(triggered(bool)), this, SLOT(splitSections()));
m_actionPasteAsText = actionRegistry->makeQAction("edit_paste_text", this);
addAction("edit_paste_text", m_actionPasteAsText);
connect(m_actionPasteAsText, SIGNAL(triggered(bool)), this, SLOT(pasteAsText()));
m_actionFormatBold = actionRegistry->makeQAction("format_bold", this);
addAction("format_bold", m_actionFormatBold);
m_actionFormatBold->setCheckable(true);
connect(m_actionFormatBold, SIGNAL(triggered(bool)), this, SLOT(bold(bool)));
m_actionFormatItalic = actionRegistry->makeQAction("format_italic", this);
m_actionFormatItalic->setCheckable(true);
addAction("format_italic", m_actionFormatItalic);
connect(m_actionFormatItalic, SIGNAL(triggered(bool)), this, SLOT(italic(bool)));
m_actionFormatUnderline = actionRegistry->makeQAction("format_underline", this);
m_actionFormatUnderline->setCheckable(true);
addAction("format_underline", m_actionFormatUnderline);
connect(m_actionFormatUnderline, SIGNAL(triggered(bool)), this, SLOT(underline(bool)));
m_actionFormatStrikeOut = actionRegistry->makeQAction("format_strike", this);
m_actionFormatStrikeOut->setCheckable(true);
addAction("format_strike", m_actionFormatStrikeOut);
connect(m_actionFormatStrikeOut, SIGNAL(triggered(bool)), this, SLOT(strikeOut(bool)));
QActionGroup *alignmentGroup = new QActionGroup(this);
m_actionAlignLeft = actionRegistry->makeQAction("format_alignleft", this);
m_actionAlignLeft->setCheckable(true);
alignmentGroup->addAction(m_actionAlignLeft);
addAction("format_alignleft", m_actionAlignLeft);
connect(m_actionAlignLeft, SIGNAL(triggered(bool)), this, SLOT(alignLeft()));
m_actionAlignRight = actionRegistry->makeQAction("format_alignright", this);
m_actionAlignRight->setCheckable(true);
alignmentGroup->addAction(m_actionAlignRight);
addAction("format_alignright", m_actionAlignRight);
connect(m_actionAlignRight, SIGNAL(triggered(bool)), this, SLOT(alignRight()));
m_actionAlignCenter = actionRegistry->makeQAction("format_aligncenter", this);
m_actionAlignCenter->setCheckable(true);
addAction("format_aligncenter", m_actionAlignCenter);
alignmentGroup->addAction(m_actionAlignCenter);
connect(m_actionAlignCenter, SIGNAL(triggered(bool)), this, SLOT(alignCenter()));
m_actionAlignBlock = actionRegistry->makeQAction("format_alignblock", this);
m_actionAlignBlock->setCheckable(true);
alignmentGroup->addAction(m_actionAlignBlock);
addAction("format_alignblock", m_actionAlignBlock);
connect(m_actionAlignBlock, SIGNAL(triggered(bool)), this, SLOT(alignBlock()));
m_actionChangeDirection = actionRegistry->makeQAction("change_text_direction", this);
m_actionChangeDirection->setCheckable(true);
addAction("change_text_direction", m_actionChangeDirection);
connect(m_actionChangeDirection, SIGNAL(triggered()), this, SLOT(textDirectionChanged()));
m_actionFormatSuper = actionRegistry->makeQAction("format_super", this);
m_actionFormatSuper->setCheckable(true);
addAction("format_super", m_actionFormatSuper);
connect(m_actionFormatSuper, SIGNAL(triggered(bool)), this, SLOT(superScript(bool)));
m_actionFormatSub = actionRegistry->makeQAction("format_sub", this);
m_actionFormatSub->setCheckable(true);
addAction("format_sub", m_actionFormatSub);
connect(m_actionFormatSub, SIGNAL(triggered(bool)), this, SLOT(subScript(bool)));
// TODO: check these rtl-things work properly
m_actionFormatIncreaseIndent = actionRegistry->makeQAction("format_increaseindent", this);
addAction("format_increaseindent", m_actionFormatIncreaseIndent);
connect(m_actionFormatIncreaseIndent, SIGNAL(triggered()), this, SLOT(increaseIndent()));
m_actionFormatDecreaseIndent = actionRegistry->makeQAction("format_decreaseindent", this);
addAction("format_decreaseindent", m_actionFormatDecreaseIndent);
connect(m_actionFormatDecreaseIndent, SIGNAL(triggered()), this, SLOT(decreaseIndent()));
const char *const increaseIndentActionIconName =
QApplication::isRightToLeft() ? koIconNameCStr("format-indent-less") : koIconNameCStr("format-indent-more");
m_actionFormatIncreaseIndent->setIcon(koIcon(increaseIndentActionIconName));
const char *const decreaseIndentActionIconName =
QApplication::isRightToLeft() ? koIconNameCStr("format_decreaseindent") : koIconNameCStr("format-indent-less");
m_actionFormatIncreaseIndent->setIcon(koIcon(decreaseIndentActionIconName));
QAction *action = actionRegistry->makeQAction("format_bulletlist", this);
addAction("format_bulletlist", action);
action = actionRegistry->makeQAction("format_numberlist", this);
addAction("format_numberlist", action);
action = actionRegistry->makeQAction("fontsizeup", this);
addAction("fontsizeup", action);
connect(action, SIGNAL(triggered()), this, SLOT(increaseFontSize()));
action = actionRegistry->makeQAction("fontsizedown", this);
addAction("fontsizedown", action);
connect(action, SIGNAL(triggered()), this, SLOT(decreaseFontSize()));
m_actionFormatFontFamily = new KoFontFamilyAction(this);
m_actionFormatFontFamily->setText(i18n("Font Family"));
addAction("format_fontfamily", m_actionFormatFontFamily);
connect(m_actionFormatFontFamily, SIGNAL(triggered(QString)),
this, SLOT(setFontFamily(QString)));
m_variableMenu = new KActionMenu(i18n("Variable"), this);
addAction("insert_variable", m_variableMenu);
// ------------------- Actions with a key binding and no GUI item
action = actionRegistry->makeQAction("nonbreaking_space", this);
addAction("nonbreaking_space", action);
connect(action, SIGNAL(triggered()), this, SLOT(nonbreakingSpace()));
action = actionRegistry->makeQAction("nonbreaking_hyphen", this);
addAction("nonbreaking_hyphen", action);
connect(action, SIGNAL(triggered()), this, SLOT(nonbreakingHyphen()));
action = actionRegistry->makeQAction("insert_index", this);
addAction("insert_index", action);
connect(action, SIGNAL(triggered()), this, SLOT(insertIndexMarker()));
action = actionRegistry->makeQAction("soft_hyphen", this);
// TODO: double check this one works, conflicts with "zoom out"
addAction("soft_hyphen", action);
connect(action, SIGNAL(triggered()), this, SLOT(softHyphen()));
if (useAdvancedText) {
action = actionRegistry->makeQAction("line_break", this);
addAction("line_break", action);
connect(action, SIGNAL(triggered()), this, SLOT(lineBreak()));
action = actionRegistry->makeQAction("insert_framebreak", this);
addAction("insert_framebreak", action);
connect(action, SIGNAL(triggered()), this, SLOT(insertFrameBreak()));
}
action = actionRegistry->makeQAction("format_font", this);
addAction("format_font", action);
connect(action, SIGNAL(triggered()), this, SLOT(selectFont()));
m_actionFormatFontSize = new FontSizeAction(i18n("Font Size"), this);
addAction("format_fontsize", m_actionFormatFontSize);
connect(m_actionFormatFontSize, SIGNAL(fontSizeChanged(qreal)), this, SLOT(setFontSize(qreal)));
m_actionFormatTextColor = new KoColorPopupAction(this);
addAction("format_textcolor", m_actionFormatTextColor);
connect(m_actionFormatTextColor, SIGNAL(colorChanged(KoColor)), this, SLOT(setTextColor(KoColor)));
m_actionFormatBackgroundColor = new KoColorPopupAction(this);
addAction("format_backgroundcolor", m_actionFormatBackgroundColor);
connect(m_actionFormatBackgroundColor, SIGNAL(colorChanged(KoColor)), this, SLOT(setBackgroundColor(KoColor)));
m_growWidthAction = actionRegistry->makeQAction("grow_to_fit_width", this);
addAction("grow_to_fit_width", m_growWidthAction);
connect(m_growWidthAction, SIGNAL(triggered(bool)), this, SLOT(setGrowWidthToFit(bool)));
m_growHeightAction = actionRegistry->makeQAction("grow_to_fit_height", this);
addAction("grow_to_fit_height", m_growHeightAction);
connect(m_growHeightAction, SIGNAL(triggered(bool)), this, SLOT(setGrowHeightToFit(bool)));
m_shrinkToFitAction = actionRegistry->makeQAction("shrink_to_fit", this);
addAction("shrink_to_fit", m_shrinkToFitAction);
connect(m_shrinkToFitAction, SIGNAL(triggered(bool)), this, SLOT(setShrinkToFit(bool)));
if (useAdvancedText) {
action = actionRegistry->makeQAction("insert_table", this);
addAction("insert_table", action);
connect(action, SIGNAL(triggered()), this, SLOT(insertTable()));
action = actionRegistry->makeQAction("insert_tablerow_above", this);
addAction("insert_tablerow_above", action);
connect(action, SIGNAL(triggered(bool)), this, SLOT(insertTableRowAbove()));
action = actionRegistry->makeQAction("insert_tablerow_below", this);
addAction("insert_tablerow_below", action);
connect(action, SIGNAL(triggered(bool)), this, SLOT(insertTableRowBelow()));
action = actionRegistry->makeQAction("insert_tablecolumn_left", this);
addAction("insert_tablecolumn_left", action);
connect(action, SIGNAL(triggered(bool)), this, SLOT(insertTableColumnLeft()));
action = actionRegistry->makeQAction("insert_tablecolumn_right", this);
addAction("insert_tablecolumn_right", action);
connect(action, SIGNAL(triggered(bool)), this, SLOT(insertTableColumnRight()));
action = actionRegistry->makeQAction("delete_tablecolumn", this);
addAction("delete_tablecolumn", action);
connect(action, SIGNAL(triggered(bool)), this, SLOT(deleteTableColumn()));
action = actionRegistry->makeQAction("delete_tablerow", this);
addAction("delete_tablerow", action);
connect(action, SIGNAL(triggered(bool)), this, SLOT(deleteTableRow()));
action = actionRegistry->makeQAction("merge_tablecells", this);
addAction("merge_tablecells", action);
connect(action, SIGNAL(triggered(bool)), this, SLOT(mergeTableCells()));
action = actionRegistry->makeQAction("split_tablecells", this);
addAction("split_tablecells", action);
connect(action, SIGNAL(triggered(bool)), this, SLOT(splitTableCells()));
action = actionRegistry->makeQAction("activate_borderpainter", this);
addAction("activate_borderpainter", action);
}
action = actionRegistry->makeQAction("format_paragraph", this);
addAction("format_paragraph", action);
connect(action, SIGNAL(triggered()), this, SLOT(formatParagraph()));
action = actionRegistry->makeQAction("format_stylist", this);
addAction("format_stylist", action);
connect(action, SIGNAL(triggered()), this, SLOT(showStyleManager()));
action = KStandardAction::selectAll(this, SLOT(selectAll()), this);
addAction("edit_select_all", action);
action = actionRegistry->makeQAction("insert_specialchar", this);
addAction("insert_specialchar", action);
connect(action, SIGNAL(triggered()), this, SLOT(insertSpecialCharacter()));
action = actionRegistry->makeQAction("repaint", this);
addAction("repaint", action);
connect(action, SIGNAL(triggered()), this, SLOT(relayoutContent()));
action = actionRegistry->makeQAction("insert_annotation", this);
addAction("insert_annotation", action);
connect(action, SIGNAL(triggered()), this, SLOT(insertAnnotation()));
#ifndef NDEBUG
action = actionRegistry->makeQAction("detailed_debug_paragraphs", this);
addAction("detailed_debug_paragraphs", action);
connect(action, SIGNAL(triggered()), this, SLOT(debugTextDocument()));
action = actionRegistry->makeQAction("detailed_debug_styles", this);
addAction("detailed_debug_styles", action);
connect(action, SIGNAL(triggered()), this, SLOT(debugTextStyles()));
#endif
}
#ifndef NDEBUG
#include "tests/MockShapes.h"
#include
#include
TextTool::TextTool(MockCanvas *canvas) // constructor for our unit tests;
: KoToolBase(canvas),
m_textShape(0),
m_textShapeData(0),
m_changeTracker(0),
m_allowActions(true),
m_allowAddUndoCommand(true),
m_allowResourceManagerUpdates(true),
m_prevCursorPosition(-1),
m_caretTimer(this),
m_caretTimerState(true),
m_currentCommand(0),
m_currentCommandHasChildren(false),
m_specialCharacterDocker(0),
m_textEditingPlugins(0)
, m_editTipTimer(this)
, m_delayedEnsureVisible(false)
, m_tableDraggedOnce(false)
, m_tablePenMode(false)
{
// we could init some vars here, but we probably don't have to
QLocale::setDefault(QLocale("en"));
QTextDocument *document = new QTextDocument(); // this document is leaked
KoInlineTextObjectManager *inlineManager = new KoInlineTextObjectManager();
KoTextDocument(document).setInlineTextObjectManager(inlineManager);
KoTextRangeManager *locationManager = new KoTextRangeManager();
KoTextDocument(document).setTextRangeManager(locationManager);
m_textEditor = new KoTextEditor(document);
KoTextDocument(document).setTextEditor(m_textEditor.data());
m_toolSelection = new TextToolSelection(m_textEditor);
m_changeTracker = new KoChangeTracker();
KoTextDocument(document).setChangeTracker(m_changeTracker);
KoTextDocument(document).setUndoStack(new KUndo2Stack());
}
#endif
TextTool::~TextTool()
{
delete m_toolSelection;
}
void TextTool::showEditTip()
{
if (!m_textShapeData || m_editTipPointedAt.position == -1) {
return;
}
QTextCursor c(m_textShapeData->document());
c.setPosition(m_editTipPointedAt.position);
QString text = "
";
toolTipWidth = QFontMetrics(QToolTip::font()).boundingRect(help).width();
}
if (m_editTipPointedAt.note) {
QString help = i18n("Ctrl+click to go to the note ");
text += help + "
";
toolTipWidth = QFontMetrics(QToolTip::font()).boundingRect(help).width();
}
if (m_editTipPointedAt.noteReference > 0) {
QString help = i18n("Ctrl+click to go to the note reference");
text += help + "";
toolTipWidth = QFontMetrics(QToolTip::font()).boundingRect(help).width();
}
QToolTip::hideText();
if (toolTipWidth) {
QRect keepRect(m_editTipPos - QPoint(3, 3), QSize(6, 6));
QToolTip::showText(m_editTipPos - QPoint(toolTipWidth / 2, 0), text, canvas()->canvasWidget(), keepRect);
}
}
void TextTool::blinkCaret()
{
if (!(canvas()->canvasWidget() && canvas()->canvasWidget()->hasFocus())) {
m_caretTimer.stop();
m_caretTimerState = false; // not visible.
} else {
m_caretTimerState = !m_caretTimerState;
}
repaintCaret();
}
void TextTool::relayoutContent()
{
KoTextDocumentLayout *lay = qobject_cast(m_textShapeData->document()->documentLayout());
Q_ASSERT(lay);
foreach (KoTextLayoutRootArea *rootArea, lay->rootAreas()) {
rootArea->setDirty();
}
lay->emitLayoutIsDirty();
}
void TextTool::paint(QPainter &painter, const KoViewConverter &converter)
{
if (m_textEditor.isNull()) {
return;
}
if (canvas()
&& (canvas()->canvasWidget() && canvas()->canvasWidget()->hasFocus())
&& !m_caretTimer.isActive()) { // make sure we blink
m_caretTimer.start();
m_caretTimerState = true;
}
if (!m_caretTimerState) {
m_caretTimer.setInterval(500); // we set it lower during typing, so set it back to normal
}
if (!m_textShapeData) {
return;
}
if (m_textShapeData->isDirty()) {
return;
}
qreal zoomX, zoomY;
converter.zoom(&zoomX, &zoomY);
painter.save();
QTransform shapeMatrix = m_textShape->absoluteTransformation(&converter);
shapeMatrix.scale(zoomX, zoomY);
shapeMatrix.translate(0, -m_textShapeData->documentOffset());
// Possibly draw table dragging visual cues
const qreal boxHeight = 20;
if (m_tableDragInfo.tableHit == KoPointedAt::ColumnDivider) {
QPointF anchorPos = m_tableDragInfo.tableDividerPos - QPointF(m_dx, 0.0);
if (m_tableDragInfo.tableColumnDivider > 0) {
//let's draw left
qreal w = m_tableDragInfo.tableLeadSize - m_dx;
QRectF rect(anchorPos - QPointF(w, 0.0), QSizeF(w, 0.0));
QRectF drawRect(shapeMatrix.map(rect.topLeft()), shapeMatrix.map(rect.bottomRight()));
drawRect.setHeight(boxHeight);
drawRect.moveTop(drawRect.top() - 1.5 * boxHeight);
QString label = m_unit.toUserStringValue(w);
int labelWidth = QFontMetrics(QToolTip::font()).boundingRect(label).width();
painter.fillRect(drawRect, QColor(64, 255, 64, 196));
painter.setPen(QColor(0, 0, 0, 196));
if (labelWidth + 10 < drawRect.width()) {
QPointF centerLeft(drawRect.left(), drawRect.center().y());
QPointF centerRight(drawRect.right(), drawRect.center().y());
painter.drawLine(centerLeft, drawRect.center() - QPointF(labelWidth / 2 + 5, 0.0));
painter.drawLine(centerLeft, centerLeft + QPointF(7, -5));
painter.drawLine(centerLeft, centerLeft + QPointF(7, 5));
painter.drawLine(drawRect.center() + QPointF(labelWidth / 2 + 5, 0.0), centerRight);
painter.drawLine(centerRight, centerRight + QPointF(-7, -5));
painter.drawLine(centerRight, centerRight + QPointF(-7, 5));
painter.drawText(drawRect, Qt::AlignCenter, label);
}
}
if (m_tableDragInfo.tableColumnDivider < m_tableDragInfo.table->columns()) {
//let's draw right
qreal w = m_tableDragInfo.tableTrailSize + m_dx;
QRectF rect(anchorPos, QSizeF(w, 0.0));
QRectF drawRect(shapeMatrix.map(rect.topLeft()), shapeMatrix.map(rect.bottomRight()));
drawRect.setHeight(boxHeight);
drawRect.moveTop(drawRect.top() - 1.5 * boxHeight);
QString label;
int labelWidth;
if (m_tableDragWithShift) {
label = i18n("follows along");
labelWidth = QFontMetrics(QToolTip::font()).boundingRect(label).width();
drawRect.setWidth(2 * labelWidth);
QLinearGradient g(drawRect.topLeft(), drawRect.topRight());
g.setColorAt(0.6, QColor(255, 64, 64, 196));
g.setColorAt(1.0, QColor(255, 64, 64, 0));
QBrush brush(g);
painter.fillRect(drawRect, brush);
} else {
label = m_unit.toUserStringValue(w);
labelWidth = QFontMetrics(QToolTip::font()).boundingRect(label).width();
drawRect.setHeight(boxHeight);
painter.fillRect(drawRect, QColor(64, 255, 64, 196));
}
painter.setPen(QColor(0, 0, 0, 196));
if (labelWidth + 10 < drawRect.width()) {
QPointF centerLeft(drawRect.left(), drawRect.center().y());
QPointF centerRight(drawRect.right(), drawRect.center().y());
painter.drawLine(centerLeft, drawRect.center() - QPointF(labelWidth / 2 + 5, 0.0));
painter.drawLine(centerLeft, centerLeft + QPointF(7, -5));
painter.drawLine(centerLeft, centerLeft + QPointF(7, 5));
if (!m_tableDragWithShift) {
painter.drawLine(drawRect.center() + QPointF(labelWidth / 2 + 5, 0.0), centerRight);
painter.drawLine(centerRight, centerRight + QPointF(-7, -5));
painter.drawLine(centerRight, centerRight + QPointF(-7, 5));
}
painter.drawText(drawRect, Qt::AlignCenter, label);
}
if (!m_tableDragWithShift) {
// let's draw a helper text too
label = i18n("Press shift to not resize this");
labelWidth = QFontMetrics(QToolTip::font()).boundingRect(label).width();
labelWidth += 10;
//if (labelWidth < drawRect.width())
{
drawRect.moveTop(drawRect.top() + boxHeight);
drawRect.moveLeft(drawRect.left() + (drawRect.width() - labelWidth) / 2);
drawRect.setWidth(labelWidth);
painter.fillRect(drawRect, QColor(64, 255, 64, 196));
painter.drawText(drawRect, Qt::AlignCenter, label);
}
}
}
}
// Possibly draw table dragging visual cues
if (m_tableDragInfo.tableHit == KoPointedAt::RowDivider) {
QPointF anchorPos = m_tableDragInfo.tableDividerPos - QPointF(0.0, m_dy);
if (m_tableDragInfo.tableRowDivider > 0) {
qreal h = m_tableDragInfo.tableLeadSize - m_dy;
QRectF rect(anchorPos - QPointF(0.0, h), QSizeF(0.0, h));
QRectF drawRect(shapeMatrix.map(rect.topLeft()), shapeMatrix.map(rect.bottomRight()));
drawRect.setWidth(boxHeight);
drawRect.moveLeft(drawRect.left() - 1.5 * boxHeight);
QString label = m_unit.toUserStringValue(h);
QRectF labelRect = QFontMetrics(QToolTip::font()).boundingRect(label);
labelRect.setHeight(boxHeight);
labelRect.setWidth(labelRect.width() + 10);
labelRect.moveTopLeft(drawRect.center() - QPointF(labelRect.width(), labelRect.height()) / 2);
painter.fillRect(drawRect, QColor(64, 255, 64, 196));
painter.fillRect(labelRect, QColor(64, 255, 64, 196));
painter.setPen(QColor(0, 0, 0, 196));
if (labelRect.height() + 10 < drawRect.height()) {
QPointF centerTop(drawRect.center().x(), drawRect.top());
QPointF centerBottom(drawRect.center().x(), drawRect.bottom());
painter.drawLine(centerTop, drawRect.center() - QPointF(0.0, labelRect.height() / 2 + 5));
painter.drawLine(centerTop, centerTop + QPointF(-5, 7));
painter.drawLine(centerTop, centerTop + QPointF(5, 7));
painter.drawLine(drawRect.center() + QPointF(0.0, labelRect.height() / 2 + 5), centerBottom);
painter.drawLine(centerBottom, centerBottom + QPointF(-5, -7));
painter.drawLine(centerBottom, centerBottom + QPointF(5, -7));
}
painter.drawText(labelRect, Qt::AlignCenter, label);
}
}
if (m_caretTimerState) {
// Lets draw the caret ourselves, as the Qt method doesn't take cursor
// charFormat into consideration.
QTextBlock block = m_textEditor.data()->block();
if (block.isValid()) {
int posInParag = m_textEditor.data()->position() - block.position();
if (posInParag <= block.layout()->preeditAreaPosition()) {
posInParag += block.layout()->preeditAreaText().length();
}
QTextLine tl = block.layout()->lineForTextPosition(m_textEditor.data()->position() - block.position());
if (tl.isValid()) {
painter.setRenderHint(QPainter::Antialiasing, false);
QRectF rect = caretRect(m_textEditor.data()->cursor());
QPointF baselinePoint;
if (tl.ascent() > 0) {
QFontMetricsF fm(m_textEditor.data()->charFormat().font(), painter.device());
rect.setY(rect.y() + tl.ascent() - qMin(tl.ascent(), fm.ascent()));
rect.setHeight(qMin(tl.ascent(), fm.ascent()) + qMin(tl.descent(), fm.descent()));
baselinePoint = QPoint(rect.x(), rect.y() + tl.ascent());
} else {
//line only filled with characters-without-size (eg anchors)
// layout will make sure line has height of block font
QFontMetricsF fm(block.charFormat().font(), painter.device());
rect.setHeight(fm.ascent() + fm.descent());
baselinePoint = QPoint(rect.x(), rect.y() + fm.ascent());
}
QRectF drawRect(shapeMatrix.map(rect.topLeft()), shapeMatrix.map(rect.bottomLeft()));
drawRect.setWidth(2);
painter.setCompositionMode(QPainter::RasterOp_SourceXorDestination);
if (m_textEditor.data()->isEditProtected(true)) {
QRectF circleRect(shapeMatrix.map(baselinePoint), QSizeF(14, 14));
circleRect.translate(-6.5, -6.5);
QPen pen(QColor(16, 255, 255));
pen.setWidthF(2.0);
painter.setPen(pen);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.drawEllipse(circleRect);
painter.drawLine(circleRect.topLeft() + QPointF(4.5, 4.5),
circleRect.bottomRight() - QPointF(4.5, 4.5));
} else {
painter.fillRect(drawRect, QColor(128, 255, 128));
}
}
}
}
painter.restore();
}
void TextTool::updateSelectedShape(const QPointF &point, bool noDocumentChange)
{
QRectF area(point, QSizeF(1, 1));
if (m_textEditor.data()->hasSelection()) {
repaintSelection();
} else {
repaintCaret();
}
QList sortedShapes = canvas()->shapeManager()->shapesAt(area, true);
qSort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex);
for (int count = sortedShapes.count() - 1; count >= 0; count--) {
KoShape *shape = sortedShapes.at(count);
if (shape->isContentProtected()) {
continue;
}
TextShape *textShape = dynamic_cast(shape);
if (textShape) {
if (textShape != m_textShape) {
if (static_cast(textShape->userData())->document() != m_textShapeData->document()) {
//we should only change to another document if allowed
if (noDocumentChange) {
return;
}
// if we change to another textdocument we need to remove selection in old document
// or it would continue to be painted etc
m_textEditor.data()->setPosition(m_textEditor.data()->position());
}
m_textShape = textShape;
setShapeData(static_cast(m_textShape->userData()));
// This is how we inform the rulers of the active range
// For now we will not consider table cells, but just give the shape dimensions
QVariant v;
QRectF rect(QPoint(), m_textShape->size());
rect = m_textShape->absoluteTransformation(0).mapRect(rect);
v.setValue(rect);
canvas()->resourceManager()->setResource(KoCanvasResourceManager::ActiveRange, v);
}
return;
}
}
}
void TextTool::mousePressEvent(KoPointerEvent *event)
{
if (m_textEditor.isNull()) {
return;
}
// request the software keyboard, if any
if (event->button() == Qt::LeftButton && qApp->autoSipEnabled()) {
QStyle::RequestSoftwareInputPanel behavior = QStyle::RequestSoftwareInputPanel(qApp->style()->styleHint(QStyle::SH_RequestSoftwareInputPanel));
// the two following bools just make it all a lot easier to read in the following if()
// basically, we require a widget for this to work (passing 0 to QApplication::sendEvent
// crashes) and there are three tests any one of which can be true to trigger the event
const bool hasWidget = canvas()->canvasWidget();
if ((behavior == QStyle::RSIP_OnMouseClick && hasWidget) ||
(hasWidget && canvas()->canvasWidget()->hasFocus())) {
QEvent event(QEvent::RequestSoftwareInputPanel);
if (hasWidget) {
QApplication::sendEvent(canvas()->canvasWidget(), &event);
}
}
}
bool shiftPressed = event->modifiers() & Qt::ShiftModifier;
updateSelectedShape(event->point, shiftPressed);
KoSelection *selection = canvas()->selectedShapesProxy()->selection();
if (m_textShape && !selection->isSelected(m_textShape) && m_textShape->isSelectable()) {
selection->deselectAll();
selection->select(m_textShape);
}
KoPointedAt pointedAt = hitTest(event->point);
m_tableDraggedOnce = false;
m_clickWithinSelection = false;
if (pointedAt.position != -1) {
m_tablePenMode = false;
if ((event->button() == Qt::LeftButton) && !shiftPressed && m_textEditor.data()->hasSelection() && m_textEditor.data()->isWithinSelection(pointedAt.position)) {
m_clickWithinSelection = true;
m_draggingOrigin = event->pos(); //we store the pixel pos
} else if (!(event->button() == Qt::RightButton && m_textEditor.data()->hasSelection() && m_textEditor.data()->isWithinSelection(pointedAt.position))) {
m_textEditor.data()->setPosition(pointedAt.position, shiftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor);
useCursor(Qt::IBeamCursor);
}
m_tableDragInfo.tableHit = KoPointedAt::None;
if (m_caretTimer.isActive()) { // make the caret not blink, (blinks again after first draw)
m_caretTimer.stop();
m_caretTimer.setInterval(50);
m_caretTimer.start();
m_caretTimerState = true; // turn caret instantly on on click
}
} else {
if (event->button() == Qt::RightButton) {
m_tablePenMode = false;
KoTextEditingPlugin *plugin = textEditingPluginContainer()->spellcheck();
if (plugin) {
plugin->setCurrentCursorPosition(m_textShapeData->document(), -1);
}
event->ignore();
} else if (m_tablePenMode) {
m_textEditor.data()->beginEditBlock(kundo2_i18n("Change Border Formatting"));
if (pointedAt.tableHit == KoPointedAt::ColumnDivider) {
if (pointedAt.tableColumnDivider < pointedAt.table->columns()) {
m_textEditor.data()->setTableBorderData(pointedAt.table,
pointedAt.tableRowDivider, pointedAt.tableColumnDivider,
KoBorder::LeftBorder, m_tablePenBorderData);
}
if (pointedAt.tableColumnDivider > 0) {
m_textEditor.data()->setTableBorderData(pointedAt.table,
pointedAt.tableRowDivider, pointedAt.tableColumnDivider - 1,
KoBorder::RightBorder, m_tablePenBorderData);
}
} else if (pointedAt.tableHit == KoPointedAt::RowDivider) {
if (pointedAt.tableRowDivider < pointedAt.table->rows()) {
m_textEditor.data()->setTableBorderData(pointedAt.table,
pointedAt.tableRowDivider, pointedAt.tableColumnDivider,
KoBorder::TopBorder, m_tablePenBorderData);
}
if (pointedAt.tableRowDivider > 0) {
m_textEditor.data()->setTableBorderData(pointedAt.table,
pointedAt.tableRowDivider - 1, pointedAt.tableColumnDivider,
KoBorder::BottomBorder, m_tablePenBorderData);
}
}
m_textEditor.data()->endEditBlock();
} else {
m_tableDragInfo = pointedAt;
m_tablePenMode = false;
}
return;
}
if (shiftPressed) { // altered selection.
repaintSelection();
} else {
repaintCaret();
}
updateSelectionHandler();
updateStyleManager();
updateActions();
//activate context-menu for spelling-suggestions
if (event->button() == Qt::RightButton) {
KoTextEditingPlugin *plugin = textEditingPluginContainer()->spellcheck();
if (plugin) {
plugin->setCurrentCursorPosition(m_textShapeData->document(), m_textEditor.data()->position());
}
event->ignore();
}
if (event->button() == Qt::MidButton) { // Paste
const QMimeData *data = QApplication::clipboard()->mimeData(QClipboard::Selection);
// on windows we do not have data if we try to paste this selection
if (data) {
m_prevCursorPosition = m_textEditor.data()->position();
m_textEditor.data()->paste(canvas(), data, canvas()->resourceManager());
editingPluginEvents();
}
}
}
void TextTool::setShapeData(KoTextShapeData *data)
{
bool docChanged = !data || !m_textShapeData || m_textShapeData->document() != data->document();
if (m_textShapeData) {
disconnect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved()));
}
m_textShapeData = data;
if (!m_textShapeData) {
return;
}
connect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved()));
if (docChanged) {
if (!m_textEditor.isNull()) {
disconnect(m_textEditor.data(), SIGNAL(textFormatChanged()), this, SLOT(updateActions()));
}
m_textEditor = KoTextDocument(m_textShapeData->document()).textEditor();
Q_ASSERT(m_textEditor.data());
if (!m_toolSelection) {
m_toolSelection = new TextToolSelection(m_textEditor.data());
} else {
m_toolSelection->m_editor = m_textEditor.data();
}
m_variableMenu->menu()->clear();
KoTextDocument document(m_textShapeData->document());
foreach (QAction *action, document.inlineTextObjectManager()->createInsertVariableActions(canvas())) {
m_variableMenu->addAction(action);
connect(action, SIGNAL(triggered()), this, SLOT(returnFocusToCanvas()));
}
connect(m_textEditor.data(), SIGNAL(textFormatChanged()), this, SLOT(updateActions()));
updateActions();
}
}
void TextTool::updateSelectionHandler()
{
if (m_textEditor) {
emit selectionChanged(m_textEditor.data()->hasSelection());
if (m_textEditor.data()->hasSelection()) {
QClipboard *clipboard = QApplication::clipboard();
if (clipboard->supportsSelection()) {
clipboard->setText(m_textEditor.data()->selectedText(), QClipboard::Selection);
}
}
}
KoCanvasResourceManager *p = canvas()->resourceManager();
m_allowResourceManagerUpdates = false;
if (m_textEditor && m_textShapeData) {
p->setResource(KoText::CurrentTextPosition, m_textEditor.data()->position());
p->setResource(KoText::CurrentTextAnchor, m_textEditor.data()->anchor());
QVariant variant;
variant.setValue(m_textShapeData->document());
p->setResource(KoText::CurrentTextDocument, variant);
} else {
p->clearResource(KoText::CurrentTextPosition);
p->clearResource(KoText::CurrentTextAnchor);
p->clearResource(KoText::CurrentTextDocument);
}
m_allowResourceManagerUpdates = true;
}
QMimeData *TextTool::generateMimeData() const
{
if (!m_textShapeData || m_textEditor.isNull() || !m_textEditor.data()->hasSelection()) {
return 0;
}
int from = m_textEditor.data()->position();
int to = m_textEditor.data()->anchor();
KoTextOdfSaveHelper saveHelper(m_textShapeData->document(), from, to);
KoTextDrag drag;
#ifdef SHOULD_BUILD_RDF
KoDocumentResourceManager *rm = 0;
if (canvas()->shapeController()) {
rm = canvas()->shapeController()->resourceManager();
}
if (rm && rm->hasResource(KoText::DocumentRdf)) {
KoDocumentRdfBase *rdf = qobject_cast(rm->resource(KoText::DocumentRdf).value());
if (rdf) {
saveHelper.setRdfModel(rdf->model());
}
}
#endif
drag.setOdf(KoOdf::mimeType(KoOdf::Text), saveHelper);
QTextDocumentFragment fragment = m_textEditor.data()->selection();
drag.setData("text/html", fragment.toHtml("utf-8").toUtf8());
drag.setData("text/plain", fragment.toPlainText().toUtf8());
return drag.takeMimeData();
}
TextEditingPluginContainer *TextTool::textEditingPluginContainer()
{
m_textEditingPlugins = canvas()->resourceManager()->
resource(TextEditingPluginContainer::ResourceId).value();
if (m_textEditingPlugins == 0) {
m_textEditingPlugins = new TextEditingPluginContainer(canvas()->resourceManager());
QVariant variant;
variant.setValue(m_textEditingPlugins.data());
canvas()->resourceManager()->setResource(TextEditingPluginContainer::ResourceId, variant);
foreach (KoTextEditingPlugin *plugin, m_textEditingPlugins->values()) {
connect(plugin, SIGNAL(startMacro(QString)),
this, SLOT(startMacro(QString)));
connect(plugin, SIGNAL(stopMacro()), this, SLOT(stopMacro()));
QHash actions = plugin->actions();
QHash::iterator i = actions.begin();
while (i != actions.end()) {
addAction(i.key(), i.value());
++i;
}
}
}
return m_textEditingPlugins;
}
void TextTool::copy() const
{
QMimeData *mimeData = generateMimeData();
if (mimeData) {
QApplication::clipboard()->setMimeData(mimeData);
}
}
void TextTool::deleteSelection()
{
m_textEditor.data()->deleteChar();
editingPluginEvents();
}
bool TextTool::paste()
{
const QMimeData *data = QApplication::clipboard()->mimeData(QClipboard::Clipboard);
// on windows we do not have data if we try to paste the selection
if (!data) {
return false;
}
// since this is not paste-as-text we will not paste in urls, but instead let KoToolProxy solve it
if (data->hasUrls()) {
return false;
}
if (data->hasFormat(KoOdf::mimeType(KoOdf::Text))
|| data->hasText()) {
m_prevCursorPosition = m_textEditor.data()->position();
m_textEditor.data()->paste(canvas(), data);
editingPluginEvents();
return true;
}
return false;
}
void TextTool::cut()
{
if (m_textEditor.data()->hasSelection()) {
copy();
KUndo2Command *topCmd = m_textEditor.data()->beginEditBlock(kundo2_i18n("Cut"));
m_textEditor.data()->deleteChar(false, topCmd);
m_textEditor.data()->endEditBlock();
}
}
void TextTool::dragMoveEvent(QDragMoveEvent *event, const QPointF &point)
{
if (event->mimeData()->hasFormat(KoOdf::mimeType(KoOdf::Text))
|| event->mimeData()->hasFormat(KoOdf::mimeType(KoOdf::OpenOfficeClipboard))
|| event->mimeData()->hasText()) {
if (m_drag) {
event->setDropAction(Qt::MoveAction);
event->accept();
} else if (event->proposedAction() == Qt::CopyAction) {
event->acceptProposedAction();
} else {
event->ignore();
return;
}
KoPointedAt pointedAt = hitTest(point);
if (pointedAt.position == -1) {
event->ignore();
}
if (m_caretTimer.isActive()) { // make the caret not blink, (blinks again after first draw)
m_caretTimer.stop();
m_caretTimer.setInterval(50);
m_caretTimer.start();
m_caretTimerState = true; // turn caret instantly on on click
}
if (m_preDragSelection.cursor.isNull()) {
repaintSelection();
m_preDragSelection.cursor = QTextCursor(*m_textEditor.data()->cursor());
if (m_drag) {
// Make a selection that looks like the current cursor selection
// so we can move the real carent around freely
QVector< QAbstractTextDocumentLayout::Selection > sels = KoTextDocument(m_textShapeData->document()).selections();
m_preDragSelection.format = QTextCharFormat();
m_preDragSelection.format.setBackground(qApp->palette().brush(QPalette::Highlight));
m_preDragSelection.format.setForeground(qApp->palette().brush(QPalette::HighlightedText));
sels.append(m_preDragSelection);
KoTextDocument(m_textShapeData->document()).setSelections(sels);
} // else we wantt the selection ot disappaear
}
repaintCaret(); // will erase caret
m_textEditor.data()->setPosition(pointedAt.position);
repaintCaret(); // will paint caret in new spot
// Selection has visually not appeared at a new spot so no need to repaint it
}
}
void TextTool::dragLeaveEvent(QDragLeaveEvent *event)
{
if (m_drag) {
// restore the old selections
QVector< QAbstractTextDocumentLayout::Selection > sels = KoTextDocument(m_textShapeData->document()).selections();
sels.pop_back();
KoTextDocument(m_textShapeData->document()).setSelections(sels);
}
repaintCaret(); // will erase caret in old spot
m_textEditor.data()->setPosition(m_preDragSelection.cursor.anchor());
m_textEditor.data()->setPosition(m_preDragSelection.cursor.position(), QTextCursor::KeepAnchor);
repaintCaret(); // will paint caret in new spot
if (!m_drag) {
repaintSelection(); // will paint selection again
}
// mark that we now are back to normal selection
m_preDragSelection.cursor = QTextCursor();
event->accept();
}
void TextTool::dropEvent(QDropEvent *event, const QPointF &)
{
if (m_drag) {
// restore the old selections
QVector< QAbstractTextDocumentLayout::Selection > sels = KoTextDocument(m_textShapeData->document()).selections();
sels.pop_back();
KoTextDocument(m_textShapeData->document()).setSelections(sels);
}
QTextCursor insertCursor(*m_textEditor.data()->cursor());
m_textEditor.data()->setPosition(m_preDragSelection.cursor.anchor());
m_textEditor.data()->setPosition(m_preDragSelection.cursor.position(), QTextCursor::KeepAnchor);
repaintSelection(); // will erase the selection in new spot
if (m_drag) {
m_textEditor.data()->deleteChar();
}
m_prevCursorPosition = insertCursor.position();
m_textEditor.data()->setPosition(m_prevCursorPosition);
m_textEditor.data()->paste(canvas(), event->mimeData());
m_textEditor.data()->setPosition(m_prevCursorPosition);
//since the paste made insertCursor we can now use that for the end position
m_textEditor.data()->setPosition(insertCursor.position(), QTextCursor::KeepAnchor);
// mark that we no are back to normal selection
m_preDragSelection.cursor = QTextCursor();
event->accept();
}
KoPointedAt TextTool::hitTest(const QPointF &point) const
{
if (!m_textShape || !m_textShapeData) {
return KoPointedAt();
}
QPointF p = m_textShape->convertScreenPos(point);
KoTextLayoutRootArea *rootArea = m_textShapeData->rootArea();
return rootArea ? rootArea->hitTest(p, Qt::FuzzyHit) : KoPointedAt();
}
void TextTool::mouseDoubleClickEvent(KoPointerEvent *event)
{
if (canvas()->shapeManager()->shapeAt(event->point) != m_textShape) {
event->ignore(); // allow the event to be used by another
return;
}
if (event->modifiers() & Qt::ShiftModifier) {
// When whift is pressed we behave as a single press
return mousePressEvent(event);
}
m_textEditor.data()->select(QTextCursor::WordUnderCursor);
m_clickWithinSelection = false;
repaintSelection();
updateSelectionHandler();
}
+void TextTool::mouseTripleClickEvent(KoPointerEvent *event)
+{
+ if (canvas()->shapeManager()->shapeAt(event->point) != m_textShape) {
+ event->ignore(); // allow the event to be used by another
+ return;
+ }
+
+ if (event->modifiers() & Qt::ShiftModifier) {
+ // When whift is pressed we behave as a single press
+ return mousePressEvent(event);
+ }
+
+ m_textEditor.data()->clearSelection();
+ m_textEditor.data()->movePosition(QTextCursor::StartOfBlock);
+ m_textEditor.data()->movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
+
+ m_clickWithinSelection = false;
+
+ repaintSelection();
+ updateSelectionHandler();
+}
+
void TextTool::mouseMoveEvent(KoPointerEvent *event)
{
m_editTipPos = event->globalPos();
if (event->buttons()) {
updateSelectedShape(event->point, true);
}
m_editTipTimer.stop();
if (QToolTip::isVisible()) {
QToolTip::hideText();
}
KoPointedAt pointedAt = hitTest(event->point);
if (event->buttons() == Qt::NoButton) {
if (m_tablePenMode) {
if (pointedAt.tableHit == KoPointedAt::ColumnDivider || pointedAt.tableHit == KoPointedAt::RowDivider) {
useTableBorderCursor();
} else {
useCursor(Qt::IBeamCursor);
}
// do nothing else
return;
}
if (!m_textShapeData || pointedAt.position < 0) {
if (pointedAt.tableHit == KoPointedAt::ColumnDivider) {
useCursor(Qt::SplitHCursor);
m_draggingOrigin = event->point;
} else if (pointedAt.tableHit == KoPointedAt::RowDivider) {
if (pointedAt.tableRowDivider > 0) {
useCursor(Qt::SplitVCursor);
m_draggingOrigin = event->point;
} else {
useCursor(Qt::IBeamCursor);
}
} else {
useCursor(Qt::IBeamCursor);
}
return;
}
QTextCursor mouseOver(m_textShapeData->document());
mouseOver.setPosition(pointedAt.position);
if (m_changeTracker && m_changeTracker->containsInlineChanges(mouseOver.charFormat())) {
m_editTipPointedAt = pointedAt;
if (QToolTip::isVisible()) {
QTimer::singleShot(0, this, SLOT(showEditTip()));
} else {
m_editTipTimer.start();
}
}
if ((pointedAt.bookmark || !pointedAt.externalHRef.isEmpty()) || pointedAt.note || (pointedAt.noteReference > 0)) {
if (event->modifiers() & Qt::ControlModifier) {
useCursor(Qt::PointingHandCursor);
}
m_editTipPointedAt = pointedAt;
if (QToolTip::isVisible()) {
QTimer::singleShot(0, this, SLOT(showEditTip()));
} else {
m_editTipTimer.start();
}
return;
}
// check if mouse pointer is over shape with hyperlink
KoShape *selectedShape = canvas()->shapeManager()->shapeAt(event->point);
if (selectedShape != 0 && selectedShape != m_textShape && selectedShape->hyperLink().size() != 0) {
useCursor(Qt::PointingHandCursor);
return;
}
useCursor(Qt::IBeamCursor);
// Set Arrow Cursor when mouse is on top of annotation shape.
if (selectedShape) {
if (selectedShape->shapeId() == "AnnotationTextShapeID") {
QPointF point(event->point);
if (point.y() <= (selectedShape->position().y() + 25)) {
useCursor(Qt::ArrowCursor);
}
}
}
return;
} else {
if (m_tableDragInfo.tableHit == KoPointedAt::ColumnDivider) {
m_tableDragWithShift = event->modifiers() & Qt::ShiftModifier;
if (m_tableDraggedOnce) {
canvas()->shapeController()->resourceManager()->undoStack()->undo();
}
KUndo2Command *topCmd = m_textEditor.data()->beginEditBlock(kundo2_i18n("Adjust Column Width"));
m_dx = m_draggingOrigin.x() - event->point.x();
if (m_tableDragInfo.tableColumnDivider < m_tableDragInfo.table->columns()
&& m_tableDragInfo.tableTrailSize + m_dx < 0) {
m_dx = -m_tableDragInfo.tableTrailSize;
}
if (m_tableDragInfo.tableColumnDivider > 0) {
if (m_tableDragInfo.tableLeadSize - m_dx < 0) {
m_dx = m_tableDragInfo.tableLeadSize;
}
m_textEditor.data()->adjustTableColumnWidth(m_tableDragInfo.table,
m_tableDragInfo.tableColumnDivider - 1,
m_tableDragInfo.tableLeadSize - m_dx, topCmd);
} else {
m_textEditor.data()->adjustTableWidth(m_tableDragInfo.table, -m_dx, 0.0);
}
if (m_tableDragInfo.tableColumnDivider < m_tableDragInfo.table->columns()) {
if (!m_tableDragWithShift) {
m_textEditor.data()->adjustTableColumnWidth(m_tableDragInfo.table,
m_tableDragInfo.tableColumnDivider,
m_tableDragInfo.tableTrailSize + m_dx, topCmd);
}
} else {
m_tableDragWithShift = true; // act like shift pressed
}
if (m_tableDragWithShift) {
m_textEditor.data()->adjustTableWidth(m_tableDragInfo.table, 0.0, m_dx);
}
m_textEditor.data()->endEditBlock();
m_tableDragInfo.tableDividerPos.setY(m_textShape->convertScreenPos(event->point).y());
if (m_tableDraggedOnce) {
//we need to redraw like this so we update outside the textshape too
if (canvas()->canvasWidget()) {
canvas()->canvasWidget()->update();
}
}
m_tableDraggedOnce = true;
} else if (m_tableDragInfo.tableHit == KoPointedAt::RowDivider) {
if (m_tableDraggedOnce) {
canvas()->shapeController()->resourceManager()->undoStack()->undo();
}
if (m_tableDragInfo.tableRowDivider > 0) {
KUndo2Command *topCmd = m_textEditor.data()->beginEditBlock(kundo2_i18n("Adjust Row Height"));
m_dy = m_draggingOrigin.y() - event->point.y();
if (m_tableDragInfo.tableLeadSize - m_dy < 0) {
m_dy = m_tableDragInfo.tableLeadSize;
}
m_textEditor.data()->adjustTableRowHeight(m_tableDragInfo.table,
m_tableDragInfo.tableRowDivider - 1,
m_tableDragInfo.tableLeadSize - m_dy, topCmd);
m_textEditor.data()->endEditBlock();
m_tableDragInfo.tableDividerPos.setX(m_textShape->convertScreenPos(event->point).x());
if (m_tableDraggedOnce) {
//we need to redraw like this so we update outside the textshape too
if (canvas()->canvasWidget()) {
canvas()->canvasWidget()->update();
}
}
m_tableDraggedOnce = true;
}
} else if (m_tablePenMode) {
// do nothing
} else if (m_clickWithinSelection) {
if (!m_drag && (event->pos() - m_draggingOrigin).manhattanLength()
>= QApplication::startDragDistance()) {
QMimeData *mimeData = generateMimeData();
if (mimeData) {
m_drag = new QDrag(canvas()->canvasWidget());
m_drag->setMimeData(mimeData);
m_drag->exec(Qt::MoveAction | Qt::CopyAction, Qt::CopyAction);
m_drag = 0;
}
}
} else {
useCursor(Qt::IBeamCursor);
if (pointedAt.position == m_textEditor.data()->position()) {
return;
}
if (pointedAt.position >= 0) {
if (m_textEditor.data()->hasSelection()) {
repaintSelection(); // will erase selection
} else {
repaintCaret();
}
m_textEditor.data()->setPosition(pointedAt.position, QTextCursor::KeepAnchor);
if (m_textEditor.data()->hasSelection()) {
repaintSelection();
} else {
repaintCaret();
}
}
}
updateSelectionHandler();
}
}
void TextTool::mouseReleaseEvent(KoPointerEvent *event)
{
event->ignore();
editingPluginEvents();
m_tableDragInfo.tableHit = KoPointedAt::None;
if (m_tableDraggedOnce) {
m_tableDraggedOnce = false;
//we need to redraw like this so we update outside the textshape too
if (canvas()->canvasWidget()) {
canvas()->canvasWidget()->update();
}
}
if (!m_textShapeData) {
return;
}
// check if mouse pointer is not over some shape with hyperlink
KoShape *selectedShape = canvas()->shapeManager()->shapeAt(event->point);
if (selectedShape != 0 && selectedShape != m_textShape && selectedShape->hyperLink().size() != 0) {
QString url = selectedShape->hyperLink();
runUrl(event, url);
return;
}
KoPointedAt pointedAt = hitTest(event->point);
if (m_clickWithinSelection && !m_drag) {
if (m_caretTimer.isActive()) { // make the caret not blink, (blinks again after first draw)
m_caretTimer.stop();
m_caretTimer.setInterval(50);
m_caretTimer.start();
m_caretTimerState = true; // turn caret instantly on on click
}
repaintCaret(); // will erase caret
repaintSelection(); // will erase selection
m_textEditor.data()->setPosition(pointedAt.position);
repaintCaret(); // will paint caret in new spot
}
// Is there an anchor here ?
if ((event->modifiers() & Qt::ControlModifier) && !m_textEditor.data()->hasSelection()) {
if (pointedAt.bookmark) {
m_textEditor.data()->setPosition(pointedAt.bookmark->rangeStart());
ensureCursorVisible();
event->accept();
return;
}
if (pointedAt.note) {
m_textEditor.data()->setPosition(pointedAt.note->textFrame()->firstPosition());
ensureCursorVisible();
event->accept();
return;
}
if (pointedAt.noteReference > 0) {
m_textEditor.data()->setPosition(pointedAt.noteReference);
ensureCursorVisible();
event->accept();
return;
}
if (!pointedAt.externalHRef.isEmpty()) {
runUrl(event, pointedAt.externalHRef);
}
}
}
void TextTool::keyPressEvent(QKeyEvent *event)
{
int destinationPosition = -1; // for those cases where the moveOperation is not relevant;
QTextCursor::MoveOperation moveOperation = QTextCursor::NoMove;
KoTextEditor *textEditor = m_textEditor.data();
m_tablePenMode = false; // keypress always stops the table (border) pen mode
Q_ASSERT(textEditor);
if (event->key() == Qt::Key_Backspace) {
if (!textEditor->hasSelection() && textEditor->block().textList()
&& (textEditor->position() == textEditor->block().position())
&& !(m_changeTracker && m_changeTracker->recordChanges())) {
if (!textEditor->blockFormat().boolProperty(KoParagraphStyle::UnnumberedListItem)) {
// backspace at beginning of numbered list item, makes it unnumbered
textEditor->toggleListNumbering(false);
} else {
KoListLevelProperties llp;
llp.setStyle(KoListStyle::None);
llp.setLevel(0);
// backspace on numbered, empty parag, removes numbering.
textEditor->setListProperties(llp);
}
} else if (textEditor->position() > 0 || textEditor->hasSelection()) {
if (!textEditor->hasSelection() && event->modifiers() & Qt::ControlModifier) { // delete prev word.
textEditor->movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor);
}
textEditor->deletePreviousChar();
editingPluginEvents();
}
} else if ((event->key() == Qt::Key_Tab)
&& ((!textEditor->hasSelection() && (textEditor->position() == textEditor->block().position())) || (textEditor->block().document()->findBlock(textEditor->anchor()) != textEditor->block().document()->findBlock(textEditor->position()))) && textEditor->block().textList()) {
ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::IncreaseLevel;
ChangeListLevelCommand *cll = new ChangeListLevelCommand(*textEditor->cursor(), type, 1);
textEditor->addCommand(cll);
editingPluginEvents();
} else if ((event->key() == Qt::Key_Backtab)
&& ((!textEditor->hasSelection() && (textEditor->position() == textEditor->block().position())) || (textEditor->block().document()->findBlock(textEditor->anchor()) != textEditor->block().document()->findBlock(textEditor->position()))) && textEditor->block().textList() && !(m_changeTracker && m_changeTracker->recordChanges())) {
ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::DecreaseLevel;
ChangeListLevelCommand *cll = new ChangeListLevelCommand(*textEditor->cursor(), type, 1);
textEditor->addCommand(cll);
editingPluginEvents();
} else if (event->key() == Qt::Key_Delete) {
if (!textEditor->hasSelection() && event->modifiers() & Qt::ControlModifier) {// delete next word.
textEditor->movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor);
}
// the event only gets through when the Del is not used in the app
// if the app forwards Del then deleteSelection is used
textEditor->deleteChar();
editingPluginEvents();
} else if ((event->key() == Qt::Key_Left) && (event->modifiers() & Qt::ControlModifier) == 0) {
moveOperation = QTextCursor::Left;
} else if ((event->key() == Qt::Key_Right) && (event->modifiers() & Qt::ControlModifier) == 0) {
moveOperation = QTextCursor::Right;
} else if ((event->key() == Qt::Key_Up) && (event->modifiers() & Qt::ControlModifier) == 0) {
moveOperation = QTextCursor::Up;
} else if ((event->key() == Qt::Key_Down) && (event->modifiers() & Qt::ControlModifier) == 0) {
moveOperation = QTextCursor::Down;
} else {
// check for shortcuts.
QKeySequence item(event->key() | ((Qt::ControlModifier | Qt::AltModifier) & event->modifiers()));
if (hit(item, KStandardShortcut::Begin))
// Goto beginning of the document. Default: Ctrl-Home
{
destinationPosition = 0;
} else if (hit(item, KStandardShortcut::End)) {
// Goto end of the document. Default: Ctrl-End
if (m_textShapeData) {
QTextBlock last = m_textShapeData->document()->lastBlock();
destinationPosition = last.position() + last.length() - 1;
}
} else if (hit(item, KStandardShortcut::Prior)) { // page up
// Scroll up one page. Default: Prior
event->ignore(); // let app level actions handle it
return;
} else if (hit(item, KStandardShortcut::Next)) {
// Scroll down one page. Default: Next
event->ignore(); // let app level actions handle it
return;
} else if (hit(item, KStandardShortcut::BeginningOfLine))
// Goto beginning of current line. Default: Home
{
moveOperation = QTextCursor::StartOfLine;
} else if (hit(item, KStandardShortcut::EndOfLine))
// Goto end of current line. Default: End
{
moveOperation = QTextCursor::EndOfLine;
} else if (hit(item, KStandardShortcut::BackwardWord)) {
moveOperation = QTextCursor::WordLeft;
} else if (hit(item, KStandardShortcut::ForwardWord)) {
moveOperation = QTextCursor::WordRight;
}
#ifdef Q_OS_OSX
// Don't reject "alt" key, it may be used for typing text on Mac OS
else if ((event->modifiers() & Qt::ControlModifier) || event->text().length() == 0) {
#else
else if ((event->modifiers() & (Qt::ControlModifier | Qt::AltModifier)) || event->text().length() == 0) {
#endif
event->ignore();
return;
} else if ((event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)) {
m_prevCursorPosition = textEditor->position();
textEditor->newLine();
updateActions();
editingPluginEvents();
} else if ((event->key() == Qt::Key_Tab || !(event->text().length() == 1 && !event->text().at(0).isPrint()))) { // insert the text
m_prevCursorPosition = textEditor->position();
startingSimpleEdit(); //signal editing plugins that this is a simple edit
textEditor->insertText(event->text());
editingPluginEvents();
}
}
if (moveOperation != QTextCursor::NoMove || destinationPosition != -1) {
useCursor(Qt::BlankCursor);
bool shiftPressed = event->modifiers() & Qt::ShiftModifier;
if (textEditor->hasSelection()) {
repaintSelection(); // will erase selection
} else {
repaintCaret();
}
QTextBlockFormat format = textEditor->blockFormat();
KoText::Direction dir = static_cast(format.intProperty(KoParagraphStyle::TextProgressionDirection));
bool isRtl;
if (dir == KoText::AutoDirection) {
isRtl = textEditor->block().text().isRightToLeft();
} else {
isRtl = dir == KoText::RightLeftTopBottom;
}
if (isRtl) { // if RTL toggle direction of cursor movement.
switch (moveOperation) {
case QTextCursor::Left: moveOperation = QTextCursor::Right; break;
case QTextCursor::Right: moveOperation = QTextCursor::Left; break;
case QTextCursor::WordRight: moveOperation = QTextCursor::WordLeft; break;
case QTextCursor::WordLeft: moveOperation = QTextCursor::WordRight; break;
default: break;
}
}
int prevPosition = textEditor->position();
if (moveOperation != QTextCursor::NoMove)
textEditor->movePosition(moveOperation,
shiftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor);
else
textEditor->setPosition(destinationPosition,
shiftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor);
if (moveOperation == QTextCursor::Down && prevPosition == textEditor->position()) {
// change behavior a little big from Qt; at the bottom of the doc we go to the end of the doc
textEditor->movePosition(QTextCursor::End,
shiftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor);
}
if (shiftPressed) { // altered selection.
repaintSelection();
} else {
repaintCaret();
}
updateActions();
editingPluginEvents();
}
if (m_caretTimer.isActive()) { // make the caret not blink but decide on the action if its visible or not.
m_caretTimer.stop();
m_caretTimer.setInterval(50);
m_caretTimer.start();
m_caretTimerState = true; // turn caret on while typing
}
if (moveOperation != QTextCursor::NoMove)
// this difference in handling is need to prevent leaving a trail of old cursors onscreen
{
ensureCursorVisible();
} else {
m_delayedEnsureVisible = true;
}
updateActions();
updateSelectionHandler();
}
QVariant TextTool::inputMethodQuery(Qt::InputMethodQuery query, const KoViewConverter &converter) const
{
KoTextEditor *textEditor = m_textEditor.data();
if (!textEditor || !m_textShapeData) {
return QVariant();
}
switch (query) {
case Qt::ImMicroFocus: {
// The rectangle covering the area of the input cursor in widget coordinates.
QRectF rect = caretRect(textEditor->cursor());
rect.moveTop(rect.top() - m_textShapeData->documentOffset());
QTransform shapeMatrix = m_textShape->absoluteTransformation(&converter);
qreal zoomX, zoomY;
converter.zoom(&zoomX, &zoomY);
shapeMatrix.scale(zoomX, zoomY);
rect = shapeMatrix.mapRect(rect);
return rect.toRect();
}
case Qt::ImFont:
// The currently used font for text input.
return textEditor->charFormat().font();
case Qt::ImCursorPosition:
// The logical position of the cursor within the text surrounding the input area (see ImSurroundingText).
return textEditor->position() - textEditor->block().position();
case Qt::ImSurroundingText:
// The plain text around the input area, for example the current paragraph.
return textEditor->block().text();
case Qt::ImCurrentSelection:
// The currently selected text.
return textEditor->selectedText();
default:
; // Qt 4.6 adds ImMaximumTextLength and ImAnchorPosition
}
return QVariant();
}
void TextTool::inputMethodEvent(QInputMethodEvent *event)
{
KoTextEditor *textEditor = m_textEditor.data();
if (textEditor == 0) {
return;
}
if (event->replacementLength() > 0) {
textEditor->setPosition(textEditor->position() + event->replacementStart());
for (int i = event->replacementLength(); i > 0; --i) {
textEditor->deleteChar();
}
}
if (!event->commitString().isEmpty()) {
QKeyEvent ke(QEvent::KeyPress, -1, 0, event->commitString());
keyPressEvent(&ke);
// The cursor may reside in a different block before vs. after keyPressEvent.
QTextBlock block = textEditor->block();
QTextLayout *layout = block.layout();
Q_ASSERT(layout);
layout->setPreeditArea(-1, QString());
} else {
QTextBlock block = textEditor->block();
QTextLayout *layout = block.layout();
Q_ASSERT(layout);
layout->setPreeditArea(textEditor->position() - block.position(), event->preeditString());
const_cast(textEditor->document())->markContentsDirty(textEditor->position(), event->preeditString().length());
}
event->accept();
}
void TextTool::ensureCursorVisible(bool moveView)
{
KoTextEditor *textEditor = m_textEditor.data();
if (!textEditor || !m_textShapeData) {
return;
}
bool upToDate;
QRectF cRect = caretRect(textEditor->cursor(), &upToDate);
KoTextDocumentLayout *lay = qobject_cast(m_textShapeData->document()->documentLayout());
Q_ASSERT(lay);
KoTextLayoutRootArea *rootArea = lay->rootAreaForPoint(cRect.center());
if (rootArea && rootArea->associatedShape() && m_textShapeData->rootArea() != rootArea) {
// If we have changed root area we need to update m_textShape and m_textShapeData
m_textShape = static_cast(rootArea->associatedShape());
Q_ASSERT(m_textShape);
disconnect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved()));
m_textShapeData = static_cast(m_textShape->userData());
Q_ASSERT(m_textShapeData);
connect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved()));
}
if (!moveView) {
return;
}
if (!upToDate) { // paragraph is not yet layouted.
// The number one usecase for this is when the user pressed enter.
// try to do it on next caret blink
m_delayedEnsureVisible = true;
return; // we shouldn't move to an obsolete position
}
cRect.moveTop(cRect.top() - m_textShapeData->documentOffset());
canvas()->ensureVisible(m_textShape->absoluteTransformation(0).mapRect(cRect));
}
void TextTool::keyReleaseEvent(QKeyEvent *event)
{
event->accept();
}
void TextTool::updateActions()
{
bool notInAnnotation = !dynamic_cast(m_textShape);
KoTextEditor *textEditor = m_textEditor.data();
if (textEditor == 0) {
return;
}
m_allowActions = false;
//Update the characterStyle related GUI elements
QTextCharFormat cf = textEditor->charFormat();
m_actionFormatBold->setChecked(cf.fontWeight() > QFont::Normal);
m_actionFormatItalic->setChecked(cf.fontItalic());
m_actionFormatUnderline->setChecked(cf.intProperty(KoCharacterStyle::UnderlineType) != KoCharacterStyle::NoLineType);
m_actionFormatStrikeOut->setChecked(cf.intProperty(KoCharacterStyle::StrikeOutType) != KoCharacterStyle::NoLineType);
bool super = false, sub = false;
switch (cf.verticalAlignment()) {
case QTextCharFormat::AlignSuperScript:
super = true;
break;
case QTextCharFormat::AlignSubScript:
sub = true;
break;
default:;
}
m_actionFormatSuper->setChecked(super);
m_actionFormatSub->setChecked(sub);
m_actionFormatFontSize->setFontSize(cf.font().pointSizeF());
m_actionFormatFontFamily->setFont(cf.font().family());
KoTextShapeData::ResizeMethod resizemethod = KoTextShapeData::AutoResize;
if (m_textShapeData) {
resizemethod = m_textShapeData->resizeMethod();
}
m_shrinkToFitAction->setEnabled(resizemethod != KoTextShapeData::AutoResize && notInAnnotation);
m_shrinkToFitAction->setChecked(resizemethod == KoTextShapeData::ShrinkToFitResize);
m_growWidthAction->setEnabled(resizemethod != KoTextShapeData::AutoResize && notInAnnotation);
m_growWidthAction->setChecked(resizemethod == KoTextShapeData::AutoGrowWidth || resizemethod == KoTextShapeData::AutoGrowWidthAndHeight);
m_growHeightAction->setEnabled(resizemethod != KoTextShapeData::AutoResize && notInAnnotation);
m_growHeightAction->setChecked(resizemethod == KoTextShapeData::AutoGrowHeight || resizemethod == KoTextShapeData::AutoGrowWidthAndHeight);
//update paragraphStyle GUI element
QTextBlockFormat bf = textEditor->blockFormat();
if (bf.hasProperty(KoParagraphStyle::TextProgressionDirection)) {
switch (bf.intProperty(KoParagraphStyle::TextProgressionDirection)) {
case KoText::RightLeftTopBottom:
m_actionChangeDirection->setChecked(true);
break;
case KoText::LeftRightTopBottom:
default:
m_actionChangeDirection->setChecked(false);
break;
}
} else {
m_actionChangeDirection->setChecked(textEditor->block().text().isRightToLeft());
}
if (bf.alignment() == Qt::AlignLeading || bf.alignment() == Qt::AlignTrailing) {
bool revert = (textEditor->block().layout()->textOption().textDirection() == Qt::RightToLeft);
if ((bf.alignment() == Qt::AlignLeading) ^ revert) {
m_actionAlignLeft->setChecked(true);
} else {
m_actionAlignRight->setChecked(true);
}
} else if (bf.alignment() == Qt::AlignHCenter) {
m_actionAlignCenter->setChecked(true);
}
if (bf.alignment() == Qt::AlignJustify) {
m_actionAlignBlock->setChecked(true);
} else if (bf.alignment() == (Qt::AlignLeft | Qt::AlignAbsolute)) {
m_actionAlignLeft->setChecked(true);
} else if (bf.alignment() == (Qt::AlignRight | Qt::AlignAbsolute)) {
m_actionAlignRight->setChecked(true);
}
if (textEditor->block().textList()) {
QTextListFormat listFormat = textEditor->block().textList()->format();
if (listFormat.intProperty(KoListStyle::Level) > 1) {
m_actionFormatDecreaseIndent->setEnabled(true);
} else {
m_actionFormatDecreaseIndent->setEnabled(false);
}
if (listFormat.intProperty(KoListStyle::Level) < 10) {
m_actionFormatIncreaseIndent->setEnabled(true);
} else {
m_actionFormatIncreaseIndent->setEnabled(false);
}
} else {
m_actionFormatDecreaseIndent->setEnabled(textEditor->blockFormat().leftMargin() > 0.);
}
m_allowActions = true;
bool useAdvancedText = !(canvas()->resourceManager()->intResource(KoCanvasResourceManager::ApplicationSpeciality)
& KoCanvasResourceManager::NoAdvancedText);
if (useAdvancedText) {
action("insert_table")->setEnabled(notInAnnotation);
bool hasTable = textEditor->currentTable();
action("insert_tablerow_above")->setEnabled(hasTable && notInAnnotation);
action("insert_tablerow_below")->setEnabled(hasTable && notInAnnotation);
action("insert_tablecolumn_left")->setEnabled(hasTable && notInAnnotation);
action("insert_tablecolumn_right")->setEnabled(hasTable && notInAnnotation);
action("delete_tablerow")->setEnabled(hasTable && notInAnnotation);
action("delete_tablecolumn")->setEnabled(hasTable && notInAnnotation);
action("merge_tablecells")->setEnabled(hasTable && notInAnnotation);
action("split_tablecells")->setEnabled(hasTable && notInAnnotation);
action("activate_borderpainter")->setEnabled(hasTable && notInAnnotation);
}
action("insert_annotation")->setEnabled(notInAnnotation);
///TODO if selection contains several different format
emit blockChanged(textEditor->block());
emit charFormatChanged(cf, textEditor->blockCharFormat());
emit blockFormatChanged(bf);
}
QMenu *TextTool::popupActionsMenu()
{
return m_contextMenu.data();
}
void TextTool::updateStyleManager()
{
if (!m_textShapeData) {
return;
}
KoStyleManager *styleManager = KoTextDocument(m_textShapeData->document()).styleManager();
emit styleManagerChanged(styleManager);
//TODO move this to its own method
m_changeTracker = KoTextDocument(m_textShapeData->document()).changeTracker();
}
void TextTool::activate(ToolActivation activation, const QSet &shapes)
{
KoToolBase::activate(activation, shapes);
m_caretTimer.start();
m_caretTimerState = true;
foreach (KoShape *shape, shapes) {
m_textShape = dynamic_cast(shape);
if (m_textShape) {
break;
}
}
if (!m_textShape) { // none found
emit done();
// This is how we inform the rulers of the active range
// No shape means no active range
canvas()->resourceManager()->setResource(KoCanvasResourceManager::ActiveRange, QVariant(QRectF()));
return;
}
// This is how we inform the rulers of the active range
// For now we will not consider table cells, but just give the shape dimensions
QVariant v;
QRectF rect(QPoint(), m_textShape->size());
rect = m_textShape->absoluteTransformation(0).mapRect(rect);
v.setValue(rect);
canvas()->resourceManager()->setResource(KoCanvasResourceManager::ActiveRange, v);
if ((!m_oldTextEditor.isNull()) && m_oldTextEditor.data()->document() != static_cast(m_textShape->userData())->document()) {
m_oldTextEditor.data()->setPosition(m_oldTextEditor.data()->position());
//we need to redraw like this so we update the old textshape whereever it may be
if (canvas()->canvasWidget()) {
canvas()->canvasWidget()->update();
}
}
setShapeData(static_cast(m_textShape->userData()));
useCursor(Qt::IBeamCursor);
updateStyleManager();
repaintSelection();
updateSelectionHandler();
updateActions();
if (m_specialCharacterDocker) {
m_specialCharacterDocker->setEnabled(true);
}
}
void TextTool::deactivate()
{
m_caretTimer.stop();
m_caretTimerState = false;
repaintCaret();
m_textShape = 0;
// This is how we inform the rulers of the active range
// No shape means no active range
canvas()->resourceManager()->setResource(KoCanvasResourceManager::ActiveRange, QVariant(QRectF()));
m_oldTextEditor = m_textEditor;
setShapeData(0);
updateSelectionHandler();
if (m_specialCharacterDocker) {
m_specialCharacterDocker->setEnabled(false);
m_specialCharacterDocker->setVisible(false);
}
KoToolBase::deactivate();
}
void TextTool::repaintDecorations()
{
if (m_textShapeData) {
repaintSelection();
}
}
void TextTool::repaintCaret()
{
KoTextEditor *textEditor = m_textEditor.data();
if (!textEditor || !m_textShapeData) {
return;
}
KoTextDocumentLayout *lay = qobject_cast(m_textShapeData->document()->documentLayout());
Q_ASSERT(lay); Q_UNUSED(lay);
// If we have changed root area we need to update m_textShape and m_textShapeData
if (m_delayedEnsureVisible) {
m_delayedEnsureVisible = false;
ensureCursorVisible();
return;
}
ensureCursorVisible(false); // ensures the various vars are updated
bool upToDate;
QRectF repaintRect = caretRect(textEditor->cursor(), &upToDate);
repaintRect.moveTop(repaintRect.top() - m_textShapeData->documentOffset());
if (repaintRect.isValid()) {
repaintRect = m_textShape->absoluteTransformation(0).mapRect(repaintRect);
// Make sure there is enough space to show an icon
QRectF iconSize = canvas()->viewConverter()->viewToDocument(QRect(0, 0, 18, 18));
repaintRect.setX(repaintRect.x() - iconSize.width() / 2);
repaintRect.setRight(repaintRect.right() + iconSize.width() / 2);
repaintRect.setTop(repaintRect.y() - iconSize.height() / 2);
repaintRect.setBottom(repaintRect.bottom() + iconSize.height() / 2);
canvas()->updateCanvas(repaintRect);
}
}
void TextTool::repaintSelection()
{
KoTextEditor *textEditor = m_textEditor.data();
if (textEditor == 0) {
return;
}
QTextCursor cursor = *textEditor->cursor();
QList shapes;
KoTextDocumentLayout *lay = qobject_cast(textEditor->document()->documentLayout());
Q_ASSERT(lay);
foreach (KoShape *shape, lay->shapes()) {
TextShape *textShape = dynamic_cast(shape);
if (textShape == 0) { // when the shape is being deleted its no longer a TextShape but a KoShape
continue;
}
//Q_ASSERT(!shapes.contains(textShape));
if (!shapes.contains(textShape)) {
shapes.append(textShape);
}
}
// loop over all shapes that contain the text and update per shape.
QRectF repaintRect = textRect(cursor);
foreach (TextShape *ts, shapes) {
QRectF rect = repaintRect;
rect.moveTop(rect.y() - ts->textShapeData()->documentOffset());
rect = ts->absoluteTransformation(0).mapRect(rect);
QRectF r = ts->boundingRect().intersected(rect);
canvas()->updateCanvas(r);
}
}
QRectF TextTool::caretRect(QTextCursor *cursor, bool *upToDate) const
{
QTextCursor tmpCursor(*cursor);
tmpCursor.setPosition(cursor->position()); // looses the anchor
QRectF rect = textRect(tmpCursor);
if (rect.size() == QSizeF(0, 0)) {
if (upToDate) {
*upToDate = false;
}
rect = m_lastImMicroFocus; // prevent block changed but layout not done
} else {
if (upToDate) {
*upToDate = true;
}
m_lastImMicroFocus = rect;
}
return rect;
}
QRectF TextTool::textRect(QTextCursor &cursor) const
{
if (!m_textShapeData) {
return QRectF();
}
KoTextEditor *textEditor = m_textEditor.data();
KoTextDocumentLayout *lay = qobject_cast(textEditor->document()->documentLayout());
return lay->selectionBoundingBox(cursor);
}
KoToolSelection *TextTool::selection()
{
return m_toolSelection;
}
QList > TextTool::createOptionWidgets()
{
QList > widgets;
SimpleCharacterWidget *scw = new SimpleCharacterWidget(this, 0);
SimpleParagraphWidget *spw = new SimpleParagraphWidget(this, 0);
if (m_textEditor.data()) {
// connect(m_textEditor.data(), SIGNAL(paragraphStyleApplied(KoParagraphStyle*)), spw, SLOT(slotParagraphStyleApplied(KoParagraphStyle*)));
// connect(m_textEditor.data(), SIGNAL(characterStyleApplied(KoCharacterStyle*)), scw, SLOT(slotCharacterStyleApplied(KoCharacterStyle*)));
//initialise the char- and par- widgets with the current block and formats.
scw->setCurrentBlockFormat(m_textEditor.data()->blockFormat());
scw->setCurrentFormat(m_textEditor.data()->charFormat(), m_textEditor.data()-> blockCharFormat());
spw->setCurrentBlock(m_textEditor.data()->block());
spw->setCurrentFormat(m_textEditor.data()->blockFormat());
}
SimpleTableWidget *stw = new SimpleTableWidget(this, 0);
SimpleInsertWidget *siw = new SimpleInsertWidget(this, 0);
/* We do not use these for now. Let's see if they become useful at a certain point in time. If not, we can remove the whole chain (SimpleCharWidget, SimpleParWidget, DockerStyleComboModel)
if (m_textShapeData && KoTextDocument(m_textShapeData->document()).styleManager()) {
scw->setInitialUsedStyles(KoTextDocument(m_textShapeData->document()).styleManager()->usedCharacterStyles());
spw->setInitialUsedStyles(KoTextDocument(m_textShapeData->document()).styleManager()->usedParagraphStyles());
}
*/
// Connect to/with simple character widget (docker)
connect(this, SIGNAL(styleManagerChanged(KoStyleManager*)), scw, SLOT(setStyleManager(KoStyleManager*)));
connect(this, SIGNAL(charFormatChanged(QTextCharFormat,QTextCharFormat)), scw, SLOT(setCurrentFormat(QTextCharFormat,QTextCharFormat)));
connect(this, SIGNAL(blockFormatChanged(QTextBlockFormat)), scw, SLOT(setCurrentBlockFormat(QTextBlockFormat)));
connect(scw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas()));
connect(scw, SIGNAL(characterStyleSelected(KoCharacterStyle*)), this, SLOT(setStyle(KoCharacterStyle*)));
connect(scw, SIGNAL(newStyleRequested(QString)), this, SLOT(createStyleFromCurrentCharFormat(QString)));
connect(scw, SIGNAL(showStyleManager(int)), this, SLOT(showStyleManager(int)));
// Connect to/with simple paragraph widget (docker)
connect(this, SIGNAL(styleManagerChanged(KoStyleManager*)), spw, SLOT(setStyleManager(KoStyleManager*)));
connect(this, SIGNAL(blockChanged(QTextBlock)), spw, SLOT(setCurrentBlock(QTextBlock)));
connect(this, SIGNAL(blockFormatChanged(QTextBlockFormat)), spw, SLOT(setCurrentFormat(QTextBlockFormat)));
connect(spw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas()));
connect(spw, SIGNAL(paragraphStyleSelected(KoParagraphStyle*)), this, SLOT(setStyle(KoParagraphStyle*)));
connect(spw, SIGNAL(newStyleRequested(QString)), this, SLOT(createStyleFromCurrentBlockFormat(QString)));
connect(spw, SIGNAL(showStyleManager(int)), this, SLOT(showStyleManager(int)));
// Connect to/with simple table widget (docker)
connect(this, SIGNAL(styleManagerChanged(KoStyleManager*)), stw, SLOT(setStyleManager(KoStyleManager*)));
connect(stw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas()));
connect(stw, SIGNAL(tableBorderDataUpdated(KoBorder::BorderData)), this, SLOT(setTableBorderData(KoBorder::BorderData)));
// Connect to/with simple insert widget (docker)
connect(siw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas()));
connect(siw, SIGNAL(insertTableQuick(int,int)), this, SLOT(insertTableQuick(int,int)));
updateStyleManager();
if (m_textShape) {
updateActions();
}
scw->setWindowTitle(i18n("Character"));
widgets.append(scw);
spw->setWindowTitle(i18n("Paragraph"));
widgets.append(spw);
bool useAdvancedText = !(canvas()->resourceManager()->intResource(KoCanvasResourceManager::ApplicationSpeciality)
& KoCanvasResourceManager::NoAdvancedText);
if (useAdvancedText) {
stw->setWindowTitle(i18n("Table"));
widgets.append(stw);
siw->setWindowTitle(i18n("Insert"));
widgets.append(siw);
}
return widgets;
}
void TextTool::returnFocusToCanvas()
{
canvas()->canvasWidget()->setFocus();
}
void TextTool::startEditing(KUndo2Command *command)
{
m_currentCommand = command;
m_currentCommandHasChildren = true;
}
void TextTool::stopEditing()
{
m_currentCommand = 0;
m_currentCommandHasChildren = false;
}
void TextTool::insertNewSection()
{
KoTextEditor *textEditor = m_textEditor.data();
if (!textEditor) {
return;
}
textEditor->newSection();
}
void TextTool::configureSection()
{
KoTextEditor *textEditor = m_textEditor.data();
if (!textEditor) {
return;
}
SectionFormatDialog *dia = new SectionFormatDialog(0, m_textEditor.data());
dia->exec();
delete dia;
returnFocusToCanvas();
updateActions();
}
void TextTool::splitSections()
{
KoTextEditor *textEditor = m_textEditor.data();
if (!textEditor) {
return;
}
SectionsSplitDialog *dia = new SectionsSplitDialog(0, m_textEditor.data());
dia->exec();
delete dia;
returnFocusToCanvas();
updateActions();
}
void TextTool::pasteAsText()
{
KoTextEditor *textEditor = m_textEditor.data();
if (!textEditor) {
return;
}
const QMimeData *data = QApplication::clipboard()->mimeData(QClipboard::Clipboard);
// on windows we do not have data if we try to paste this selection
if (!data) {
return;
}
if (data->hasFormat(KoOdf::mimeType(KoOdf::Text))
|| data->hasText()) {
m_prevCursorPosition = m_textEditor.data()->position();
m_textEditor.data()->paste(canvas(), data, true);
editingPluginEvents();
}
}
void TextTool::bold(bool bold)
{
m_textEditor.data()->bold(bold);
}
void TextTool::italic(bool italic)
{
m_textEditor.data()->italic(italic);
}
void TextTool::underline(bool underline)
{
m_textEditor.data()->underline(underline);
}
void TextTool::strikeOut(bool strikeOut)
{
m_textEditor.data()->strikeOut(strikeOut);
}
void TextTool::nonbreakingSpace()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
m_textEditor.data()->insertText(QString(QChar(Qt::Key_nobreakspace)));
}
void TextTool::nonbreakingHyphen()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
m_textEditor.data()->insertText(QString(QChar(0x2013)));
}
void TextTool::softHyphen()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
m_textEditor.data()->insertText(QString(QChar(Qt::Key_hyphen)));
}
void TextTool::lineBreak()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
m_textEditor.data()->insertText(QString(QChar(0x2028)));
}
void TextTool::alignLeft()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
m_textEditor.data()->setHorizontalTextAlignment(Qt::AlignLeft | Qt::AlignAbsolute);
}
void TextTool::alignRight()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
m_textEditor.data()->setHorizontalTextAlignment(Qt::AlignRight | Qt::AlignAbsolute);
}
void TextTool::alignCenter()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
m_textEditor.data()->setHorizontalTextAlignment(Qt::AlignHCenter);
}
void TextTool::alignBlock()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
m_textEditor.data()->setHorizontalTextAlignment(Qt::AlignJustify);
}
void TextTool::superScript(bool on)
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
if (on) {
m_actionFormatSub->setChecked(false);
}
m_textEditor.data()->setVerticalTextAlignment(on ? Qt::AlignTop : Qt::AlignVCenter);
}
void TextTool::subScript(bool on)
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
if (on) {
m_actionFormatSuper->setChecked(false);
}
m_textEditor.data()->setVerticalTextAlignment(on ? Qt::AlignBottom : Qt::AlignVCenter);
}
void TextTool::increaseIndent()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
if (m_textEditor.data()->block().textList()) {
ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::IncreaseLevel;
ChangeListLevelCommand *cll = new ChangeListLevelCommand(*(m_textEditor.data()->cursor()), type, 1);
m_textEditor.data()->addCommand(cll);
editingPluginEvents();
} else {
m_textEditor.data()->increaseIndent();
}
updateActions();
}
void TextTool::decreaseIndent()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
if (m_textEditor.data()->block().textList()) {
ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::DecreaseLevel;
ChangeListLevelCommand *cll = new ChangeListLevelCommand(*(m_textEditor.data()->cursor()), type, 1);
m_textEditor.data()->addCommand(cll);
editingPluginEvents();
} else {
m_textEditor.data()->decreaseIndent();
}
updateActions();
}
void TextTool::decreaseFontSize()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
m_textEditor.data()->decreaseFontSize();
}
void TextTool::increaseFontSize()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
m_textEditor.data()->increaseFontSize();
}
void TextTool::setFontFamily(const QString &font)
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
m_textEditor.data()->setFontFamily(font);
}
void TextTool::setFontSize(qreal size)
{
if (!m_allowActions || !m_textEditor.data() || m_textEditor.isNull()) {
return;
}
m_textEditor.data()->setFontSize(size);
}
void TextTool::insertIndexMarker()
{
// TODO handle result when we figure out how to report errors from a tool.
m_textEditor.data()->insertIndexMarker();
}
void TextTool::insertFrameBreak()
{
m_textEditor.data()->insertFrameBreak();
ensureCursorVisible();
m_delayedEnsureVisible = true;
}
void TextTool::setStyle(KoCharacterStyle *style)
{
KoCharacterStyle *charStyle = style;
//if the given KoCharacterStyle is null, set the KoParagraphStyle character properties
if (!charStyle) {
charStyle = static_cast(KoTextDocument(m_textShapeData->document()).styleManager()->paragraphStyle(m_textEditor.data()->blockFormat().intProperty(KoParagraphStyle::StyleId)));
}
if (charStyle) {
m_textEditor.data()->setStyle(charStyle);
updateActions();
}
}
void TextTool::setStyle(KoParagraphStyle *style)
{
m_textEditor.data()->setStyle(style);
updateActions();
}
void TextTool::insertTable()
{
TableDialog *dia = new TableDialog(0);
if (dia->exec() == TableDialog::Accepted) {
m_textEditor.data()->insertTable(dia->rows(), dia->columns());
}
delete dia;
updateActions();
}
void TextTool::insertTableQuick(int rows, int columns)
{
m_textEditor.data()->insertTable(rows, columns);
updateActions();
}
void TextTool::insertTableRowAbove()
{
m_textEditor.data()->insertTableRowAbove();
}
void TextTool::insertTableRowBelow()
{
m_textEditor.data()->insertTableRowBelow();
}
void TextTool::insertTableColumnLeft()
{
m_textEditor.data()->insertTableColumnLeft();
}
void TextTool::insertTableColumnRight()
{
m_textEditor.data()->insertTableColumnRight();
}
void TextTool::deleteTableColumn()
{
m_textEditor.data()->deleteTableColumn();
}
void TextTool::deleteTableRow()
{
m_textEditor.data()->deleteTableRow();
}
void TextTool::mergeTableCells()
{
m_textEditor.data()->mergeTableCells();
}
void TextTool::splitTableCells()
{
m_textEditor.data()->splitTableCells();
}
void TextTool::useTableBorderCursor()
{
static const unsigned char data[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x68, 0x00,
0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, 0xfd, 0x00,
0x00, 0x80, 0x7e, 0x00, 0x00, 0x40, 0x3f, 0x00, 0x00, 0xa0, 0x1f, 0x00,
0x00, 0xd0, 0x0f, 0x00, 0x00, 0xe8, 0x07, 0x00, 0x00, 0xf4, 0x03, 0x00,
0x00, 0xe4, 0x01, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00,
0x40, 0x32, 0x00, 0x00, 0xa0, 0x0f, 0x00, 0x00, 0xd0, 0x0f, 0x00, 0x00,
0xd0, 0x0f, 0x00, 0x00, 0xe8, 0x07, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x00,
0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
QBitmap result(32, 32);
result.fill(Qt::color0);
QPainter painter(&result);
painter.drawPixmap(0, 0, QBitmap::fromData(QSize(25, 23), data));
QBitmap brushMask = result.createHeuristicMask(false);
useCursor(QCursor(result, brushMask, 1, 21));
}
void TextTool::setTableBorderData(const KoBorder::BorderData &data)
{
m_tablePenMode = true;
m_tablePenBorderData = data;
}
void TextTool::formatParagraph()
{
ParagraphSettingsDialog *dia = new ParagraphSettingsDialog(this, m_textEditor.data());
dia->setUnit(canvas()->unit());
dia->setImageCollection(m_textShape->imageCollection());
dia->exec();
delete dia;
returnFocusToCanvas();
}
void TextTool::testSlot(bool on)
{
qDebug() << "signal received. bool:" << on;
}
void TextTool::selectAll()
{
KoTextEditor *textEditor = m_textEditor.data();
if (!textEditor || !m_textShapeData) {
return;
}
const int selectionLength = qAbs(textEditor->position() - textEditor->anchor());
textEditor->movePosition(QTextCursor::End);
textEditor->setPosition(0, QTextCursor::KeepAnchor);
repaintSelection();
if (selectionLength != qAbs(textEditor->position() - textEditor->anchor())) { // it actually changed
emit selectionChanged(true);
}
}
void TextTool::startMacro(const QString &title)
{
if (title != i18n("Key Press") && title != i18n("Autocorrection")) { //dirty hack while waiting for refactor of text editing
m_textTyping = false;
} else {
m_textTyping = true;
}
if (title != i18n("Delete") && title != i18n("Autocorrection")) { //same dirty hack as above
m_textDeleting = false;
} else {
m_textDeleting = true;
}
if (m_currentCommand) {
return;
}
class MacroCommand : public KUndo2Command
{
public:
MacroCommand(const KUndo2MagicString &title) : KUndo2Command(title), m_first(true) {}
void redo() override
{
if (!m_first) {
KUndo2Command::redo();
}
m_first = false;
}
bool mergeWith(const KUndo2Command *) override
{
return false;
}
bool m_first;
};
/**
* FIXME: The messages genearted by the Text Tool might not be
* properly translated, since we don't control it in
* type-safe way.
*
* The title is already translated string, we just don't
* have any type control over it.
*/
KUndo2MagicString title_workaround = kundo2_noi18n(title);
m_currentCommand = new MacroCommand(title_workaround);
m_currentCommandHasChildren = false;
}
void TextTool::stopMacro()
{
if (!m_currentCommand) {
return;
}
if (!m_currentCommandHasChildren) {
delete m_currentCommand;
}
m_currentCommand = 0;
}
void TextTool::showStyleManager(int styleId)
{
if (!m_textShapeData) {
return;
}
KoStyleManager *styleManager = KoTextDocument(m_textShapeData->document()).styleManager();
Q_ASSERT(styleManager);
if (!styleManager) {
return; //don't crash
}
StyleManagerDialog *dia = new StyleManagerDialog(canvas()->canvasWidget());
dia->setStyleManager(styleManager);
dia->setUnit(canvas()->unit());
KoParagraphStyle *paragraphStyle = styleManager->paragraphStyle(styleId);
if (paragraphStyle) {
dia->setParagraphStyle(paragraphStyle);
}
KoCharacterStyle *characterStyle = styleManager->characterStyle(styleId);
if (characterStyle) {
dia->setCharacterStyle(characterStyle);
}
dia->show();
}
void TextTool::startTextEditingPlugin(const QString &pluginId)
{
KoTextEditingPlugin *plugin = textEditingPluginContainer()->plugin(pluginId);
if (plugin) {
if (m_textEditor.data()->hasSelection()) {
plugin->checkSection(m_textShapeData->document(), m_textEditor.data()->selectionStart(), m_textEditor.data()->selectionEnd());
} else {
plugin->finishedWord(m_textShapeData->document(), m_textEditor.data()->position());
}
}
}
void TextTool::canvasResourceChanged(int key, const QVariant &var)
{
if (m_textEditor.isNull()) {
return;
}
if (!m_textShapeData) {
return;
}
if (m_allowResourceManagerUpdates == false) {
return;
}
if (key == KoText::CurrentTextPosition) {
repaintSelection();
m_textEditor.data()->setPosition(var.toInt());
ensureCursorVisible();
} else if (key == KoText::CurrentTextAnchor) {
repaintSelection();
int pos = m_textEditor.data()->position();
m_textEditor.data()->setPosition(var.toInt());
m_textEditor.data()->setPosition(pos, QTextCursor::KeepAnchor);
} else if (key == KoCanvasResourceManager::Unit) {
m_unit = var.value();
} else {
return;
}
repaintSelection();
}
void TextTool::insertSpecialCharacter()
{
if (m_specialCharacterDocker == 0) {
m_specialCharacterDocker = new InsertCharacter(canvas()->canvasWidget());
connect(m_specialCharacterDocker, SIGNAL(insertCharacter(QString)),
this, SLOT(insertString(QString)));
}
m_specialCharacterDocker->show();
}
void TextTool::insertString(const QString &string)
{
m_textEditor.data()->insertText(string);
returnFocusToCanvas();
}
void TextTool::selectFont()
{
FontDia *fontDlg = new FontDia(m_textEditor.data());
fontDlg->exec();
delete fontDlg;
returnFocusToCanvas();
}
void TextTool::shapeAddedToCanvas()
{
qDebug();
if (m_textShape) {
KoSelection *selection = canvas()->selectedShapesProxy()->selection();
KoShape *shape = selection->firstSelectedShape();
if (shape != m_textShape && canvas()->shapeManager()->shapes().contains(m_textShape)) {
// this situation applies when someone, not us, changed the selection by selecting another
// text shape. Possibly by adding one.
// Deselect the new shape again, so we can keep editing what we were already editing
selection->select(m_textShape);
selection->deselect(shape);
}
}
}
void TextTool::shapeDataRemoved()
{
m_textShapeData = 0;
m_textShape = 0;
if (!m_textEditor.isNull() && !m_textEditor.data()->cursor()->isNull()) {
const QTextDocument *doc = m_textEditor.data()->document();
Q_ASSERT(doc);
KoTextDocumentLayout *lay = qobject_cast(doc->documentLayout());
if (!lay || lay->shapes().isEmpty()) {
emit done();
return;
}
m_textShape = static_cast(lay->shapes().first());
m_textShapeData = static_cast(m_textShape->userData());
connect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved()));
}
}
void TextTool::createStyleFromCurrentBlockFormat(const QString &name)
{
KoTextDocument document(m_textShapeData->document());
KoStyleManager *styleManager = document.styleManager();
KoParagraphStyle *paragraphStyle = new KoParagraphStyle(m_textEditor.data()->blockFormat(), m_textEditor.data()->charFormat());
paragraphStyle->setName(name);
styleManager->add(paragraphStyle);
m_textEditor.data()->setStyle(paragraphStyle);
emit charFormatChanged(m_textEditor.data()->charFormat(), m_textEditor.data()->blockCharFormat());
emit blockFormatChanged(m_textEditor.data()->blockFormat());
}
void TextTool::createStyleFromCurrentCharFormat(const QString &name)
{
KoTextDocument document(m_textShapeData->document());
KoStyleManager *styleManager = document.styleManager();
KoCharacterStyle *originalCharStyle = styleManager->characterStyle(m_textEditor.data()->charFormat().intProperty(KoCharacterStyle::StyleId));
KoCharacterStyle *autoStyle;
if (!originalCharStyle) {
KoCharacterStyle blankStyle;
originalCharStyle = &blankStyle;
autoStyle = originalCharStyle->autoStyle(m_textEditor.data()->charFormat(), m_textEditor.data()->blockCharFormat());
autoStyle->setParentStyle(0);
} else {
autoStyle = originalCharStyle->autoStyle(m_textEditor.data()->charFormat(), m_textEditor.data()->blockCharFormat());
}
autoStyle->setName(name);
styleManager->add(autoStyle);
m_textEditor.data()->setStyle(autoStyle);
emit charFormatChanged(m_textEditor.data()->charFormat(), m_textEditor.data()->blockCharFormat());
}
// ---------- editing plugins methods.
void TextTool::editingPluginEvents()
{
if (m_prevCursorPosition == -1 || m_prevCursorPosition == m_textEditor.data()->position()) {
qDebug() << "m_prevCursorPosition=" << m_prevCursorPosition << "m_textEditor.data()->position()=" << m_textEditor.data()->position();
return;
}
QTextBlock block = m_textEditor.data()->block();
if (!block.contains(m_prevCursorPosition)) {
qDebug() << "m_prevCursorPosition=" << m_prevCursorPosition;
finishedWord();
finishedParagraph();
m_prevCursorPosition = -1;
} else {
int from = m_prevCursorPosition;
int to = m_textEditor.data()->position();
if (from > to) {
qSwap(from, to);
}
QString section = block.text().mid(from - block.position(), to - from);
qDebug() << "from=" << from << "to=" << to;
if (section.contains(' ')) {
finishedWord();
m_prevCursorPosition = -1;
}
}
}
void TextTool::finishedWord()
{
if (m_textShapeData && textEditingPluginContainer()) {
foreach (KoTextEditingPlugin *plugin, textEditingPluginContainer()->values()) {
plugin->finishedWord(m_textShapeData->document(), m_prevCursorPosition);
}
}
}
void TextTool::finishedParagraph()
{
if (m_textShapeData && textEditingPluginContainer()) {
foreach (KoTextEditingPlugin *plugin, textEditingPluginContainer()->values()) {
plugin->finishedParagraph(m_textShapeData->document(), m_prevCursorPosition);
}
}
}
void TextTool::startingSimpleEdit()
{
if (m_textShapeData && textEditingPluginContainer()) {
foreach (KoTextEditingPlugin *plugin, textEditingPluginContainer()->values()) {
plugin->startingSimpleEdit(m_textShapeData->document(), m_prevCursorPosition);
}
}
}
void TextTool::setTextColor(const KoColor &color)
{
m_textEditor.data()->setTextColor(color.toQColor());
}
void TextTool::setBackgroundColor(const KoColor &color)
{
m_textEditor.data()->setTextBackgroundColor(color.toQColor());
}
void TextTool::setGrowWidthToFit(bool enabled)
{
m_textEditor.data()->addCommand(new AutoResizeCommand(m_textShapeData, KoTextShapeData::AutoGrowWidth, enabled));
updateActions();
}
void TextTool::setGrowHeightToFit(bool enabled)
{
m_textEditor.data()->addCommand(new AutoResizeCommand(m_textShapeData, KoTextShapeData::AutoGrowHeight, enabled));
updateActions();
}
void TextTool::setShrinkToFit(bool enabled)
{
m_textEditor.data()->addCommand(new AutoResizeCommand(m_textShapeData, KoTextShapeData::ShrinkToFitResize, enabled));
updateActions();
}
void TextTool::runUrl(KoPointerEvent *event, QString &url)
{
QUrl _url = QUrl::fromUserInput(url);
if (!_url.isLocalFile()) {
QDesktopServices::openUrl(_url);
}
event->accept();
}
void TextTool::debugTextDocument()
{
#ifndef NDEBUG
if (!m_textShapeData) {
return;
}
const int CHARSPERLINE = 80; // TODO Make configurable using ENV var?
const int CHARPOSITION = 278301935;
KoTextDocument document(m_textShapeData->document());
KoStyleManager *styleManager = document.styleManager();
KoInlineTextObjectManager *inlineManager = document.inlineTextObjectManager();
QTextBlock block = m_textShapeData->document()->begin();
for (; block.isValid(); block = block.next()) {
QVariant var = block.blockFormat().property(KoParagraphStyle::StyleId);
if (!var.isNull()) {
KoParagraphStyle *ps = styleManager->paragraphStyle(var.toInt());
qDebug() << "--- Paragraph Style:" << (ps ? ps->name() : QString()) << var.toInt();
}
var = block.charFormat().property(KoCharacterStyle::StyleId);
if (!var.isNull()) {
KoCharacterStyle *cs = styleManager->characterStyle(var.toInt());
qDebug() << "--- Character Style:" << (cs ? cs->name() : QString()) << var.toInt();
}
int lastPrintedChar = -1;
QTextBlock::iterator it;
QString fragmentText;
QList inlineCharacters;
for (it = block.begin(); !it.atEnd(); ++it) {
QTextFragment fragment = it.fragment();
if (!fragment.isValid()) {
continue;
}
QTextCharFormat fmt = fragment.charFormat();
qDebug() << "changeId: " << fmt.property(KoCharacterStyle::ChangeTrackerId);
const int fragmentStart = fragment.position() - block.position();
for (int i = fragmentStart; i < fragmentStart + fragment.length(); i += CHARSPERLINE) {
if (lastPrintedChar == fragmentStart - 1) {
fragmentText += '|';
}
if (lastPrintedChar < fragmentStart || i > fragmentStart) {
QString debug = block.text().mid(lastPrintedChar, CHARSPERLINE);
lastPrintedChar += CHARSPERLINE;
if (lastPrintedChar > block.length()) {
debug += "\\n";
}
qDebug() << debug;
}
var = fmt.property(KoCharacterStyle::StyleId);
QString charStyleLong, charStyleShort;
if (!var.isNull()) { // named style
charStyleShort = QString::number(var.toInt());
KoCharacterStyle *cs = styleManager->characterStyle(var.toInt());
if (cs) {
charStyleLong = cs->name();
}
}
if (inlineManager && fmt.hasProperty(KoCharacterStyle::InlineInstanceId)) {
QTextCharFormat inlineFmt = fmt;
inlineFmt.setProperty(CHARPOSITION, fragmentStart);
inlineCharacters << inlineFmt;
}
if (fragment.length() > charStyleLong.length()) {
fragmentText += charStyleLong;
} else if (fragment.length() > charStyleShort.length()) {
fragmentText += charStyleShort;
} else if (fragment.length() >= 2) {
fragmentText += QChar(8230); // elipses
}
int rest = fragmentStart - (lastPrintedChar - CHARSPERLINE) + fragment.length() - fragmentText.length();
rest = qMin(rest, CHARSPERLINE - fragmentText.length());
if (rest >= 2) {
fragmentText = QString("%1%2").arg(fragmentText).arg(' ', rest);
}
if (rest >= 0) {
fragmentText += '|';
}
if (fragmentText.length() >= CHARSPERLINE) {
qDebug() << fragmentText;
fragmentText.clear();
}
}
}
if (!fragmentText.isEmpty()) {
qDebug() << fragmentText;
} else if (block.length() == 1) { // no actual tet
qDebug() << "\\n";
}
foreach (const QTextCharFormat &cf, inlineCharacters) {
KoInlineObject *object = inlineManager->inlineTextObject(cf);
qDebug() << "At pos:" << cf.intProperty(CHARPOSITION) << object;
// qDebug() << "-> id:" << cf.intProperty(577297549);
}
QTextList *list = block.textList();
if (list) {
if (list->format().hasProperty(KoListStyle::StyleId)) {
KoListStyle *ls = styleManager->listStyle(list->format().intProperty(KoListStyle::StyleId));
qDebug() << " List style applied:" << ls->styleId() << ls->name();
} else {
qDebug() << " +- is a list..." << list;
}
}
}
#endif
}
void TextTool::debugTextStyles()
{
#ifndef NDEBUG
if (!m_textShapeData) {
return;
}
KoTextDocument document(m_textShapeData->document());
KoStyleManager *styleManager = document.styleManager();
QSet seenStyles;
foreach (KoParagraphStyle *style, styleManager->paragraphStyles()) {
qDebug() << style->styleId() << style->name() << (styleManager->defaultParagraphStyle() == style ? "[Default]" : "");
KoListStyle *ls = style->listStyle();
if (ls) { // optional ;)
qDebug() << " +- ListStyle: " << ls->styleId() << ls->name()
<< (ls == styleManager->defaultListStyle() ? "[Default]" : "");
foreach (int level, ls->listLevels()) {
KoListLevelProperties llp = ls->levelProperties(level);
qDebug() << " | level" << llp.level() << " style (enum):" << llp.style();
if (llp.bulletCharacter().unicode() != 0) {
qDebug() << " | bullet" << llp.bulletCharacter();
}
}
seenStyles << ls->styleId();
}
}
bool first = true;
foreach (KoCharacterStyle *style, styleManager->characterStyles()) {
if (seenStyles.contains(style->styleId())) {
continue;
}
if (first) {
qDebug() << "--- Character styles ---";
first = false;
}
qDebug() << style->styleId() << style->name();
qDebug() << style->font();
}
first = true;
foreach (KoListStyle *style, styleManager->listStyles()) {
if (seenStyles.contains(style->styleId())) {
continue;
}
if (first) {
qDebug() << "--- List styles ---";
first = false;
}
qDebug() << style->styleId() << style->name()
<< (style == styleManager->defaultListStyle() ? "[Default]" : "");
}
#endif
}
void TextTool::textDirectionChanged()
{
if (!m_allowActions || !m_textEditor.data()) {
return;
}
QTextBlockFormat blockFormat;
if (m_actionChangeDirection->isChecked()) {
blockFormat.setProperty(KoParagraphStyle::TextProgressionDirection, KoText::RightLeftTopBottom);
} else {
blockFormat.setProperty(KoParagraphStyle::TextProgressionDirection, KoText::LeftRightTopBottom);
}
m_textEditor.data()->mergeBlockFormat(blockFormat);
}
void TextTool::setListLevel(int level)
{
if (level < 1 || level > 10) {
return;
}
KoTextEditor *textEditor = m_textEditor.data();
if (textEditor->block().textList()) {
ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::SetLevel;
ChangeListLevelCommand *cll = new ChangeListLevelCommand(*textEditor->cursor(), type, level);
textEditor->addCommand(cll);
editingPluginEvents();
}
}
void TextTool::insertAnnotation()
{
AnnotationTextShape *shape = (AnnotationTextShape *)KoShapeRegistry::instance()->value(AnnotationShape_SHAPEID)->createDefaultShape(canvas()->shapeController()->resourceManager());
textEditor()->addAnnotation(shape);
// Set annotation creator.
KConfig cfg("kritarc");
cfg.reparseConfiguration();
KConfigGroup authorGroup(&cfg, "Author");
QStringList profiles = authorGroup.readEntry("profile-names", QStringList());
KSharedConfig::openConfig()->reparseConfiguration();
KConfigGroup appAuthorGroup(KSharedConfig::openConfig(), "Author");
QString profile = appAuthorGroup.readEntry("active-profile", "");
KConfigGroup cgs(&authorGroup, "Author-" + profile);
if (profiles.contains(profile)) {
KConfigGroup cgs(&authorGroup, "Author-" + profile);
shape->setCreator(cgs.readEntry("creator"));
} else {
if (profile == "anonymous") {
shape->setCreator("Anonymous");
} else {
KUser user(KUser::UseRealUserID);
shape->setCreator(user.property(KUser::FullName).toString());
}
}
// Set Annotation creation date.
shape->setDate(QDate::currentDate().toString(Qt::ISODate));
}
diff --git a/plugins/flake/textshape/TextTool.h b/plugins/flake/textshape/TextTool.h
index c0a36cf182..d3c0f13f9c 100644
--- a/plugins/flake/textshape/TextTool.h
+++ b/plugins/flake/textshape/TextTool.h
@@ -1,417 +1,427 @@
/* This file is part of the KDE project
* Copyright (C) 2006-2009 Thomas Zander
* Copyright (C) 2008 Thorsten Zachmann
* Copyright (C) 2009 KO GmbH
* Copyright (C) 2011 Mojtaba Shahi Senobari
* Copyright (C) 2008, 2012 Pierre Stirnweiss
* Copyright (C) 2014 Denis Kuplyakov
*
* 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 KOTEXTTOOL_H
#define KOTEXTTOOL_H
#include "TextShape.h"
#include "KoPointedAt.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
class InsertCharacter;
class KoChangeTracker;
class KoCharacterStyle;
class KoColor;
class KoColorPopupAction;
class KoParagraphStyle;
class KoStyleManager;
class KoTextEditor;
class UndoTextCommand;
class QAction;
class KActionMenu;
class KoFontFamilyAction;
class FontSizeAction;
class KUndo2Command;
class QDrag;
class QMimeData;
class QMenu;
class MockCanvas;
class TextToolSelection;
/**
* This is the tool for the text-shape (which is a flake-based plugin).
*/
class TextTool : public KoToolBase, public KoUndoableTool
{
Q_OBJECT
public:
explicit TextTool(KoCanvasBase *canvas);
#ifndef NDEBUG
explicit TextTool(MockCanvas *canvas);
#endif
~TextTool() override;
+ /// reimplemented from superclass
void paint(QPainter &painter, const KoViewConverter &converter) override;
+ /// reimplemented from superclass
void mousePressEvent(KoPointerEvent *event) override;
+ /// reimplemented from superclass
void mouseDoubleClickEvent(KoPointerEvent *event) override;
+ /// reimplemented from superclass
+ void mouseTripleClickEvent(KoPointerEvent *event) override;
+ /// reimplemented from superclass
void mouseMoveEvent(KoPointerEvent *event) override;
+ /// reimplemented from superclass
void mouseReleaseEvent(KoPointerEvent *event) override;
+ /// reimplemented from superclass
void keyPressEvent(QKeyEvent *event) override;
+ /// reimplemented from superclass
void keyReleaseEvent(QKeyEvent *event) override;
/// reimplemented from superclass
void activate(ToolActivation activation, const QSet &shapes) override;
/// reimplemented from superclass
void deactivate() override;
+ /// reimplemented from superclass
void copy() const override;
/// reimplemented from KoUndoableTool
void setAddUndoCommandAllowed(bool allowed) override
{
m_allowAddUndoCommand = allowed;
}
///reimplemented
void deleteSelection() override;
/// reimplemented from superclass
void cut() override;
/// reimplemented from superclass
bool paste() override;
/// reimplemented from superclass
void dragMoveEvent(QDragMoveEvent *event, const QPointF &point) override;
/// reimplemented from superclass
void dragLeaveEvent(QDragLeaveEvent *event) override;
/// reimplemented from superclass
void dropEvent(QDropEvent *event, const QPointF &point) override;
/// reimplemented from superclass
void repaintDecorations() override;
/// reimplemented from superclass
KoToolSelection *selection() override;
/// reimplemented from superclass
QList > createOptionWidgets() override;
/// reimplemented from superclass
QVariant inputMethodQuery(Qt::InputMethodQuery query, const KoViewConverter &converter) const override;
/// reimplemented from superclass
void inputMethodEvent(QInputMethodEvent *event) override;
/// The following two methods allow an undo/redo command to tell the tool, it will modify the QTextDocument and wants to be parent of the undo/redo commands resulting from these changes.
void startEditing(KUndo2Command *command);
void stopEditing();
void setShapeData(KoTextShapeData *data);
QRectF caretRect(QTextCursor *cursor, bool *upToDate = 0) const;
QRectF textRect(QTextCursor &cursor) const;
protected:
virtual void createActions();
TextShape *textShape()
{
return m_textShape;
}
friend class SimpleParagraphWidget;
friend class ParagraphSettingsDialog;
KoTextEditor *textEditor()
{
return m_textEditor.data();
}
public Q_SLOTS:
/// Insert comment to document.
void insertAnnotation();
/// start the textedit-plugin.
void startTextEditingPlugin(const QString &pluginId);
/// reimplemented from KoToolBase
void canvasResourceChanged(int key, const QVariant &res) override;
Q_SIGNALS:
/// emitted every time a different styleManager is set.
void styleManagerChanged(KoStyleManager *manager);
/// emitted every time a caret move leads to a different character format being under the caret
void charFormatChanged(const QTextCharFormat &format, const QTextCharFormat &refBlockCharFormat);
/// emitted every time a caret move leads to a different paragraph format being under the caret
void blockFormatChanged(const QTextBlockFormat &format);
/// emitted every time a caret move leads to a different paragraph format being under the caret
void blockChanged(const QTextBlock &block);
private Q_SLOTS:
/// inserts new paragraph and includes it into the new section
void insertNewSection();
/// configures params of the current section
void configureSection();
/// inserts paragraph between sections bounds
void splitSections();
/// paste text from the clipboard without formatting
void pasteAsText();
/// make the selected text bold or not
void bold(bool);
/// make the selected text italic or not
void italic(bool);
/// underline of the selected text
void underline(bool underline);
/// strikethrough of the selected text
void strikeOut(bool strikeOut);
/// insert a non breaking space at the caret position
void nonbreakingSpace();
/// insert a non breaking hyphen at the caret position
void nonbreakingHyphen();
/// insert a soft hyphen at the caret position
void softHyphen();
/// insert a linebreak at the caret position
void lineBreak();
/// force the remainder of the text into the next page
void insertFrameBreak();
/// align all of the selected text left
void alignLeft();
/// align all of the selected text right
void alignRight();
/// align all of the selected text centered
void alignCenter();
/// align all of the selected text block-justified
void alignBlock();
/// make the selected text switch to be super-script
void superScript(bool);
/// make the selected text switch to be sub-script
void subScript(bool);
/// move the paragraph indent of the selected text to be less (left on LtR text)
void decreaseIndent();
/// move the paragraph indent of the selected text to be more (right on LtR text)
void increaseIndent();
/// Increase the font size. This will preserve eventual difference in font size within the selection.
void increaseFontSize();
/// Decrease font size. See above.
void decreaseFontSize();
/// Set font family
void setFontFamily(const QString &);
/// Set Font size
void setFontSize(qreal size);
/// see KoTextEditor::insertIndexMarker
void insertIndexMarker();
/// shows a dialog to insert a table
void insertTable();
/// insert a table of given dimensions
void insertTableQuick(int rows, int columns);
/// insert a row above
void insertTableRowAbove();
/// insert a row below
void insertTableRowBelow();
/// insert a column left
void insertTableColumnLeft();
/// insert a column right
void insertTableColumnRight();
/// delete a column
void deleteTableColumn();
/// delete a row
void deleteTableRow();
/// merge table cells
void mergeTableCells();
/// split previous merged table cells
void splitTableCells();
/// format the table border (enter table pen mode)
void setTableBorderData(const KoBorder::BorderData &data);
/// shows a dialog to alter the paragraph properties
void formatParagraph();
/// select all text in the current document.
void selectAll();
/// show the style manager
void showStyleManager(int styleId = -1);
/// change color of a selected text
void setTextColor(const KoColor &color);
/// change background color of a selected text
void setBackgroundColor(const KoColor &color);
/// Enable or disable grow-width-to-fit-text.
void setGrowWidthToFit(bool enabled);
/// Enable or disable grow-height-to-fit-text.
void setGrowHeightToFit(bool enabled);
/// Enable or disable shrink-to-fit-text.
void setShrinkToFit(bool enabled);
/// set Paragraph style of current selection. Existing style will be completely overridden.
void setStyle(KoParagraphStyle *syle);
/// set the characterStyle of the current selection. see above.
void setStyle(KoCharacterStyle *style);
/// set the level of current selected list
void setListLevel(int level);
/// slot to call when a series of commands is started that together need to become 1 undo action.
void startMacro(const QString &title);
/// slot to call when a series of commands has ended that together should be 1 undo action.
void stopMacro();
/// show the insert special character docker.
void insertSpecialCharacter();
/// insert string
void insertString(const QString &string);
/// returns the focus to canvas when styles are selected in the optionDocker
void returnFocusToCanvas();
void selectFont();
void shapeAddedToCanvas();
void blinkCaret();
void relayoutContent();
// called when the m_textShapeData has been deleted.
void shapeDataRemoved();
//Show tooltip with editing info
void showEditTip();
/// print debug about the details of the text document
void debugTextDocument();
/// print debug about the details of the styles on the current text document
void debugTextStyles();
void ensureCursorVisible(bool moveView = true);
void createStyleFromCurrentBlockFormat(const QString &name);
void createStyleFromCurrentCharFormat(const QString &name);
void testSlot(bool);
/// change block text direction
void textDirectionChanged();
void updateActions();
QMenu* popupActionsMenu() override;
private:
void repaintCaret();
void repaintSelection();
KoPointedAt hitTest(const QPointF &point) const;
void updateStyleManager();
void updateSelectedShape(const QPointF &point, bool noDocumentChange);
void updateSelectionHandler();
void editingPluginEvents();
void finishedWord();
void finishedParagraph();
void startingSimpleEdit();
void runUrl(KoPointerEvent *event, QString &url);
void useTableBorderCursor();
QMimeData *generateMimeData() const;
TextEditingPluginContainer *textEditingPluginContainer();
private:
friend class UndoTextCommand;
friend class ChangeTracker;
friend class TextCutCommand;
friend class ShowChangesCommand;
TextShape *m_textShape; // where caret of m_textEditor currently is
KoTextShapeData *m_textShapeData; // where caret of m_textEditor currently is
QWeakPointer m_textEditor;
QWeakPointer m_oldTextEditor;
KoChangeTracker *m_changeTracker;
KoUnit m_unit;
bool m_allowActions;
bool m_allowAddUndoCommand;
bool m_allowResourceManagerUpdates;
int m_prevCursorPosition; /// used by editingPluginEvents
int m_prevMouseSelectionStart, m_prevMouseSelectionEnd;
QTimer m_caretTimer;
bool m_caretTimerState;
QAction *m_actionPasteAsText;
QAction *m_actionFormatBold;
QAction *m_actionFormatItalic;
QAction *m_actionFormatUnderline;
QAction *m_actionFormatStrikeOut;
QAction *m_actionAlignLeft;
QAction *m_actionAlignRight;
QAction *m_actionAlignCenter;
QAction *m_actionAlignBlock;
QAction *m_actionFormatSuper;
QAction *m_actionFormatSub;
QAction *m_actionFormatIncreaseIndent;
QAction *m_actionFormatDecreaseIndent;
QAction *m_growWidthAction;
QAction *m_growHeightAction;
QAction *m_shrinkToFitAction;
QAction *m_actionChangeDirection;
QAction *m_actionInsertSection;
QAction *m_actionConfigureSection;
QAction *m_actionSplitSections;
KActionMenu *m_variableMenu;
FontSizeAction *m_actionFormatFontSize;
KoFontFamilyAction *m_actionFormatFontFamily;
KoColorPopupAction *m_actionFormatTextColor;
KoColorPopupAction *m_actionFormatBackgroundColor;
KUndo2Command *m_currentCommand; //this command will be the direct parent of undoCommands generated as the result of QTextDocument changes
bool m_currentCommandHasChildren;
InsertCharacter *m_specialCharacterDocker;
QPointer m_textEditingPlugins;
bool m_textTyping;
bool m_textDeleting;
QTimer m_editTipTimer;
KoPointedAt m_editTipPointedAt;
QPoint m_editTipPos;
bool m_delayedEnsureVisible;
TextToolSelection *m_toolSelection;
KoPointedAt m_tableDragInfo;
bool m_tableDraggedOnce;
bool m_tableDragWithShift;
QPointF m_draggingOrigin;
qreal m_dx;
qreal m_dy;
bool m_tablePenMode;
KoBorder::BorderData m_tablePenBorderData;
mutable QRectF m_lastImMicroFocus;
bool m_clickWithinSelection;
QDrag *m_drag;
QAbstractTextDocumentLayout::Selection m_preDragSelection;
QScopedPointer m_contextMenu;
};
#endif
diff --git a/plugins/impex/jpeg/kis_jpeg_export.cc b/plugins/impex/jpeg/kis_jpeg_export.cc
index c6d74f926a..03b97fe70a 100644
--- a/plugins/impex/jpeg/kis_jpeg_export.cc
+++ b/plugins/impex/jpeg/kis_jpeg_export.cc
@@ -1,223 +1,233 @@
/*
* Copyright (c) 2005 Cyrille Berger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_jpeg_export.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kis_jpeg_converter.h"
class KisExternalLayer;
K_PLUGIN_FACTORY_WITH_JSON(KisJPEGExportFactory, "krita_jpeg_export.json", registerPlugin();)
KisJPEGExport::KisJPEGExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisJPEGExport::~KisJPEGExport()
{
}
KisImportExportFilter::ConversionStatus KisJPEGExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration)
{
KisImageSP image = document->savingImage();
Q_CHECK_PTR(image);
// An extra option to pass to the config widget to set the state correctly, this isn't saved
const KoColorSpace* cs = image->projection()->colorSpace();
bool sRGB = cs->profile()->name().contains(QLatin1String("srgb"), Qt::CaseInsensitive);
configuration->setProperty("is_sRGB", sRGB);
KisJPEGOptions options;
options.progressive = configuration->getBool("progressive", false);
options.quality = configuration->getInt("quality", 80);
options.forceSRGB = configuration->getBool("forceSRGB", false);
options.saveProfile = configuration->getBool("saveProfile", true);
options.optimize = configuration->getBool("optimize", true);
options.smooth = configuration->getInt("smoothing", 0);
options.baseLineJPEG = configuration->getBool("baseline", true);
options.subsampling = configuration->getInt("subsampling", 0);
options.exif = configuration->getBool("exif", true);
options.iptc = configuration->getBool("iptc", true);
options.xmp = configuration->getBool("xmp", true);
- QStringList rgb = configuration->getString("transparencyFillcolor", "255,255,255").split(',');
- options.transparencyFillColor = QColor(rgb[0].toInt(), rgb[1].toInt(), rgb[2].toInt());
+ KoColor c(KoColorSpaceRegistry::instance()->rgb8());
+ c.fromQColor(Qt::white);
+ options.transparencyFillColor = configuration->getColor("transparencyFillcolor", c).toQColor();
KisMetaData::FilterRegistryModel m;
m.setEnabledFilters(configuration->getString("filters").split(","));
options.filters = m.enabledFilters();
KisPaintDeviceSP pd = new KisPaintDevice(*image->projection());
KisJPEGConverter kpc(document, batchMode());
KisPaintLayerSP l = new KisPaintLayer(image, "projection", OPACITY_OPAQUE_U8, pd);
KisExifInfoVisitor eIV;
eIV.visit(image->rootLayer().data());
KisMetaData::Store* eI = 0;
if (eIV.countPaintLayer() == 1) {
eI = eIV.exifInfo();
}
if (eI) {
KisMetaData::Store* copy = new KisMetaData::Store(*eI);
eI = copy;
}
KisImageBuilder_Result res = kpc.buildFile(io, l, options, eI);
if (res == KisImageBuilder_RESULT_OK) {
delete eI;
return KisImportExportFilter::OK;
}
delete eI;
dbgFile << " Result =" << res;
return KisImportExportFilter::InternalError;
}
KisPropertiesConfigurationSP KisJPEGExport::defaultConfiguration(const QByteArray &/*from*/, const QByteArray &/*to*/) const
{
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
cfg->setProperty("progressive", false);
cfg->setProperty("quality", 80);
cfg->setProperty("forceSRGB", false);
cfg->setProperty("saveProfile", true);
cfg->setProperty("optimize", true);
cfg->setProperty("smoothing", 0);
cfg->setProperty("baseline", true);
cfg->setProperty("subsampling", 0);
cfg->setProperty("exif", true);
cfg->setProperty("iptc", true);
cfg->setProperty("xmp", true);
- cfg->setProperty("transparencyFillcolor", QString("255,255,255"));
+
+ KoColor fill_color(KoColorSpaceRegistry::instance()->rgb8());
+ fill_color = KoColor();
+ fill_color.fromQColor(Qt::white);
+ QVariant v;
+ v.setValue(fill_color);
+
+ cfg->setProperty("transparencyFillcolor", v);
cfg->setProperty("filters", "");
return cfg;
}
KisConfigWidget *KisJPEGExport::createConfigurationWidget(QWidget *parent, const QByteArray &/*from*/, const QByteArray &/*to*/) const
{
return new KisWdgOptionsJPEG(parent);
}
void KisJPEGExport::initializeCapabilities()
{
addCapability(KisExportCheckRegistry::instance()->get("sRGBProfileCheck")->create(KisExportCheckBase::SUPPORTED));
addCapability(KisExportCheckRegistry::instance()->get("ExifCheck")->create(KisExportCheckBase::SUPPORTED));
QList > supportedColorModels;
supportedColorModels << QPair()
<< QPair(RGBAColorModelID, Integer8BitsColorDepthID)
<< QPair(GrayAColorModelID, Integer8BitsColorDepthID)
<< QPair(CMYKAColorModelID, Integer8BitsColorDepthID);
addSupportedColorModels(supportedColorModels, "JPEG");
}
KisWdgOptionsJPEG::KisWdgOptionsJPEG(QWidget *parent)
: KisConfigWidget(parent)
{
setupUi(this);
metaDataFilters->setModel(&m_filterRegistryModel);
qualityLevel->setRange(0, 100, 0);
qualityLevel->setSuffix("%");
smoothLevel->setRange(0, 100, 0);
smoothLevel->setSuffix("%");
}
void KisWdgOptionsJPEG::setConfiguration(const KisPropertiesConfigurationSP cfg)
{
progressive->setChecked(cfg->getBool("progressive", false));
qualityLevel->setValue(cfg->getInt("quality", 80));
optimize->setChecked(cfg->getBool("optimize", true));
smoothLevel->setValue(cfg->getInt("smoothing", 0));
baseLineJPEG->setChecked(cfg->getBool("baseline", true));
subsampling->setCurrentIndex(cfg->getInt("subsampling", 0));
exif->setChecked(cfg->getBool("exif", true));
iptc->setChecked(cfg->getBool("iptc", true));
xmp->setChecked(cfg->getBool("xmp", true));
chkForceSRGB->setVisible(cfg->getBool("is_sRGB"));
chkForceSRGB->setChecked(cfg->getBool("forceSRGB", false));
chkSaveProfile->setChecked(cfg->getBool("saveProfile", true));
- QStringList rgb = cfg->getString("transparencyFillcolor", "255,255,255").split(',');
KoColor background(KoColorSpaceRegistry::instance()->rgb8());
background.fromQColor(Qt::white);
bnTransparencyFillColor->setDefaultColor(background);
- background.fromQColor(QColor(rgb[0].toInt(), rgb[1].toInt(), rgb[2].toInt()));
- bnTransparencyFillColor->setColor(background);
+ bnTransparencyFillColor->setColor(cfg->getColor("transparencyFillcolor", background));
m_filterRegistryModel.setEnabledFilters(cfg->getString("filters").split(','));
}
KisPropertiesConfigurationSP KisWdgOptionsJPEG::configuration() const
{
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
+
+ QVariant transparencyFillcolor;
+ transparencyFillcolor.setValue(bnTransparencyFillColor->color());
+
cfg->setProperty("progressive", progressive->isChecked());
cfg->setProperty("quality", (int)qualityLevel->value());
cfg->setProperty("forceSRGB", chkForceSRGB->isChecked());
cfg->setProperty("saveProfile", chkSaveProfile->isChecked());
cfg->setProperty("optimize", optimize->isChecked());
cfg->setProperty("smoothing", (int)smoothLevel->value());
cfg->setProperty("baseline", baseLineJPEG->isChecked());
cfg->setProperty("subsampling", subsampling->currentIndex());
cfg->setProperty("exif", exif->isChecked());
cfg->setProperty("iptc", iptc->isChecked());
cfg->setProperty("xmp", xmp->isChecked());
- QColor c = bnTransparencyFillColor->color().toQColor();
- cfg->setProperty("transparencyFillcolor", QString("%1,%2,%3").arg(c.red()).arg(c.green()).arg(c.blue()));
+ cfg->setProperty("transparencyFillcolor", transparencyFillcolor);
+
QString enabledFilters;
Q_FOREACH (const KisMetaData::Filter* filter, m_filterRegistryModel.enabledFilters()) {
enabledFilters = enabledFilters + filter->id() + ',';
}
cfg->setProperty("filters", enabledFilters);
return cfg;
}
#include
diff --git a/plugins/impex/png/kis_png_export.cc b/plugins/impex/png/kis_png_export.cc
index 9914f51d93..015dbe0d08 100644
--- a/plugins/impex/png/kis_png_export.cc
+++ b/plugins/impex/png/kis_png_export.cc
@@ -1,225 +1,232 @@
/*
* Copyright (c) 2005 Cyrille Berger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_png_export.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kis_png_converter.h"
#include
K_PLUGIN_FACTORY_WITH_JSON(KisPNGExportFactory, "krita_png_export.json", registerPlugin();)
KisPNGExport::KisPNGExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KisPNGExport::~KisPNGExport()
{
}
bool hasVisibleWidgets()
{
QWidgetList wl = QApplication::allWidgets();
Q_FOREACH (QWidget* w, wl) {
if (w->isVisible() && strcmp(w->metaObject()->className(), "QDesktopWidget")) {
dbgFile << "Widget " << w << " " << w->objectName() << " " << w->metaObject()->className() << " is visible";
return true;
}
}
return false;
}
KisImportExportFilter::ConversionStatus KisPNGExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration)
{
KisImageSP image = document->savingImage();
KisPNGOptions options;
options.alpha = configuration->getBool("alpha", true);
options.interlace = configuration->getBool("interlaced", false);
options.compression = configuration->getInt("compression", 3);
options.tryToSaveAsIndexed = configuration->getBool("indexed", false);
- QStringList rgb = configuration->getString("transparencyFillcolor", "255,255,255").split(',');
- options.transparencyFillColor = QColor(rgb[0].toInt(), rgb[1].toInt(), rgb[2].toInt());
+ KoColor c(KoColorSpaceRegistry::instance()->rgb8());
+ c.fromQColor(Qt::white);
+ options.transparencyFillColor = configuration->getColor("transparencyFillcolor", c).toQColor();
options.saveSRGBProfile = configuration->getBool("saveSRGBProfile", false);
options.forceSRGB = configuration->getBool("forceSRGB", true);
vKisAnnotationSP_it beginIt = image->beginAnnotations();
vKisAnnotationSP_it endIt = image->endAnnotations();
KisExifInfoVisitor eIV;
eIV.visit(image->rootLayer().data());
KisMetaData::Store *eI = 0;
if (eIV.countPaintLayer() == 1) {
eI = eIV.exifInfo();
}
if (eI) {
KisMetaData::Store* copy = new KisMetaData::Store(*eI);
eI = copy;
}
KisPNGConverter pngConverter(document);
KisImageBuilder_Result res = pngConverter.buildFile(io, image->bounds(), image->xRes(), image->yRes(), image->projection(), beginIt, endIt, options, eI);
if (res == KisImageBuilder_RESULT_OK) {
delete eI;
return KisImportExportFilter::OK;
}
delete eI;
dbgFile << " Result =" << res;
return KisImportExportFilter::InternalError;
}
KisPropertiesConfigurationSP KisPNGExport::defaultConfiguration(const QByteArray &, const QByteArray &) const
{
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
cfg->setProperty("alpha", true);
cfg->setProperty("indexed", false);
cfg->setProperty("compression", 3);
cfg->setProperty("interlaced", false);
- cfg->setProperty("transparencyFillcolor", QString("255,255,255"));
+
+ KoColor fill_color(KoColorSpaceRegistry::instance()->rgb8());
+ fill_color = KoColor();
+ fill_color.fromQColor(Qt::white);
+ QVariant v;
+ v.setValue(fill_color);
+
+ cfg->setProperty("transparencyFillcolor", v);
cfg->setProperty("saveSRGBProfile", false);
cfg->setProperty("forceSRGB", true);
return cfg;
}
KisConfigWidget *KisPNGExport::createConfigurationWidget(QWidget *parent, const QByteArray &, const QByteArray &) const
{
return new KisWdgOptionsPNG(parent);
}
void KisPNGExport::initializeCapabilities()
{
addCapability(KisExportCheckRegistry::instance()->get("sRGBProfileCheck")->create(KisExportCheckBase::SUPPORTED));
QList > supportedColorModels;
supportedColorModels << QPair()
<< QPair(RGBAColorModelID, Integer8BitsColorDepthID)
<< QPair(RGBAColorModelID, Integer16BitsColorDepthID)
<< QPair(GrayAColorModelID, Integer8BitsColorDepthID)
<< QPair(GrayAColorModelID, Integer16BitsColorDepthID);
addSupportedColorModels(supportedColorModels, "PNG");
}
void KisWdgOptionsPNG::setConfiguration(const KisPropertiesConfigurationSP cfg)
{
// the export manager should have prepared some info for us!
KIS_SAFE_ASSERT_RECOVER_NOOP(cfg->hasProperty(KisImportExportFilter::ImageContainsTransparencyTag));
KIS_SAFE_ASSERT_RECOVER_NOOP(cfg->hasProperty(KisImportExportFilter::ColorModelIDTag));
KIS_SAFE_ASSERT_RECOVER_NOOP(cfg->hasProperty(KisImportExportFilter::sRGBTag));
const bool isThereAlpha = cfg->getBool(KisImportExportFilter::ImageContainsTransparencyTag);
alpha->setChecked(cfg->getBool("alpha", isThereAlpha) && isThereAlpha);
alpha->setEnabled(isThereAlpha);
bnTransparencyFillColor->setEnabled(!alpha->isChecked());
if (cfg->getString(KisImportExportFilter::ColorModelIDTag) == RGBAColorModelID.id()) {
tryToSaveAsIndexed->setVisible(true);
if (alpha->isChecked()) {
tryToSaveAsIndexed->setChecked(false);
}
else {
tryToSaveAsIndexed->setChecked(cfg->getBool("indexed", false));
}
}
else {
tryToSaveAsIndexed->setVisible(false);
}
interlacing->setChecked(cfg->getBool("interlaced", false));
compressionLevel->setValue(cfg->getInt("compression", 3));
- compressionLevel->setRange(1, 9 , 0);
+ compressionLevel->setRange(1, 9, 0);
tryToSaveAsIndexed->setVisible(!isThereAlpha);
const bool sRGB = cfg->getBool(KisImportExportFilter::sRGBTag, false);
chkSRGB->setEnabled(sRGB);
chkSRGB->setChecked(cfg->getBool("saveSRGBProfile", true));
chkForceSRGB->setEnabled(!sRGB);
chkForceSRGB->setChecked(cfg->getBool("forceSRGB", false));
- QStringList rgb = cfg->getString("transparencyFillcolor", "255,255,255").split(',');
- KoColor c(KoColorSpaceRegistry::instance()->rgb8());
- c.fromQColor(Qt::white);
- bnTransparencyFillColor->setDefaultColor(c);
- c.fromQColor(QColor(rgb[0].toInt(), rgb[1].toInt(), rgb[2].toInt()));
- bnTransparencyFillColor->setColor(c);
-
+ KoColor background(KoColorSpaceRegistry::instance()->rgb8());
+ background.fromQColor(Qt::white);
+ bnTransparencyFillColor->setDefaultColor(background);
+ bnTransparencyFillColor->setColor(cfg->getColor("transparencyFillcolor", background));
}
KisPropertiesConfigurationSP KisWdgOptionsPNG::configuration() const
{
KisPropertiesConfigurationSP cfg(new KisPropertiesConfiguration());
bool alpha = this->alpha->isChecked();
bool interlace = interlacing->isChecked();
int compression = (int)compressionLevel->value();
bool tryToSaveAsIndexed = this->tryToSaveAsIndexed->isChecked();
- QColor c = bnTransparencyFillColor->color().toQColor();
bool saveSRGB = chkSRGB->isChecked();
bool forceSRGB = chkForceSRGB->isChecked();
+ QVariant transparencyFillcolor;
+ transparencyFillcolor.setValue(bnTransparencyFillColor->color());
+
cfg->setProperty("alpha", alpha);
cfg->setProperty("indexed", tryToSaveAsIndexed);
cfg->setProperty("compression", compression);
cfg->setProperty("interlaced", interlace);
- cfg->setProperty("transparencyFillcolor", QString("%1,%2,%3").arg(c.red()).arg(c.green()).arg(c.blue()));
+ cfg->setProperty("transparencyFillcolor", transparencyFillcolor);
cfg->setProperty("saveSRGBProfile", saveSRGB);
cfg->setProperty("forceSRGB", forceSRGB);
return cfg;
}
void KisWdgOptionsPNG::on_alpha_toggled(bool checked)
{
bnTransparencyFillColor->setEnabled(!checked);
}
#include "kis_png_export.moc"