diff --git a/krita/data/actions/InteractionTool.action b/krita/data/actions/InteractionTool.action
index 575906569b..3852938cfd 100644
--- a/krita/data/actions/InteractionTool.action
+++ b/krita/data/actions/InteractionTool.action
@@ -1,262 +1,292 @@
Interaction Tool
Raise
Ctrl+]
Raise
object-order-raise-calligra
false
&Raise
Align Right
Align Right
object-align-horizontal-right-calligra
false
Align Right
Ungroup
Ungroup
object-ungroup-calligra
false
Ungroup
Send to Back
Ctrl+Shift+[
Send to Back
object-order-back-calligra
false
Send to &Back
Bring to Front
Ctrl+Shift+]
Bring to Front
object-order-front-calligra
false
Bring to &Front
Vertically Center
Vertically Center
object-align-vertical-center-calligra
false
Vertically Center
Group
Group
object-group-calligra
false
Group
Align Left
Align Left
object-align-horizontal-left-calligra
false
Align Left
Align Top
Align Top
object-align-vertical-top-calligra
false
Align Top
Horizontally Center
Horizontally Center
object-align-horizontal-center-calligra
false
Horizontally Center
Lower
Ctrl+[
Lower
object-order-lower-calligra
false
&Lower
Align Bottom
Align Bottom
object-align-vertical-bottom-calligra
false
Align Bottom
Distribute Left
Distribute left edges equidistantly
distribute-horizontal-left
false
Distribute Centers Horizontally
Distribute centers equidistantly horizontally
distribute-horizontal-center
false
Distribute Right
Distribute right edges equidistantly
distribute-horizontal-right
false
Distribute Horizontal Gap
Make horizontal gaps between objects equal
distribute-horizontal
false
Distribute Top
Distribute top edges equidistantly
distribute-vertical-top
false
Distribute Centers Vertically
Distribute centers equidistantly vertically
distribute-vertical-center
false
Distribute Bottom
Distribute bottom edges equidistantly
distribute-vertical-bottom
false
Distribute Vertical Gap
Make vertical gaps between objects equal
distribute-vertical
false
Rotate 90° CW
Rotate object 90° clockwise
object-rotate-right
false
Rotate 90° CCW
Rotate object 90° counterclockwise
object-rotate-left
false
Rotate 180° CCW
Rotate object 180°
false
Mirror Horizontally
Mirror object horizontally
symmetry-horizontal
false
Mirror Vertically
Mirror object vertically
symmetry-vertical
false
Reset Transformations
Reset object transformations
false
+
+ Unite
+ Create boolean onion of multiple objects
+
+
+
+
+ false
+
+
+
+ Intersect
+ Create boolean intersection of multiple objects
+
+
+
+
+ false
+
+
+
+ Subtract
+ Subtract multiple objects from the first selected one
+
+
+
+
+ false
+
+
diff --git a/libs/basicflakes/tools/KoCreatePathTool.cpp b/libs/basicflakes/tools/KoCreatePathTool.cpp
index fa8027221f..652e3bf8eb 100644
--- a/libs/basicflakes/tools/KoCreatePathTool.cpp
+++ b/libs/basicflakes/tools/KoCreatePathTool.cpp
@@ -1,500 +1,500 @@
/* This file is part of the KDE project
*
* Copyright (C) 2006 Thorsten Zachmann
* Copyright (C) 2008-2010 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 "KoCreatePathTool.h"
#include "KoCreatePathTool_p.h"
#include
#include "KoPointerEvent.h"
#include "KoPathShape.h"
#include "KoSelection.h"
#include "KoDocumentResourceManager.h"
#include "KoShapePaintingContext.h"
#include "KoShapeStroke.h"
#include "KoCanvasBase.h"
#include "kis_int_parse_spin_box.h"
#include
#include "kis_canvas_resource_provider.h"
#include
#include
#include
#include
#include
#include
#include
KoCreatePathTool::KoCreatePathTool(KoCanvasBase *canvas)
: KoToolBase(*(new KoCreatePathToolPrivate(this, canvas)))
{
}
KoCreatePathTool::~KoCreatePathTool()
{
}
void KoCreatePathTool::paint(QPainter &painter, const KoViewConverter &converter)
{
Q_D(KoCreatePathTool);
if (pathStarted()) {
painter.save();
paintPath(*(d->shape), painter, converter);
painter.restore();
KisHandlePainterHelper helper =
KoShape::createHandlePainterHelper(&painter, d->shape, converter, d->handleRadius);
const bool firstPointActive = d->firstPoint == d->activePoint;
if (d->pointIsDragged || firstPointActive) {
const bool onlyPaintActivePoints = false;
KoPathPoint::PointTypes paintFlags = KoPathPoint::ControlPoint2;
if (d->activePoint->activeControlPoint1()) {
paintFlags |= KoPathPoint::ControlPoint1;
}
helper.setHandleStyle(KisHandleStyle::highlightedPrimaryHandles());
d->activePoint->paint(helper, paintFlags, onlyPaintActivePoints);
}
if (!firstPointActive) {
helper.setHandleStyle(d->mouseOverFirstPoint ?
KisHandleStyle::highlightedPrimaryHandles() :
KisHandleStyle::primarySelection());
d->firstPoint->paint(helper, KoPathPoint::Node);
}
}
if (d->hoveredPoint) {
KisHandlePainterHelper helper = KoShape::createHandlePainterHelper(&painter, d->hoveredPoint->parent(), converter, d->handleRadius);
helper.setHandleStyle(KisHandleStyle::highlightedPrimaryHandles());
d->hoveredPoint->paint(helper, KoPathPoint::Node);
}
painter.save();
KoShape::applyConversion(painter, converter);
canvas()->snapGuide()->paint(painter, converter);
painter.restore();
}
void KoCreatePathTool::paintPath(KoPathShape& pathShape, QPainter &painter, const KoViewConverter &converter)
{
Q_D(KoCreatePathTool);
painter.setTransform(pathShape.absoluteTransformation(&converter) * painter.transform());
painter.save();
KoShapePaintingContext paintContext; //FIXME
pathShape.paint(painter, converter, paintContext);
painter.restore();
if (pathShape.stroke()) {
painter.save();
pathShape.stroke()->paint(d->shape, painter, converter);
painter.restore();
}
}
void KoCreatePathTool::mousePressEvent(KoPointerEvent *event)
{
Q_D(KoCreatePathTool);
//Right click removes last point
if (event->button() == Qt::RightButton) {
removeLastPoint();
return;
}
const bool isOverFirstPoint = d->shape &&
handleGrabRect(d->firstPoint->point()).contains(event->point);
bool haveCloseModifier = (listeningToModifiers() && (event->modifiers() & Qt::ShiftModifier));
if ((event->button() == Qt::LeftButton) && haveCloseModifier && !isOverFirstPoint) {
endPathWithoutLastPoint();
return;
}
d->finishAfterThisPoint = false;
if (pathStarted()) {
if (isOverFirstPoint) {
d->activePoint->setPoint(d->firstPoint->point());
canvas()->updateCanvas(d->shape->boundingRect());
canvas()->updateCanvas(canvas()->snapGuide()->boundingRect());
if (haveCloseModifier) {
d->shape->closeMerge();
// we are closing the path, so reset the existing start path point
d->existingStartPoint = 0;
// finish path
endPath();
} else {
// the path shape will get closed when the user releases
// the mouse button
d->finishAfterThisPoint = true;
}
} else {
canvas()->updateCanvas(canvas()->snapGuide()->boundingRect());
QPointF point = canvas()->snapGuide()->snap(event->point, event->modifiers());
// check whether we hit an start/end node of an existing path
d->existingEndPoint = d->endPointAtPosition(point);
if (d->existingEndPoint.isValid() && d->existingEndPoint != d->existingStartPoint) {
point = d->existingEndPoint.path->shapeToDocument(d->existingEndPoint.point->point());
d->activePoint->setPoint(point);
// finish path
endPath();
} else {
d->activePoint->setPoint(point);
canvas()->updateCanvas(d->shape->boundingRect());
canvas()->updateCanvas(canvas()->snapGuide()->boundingRect());
}
}
} else {
KoPathShape *pathShape = new KoPathShape();
d->shape = pathShape;
pathShape->setShapeId(KoPathShapeId);
KoShapeStrokeSP stroke(new KoShapeStroke());
const qreal size = canvas()->resourceManager()->resource(KisCanvasResourceProvider::Size).toReal();
stroke->setLineWidth(canvas()->unit().fromUserValue(size));
stroke->setColor(canvas()->resourceManager()->foregroundColor().toQColor());
pathShape->setStroke(stroke);
canvas()->updateCanvas(canvas()->snapGuide()->boundingRect());
QPointF point = canvas()->snapGuide()->snap(event->point, event->modifiers());
// check whether we hit an start/end node of an existing path
d->existingStartPoint = d->endPointAtPosition(point);
if (d->existingStartPoint.isValid()) {
point = d->existingStartPoint.path->shapeToDocument(d->existingStartPoint.point->point());
}
d->activePoint = pathShape->moveTo(point);
d->firstPoint = d->activePoint;
canvas()->updateCanvas(handlePaintRect(point));
canvas()->updateCanvas(canvas()->snapGuide()->boundingRect());
canvas()->snapGuide()->setAdditionalEditedShape(pathShape);
d->angleSnapStrategy = new AngleSnapStrategy(d->angleSnappingDelta, d->angleSnapStatus);
canvas()->snapGuide()->addCustomSnapStrategy(d->angleSnapStrategy);
}
if (d->angleSnapStrategy)
d->angleSnapStrategy->setStartPoint(d->activePoint->point());
}
bool KoCreatePathTool::listeningToModifiers()
{
Q_D(KoCreatePathTool);
return d->listeningToModifiers;
}
bool KoCreatePathTool::pathStarted()
{
Q_D(KoCreatePathTool);
return ((bool) d->shape);
}
void KoCreatePathTool::mouseDoubleClickEvent(KoPointerEvent *event)
{
//remove handle
canvas()->updateCanvas(handlePaintRect(event->point));
endPathWithoutLastPoint();
}
void KoCreatePathTool::mouseMoveEvent(KoPointerEvent *event)
{
Q_D(KoCreatePathTool);
KoPathPoint *endPoint = d->endPointAtPosition(event->point);
if (d->hoveredPoint != endPoint) {
if (d->hoveredPoint) {
QPointF nodePos = d->hoveredPoint->parent()->shapeToDocument(d->hoveredPoint->point());
canvas()->updateCanvas(handlePaintRect(nodePos));
}
d->hoveredPoint = endPoint;
if (d->hoveredPoint) {
QPointF nodePos = d->hoveredPoint->parent()->shapeToDocument(d->hoveredPoint->point());
canvas()->updateCanvas(handlePaintRect(nodePos));
}
}
if (!pathStarted()) {
canvas()->updateCanvas(canvas()->snapGuide()->boundingRect());
canvas()->snapGuide()->snap(event->point, event->modifiers());
canvas()->updateCanvas(canvas()->snapGuide()->boundingRect());
d->mouseOverFirstPoint = false;
return;
}
d->mouseOverFirstPoint = handleGrabRect(d->firstPoint->point()).contains(event->point);
canvas()->updateCanvas(d->shape->boundingRect());
canvas()->updateCanvas(canvas()->snapGuide()->boundingRect());
QPointF snappedPosition = canvas()->snapGuide()->snap(event->point, event->modifiers());
d->repaintActivePoint();
if (event->buttons() & Qt::LeftButton) {
d->pointIsDragged = true;
QPointF offset = snappedPosition - d->activePoint->point();
d->activePoint->setControlPoint2(d->activePoint->point() + offset);
// pressing stops controls points moving symmetrically
if ((event->modifiers() & Qt::AltModifier) == 0) {
d->activePoint->setControlPoint1(d->activePoint->point() - offset);
}
d->repaintActivePoint();
} else {
d->activePoint->setPoint(snappedPosition);
}
canvas()->updateCanvas(d->shape->boundingRect());
canvas()->updateCanvas(canvas()->snapGuide()->boundingRect());
}
void KoCreatePathTool::mouseReleaseEvent(KoPointerEvent *event)
{
Q_D(KoCreatePathTool);
if (! d->shape || (event->buttons() & Qt::RightButton)) return;
d->listeningToModifiers = true; // After the first press-and-release
d->repaintActivePoint();
d->pointIsDragged = false;
KoPathPoint *lastActivePoint = d->activePoint;
if (!d->finishAfterThisPoint) {
d->activePoint = d->shape->lineTo(event->point);
canvas()->snapGuide()->setIgnoredPathPoints((QList() << d->activePoint));
}
// apply symmetric point property if applicable
if (lastActivePoint->activeControlPoint1() && lastActivePoint->activeControlPoint2()) {
QPointF diff1 = lastActivePoint->point() - lastActivePoint->controlPoint1();
QPointF diff2 = lastActivePoint->controlPoint2() - lastActivePoint->point();
if (qFuzzyCompare(diff1.x(), diff2.x()) && qFuzzyCompare(diff1.y(), diff2.y()))
lastActivePoint->setProperty(KoPathPoint::IsSymmetric);
}
if (d->finishAfterThisPoint) {
d->firstPoint->setControlPoint1(d->activePoint->controlPoint1());
delete d->shape->removePoint(d->shape->pathPointIndex(d->activePoint));
d->activePoint = d->firstPoint;
d->shape->closeMerge();
// we are closing the path, so reset the existing start path point
d->existingStartPoint = 0;
// finish path
endPath();
}
if (d->angleSnapStrategy && lastActivePoint->activeControlPoint2()) {
d->angleSnapStrategy->deactivate();
}
}
void KoCreatePathTool::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Escape) {
emit done();
} else {
event->ignore();
}
}
void KoCreatePathTool::endPath()
{
Q_D(KoCreatePathTool);
d->addPathShape();
}
void KoCreatePathTool::endPathWithoutLastPoint()
{
Q_D(KoCreatePathTool);
if (d->shape) {
QRectF dirtyRect = d->shape->boundingRect();
delete d->shape->removePoint(d->shape->pathPointIndex(d->activePoint));
canvas()->updateCanvas(dirtyRect);
d->addPathShape();
}
}
void KoCreatePathTool::cancelPath()
{
Q_D(KoCreatePathTool);
if (d->shape) {
canvas()->updateCanvas(handlePaintRect(d->firstPoint->point()));
canvas()->updateCanvas(d->shape->boundingRect());
d->firstPoint = 0;
d->activePoint = 0;
}
d->cleanUp();
}
void KoCreatePathTool::removeLastPoint()
{
Q_D(KoCreatePathTool);
if ((d->shape)) {
KoPathPointIndex lastPointIndex = d->shape->pathPointIndex(d->activePoint);
if (lastPointIndex.second > 1) {
lastPointIndex.second--;
delete d->shape->removePoint(lastPointIndex);
d->hoveredPoint = 0;
d->repaintActivePoint();
canvas()->updateCanvas(d->shape->boundingRect());
}
}
}
void KoCreatePathTool::activate(ToolActivation activation, const QSet &shapes)
{
KoToolBase::activate(activation, shapes);
Q_D(KoCreatePathTool);
useCursor(Qt::ArrowCursor);
// retrieve the actual global handle radius
d->handleRadius = handleRadius();
// reset snap guide
canvas()->updateCanvas(canvas()->snapGuide()->boundingRect());
canvas()->snapGuide()->reset();
}
void KoCreatePathTool::deactivate()
{
cancelPath();
KoToolBase::deactivate();
}
void KoCreatePathTool::documentResourceChanged(int key, const QVariant & res)
{
Q_D(KoCreatePathTool);
switch (key) {
case KoDocumentResourceManager::HandleRadius: {
d->handleRadius = res.toUInt();
}
break;
default:
return;
}
}
void KoCreatePathTool::addPathShape(KoPathShape *pathShape)
{
Q_D(KoCreatePathTool);
KoPathShape *startShape = 0;
KoPathShape *endShape = 0;
pathShape->normalize();
// check if existing start/end points are still valid
d->existingStartPoint.validate(canvas());
d->existingEndPoint.validate(canvas());
if (d->connectPaths(pathShape, d->existingStartPoint, d->existingEndPoint)) {
if (d->existingStartPoint.isValid()) {
startShape = d->existingStartPoint.path;
}
if (d->existingEndPoint.isValid() && d->existingEndPoint != d->existingStartPoint) {
endShape = d->existingEndPoint.path;
}
}
- KUndo2Command *cmd = canvas()->shapeController()->addShape(pathShape);
+ KUndo2Command *cmd = canvas()->shapeController()->addShape(pathShape, 0);
if (cmd) {
KoSelection *selection = canvas()->shapeManager()->selection();
selection->deselectAll();
selection->select(pathShape);
if (startShape) {
canvas()->shapeController()->removeShape(startShape, cmd);
}
if (endShape && startShape != endShape) {
canvas()->shapeController()->removeShape(endShape, cmd);
}
canvas()->addCommand(cmd);
} else {
canvas()->updateCanvas(pathShape->boundingRect());
delete pathShape;
}
}
QList > KoCreatePathTool::createOptionWidgets()
{
Q_D(KoCreatePathTool);
QList > list;
QWidget *angleWidget = new QWidget();
angleWidget->setObjectName("Angle Constraints");
QGridLayout *layout = new QGridLayout(angleWidget);
layout->addWidget(new QLabel(i18n("Angle snapping delta:"), angleWidget), 0, 0);
QSpinBox *angleEdit = new KisIntParseSpinBox(angleWidget);
angleEdit->setValue(d->angleSnappingDelta);
angleEdit->setRange(1, 360);
angleEdit->setSingleStep(1);
angleEdit->setSuffix(QChar(Qt::Key_degree));
layout->addWidget(angleEdit, 0, 1);
layout->addWidget(new QLabel(i18n("Activate angle snap:"), angleWidget), 1, 0);
QCheckBox *angleSnap = new QCheckBox(angleWidget);
angleSnap->setChecked(false);
angleSnap->setCheckable(true);
layout->addWidget(angleSnap, 1, 1);
QWidget *specialSpacer = new QWidget();
specialSpacer->setObjectName("SpecialSpacer");
layout->addWidget(specialSpacer, 2, 1);
angleWidget->setWindowTitle(i18n("Angle Constraints"));
list.append(angleWidget);
connect(angleEdit, SIGNAL(valueChanged(int)), this, SLOT(angleDeltaChanged(int)));
connect(angleSnap, SIGNAL(stateChanged(int)), this, SLOT(angleSnapChanged(int)));
return list;
}
//have to include this because of Q_PRIVATE_SLOT
#include
diff --git a/libs/basicflakes/tools/KoPencilTool.cpp b/libs/basicflakes/tools/KoPencilTool.cpp
index e837a38457..ac331d3839 100644
--- a/libs/basicflakes/tools/KoPencilTool.cpp
+++ b/libs/basicflakes/tools/KoPencilTool.cpp
@@ -1,581 +1,581 @@
/* This file is part of the KDE project
* Copyright (C) 2007,2009,2011 Jan Hambrecht
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoPencilTool.h"
#include "KoCurveFit.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 "KoCreatePathTool_p.h"
#include "kis_double_parse_spin_box.h"
KoPencilTool::KoPencilTool(KoCanvasBase *canvas)
: KoToolBase(canvas)
, m_mode(ModeCurve)
, m_optimizeRaw(false)
, m_optimizeCurve(false)
, m_combineAngle(15.0)
, m_fittingError(5.0)
, m_close(false)
, m_shape(0)
, m_existingStartPoint(0)
, m_existingEndPoint(0)
, m_hoveredPoint(0)
, m_strokeWidget(0)
{
}
KoPencilTool::~KoPencilTool()
{
}
void KoPencilTool::paint(QPainter &painter, const KoViewConverter &converter)
{
if (m_shape) {
painter.save();
painter.setTransform(m_shape->absoluteTransformation(&converter) * painter.transform());
painter.save();
KoShapePaintingContext paintContext; //FIXME
m_shape->paint(painter, converter, paintContext);
painter.restore();
if (m_shape->stroke()) {
painter.save();
m_shape->stroke()->paint(m_shape, painter, converter);
painter.restore();
}
painter.restore();
}
if (m_hoveredPoint) {
KisHandlePainterHelper helper =
KoShape::createHandlePainterHelper(&painter, m_hoveredPoint->parent(), converter, handleRadius());
helper.setHandleStyle(KisHandleStyle::primarySelection());
m_hoveredPoint->paint(helper, KoPathPoint::Node);
}
}
void KoPencilTool::repaintDecorations()
{
}
void KoPencilTool::mousePressEvent(KoPointerEvent *event)
{
KoShapeStrokeSP stroke = createStroke();
if (!m_shape && stroke && stroke->isVisible()) {
m_shape = new KoPathShape();
m_shape->setShapeId(KoPathShapeId);
m_shape->setStroke(createStroke());
m_points.clear();
QPointF point = event->point;
m_existingStartPoint = endPointAtPosition(point);
if (m_existingStartPoint)
point = m_existingStartPoint->parent()->shapeToDocument(m_existingStartPoint->point());
addPoint(point);
}
}
void KoPencilTool::mouseMoveEvent(KoPointerEvent *event)
{
if (event->buttons() & Qt::LeftButton)
addPoint(event->point);
KoPathPoint * endPoint = endPointAtPosition(event->point);
if (m_hoveredPoint != endPoint) {
if (m_hoveredPoint) {
QPointF nodePos = m_hoveredPoint->parent()->shapeToDocument(m_hoveredPoint->point());
canvas()->updateCanvas(handlePaintRect(nodePos));
}
m_hoveredPoint = endPoint;
if (m_hoveredPoint) {
QPointF nodePos = m_hoveredPoint->parent()->shapeToDocument(m_hoveredPoint->point());
canvas()->updateCanvas(handlePaintRect(nodePos));
}
}
}
void KoPencilTool::mouseReleaseEvent(KoPointerEvent *event)
{
if (! m_shape)
return;
QPointF point = event->point;
m_existingEndPoint = endPointAtPosition(point);
if (m_existingEndPoint)
point = m_existingEndPoint->parent()->shapeToDocument(m_existingEndPoint->point());
addPoint(point);
finish(event->modifiers() & Qt::ShiftModifier);
m_existingStartPoint = 0;
m_existingEndPoint = 0;
m_hoveredPoint = 0;
// the original path may be different from the one added
canvas()->updateCanvas(m_shape->boundingRect());
delete m_shape;
m_shape = 0;
m_points.clear();
}
void KoPencilTool::keyPressEvent(QKeyEvent *event)
{
if (m_shape) {
event->accept();
} else {
event->ignore();
}
}
void KoPencilTool::activate(ToolActivation activation, const QSet &shapes)
{
KoToolBase::activate(activation, shapes);
m_points.clear();
m_close = false;
slotUpdatePencilCursor();
if (m_strokeWidget) {
m_strokeWidget->activate();
}
}
void KoPencilTool::deactivate()
{
m_points.clear();
delete m_shape;
m_shape = 0;
m_existingStartPoint = 0;
m_existingEndPoint = 0;
m_hoveredPoint = 0;
if (m_strokeWidget) {
m_strokeWidget->deactivate();
}
KoToolBase::deactivate();
}
void KoPencilTool::slotUpdatePencilCursor()
{
KoShapeStrokeSP stroke = createStroke();
useCursor((stroke && stroke->isVisible()) ? Qt::ArrowCursor : Qt::ForbiddenCursor);
}
void KoPencilTool::addPoint(const QPointF & point)
{
if (! m_shape)
return;
// do a moveTo for the first point added
if (m_points.empty())
m_shape->moveTo(point);
// do not allow coincident points
else if (point != m_points.last())
m_shape->lineTo(point);
else
return;
m_points.append(point);
canvas()->updateCanvas(m_shape->boundingRect());
}
qreal KoPencilTool::lineAngle(const QPointF &p1, const QPointF &p2)
{
qreal angle = atan2(p2.y() - p1.y(), p2.x() - p1.x());
if (angle < 0.0)
angle += 2 * M_PI;
return angle * 180.0 / M_PI;
}
void KoPencilTool::finish(bool closePath)
{
if (m_points.count() < 2)
return;
KoPathShape * path = 0;
QList complete;
QList *points = &m_points;
if (m_mode == ModeStraight || m_optimizeRaw || m_optimizeCurve) {
float combineAngle;
if (m_mode == ModeStraight)
combineAngle = m_combineAngle;
else
combineAngle = 0.50f;
//Add the first two points
complete.append(m_points[0]);
complete.append(m_points[1]);
//Now we need to get the angle of the first line
float lastAngle = lineAngle(complete[0], complete[1]);
uint pointCount = m_points.count();
for (uint i = 2; i < pointCount; ++i) {
float angle = lineAngle(complete.last(), m_points[i]);
if (qAbs(angle - lastAngle) < combineAngle)
complete.removeLast();
complete.append(m_points[i]);
lastAngle = angle;
}
m_points.clear();
points = &complete;
}
switch (m_mode) {
case ModeCurve: {
path = bezierFit(*points, m_fittingError);
}
break;
case ModeStraight:
case ModeRaw: {
path = new KoPathShape();
uint pointCount = points->count();
path->moveTo(points->at(0));
for (uint i = 1; i < pointCount; ++i)
path->lineTo(points->at(i));
}
break;
}
if (! path)
return;
path->setShapeId(KoPathShapeId);
path->setStroke(createStroke());
addPathShape(path, closePath);
}
QList > KoPencilTool::createOptionWidgets()
{
QList > widgets;
QWidget *optionWidget = new QWidget();
QVBoxLayout * layout = new QVBoxLayout(optionWidget);
QHBoxLayout *modeLayout = new QHBoxLayout;
modeLayout->setSpacing(3);
QLabel *modeLabel = new QLabel(i18n("Precision:"), optionWidget);
QComboBox * modeBox = new QComboBox(optionWidget);
modeBox->addItem(i18nc("The raw line data", "Raw"));
modeBox->addItem(i18n("Curve"));
modeBox->addItem(i18n("Straight"));
modeLayout->addWidget(modeLabel);
modeLayout->addWidget(modeBox, 1);
layout->addLayout(modeLayout);
QStackedWidget * stackedWidget = new QStackedWidget(optionWidget);
QWidget * rawBox = new QWidget(stackedWidget);
QVBoxLayout * rawLayout = new QVBoxLayout(rawBox);
QCheckBox * optimizeRaw = new QCheckBox(i18n("Optimize"), rawBox);
rawLayout->addWidget(optimizeRaw);
rawLayout->setContentsMargins(0, 0, 0, 0);
QWidget * curveBox = new QWidget(stackedWidget);
QHBoxLayout * curveLayout = new QHBoxLayout(curveBox);
QCheckBox * optimizeCurve = new QCheckBox(i18n("Optimize"), curveBox);
QDoubleSpinBox * fittingError = new KisDoubleParseSpinBox(curveBox);
fittingError->setValue(0.50);
fittingError->setMaximum(400.0);
fittingError->setMinimum(0.0);
fittingError->setSingleStep(m_fittingError);
fittingError->setToolTip(i18n("Exactness:"));
curveLayout->addWidget(optimizeCurve);
curveLayout->addWidget(fittingError);
curveLayout->setContentsMargins(0, 0, 0, 0);
QWidget *straightBox = new QWidget(stackedWidget);
QVBoxLayout *straightLayout = new QVBoxLayout(straightBox);
QDoubleSpinBox *combineAngle = new KisDoubleParseSpinBox(straightBox);
combineAngle->setValue(0.50);
combineAngle->setMaximum(360.0);
combineAngle->setMinimum(0.0);
combineAngle->setSingleStep(m_combineAngle);
combineAngle->setSuffix(" deg");
// QT5TODO
//combineAngle->setLabel(i18n("Combine angle:"), Qt::AlignLeft | Qt::AlignVCenter);
straightLayout->addWidget(combineAngle);
straightLayout->setContentsMargins(0, 0, 0, 0);
stackedWidget->addWidget(rawBox);
stackedWidget->addWidget(curveBox);
stackedWidget->addWidget(straightBox);
layout->addWidget(stackedWidget);
layout->addStretch(1);
connect(modeBox, SIGNAL(activated(int)), stackedWidget, SLOT(setCurrentIndex(int)));
connect(modeBox, SIGNAL(activated(int)), this, SLOT(selectMode(int)));
connect(optimizeRaw, SIGNAL(stateChanged(int)), this, SLOT(setOptimize(int)));
connect(optimizeCurve, SIGNAL(stateChanged(int)), this, SLOT(setOptimize(int)));
connect(fittingError, SIGNAL(valueChanged(double)), this, SLOT(setDelta(double)));
connect(combineAngle, SIGNAL(valueChanged(double)), this, SLOT(setDelta(double)));
modeBox->setCurrentIndex(m_mode);
stackedWidget->setCurrentIndex(m_mode);
optionWidget->setObjectName(i18n("Pencil"));
optionWidget->setWindowTitle(i18n("Pencil"));
widgets.append(optionWidget);
m_strokeWidget = new KoStrokeConfigWidget(canvas(), 0);
m_strokeWidget->setNoSelectionTrackingMode(true);
m_strokeWidget->setWindowTitle(i18n("Line"));
connect(m_strokeWidget, SIGNAL(sigStrokeChanged()), SLOT(slotUpdatePencilCursor()));
if (isActivated()) {
m_strokeWidget->activate();
}
widgets.append(m_strokeWidget);
return widgets;
}
void KoPencilTool::addPathShape(KoPathShape* path, bool closePath)
{
KoShape * startShape = 0;
KoShape * endShape = 0;
if (closePath) {
path->close();
path->normalize();
} else {
path->normalize();
if (connectPaths(path, m_existingStartPoint, m_existingEndPoint)) {
if (m_existingStartPoint)
startShape = m_existingStartPoint->parent();
if (m_existingEndPoint && m_existingEndPoint != m_existingStartPoint)
endShape = m_existingEndPoint->parent();
}
}
- KUndo2Command * cmd = canvas()->shapeController()->addShape(path);
+ KUndo2Command * cmd = canvas()->shapeController()->addShape(path, 0);
if (cmd) {
KoSelection *selection = canvas()->shapeManager()->selection();
selection->deselectAll();
selection->select(path);
if (startShape)
canvas()->shapeController()->removeShape(startShape, cmd);
if (endShape && startShape != endShape)
canvas()->shapeController()->removeShape(endShape, cmd);
canvas()->addCommand(cmd);
} else {
canvas()->updateCanvas(path->boundingRect());
delete path;
}
}
void KoPencilTool::selectMode(int mode)
{
m_mode = static_cast(mode);
}
void KoPencilTool::setOptimize(int state)
{
if (m_mode == ModeRaw)
m_optimizeRaw = state == Qt::Checked ? true : false;
else
m_optimizeCurve = state == Qt::Checked ? true : false;
}
void KoPencilTool::setDelta(double delta)
{
if (m_mode == ModeCurve)
m_fittingError = delta;
else if (m_mode == ModeStraight)
m_combineAngle = delta;
}
KoShapeStrokeSP KoPencilTool::createStroke()
{
KoShapeStrokeSP stroke;
if (m_strokeWidget) {
stroke = m_strokeWidget->createShapeStroke();
}
return stroke;
}
KoPathPoint* KoPencilTool::endPointAtPosition(const QPointF &position)
{
QRectF roi = handleGrabRect(position);
QList shapes = canvas()->shapeManager()->shapesAt(roi);
KoPathPoint * nearestPoint = 0;
qreal minDistance = HUGE_VAL;
qreal maxDistance = canvas()->viewConverter()->viewToDocumentX(grabSensitivity());
Q_FOREACH(KoShape * shape, shapes) {
KoPathShape * path = dynamic_cast(shape);
if (!path)
continue;
KoParameterShape *paramShape = dynamic_cast(shape);
if (paramShape && paramShape->isParametricShape())
continue;
KoPathPoint * p = 0;
uint subpathCount = path->subpathCount();
for (uint i = 0; i < subpathCount; ++i) {
if (path->isClosedSubpath(i))
continue;
p = path->pointByIndex(KoPathPointIndex(i, 0));
// check start of subpath
qreal d = squareDistance(position, path->shapeToDocument(p->point()));
if (d < minDistance && d < maxDistance) {
nearestPoint = p;
minDistance = d;
}
// check end of subpath
p = path->pointByIndex(KoPathPointIndex(i, path->subpathPointCount(i) - 1));
d = squareDistance(position, path->shapeToDocument(p->point()));
if (d < minDistance && d < maxDistance) {
nearestPoint = p;
minDistance = d;
}
}
}
return nearestPoint;
}
bool KoPencilTool::connectPaths(KoPathShape *pathShape, KoPathPoint *pointAtStart, KoPathPoint *pointAtEnd)
{
// at least one point must be valid
if (!pointAtStart && !pointAtEnd)
return false;
// do not allow connecting to the same point twice
if (pointAtStart == pointAtEnd)
pointAtEnd = 0;
// we have hit an existing path point on start/finish
// what we now do is:
// 1. combine the new created path with the ones we hit on start/finish
// 2. merge the endpoints of the corresponding subpaths
uint newPointCount = pathShape->subpathPointCount(0);
KoPathPointIndex newStartPointIndex(0, 0);
KoPathPointIndex newEndPointIndex(0, newPointCount - 1);
KoPathPoint * newStartPoint = pathShape->pointByIndex(newStartPointIndex);
KoPathPoint * newEndPoint = pathShape->pointByIndex(newEndPointIndex);
KoPathShape * startShape = pointAtStart ? pointAtStart->parent() : 0;
KoPathShape * endShape = pointAtEnd ? pointAtEnd->parent() : 0;
// combine with the path we hit on start
KoPathPointIndex startIndex(-1, -1);
if (pointAtStart) {
startIndex = startShape->pathPointIndex(pointAtStart);
pathShape->combine(startShape);
pathShape->moveSubpath(0, pathShape->subpathCount() - 1);
}
// combine with the path we hit on finish
KoPathPointIndex endIndex(-1, -1);
if (pointAtEnd) {
endIndex = endShape->pathPointIndex(pointAtEnd);
if (endShape != startShape) {
endIndex.first += pathShape->subpathCount();
pathShape->combine(endShape);
}
}
// do we connect twice to a single subpath ?
bool connectToSingleSubpath = (startShape == endShape && startIndex.first == endIndex.first);
if (startIndex.second == 0 && !connectToSingleSubpath) {
pathShape->reverseSubpath(startIndex.first);
startIndex.second = pathShape->subpathPointCount(startIndex.first) - 1;
}
if (endIndex.second > 0 && !connectToSingleSubpath) {
pathShape->reverseSubpath(endIndex.first);
endIndex.second = 0;
}
// after combining we have a path where with the subpaths in the following
// order:
// 1. the subpaths of the pathshape we started the new path at
// 2. the subpath we just created
// 3. the subpaths of the pathshape we finished the new path at
// get the path points we want to merge, as these are not going to
// change while merging
KoPathPoint * existingStartPoint = pathShape->pointByIndex(startIndex);
KoPathPoint * existingEndPoint = pathShape->pointByIndex(endIndex);
// merge first two points
if (existingStartPoint) {
KoPathPointData pd1(pathShape, pathShape->pathPointIndex(existingStartPoint));
KoPathPointData pd2(pathShape, pathShape->pathPointIndex(newStartPoint));
KoPathPointMergeCommand cmd1(pd1, pd2);
cmd1.redo();
}
// merge last two points
if (existingEndPoint) {
KoPathPointData pd3(pathShape, pathShape->pathPointIndex(newEndPoint));
KoPathPointData pd4(pathShape, pathShape->pathPointIndex(existingEndPoint));
KoPathPointMergeCommand cmd2(pd3, pd4);
cmd2.redo();
}
return true;
}
qreal KoPencilTool::getFittingError()
{
return this->m_fittingError;
}
void KoPencilTool::setFittingError(qreal fittingError)
{
this->m_fittingError = fittingError;
}
diff --git a/libs/flake/CMakeLists.txt b/libs/flake/CMakeLists.txt
index c0847bdaad..5bfb182d84 100644
--- a/libs/flake/CMakeLists.txt
+++ b/libs/flake/CMakeLists.txt
@@ -1,242 +1,243 @@
project(kritaflake)
include_directories(
${CMAKE_SOURCE_DIR}/libs/flake/commands
${CMAKE_SOURCE_DIR}/libs/flake/tools
${CMAKE_SOURCE_DIR}/libs/flake/svg
${CMAKE_SOURCE_DIR}/libs/flake/text
${CMAKE_BINARY_DIR}/libs/flake
)
add_subdirectory(styles)
add_subdirectory(tests)
set(kritaflake_SRCS
KoGradientHelper.cpp
KoFlake.cpp
KoCanvasBase.cpp
KoResourceManager_p.cpp
KoDerivedResourceConverter.cpp
KoResourceUpdateMediator.cpp
KoCanvasResourceManager.cpp
KoDocumentResourceManager.cpp
KoCanvasObserverBase.cpp
KoCanvasSupervisor.cpp
KoDockFactoryBase.cpp
KoDockRegistry.cpp
KoDataCenterBase.cpp
KoInsets.cpp
KoPathShape.cpp
KoPathPoint.cpp
KoPathSegment.cpp
KoSelection.cpp
KoSelectedShapesProxy.cpp
KoSelectedShapesProxySimple.cpp
KoShape.cpp
KoShapeAnchor.cpp
KoShapeBasedDocumentBase.cpp
KoShapeApplicationData.cpp
KoShapeContainer.cpp
KoShapeContainerModel.cpp
KoShapeGroup.cpp
KoShapeManager.cpp
KoShapePaintingContext.cpp
KoFrameShape.cpp
KoUnavailShape.cpp
KoMarker.cpp
KoMarkerCollection.cpp
KoToolBase.cpp
KoCanvasController.cpp
KoCanvasControllerWidget.cpp
KoCanvasControllerWidgetViewport_p.cpp
KoShapeRegistry.cpp
KoDeferredShapeFactoryBase.cpp
KoToolFactoryBase.cpp
KoPathShapeFactory.cpp
KoShapeFactoryBase.cpp
KoShapeUserData.cpp
KoParameterShape.cpp
KoPointerEvent.cpp
KoShapeController.cpp
KoToolSelection.cpp
KoShapeLayer.cpp
KoPostscriptPaintDevice.cpp
KoInputDevice.cpp
KoToolManager_p.cpp
KoToolManager.cpp
KoToolRegistry.cpp
KoToolProxy.cpp
KoShapeSavingContext.cpp
KoShapeLoadingContext.cpp
KoLoadingShapeUpdater.cpp
KoPathShapeLoader.cpp
KoShapeStrokeModel.cpp
KoShapeStroke.cpp
KoShapeBackground.cpp
KoColorBackground.cpp
KoGradientBackground.cpp
KoOdfGradientBackground.cpp
KoHatchBackground.cpp
KoPatternBackground.cpp
KoVectorPatternBackground.cpp
KoShapeConfigWidgetBase.cpp
KoDrag.cpp
KoSvgPaste.cpp
KoDragOdfSaveHelper.cpp
KoShapeOdfSaveHelper.cpp
KoConnectionPoint.cpp
KoConnectionShape.cpp
KoConnectionShapeLoadingUpdater.cpp
KoConnectionShapeFactory.cpp
KoConnectionShapeConfigWidget.cpp
KoSnapGuide.cpp
KoSnapProxy.cpp
KoSnapStrategy.cpp
KoSnapData.cpp
KoShapeShadow.cpp
KoSharedLoadingData.cpp
KoSharedSavingData.cpp
KoViewConverter.cpp
KoInputDeviceHandler.cpp
KoInputDeviceHandlerEvent.cpp
KoInputDeviceHandlerRegistry.cpp
KoImageData.cpp
KoImageData_p.cpp
KoImageCollection.cpp
KoOdfWorkaround.cpp
KoFilterEffect.cpp
KoFilterEffectStack.cpp
KoFilterEffectFactoryBase.cpp
KoFilterEffectRegistry.cpp
KoFilterEffectConfigWidgetBase.cpp
KoFilterEffectRenderContext.cpp
KoFilterEffectLoadingContext.cpp
KoTextShapeDataBase.cpp
KoTosContainer.cpp
KoTosContainerModel.cpp
KoClipPath.cpp
KoClipMask.cpp
KoClipMaskPainter.cpp
KoCurveFit.cpp
commands/KoShapeGroupCommand.cpp
commands/KoShapeAlignCommand.cpp
commands/KoShapeBackgroundCommand.cpp
commands/KoShapeCreateCommand.cpp
commands/KoShapeDeleteCommand.cpp
commands/KoShapeDistributeCommand.cpp
commands/KoShapeLockCommand.cpp
commands/KoShapeMoveCommand.cpp
commands/KoShapeResizeCommand.cpp
commands/KoShapeShearCommand.cpp
commands/KoShapeSizeCommand.cpp
commands/KoShapeStrokeCommand.cpp
commands/KoShapeUngroupCommand.cpp
commands/KoShapeReorderCommand.cpp
commands/KoShapeKeepAspectRatioCommand.cpp
commands/KoPathBaseCommand.cpp
commands/KoPathPointMoveCommand.cpp
commands/KoPathControlPointMoveCommand.cpp
commands/KoPathPointTypeCommand.cpp
commands/KoPathPointRemoveCommand.cpp
commands/KoPathPointInsertCommand.cpp
commands/KoPathSegmentBreakCommand.cpp
commands/KoPathBreakAtPointCommand.cpp
commands/KoPathSegmentTypeCommand.cpp
commands/KoPathCombineCommand.cpp
commands/KoSubpathRemoveCommand.cpp
commands/KoSubpathJoinCommand.cpp
commands/KoParameterHandleMoveCommand.cpp
commands/KoParameterToPathCommand.cpp
commands/KoShapeTransformCommand.cpp
commands/KoPathFillRuleCommand.cpp
commands/KoConnectionShapeTypeCommand.cpp
commands/KoShapeShadowCommand.cpp
commands/KoPathReverseCommand.cpp
commands/KoShapeRenameCommand.cpp
commands/KoShapeRunAroundCommand.cpp
commands/KoPathPointMergeCommand.cpp
commands/KoShapeTransparencyCommand.cpp
commands/KoShapeClipCommand.cpp
commands/KoShapeUnclipCommand.cpp
commands/KoPathShapeMarkerCommand.cpp
commands/KoShapeConnectionChangeCommand.cpp
commands/KoMultiPathPointMergeCommand.cpp
commands/KoMultiPathPointJoinCommand.cpp
+ commands/KoKeepShapesSelectedCommand.cpp
tools/KoCreateShapeStrategy.cpp
tools/KoPathToolFactory.cpp
tools/KoPathTool.cpp
tools/KoPathToolSelection.cpp
tools/KoPathToolHandle.cpp
tools/PathToolOptionWidget.cpp
tools/KoPathPointRubberSelectStrategy.cpp
tools/KoPathPointMoveStrategy.cpp
tools/KoPathConnectionPointStrategy.cpp
tools/KoPathControlPointMoveStrategy.cpp
tools/KoParameterChangeStrategy.cpp
tools/KoZoomTool.cpp
tools/KoZoomToolFactory.cpp
tools/KoZoomToolWidget.cpp
tools/KoZoomStrategy.cpp
tools/KoPanTool.cpp
tools/KoPanToolFactory.cpp
tools/KoInteractionTool.cpp
tools/KoInteractionStrategy.cpp
tools/KoInteractionStrategyFactory.cpp
tools/KoCreateShapesTool.cpp
tools/KoCreateShapesToolFactory.cpp
tools/KoShapeRubberSelectStrategy.cpp
tools/KoPathSegmentChangeStrategy.cpp
svg/KoShapePainter.cpp
svg/SvgUtil.cpp
svg/SvgGraphicContext.cpp
svg/SvgSavingContext.cpp
svg/SvgWriter.cpp
svg/SvgStyleWriter.cpp
svg/SvgShape.cpp
svg/SvgParser.cpp
svg/SvgStyleParser.cpp
svg/SvgGradientHelper.cpp
svg/SvgFilterHelper.cpp
svg/SvgCssHelper.cpp
svg/SvgClipPathHelper.cpp
svg/SvgLoadingContext.cpp
svg/SvgShapeFactory.cpp
svg/parsers/SvgTransformParser.cpp
text/KoSvgText.cpp
text/KoSvgTextProperties.cpp
text/KoSvgTextChunkShape.cpp
text/KoSvgTextShape.cpp
text/KoSvgTextShapeMarkupConverter.cpp
resources/KoSvgSymbolCollectionResource.cpp
FlakeDebug.cpp
tests/MockShapes.cpp
)
ki18n_wrap_ui(kritaflake_SRCS
tools/PathToolOptionWidgetBase.ui
KoConnectionShapeConfigWidget.ui
tools/KoZoomToolWidget.ui
)
add_library(kritaflake SHARED ${kritaflake_SRCS})
generate_export_header(kritaflake BASE_NAME kritaflake)
target_include_directories(kritaflake
PUBLIC
$
$
$
$
)
target_link_libraries(kritaflake kritapigment kritawidgetutils kritaodf kritacommand KF5::WidgetsAddons Qt5::Svg)
set_target_properties(kritaflake PROPERTIES
VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION}
)
install(TARGETS kritaflake ${INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/libs/flake/KoCanvasControllerWidgetViewport_p.cpp b/libs/flake/KoCanvasControllerWidgetViewport_p.cpp
index 5ab19095b7..aeb624a987 100644
--- a/libs/flake/KoCanvasControllerWidgetViewport_p.cpp
+++ b/libs/flake/KoCanvasControllerWidgetViewport_p.cpp
@@ -1,408 +1,408 @@
/* This file is part of the KDE project
*
* Copyright (C) 2006-2007, 2009 Thomas Zander
* Copyright (C) 2006 Thorsten Zachmann
* Copyright (C) 2007-2010 Boudewijn Rempt
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoCanvasControllerWidgetViewport_p.h"
#include
#include
#include
#include
#include
#include
#include
#include "KoShape.h"
#include "KoShape_p.h"
#include "KoShapeFactoryBase.h" // for the SHAPE mimetypes
#include "KoShapeRegistry.h"
#include "KoShapeController.h"
#include "KoShapeManager.h"
#include "KoSelection.h"
#include "KoCanvasBase.h"
#include "KoShapeLayer.h"
#include "KoShapePaintingContext.h"
#include "KoToolProxy.h"
#include "KoCanvasControllerWidget.h"
#include "KoViewConverter.h"
#include "KoSvgPaste.h"
// ********** Viewport **********
Viewport::Viewport(KoCanvasControllerWidget *parent)
: QWidget(parent)
, m_draggedShape(0)
, m_drawShadow(false)
, m_canvas(0)
, m_documentOffset(QPoint(0, 0))
, m_margin(0)
{
setAutoFillBackground(true);
setAcceptDrops(true);
setMouseTracking(true);
m_parent = parent;
}
void Viewport::setCanvas(QWidget *canvas)
{
if (m_canvas) {
m_canvas->hide();
delete m_canvas;
}
m_canvas = canvas;
if (!canvas) return;
m_canvas->setParent(this);
m_canvas->show();
if (!m_canvas->minimumSize().isNull()) {
m_documentSize = m_canvas->minimumSize();
}
resetLayout();
}
void Viewport::setDocumentSize(const QSize &size)
{
m_documentSize = size;
resetLayout();
}
void Viewport::documentOffsetMoved(const QPoint &pt)
{
m_documentOffset = pt;
resetLayout();
}
void Viewport::setDrawShadow(bool drawShadow)
{
m_drawShadow = drawShadow;
}
void Viewport::handleDragEnterEvent(QDragEnterEvent *event)
{
// if not a canvas set then ignore this, makes it possible to assume
// we have a canvas in all the support methods.
if (!(m_parent->canvas() && m_parent->canvas()->canvasWidget())) {
event->ignore();
return;
}
delete m_draggedShape;
m_draggedShape = 0;
// only allow dropping when active layer is editable
KoSelection *selection = m_parent->canvas()->shapeManager()->selection();
KoShapeLayer *activeLayer = selection->activeLayer();
if (activeLayer && (!activeLayer->isEditable() || activeLayer->isGeometryProtected())) {
event->ignore();
return;
}
const QMimeData *data = event->mimeData();
if (data->hasFormat(SHAPETEMPLATE_MIMETYPE) ||
data->hasFormat(SHAPEID_MIMETYPE) ||
data->hasFormat("image/svg+xml"))
{
if (data->hasFormat("image/svg+xml")) {
KoCanvasBase *canvas = m_parent->canvas();
QSizeF fragmentSize;
QList shapes = KoSvgPaste::fetchShapesFromData(data->data("image/svg+xml"),
canvas->shapeController()->documentRectInPixels(),
canvas->shapeController()->pixelsPerInch(),
&fragmentSize);
if (!shapes.isEmpty()) {
m_draggedShape = shapes[0];
}
}
else {
QByteArray itemData;
bool isTemplate = true;
if (data->hasFormat(SHAPETEMPLATE_MIMETYPE)) {
itemData = data->data(SHAPETEMPLATE_MIMETYPE);
}
else if (data->hasFormat(SHAPEID_MIMETYPE)) {
isTemplate = false;
itemData = data->data(SHAPEID_MIMETYPE);
}
QDataStream dataStream(&itemData, QIODevice::ReadOnly);
QString id;
dataStream >> id;
QString properties;
if (isTemplate)
dataStream >> properties;
// and finally, there is a point.
QPointF offset;
dataStream >> offset;
// The rest of this method is mostly a copy paste from the KoCreateShapeStrategy
// So, lets remove this again when Zagge adds his new class that does this kind of thing. (KoLoadSave)
KoShapeFactoryBase *factory = KoShapeRegistry::instance()->value(id);
if (! factory) {
warnFlake << "Application requested a shape that is not registered '" <<
id << "', Ignoring";
event->ignore();
return;
}
if (isTemplate) {
KoProperties props;
props.load(properties);
m_draggedShape = factory->createShape(&props, m_parent->canvas()->shapeController()->resourceManager());
}
else {
m_draggedShape = factory->createDefaultShape(m_parent->canvas()->shapeController()->resourceManager());
}
if (m_draggedShape->shapeId().isEmpty()) {
m_draggedShape->setShapeId(factory->id());
}
}
event->setDropAction(Qt::CopyAction);
event->accept();
Q_ASSERT(m_draggedShape);
if (!m_draggedShape) return;
m_draggedShape->setZIndex(KoShapePrivate::MaxZIndex);
m_draggedShape->setAbsolutePosition(correctPosition(event->pos()));
m_parent->canvas()->shapeManager()->addShape(m_draggedShape);
} else {
event->ignore();
}
}
void Viewport::handleDropEvent(QDropEvent *event)
{
if (!m_draggedShape) {
m_parent->canvas()->toolProxy()->dropEvent(event, correctPosition(event->pos()));
return;
}
repaint(m_draggedShape);
m_parent->canvas()->shapeManager()->remove(m_draggedShape); // remove it to not interfere with z-index calc.
m_draggedShape->setPosition(QPointF(0, 0)); // always save position.
QPointF newPos = correctPosition(event->pos());
m_parent->canvas()->clipToDocument(m_draggedShape, newPos); // ensure the shape is dropped inside the document.
m_draggedShape->setAbsolutePosition(newPos);
- KUndo2Command * cmd = m_parent->canvas()->shapeController()->addShape(m_draggedShape);
+ KUndo2Command * cmd = m_parent->canvas()->shapeController()->addShape(m_draggedShape, 0);
if (cmd) {
m_parent->canvas()->addCommand(cmd);
KoSelection *selection = m_parent->canvas()->shapeManager()->selection();
// repaint selection before selecting newly create shape
Q_FOREACH (KoShape * shape, selection->selectedShapes()) {
shape->update();
}
selection->deselectAll();
selection->select(m_draggedShape);
} else {
delete m_draggedShape;
}
m_draggedShape = 0;
}
QPointF Viewport::correctPosition(const QPoint &point) const
{
QWidget *canvasWidget = m_parent->canvas()->canvasWidget();
Q_ASSERT(canvasWidget); // since we should not allow drag if there is not.
QPoint correctedPos(point.x() - canvasWidget->x(), point.y() - canvasWidget->y());
correctedPos += m_documentOffset;
return m_parent->canvas()->viewToDocument(correctedPos);
}
void Viewport::handleDragMoveEvent(QDragMoveEvent *event)
{
if (!m_draggedShape) {
m_parent->canvas()->toolProxy()->dragMoveEvent(event, correctPosition(event->pos()));
return;
}
m_draggedShape->update();
repaint(m_draggedShape);
m_draggedShape->setAbsolutePosition(correctPosition(event->pos()));
m_draggedShape->update();
repaint(m_draggedShape);
}
void Viewport::repaint(KoShape *shape)
{
QRect rect = m_parent->canvas()->viewConverter()->documentToView(shape->boundingRect()).toRect();
QWidget *canvasWidget = m_parent->canvas()->canvasWidget();
Q_ASSERT(canvasWidget); // since we should not allow drag if there is not.
rect.moveLeft(rect.left() + canvasWidget->x() - m_documentOffset.x());
rect.moveTop(rect.top() + canvasWidget->y() - m_documentOffset.y());
rect.adjust(-2, -2, 2, 2); // adjust for antialias
update(rect);
}
void Viewport::handleDragLeaveEvent(QDragLeaveEvent *event)
{
if (m_draggedShape) {
repaint(m_draggedShape);
m_parent->canvas()->shapeManager()->remove(m_draggedShape);
delete m_draggedShape;
m_draggedShape = 0;
} else {
m_parent->canvas()->toolProxy()->dragLeaveEvent(event);
}
}
void Viewport::handlePaintEvent(QPainter &painter, QPaintEvent *event)
{
Q_UNUSED(event);
// Draw the shadow around the canvas.
if (m_parent->canvas() && m_parent->canvas()->canvasWidget() && m_drawShadow) {
QWidget *canvas = m_parent->canvas()->canvasWidget();
painter.setPen(QPen(Qt::black, 0));
QRect rect(canvas->x(), canvas->y(), canvas->width(), canvas->height());
rect.adjust(-1, -1, 0, 0);
painter.drawRect(rect);
painter.drawLine(rect.right() + 2, rect.top() + 2, rect.right() + 2, rect.bottom() + 2);
painter.drawLine(rect.left() + 2, rect.bottom() + 2, rect.right() + 2, rect.bottom() + 2);
}
if (m_draggedShape) {
const KoViewConverter *vc = m_parent->canvas()->viewConverter();
painter.save();
QWidget *canvasWidget = m_parent->canvas()->canvasWidget();
Q_ASSERT(canvasWidget); // since we should not allow drag if there is not.
painter.translate(canvasWidget->x() - m_documentOffset.x(),
canvasWidget->y() - m_documentOffset.y());
QPointF offset = vc->documentToView(m_draggedShape->position());
painter.setOpacity(0.6);
painter.translate(offset.x(), offset.y());
painter.setRenderHint(QPainter::Antialiasing);
KoShapePaintingContext paintContext; //FIXME
m_draggedShape->paint(painter, *vc, paintContext);
painter.restore();
}
}
void Viewport::resetLayout()
{
// Determine the area we have to show
QRect viewRect(m_documentOffset, size());
const int viewH = viewRect.height();
const int viewW = viewRect.width();
const int docH = m_documentSize.height();
const int docW = m_documentSize.width();
int moveX = 0;
int moveY = 0;
int resizeW = viewW;
int resizeH = viewH;
// debugFlake <<"viewH:" << viewH << endl
// << "docH: " << docH << endl
// << "viewW: " << viewW << endl
// << "docW: " << docW << endl;
if (viewH == docH && viewW == docW) {
// Do nothing
resizeW = docW;
resizeH = docH;
} else if (viewH > docH && viewW > docW) {
// Show entire canvas centered
moveX = (viewW - docW) / 2;
moveY = (viewH - docH) / 2;
resizeW = docW;
resizeH = docH;
} else if (viewW > docW) {
// Center canvas horizontally
moveX = (viewW - docW) / 2;
resizeW = docW;
int marginTop = m_margin - m_documentOffset.y();
int marginBottom = viewH - (m_documentSize.height() - m_documentOffset.y());
if (marginTop > 0) moveY = marginTop;
if (marginTop > 0) resizeH = viewH - marginTop;
if (marginBottom > 0) resizeH = viewH - marginBottom;
} else if (viewH > docH) {
// Center canvas vertically
moveY = (viewH - docH) / 2;
resizeH = docH;
int marginLeft = m_margin - m_documentOffset.x();
int marginRight = viewW - (m_documentSize.width() - m_documentOffset.x());
if (marginLeft > 0) moveX = marginLeft;
if (marginLeft > 0) resizeW = viewW - marginLeft;
if (marginRight > 0) resizeW = viewW - marginRight;
} else {
// Take care of the margin around the canvas
int marginTop = m_margin - m_documentOffset.y();
int marginLeft = m_margin - m_documentOffset.x();
int marginRight = viewW - (m_documentSize.width() - m_documentOffset.x());
int marginBottom = viewH - (m_documentSize.height() - m_documentOffset.y());
if (marginTop > 0) moveY = marginTop;
if (marginLeft > 0) moveX = marginLeft;
if (marginTop > 0) resizeH = viewH - marginTop;
if (marginLeft > 0) resizeW = viewW - marginLeft;
if (marginRight > 0) resizeW = viewW - marginRight;
if (marginBottom > 0) resizeH = viewH - marginBottom;
}
if (m_parent->canvasMode() == KoCanvasController::AlignTop) {
// have up to m_margin pixels at top.
moveY = qMin(m_margin, moveY);
}
if (m_canvas) {
QRect geom;
if (m_parent->canvasMode() == KoCanvasController::Infinite)
geom = QRect(0, 0, viewW, viewH);
else
geom = QRect(moveX, moveY, resizeW, resizeH);
if (m_canvas->geometry() != geom) {
m_canvas->setGeometry(geom);
m_canvas->update();
}
}
if (m_drawShadow) {
update();
}
emit sizeChanged();
#if 0
debugFlake <<"View port geom:" << geometry();
if (m_canvas)
debugFlake <<"Canvas widget geom:" << m_canvas->geometry();
#endif
}
diff --git a/libs/flake/KoShapeController.cpp b/libs/flake/KoShapeController.cpp
index 0c67c96b15..8fd031c123 100644
--- a/libs/flake/KoShapeController.cpp
+++ b/libs/flake/KoShapeController.cpp
@@ -1,212 +1,212 @@
/* This file is part of the KDE project
*
* Copyright (C) 2006-2007, 2010 Thomas Zander
* Copyright (C) 2006-2008 Thorsten Zachmann
* Copyright (C) 2011 Jan Hambrecht
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoShapeController.h"
#include "KoShapeBasedDocumentBase.h"
#include "KoShapeRegistry.h"
#include "KoDocumentResourceManager.h"
#include "KoShapeManager.h"
#include "KoShapeLayer.h"
#include "KoSelection.h"
#include "commands/KoShapeCreateCommand.h"
#include "commands/KoShapeDeleteCommand.h"
#include "commands/KoShapeConnectionChangeCommand.h"
#include "KoCanvasBase.h"
#include "KoShapeConfigWidgetBase.h"
#include "KoShapeFactoryBase.h"
#include "KoShape.h"
#include "KoConnectionShape.h"
#include
#include
#include
#include
class KoShapeController::Private
{
public:
Private()
: canvas(0),
shapeBasedDocument(0)
{
}
KoCanvasBase *canvas;
KoShapeBasedDocumentBase *shapeBasedDocument;
- KUndo2Command* addShape(KoShape *shape, bool showDialog, KUndo2Command *parent) {
+ KUndo2Command* addShape(KoShape *shape, bool showDialog, KoShapeContainer *parentShape, KUndo2Command *parent) {
if (canvas) {
if (showDialog && !shape->shapeId().isEmpty()) {
KoShapeFactoryBase *factory = KoShapeRegistry::instance()->value(shape->shapeId());
Q_ASSERT(factory);
int z = 0;
Q_FOREACH (KoShape *sh, canvas->shapeManager()->shapes())
z = qMax(z, sh->zIndex());
shape->setZIndex(z + 1);
// show config dialog.
KPageDialog *dialog = new KPageDialog(canvas->canvasWidget());
dialog->setWindowTitle(i18n("%1 Options", factory->name()));
int pageCount = 0;
QList widgets;
Q_FOREACH (KoShapeConfigWidgetBase* panel, factory->createShapeOptionPanels()) {
if (! panel->showOnShapeCreate())
continue;
panel->open(shape);
panel->connect(panel, SIGNAL(accept()), dialog, SLOT(accept()));
widgets.append(panel);
panel->setResourceManager(canvas->resourceManager());
panel->setUnit(canvas->unit());
QString title = panel->windowTitle().isEmpty() ? panel->objectName() : panel->windowTitle();
dialog->addPage(panel, title);
pageCount ++;
}
if (pageCount > 0) {
if (pageCount > 1)
dialog->setFaceType(KPageDialog::Tabbed);
if (dialog->exec() != KPageDialog::Accepted) {
delete dialog;
return 0;
}
Q_FOREACH (KoShapeConfigWidgetBase *widget, widgets)
widget->save();
}
delete dialog;
}
}
- return addShapesDirect({shape}, parent);
+ return addShapesDirect({shape}, parentShape, parent);
}
- KUndo2Command* addShapesDirect(const QList shapes, KUndo2Command *parent)
+ KUndo2Command* addShapesDirect(const QList shapes, KoShapeContainer *parentShape, KUndo2Command *parent)
{
- return new KoShapeCreateCommand(shapeBasedDocument, shapes, parent);
+ return new KoShapeCreateCommand(shapeBasedDocument, shapes, parentShape, parent);
}
void handleAttachedConnections(KoShape *shape, KUndo2Command *parentCmd) {
foreach (KoShape *dependee, shape->dependees()) {
KoConnectionShape *connection = dynamic_cast(dependee);
if (connection) {
if (shape == connection->firstShape()) {
new KoShapeConnectionChangeCommand(connection, KoConnectionShape::StartHandle,
shape, connection->firstConnectionId(), 0, -1, parentCmd);
} else if (shape == connection->secondShape()) {
new KoShapeConnectionChangeCommand(connection, KoConnectionShape::EndHandle,
shape, connection->secondConnectionId(), 0, -1, parentCmd);
}
}
}
}
};
KoShapeController::KoShapeController(KoCanvasBase *canvas, KoShapeBasedDocumentBase *shapeBasedDocument)
: d(new Private())
{
d->canvas = canvas;
d->shapeBasedDocument = shapeBasedDocument;
if (shapeBasedDocument) {
shapeBasedDocument->resourceManager()->setShapeController(this);
}
}
KoShapeController::~KoShapeController()
{
delete d;
}
void KoShapeController::reset()
{
d->canvas = 0;
d->shapeBasedDocument = 0;
}
-KUndo2Command* KoShapeController::addShape(KoShape *shape, KUndo2Command *parent)
+KUndo2Command* KoShapeController::addShape(KoShape *shape, KoShapeContainer *parentShape, KUndo2Command *parent)
{
- return d->addShape(shape, true, parent);
+ return d->addShape(shape, true, parentShape, parent);
}
-KUndo2Command* KoShapeController::addShapeDirect(KoShape *shape, KUndo2Command *parent)
+KUndo2Command* KoShapeController::addShapeDirect(KoShape *shape, KoShapeContainer *parentShape, KUndo2Command *parent)
{
- return d->addShapesDirect({shape}, parent);
+ return d->addShapesDirect({shape}, parentShape, parent);
}
-KUndo2Command *KoShapeController::addShapesDirect(const QList shapes, KUndo2Command *parent)
+KUndo2Command *KoShapeController::addShapesDirect(const QList shapes, KoShapeContainer *parentShape, KUndo2Command *parent)
{
- return d->addShapesDirect(shapes, parent);
+ return d->addShapesDirect(shapes, parentShape, parent);
}
KUndo2Command* KoShapeController::removeShape(KoShape *shape, KUndo2Command *parent)
{
KUndo2Command *cmd = new KoShapeDeleteCommand(d->shapeBasedDocument, shape, parent);
QList shapes;
shapes.append(shape);
d->shapeBasedDocument->shapesRemoved(shapes, cmd);
// detach shape from any attached connection shapes
d->handleAttachedConnections(shape, cmd);
return cmd;
}
KUndo2Command* KoShapeController::removeShapes(const QList &shapes, KUndo2Command *parent)
{
KUndo2Command *cmd = new KoShapeDeleteCommand(d->shapeBasedDocument, shapes, parent);
d->shapeBasedDocument->shapesRemoved(shapes, cmd);
foreach (KoShape *shape, shapes) {
d->handleAttachedConnections(shape, cmd);
}
return cmd;
}
void KoShapeController::setShapeControllerBase(KoShapeBasedDocumentBase *shapeBasedDocument)
{
d->shapeBasedDocument = shapeBasedDocument;
}
QRectF KoShapeController::documentRectInPixels() const
{
return d->shapeBasedDocument ? d->shapeBasedDocument->documentRectInPixels() : QRectF(0,0,1920,1080);
}
qreal KoShapeController::pixelsPerInch() const
{
return d->shapeBasedDocument ? d->shapeBasedDocument->pixelsPerInch() : 72.0;
}
QRectF KoShapeController::documentRect() const
{
return d->shapeBasedDocument ? d->shapeBasedDocument->documentRect() : documentRectInPixels();
}
KoDocumentResourceManager *KoShapeController::resourceManager() const
{
if (!d->shapeBasedDocument)
return 0;
return d->shapeBasedDocument->resourceManager();
}
KoShapeBasedDocumentBase *KoShapeController::documentBase() const
{
return d->shapeBasedDocument;
}
diff --git a/libs/flake/KoShapeController.h b/libs/flake/KoShapeController.h
index 26171e50af..b1d2912db2 100644
--- a/libs/flake/KoShapeController.h
+++ b/libs/flake/KoShapeController.h
@@ -1,169 +1,170 @@
/* This file is part of the KDE project
*
* Copyright (C) 2006-2007, 2010 Thomas Zander
* Copyright (C) 2006-2008 Thorsten Zachmann
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOSHAPECONTROLLER_H
#define KOSHAPECONTROLLER_H
#include "kritaflake_export.h"
#include
#include
#include
class KoCanvasBase;
class KoShape;
+class KoShapeContainer;
class KoShapeBasedDocumentBase;
class KUndo2Command;
class KoDocumentResourceManager;
/**
* Class used by tools to maintain the list of shapes.
* All applications have some sort of list of all shapes that belong to the document.
* The applications implement the KoShapeBasedDocumentBase interface (all pure virtuals)
* to add and remove shapes from the document. To ensure that an application can expect
* a certain protocol to be adhered to when adding/removing shapes, all tools use the API
* from this class for maintaining the list of shapes in the document. So no tool gets
* to access the application directly.
*/
class KRITAFLAKE_EXPORT KoShapeController : public QObject
{
Q_OBJECT
public:
/**
* Create a new Controller; typically not called by applications, only
* by the KonCanvasBase constructor.
* @param canvas the canvas this controller works for. The canvas can be 0
* @param shapeBasedDocument the application provided shapeBasedDocument that we can call.
*/
KoShapeController(KoCanvasBase *canvas, KoShapeBasedDocumentBase *shapeBasedDocument);
/// destructor
~KoShapeController() override;
/**
* @brief reset sets the canvas and shapebased document to 0.
*/
void reset();
/**
* @brief Add a shape to the document.
* If the shape has no parent, the active layer will become its parent.
*
* @param shape to add to the document
* @param parent the parent command if the resulting command is a compound undo command.
*
* @return command which will insert the shape into the document or 0 if the
* insertion was cancelled. The command is not yet executed.
*/
- KUndo2Command* addShape(KoShape *shape, KUndo2Command *parent = 0);
+ KUndo2Command* addShape(KoShape *shape, KoShapeContainer *parentShape, KUndo2Command *parent = 0);
/**
* @brief Add a shape to the document, skipping any dialogs or other user interaction.
*
* @param shape to add to the document
* @param parent the parent command if the resulting command is a compound undo command.
*
* @return command which will insert the shape into the document. The command is not yet executed.
*/
- KUndo2Command* addShapeDirect(KoShape *shape, KUndo2Command *parent = 0);
+ KUndo2Command* addShapeDirect(KoShape *shape, KoShapeContainer *parentShape, KUndo2Command *parent = 0);
/**
* @brief Add shapes to the document, skipping any dialogs or other user interaction.
*
* @param shapes to add to the document
* @param parent the parent command if the resulting command is a compound undo command.
*
* @return command which will insert the shapes into the document. The command is not yet executed.
*/
- KUndo2Command* addShapesDirect(const QList shape, KUndo2Command *parent = 0);
+ KUndo2Command* addShapesDirect(const QList shape, KoShapeContainer *parentShape, KUndo2Command *parent = 0);
/**
* @brief Remove a shape from the document.
*
* @param shape to remove from the document
* @param parent the parent command if the resulting command is a compound undo command.
*
* @return command which will remove the shape from the document.
* The command is not yet executed.
*/
KUndo2Command* removeShape(KoShape *shape, KUndo2Command *parent = 0);
/**
* Remove a shape from the document.
*
* @param shapes the set of shapes to remove from the document
* @param parent the parent command if the resulting command is a compound undo command.
*
* @return command which will remove the shape from the document.
* The command is not yet executed.
*/
KUndo2Command* removeShapes(const QList &shapes, KUndo2Command *parent = 0);
/**
* @brief Set the KoShapeBasedDocumentBase used to add/remove shapes.
*
* NOTE: only Sheets uses this method. Do not use it in your application. Sheets
* has to also call:
* KoToolManager::instance()->updateShapeControllerBase(shapeBasedDocument, canvas->canvasController());
*
* @param shapeBasedDocument the new shapeBasedDocument.
*/
void setShapeControllerBase(KoShapeBasedDocumentBase *shapeBasedDocument);
/**
* The size of the document measured in rasterized pixels. This information is needed for loading
* SVG documents that use 'px' as the default unit.
*/
QRectF documentRectInPixels() const;
/**
* Resolution of the rasterized representaiton of the document. Used to load SVG documents correctly.
*/
qreal pixelsPerInch() const;
/**
* Document rect measured in 'pt'
*/
QRectF documentRect() const;
/**
* Return a pointer to the resource manager associated with the
* shape-set (typically a document). The resource manager contains
* document wide resources * such as variable managers, the image
* collection and others.
*/
KoDocumentResourceManager *resourceManager() const;
/**
* @brief Returns the KoShapeBasedDocumentBase used to add/remove shapes.
*
* @return the KoShapeBasedDocumentBase
*/
KoShapeBasedDocumentBase *documentBase() const;
private:
class Private;
Private * const d;
};
Q_DECLARE_METATYPE(KoShapeController *)
#endif
diff --git a/libs/flake/KoToolProxy.cpp b/libs/flake/KoToolProxy.cpp
index fcdb2f3b82..13678d100c 100644
--- a/libs/flake/KoToolProxy.cpp
+++ b/libs/flake/KoToolProxy.cpp
@@ -1,589 +1,589 @@
/* 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 "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::touchEvent(QTouchEvent *event)
{
QPointF point;
QList touchPoints;
bool isPrimary = true;
Q_FOREACH (QTouchEvent::TouchPoint p, event->touchPoints()) {
QPointF docPoint = widgetToDocument(p.screenPos());
if (isPrimary) {
point = docPoint;
isPrimary = false;
}
KoTouchPoint touchPoint;
touchPoint.touchPoint = p;
touchPoint.point = point;
touchPoint.lastPoint = widgetToDocument(p.lastNormalizedPos());
touchPoints << touchPoint;
}
KoPointerEvent ev(event, point, touchPoints);
KoInputDevice id;
KoToolManager::instance()->priv()->switchInputDevice(id);
switch (event->type()) {
case QEvent::TouchBegin:
ev.setTabletButton(Qt::LeftButton);
if (d->activeTool) {
if( d->activeTool->wantsTouch() )
d->activeTool->touchEvent(event);
else
d->activeTool->mousePressEvent(&ev);
}
break;
case QEvent::TouchUpdate:
ev.setTabletButton(Qt::LeftButton);
if (d->activeTool) {
if( d->activeTool->wantsTouch() )
d->activeTool->touchEvent(event);
else
d->activeTool->mouseMoveEvent(&ev);
}
break;
case QEvent::TouchEnd:
ev.setTabletButton(Qt::LeftButton);
if (d->activeTool) {
if( d->activeTool->wantsTouch() )
d->activeTool->touchEvent(event);
else
d->activeTool->mouseReleaseEvent(&ev);
}
break;
default:
; // ingore the rest
}
d->mouseLeaveWorkaround = true;
}
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.
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)
{
// 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::wheelEvent(QWheelEvent *event, const QPointF &point)
{
KoPointerEvent ev(event, point);
if (d->activeTool)
d->activeTool->wheelEvent(&ev);
else
event->ignore();
}
void KoToolProxy::wheelEvent(KoPointerEvent *event)
{
if (d->activeTool)
d->activeTool->wheelEvent(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->shapeController()->addShapesDirect(pastedShapes, 0, 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/commands/KoKeepShapesSelectedCommand.cpp b/libs/flake/commands/KoKeepShapesSelectedCommand.cpp
new file mode 100644
index 0000000000..f1fcc2b82d
--- /dev/null
+++ b/libs/flake/commands/KoKeepShapesSelectedCommand.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2017 Dmitry Kazakov
+ *
+ * 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 "KoKeepShapesSelectedCommand.h"
+
+#include
+#include
+
+
+KoKeepShapesSelectedCommand::KoKeepShapesSelectedCommand(const QList &selectedBefore,
+ const QList &selectedAfter,
+ KoSelection *selection,
+ bool isFinalizing,
+ KUndo2Command *parent)
+ : KisCommandUtils::FlipFlopCommand(isFinalizing, parent),
+ m_selectedBefore(selectedBefore),
+ m_selectedAfter(selectedAfter),
+ m_selection(selection)
+{
+
+}
+
+void KoKeepShapesSelectedCommand::end()
+{
+ m_selection->deselectAll();
+
+ const QList newSelectedShapes =
+ isFinalizing() ? m_selectedAfter : m_selectedBefore;
+
+ Q_FOREACH (KoShape *shape, newSelectedShapes) {
+ m_selection->select(shape);
+ }
+}
+
diff --git a/libs/flake/commands/KoKeepShapesSelectedCommand.h b/libs/flake/commands/KoKeepShapesSelectedCommand.h
new file mode 100644
index 0000000000..195ad91a6f
--- /dev/null
+++ b/libs/flake/commands/KoKeepShapesSelectedCommand.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2017 Dmitry Kazakov
+ *
+ * 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 KOKEEPSHAPESSELECTEDCOMMAND_H
+#define KOKEEPSHAPESSELECTEDCOMMAND_H
+
+#include "kis_command_utils.h"
+#include
+
+class KoSelection;
+class KoShape;
+
+class KRITAFLAKE_EXPORT KoKeepShapesSelectedCommand : public KisCommandUtils::FlipFlopCommand
+{
+public:
+ KoKeepShapesSelectedCommand(const QList &selectedBefore,
+ const QList &selectedAfter,
+ KoSelection *selection,
+ bool isFinalizing,
+ KUndo2Command *parent);
+
+protected:
+ void end();
+
+private:
+ QList m_selectedBefore;
+ QList m_selectedAfter;
+ KoSelection *m_selection;
+};
+
+#endif // KOKEEPSHAPESSELECTEDCOMMAND_H
diff --git a/libs/flake/commands/KoShapeCreateCommand.cpp b/libs/flake/commands/KoShapeCreateCommand.cpp
index a2c443c217..f3312b3eb2 100644
--- a/libs/flake/commands/KoShapeCreateCommand.cpp
+++ b/libs/flake/commands/KoShapeCreateCommand.cpp
@@ -1,127 +1,126 @@
/* This file is part of the KDE project
* Copyright (C) 2006 Thomas Zander
* Copyright (C) 2006 Jan Hambrecht
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoShapeCreateCommand.h"
#include "KoShape.h"
#include "KoShapeContainer.h"
#include "KoShapeBasedDocumentBase.h"
#include
#include "kis_assert.h"
#include
#include
#include
#include
class Q_DECL_HIDDEN KoShapeCreateCommand::Private
{
public:
- Private(KoShapeBasedDocumentBase *_document, const QList &_shapes)
+ Private(KoShapeBasedDocumentBase *_document, const QList &_shapes, KoShapeContainer *_parentShape)
: shapesDocument(_document),
shapes(_shapes),
+ explicitParentShape(_parentShape),
deleteShapes(true)
{
- Q_FOREACH(KoShape *shape, shapes) {
- originalShapeParents << shape->parent();
- }
}
~Private() {
if (deleteShapes) {
qDeleteAll(shapes);
}
}
KoShapeBasedDocumentBase *shapesDocument;
QList shapes;
- QList originalShapeParents;
+ KoShapeContainer *explicitParentShape;
bool deleteShapes;
std::vector> reorderingCommands;
QScopedPointer reorderingCommand;
};
-KoShapeCreateCommand::KoShapeCreateCommand(KoShapeBasedDocumentBase *controller, KoShape *shape, KUndo2Command *parent)
- : KoShapeCreateCommand(controller, QList() << shape, parent)
+KoShapeCreateCommand::KoShapeCreateCommand(KoShapeBasedDocumentBase *controller, KoShape *shape, KoShapeContainer *parentShape, KUndo2Command *parent)
+ : KoShapeCreateCommand(controller, QList() << shape, parentShape, parent)
{
}
-KoShapeCreateCommand::KoShapeCreateCommand(KoShapeBasedDocumentBase *controller, const QList shapes, KUndo2Command *parent)
- : KUndo2Command(kundo2_i18np("Create shape", "Create shapes", shapes.size()), parent),
- d(new Private(controller, shapes))
+KoShapeCreateCommand::KoShapeCreateCommand(KoShapeBasedDocumentBase *controller, const QList shapes, KoShapeContainer *parentShape, KUndo2Command *parent)
+ : KUndo2Command(kundo2_i18np("Create shape", "Create shapes", shapes.size()), parent),
+ d(new Private(controller, shapes, parentShape))
{
}
KoShapeCreateCommand::~KoShapeCreateCommand()
{
delete d;
}
void KoShapeCreateCommand::redo()
{
KUndo2Command::redo();
KIS_ASSERT(d->shapesDocument);
d->deleteShapes = false;
d->reorderingCommands.clear();
Q_FOREACH(KoShape *shape, d->shapes) {
+ if (d->explicitParentShape) {
+ shape->setParent(d->explicitParentShape);
+ }
+
d->shapesDocument->addShape(shape);
KoShapeContainer *shapeParent = shape->parent();
KIS_SAFE_ASSERT_RECOVER_NOOP(shape->parent() ||
dynamic_cast(shape));
if (shapeParent) {
KUndo2Command *cmd = KoShapeReorderCommand::mergeInShape(shapeParent->shapes(), shape);
if (d->reorderingCommand) {
cmd->redo();
d->reorderingCommands.push_back(
std::unique_ptr(cmd));
}
}
}
}
void KoShapeCreateCommand::undo()
{
KUndo2Command::undo();
KIS_ASSERT(d->shapesDocument);
while (!d->reorderingCommands.empty()) {
std::unique_ptr cmd = std::move(d->reorderingCommands.back());
cmd->undo();
d->reorderingCommands.pop_back();
}
- KIS_SAFE_ASSERT_RECOVER_RETURN(d->shapes.size() == d->originalShapeParents.size());
-
- for (int i = 0; i < d->shapes.size(); i++) {
- d->shapesDocument->removeShape(d->shapes[i]);
- d->shapes[i]->setParent(d->originalShapeParents[i]);
+ Q_FOREACH(KoShape *shape, d->shapes) {
+ d->shapesDocument->removeShape(shape);
}
d->deleteShapes = true;
}
diff --git a/libs/flake/commands/KoShapeCreateCommand.h b/libs/flake/commands/KoShapeCreateCommand.h
index 82a7e6f8d8..140f33a463 100644
--- a/libs/flake/commands/KoShapeCreateCommand.h
+++ b/libs/flake/commands/KoShapeCreateCommand.h
@@ -1,62 +1,65 @@
/* This file is part of the KDE project
* Copyright (C) 2006 Thomas Zander
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOSHAPECREATECOMMAND_H
#define KOSHAPECREATECOMMAND_H
#include "kritaflake_export.h"
#include
class KoShape;
+class KoShapeContainer;
class KoShapeBasedDocumentBase;
/// The undo / redo command for creating shapes
class KRITAFLAKE_EXPORT KoShapeCreateCommand : public KUndo2Command
{
public:
/**
* Command used on creation of new shapes
* @param controller the controller used to add/remove the shape from
* @param shape the shape thats just been created.
* @param parent the parent command used for macro commands
*/
KoShapeCreateCommand(KoShapeBasedDocumentBase *controller, KoShape *shape,
+ KoShapeContainer *parentShape = 0,
KUndo2Command *parent = 0);
/**
* Command used on creation of new shapes
* @param controller the controller used to add/remove the shape from
* @param shapes the shapes that have just been created.
* @param parent the parent command used for macro commands
*/
KoShapeCreateCommand(KoShapeBasedDocumentBase *controller, const QList shape,
+ KoShapeContainer *parentShape = 0,
KUndo2Command *parent = 0);
~KoShapeCreateCommand() override;
/// redo the command
void redo() override;
/// revert the actions done in redo
void undo() override;
private:
class Private;
Private * const d;
};
#endif
diff --git a/libs/flake/tests/TestShapePainting.cpp b/libs/flake/tests/TestShapePainting.cpp
index b74c4e0a67..3820c2a6ef 100644
--- a/libs/flake/tests/TestShapePainting.cpp
+++ b/libs/flake/tests/TestShapePainting.cpp
@@ -1,325 +1,325 @@
/*
* This file is part of Calligra tests
*
* Copyright (C) 2006-2010 Thomas Zander
*
* 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 "TestShapePainting.h"
#include
#include "KoShapeContainer.h"
#include "KoShapeManager.h"
#include "KoShapePaintingContext.h"
#include "KoViewConverter.h"
#include
#include
void TestShapePainting::testPaintShape()
{
MockShape *shape1 = new MockShape();
MockShape *shape2 = new MockShape();
MockContainer *container = new MockContainer();
container->addShape(shape1);
container->addShape(shape2);
QCOMPARE(shape1->parent(), container);
QCOMPARE(shape2->parent(), container);
container->setClipped(shape1, false);
container->setClipped(shape2, false);
QCOMPARE(container->isClipped(shape1), false);
QCOMPARE(container->isClipped(shape2), false);
MockCanvas canvas;
KoShapeManager manager(&canvas);
manager.addShape(container);
QCOMPARE(manager.shapes().count(), 3);
QImage image(100, 100, QImage::Format_Mono);
QPainter painter(&image);
KoViewConverter vc;
manager.paint(painter, vc, false);
// with the shape not being clipped, the shapeManager will paint it for us.
QCOMPARE(shape1->paintedCount, 1);
QCOMPARE(shape2->paintedCount, 1);
QCOMPARE(container->paintedCount, 1);
// the container should thus not paint the shape
shape1->paintedCount = 0;
shape2->paintedCount = 0;
container->paintedCount = 0;
KoShapePaintingContext paintContext;
container->paint(painter, vc, paintContext);
QCOMPARE(shape1->paintedCount, 0);
QCOMPARE(shape2->paintedCount, 0);
QCOMPARE(container->paintedCount, 1);
container->setClipped(shape1, false);
container->setClipped(shape2, true);
QCOMPARE(container->isClipped(shape1), false);
QCOMPARE(container->isClipped(shape2), true);
shape1->paintedCount = 0;
shape2->paintedCount = 0;
container->paintedCount = 0;
manager.paint(painter, vc, false);
// with this shape not being clipped, the shapeManager will paint the container and this shape
QCOMPARE(shape1->paintedCount, 1);
// with this shape being clipped, the container will paint it for us.
QCOMPARE(shape2->paintedCount, 1);
QCOMPARE(container->paintedCount, 1);
delete container;
}
void TestShapePainting::testPaintHiddenShape()
{
MockShape *shape = new MockShape();
MockContainer *fourth = new MockContainer();
MockContainer *thirth = new MockContainer();
MockContainer *second = new MockContainer();
MockContainer *top = new MockContainer();
top->addShape(second);
second->addShape(thirth);
thirth->addShape(fourth);
fourth->addShape(shape);
second->setVisible(false);
MockCanvas canvas;
KoShapeManager manager(&canvas);
manager.addShape(top);
QCOMPARE(manager.shapes().count(), 5);
QImage image(100, 100, QImage::Format_Mono);
QPainter painter(&image);
KoViewConverter vc;
manager.paint(painter, vc, false);
QCOMPARE(top->paintedCount, 1);
QCOMPARE(second->paintedCount, 0);
QCOMPARE(thirth->paintedCount, 0);
QCOMPARE(fourth->paintedCount, 0);
QCOMPARE(shape->paintedCount, 0);
delete top;
}
void TestShapePainting::testPaintOrder()
{
// the stacking order determines the painting order so things on top
// get their paint called last.
// Each shape has a zIndex and within the children a container has
// it determines the stacking order. Its important to realize that
// the zIndex is thus local to a container, if you have layer1 and layer2
// with both various child shapes the stacking order of the layer shapes
// is most important, then within this the child shape index is used.
class OrderedMockShape : public MockShape {
public:
OrderedMockShape(QList &list) : order(list) {}
void paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintcontext) override {
order.append(this);
MockShape::paint(painter, converter, paintcontext);
}
QList ℴ
};
QList order;
MockContainer *top = new MockContainer();
top->setZIndex(2);
OrderedMockShape *shape1 = new OrderedMockShape(order);
shape1->setZIndex(5);
OrderedMockShape *shape2 = new OrderedMockShape(order);
shape2->setZIndex(0);
top->addShape(shape1);
top->addShape(shape2);
MockContainer *bottom = new MockContainer();
bottom->setZIndex(1);
OrderedMockShape *shape3 = new OrderedMockShape(order);
shape3->setZIndex(-1);
OrderedMockShape *shape4 = new OrderedMockShape(order);
shape4->setZIndex(9);
bottom->addShape(shape3);
bottom->addShape(shape4);
MockCanvas canvas;
KoShapeManager manager(&canvas);
manager.addShape(top);
manager.addShape(bottom);
QCOMPARE(manager.shapes().count(), 6);
QImage image(100, 100, QImage::Format_Mono);
QPainter painter(&image);
KoViewConverter vc;
manager.paint(painter, vc, false);
QCOMPARE(top->paintedCount, 1);
QCOMPARE(bottom->paintedCount, 1);
QCOMPARE(shape1->paintedCount, 1);
QCOMPARE(shape2->paintedCount, 1);
QCOMPARE(shape3->paintedCount, 1);
QCOMPARE(shape4->paintedCount, 1);
QCOMPARE(order.count(), 4);
QVERIFY(order[0] == shape3); // lowest first
QVERIFY(order[1] == shape4);
QVERIFY(order[2] == shape2);
QVERIFY(order[3] == shape1);
// again, with clipping.
order.clear();
painter.setClipRect(0, 0, 100, 100);
manager.paint(painter, vc, false);
QCOMPARE(top->paintedCount, 2);
QCOMPARE(bottom->paintedCount, 2);
QCOMPARE(shape1->paintedCount, 2);
QCOMPARE(shape2->paintedCount, 2);
QCOMPARE(shape3->paintedCount, 2);
QCOMPARE(shape4->paintedCount, 2);
QCOMPARE(order.count(), 4);
QVERIFY(order[0] == shape3); // lowest first
QVERIFY(order[1] == shape4);
QVERIFY(order[2] == shape2);
QVERIFY(order[3] == shape1);
order.clear();
MockContainer *root = new MockContainer();
root->setZIndex(0);
MockContainer *branch1 = new MockContainer();
branch1->setZIndex(1);
OrderedMockShape *child1_1 = new OrderedMockShape(order);
child1_1->setZIndex(1);
OrderedMockShape *child1_2 = new OrderedMockShape(order);
child1_2->setZIndex(2);
branch1->addShape(child1_1);
branch1->addShape(child1_2);
MockContainer *branch2 = new MockContainer();
branch2->setZIndex(2);
OrderedMockShape *child2_1 = new OrderedMockShape(order);
child2_1->setZIndex(1);
OrderedMockShape *child2_2 = new OrderedMockShape(order);
child2_2->setZIndex(2);
branch2->addShape(child2_1);
branch2->addShape(child2_2);
root->addShape(branch1);
root->addShape(branch2);
QList sortedShapes;
sortedShapes.append(root);
sortedShapes.append(branch1);
sortedShapes.append(branch2);
sortedShapes.append(branch1->shapes());
sortedShapes.append(branch2->shapes());
qSort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex);
QCOMPARE(sortedShapes.count(), 7);
QVERIFY(sortedShapes[0] == root);
QVERIFY(sortedShapes[1] == branch1);
QVERIFY(sortedShapes[2] == child1_1);
QVERIFY(sortedShapes[3] == child1_2);
QVERIFY(sortedShapes[4] == branch2);
QVERIFY(sortedShapes[5] == child2_1);
QVERIFY(sortedShapes[6] == child2_2);
delete top;
delete bottom;
delete root;
}
#include
#include
#include
#include
#include "kis_debug.h"
void TestShapePainting::testGroupUngroup()
{
MockShape *shape1 = new MockShape();
MockShape *shape2 = new MockShape();
shape1->setName("shape1");
shape2->setName("shape2");
QList groupedShapes = {shape1, shape2};
MockShapeController controller;
MockCanvas canvas(&controller);
KoShapeManager *manager = canvas.shapeManager();
controller.addShape(shape1);
controller.addShape(shape2);
QImage image(100, 100, QImage::Format_Mono);
QPainter painter(&image);
painter.setClipRect(image.rect());
KoViewConverter vc;
KoShapeGroup *group = 0;
for (int i = 0; i < 3; i++) {
{
group = new KoShapeGroup();
group->setName("group");
KUndo2Command groupingCommand;
- canvas.shapeController()->addShapeDirect(group, &groupingCommand);
+ canvas.shapeController()->addShapeDirect(group, 0, &groupingCommand);
new KoShapeGroupCommand(group, groupedShapes, false, true, true, &groupingCommand);
groupingCommand.redo();
manager->paint(painter, vc, false);
QCOMPARE(shape1->paintedCount, 2 * i + 1);
QCOMPARE(shape2->paintedCount, 2 * i + 1);
QCOMPARE(manager->shapes().size(), 3);
}
{
KUndo2Command ungroupingCommand;
new KoShapeUngroupCommand(group, group->shapes(), QList(), &ungroupingCommand);
canvas.shapeController()->removeShape(group, &ungroupingCommand);
ungroupingCommand.redo();
manager->paint(painter, vc, false);
QCOMPARE(shape1->paintedCount, 2 * i + 2);
QCOMPARE(shape2->paintedCount, 2 * i + 2);
QCOMPARE(manager->shapes().size(), 2);
group = 0;
}
}
}
QTEST_MAIN(TestShapePainting)
diff --git a/libs/flake/tools/KoCreateShapeStrategy.cpp b/libs/flake/tools/KoCreateShapeStrategy.cpp
index 054a56b7c4..83fbc4424e 100644
--- a/libs/flake/tools/KoCreateShapeStrategy.cpp
+++ b/libs/flake/tools/KoCreateShapeStrategy.cpp
@@ -1,134 +1,134 @@
/* This file is part of the KDE project
* Copyright (C) 2006 Thomas Zander
* Copyright (C) 2006 Thorsten Zachmann
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoCreateShapeStrategy.h"
#include "KoShapeRubberSelectStrategy_p.h"
#include "KoCreateShapesTool.h"
#include "KoShape.h"
#include "KoShapeRegistry.h"
#include "KoShapeManager.h"
#include "KoCanvasBase.h"
#include "KoSelection.h"
#include "KoShapeFactoryBase.h"
#include "KoShapeController.h"
#include "KoViewConverter.h"
#include
#include
KoCreateShapeStrategy::KoCreateShapeStrategy(KoCreateShapesTool *tool, const QPointF &clicked)
: KoShapeRubberSelectStrategy(tool, clicked, tool->canvas()->snapToGrid())
{
KoCreateShapesTool *parent = static_cast(d_ptr->tool);
KoShapeFactoryBase *factory = KoShapeRegistry::instance()->value(parent->shapeId());
if (factory) {
const KoProperties *props = parent->shapeProperties();
KoShape *shape;
if (props) {
shape = factory->createShape(props);
} else {
shape = factory->createDefaultShape();
}
m_outline = shape->outline();
m_outlineBoundingRect = m_outline.boundingRect();
delete shape;
}
}
KUndo2Command* KoCreateShapeStrategy::createCommand()
{
Q_D(KoShapeRubberSelectStrategy);
KoCreateShapesTool *parent = static_cast(d_ptr->tool);
KoShapeFactoryBase *factory = KoShapeRegistry::instance()->value(parent->shapeId());
if (! factory) {
warnFlake << "Application requested a shape that is not registered" << parent->shapeId();
return 0;
}
const KoProperties *props = parent->shapeProperties();
KoShape *shape;
if (props)
shape = factory->createShape(props, parent->canvas()->shapeController()->resourceManager());
else
shape = factory->createDefaultShape(parent->canvas()->shapeController()->resourceManager());
if (shape->shapeId().isEmpty())
shape->setShapeId(factory->id());
QRectF rect = d->selectedRect();
shape->setPosition(rect.topLeft());
QSizeF newSize = rect.size();
// if the user has dragged when creating the shape,
// resize the shape to the dragged size
if (newSize.width() > 1.0 && newSize.height() > 1.0)
shape->setSize(newSize);
- KUndo2Command * cmd = parent->canvas()->shapeController()->addShape(shape);
+ KUndo2Command * cmd = parent->canvas()->shapeController()->addShape(shape, 0);
if (cmd) {
KoSelection *selection = parent->canvas()->shapeManager()->selection();
selection->deselectAll();
selection->select(shape);
}
return cmd;
}
void KoCreateShapeStrategy::finishInteraction(Qt::KeyboardModifiers modifiers)
{
Q_UNUSED(modifiers);
Q_D(KoShapeRubberSelectStrategy);
d->tool->canvas()->updateCanvas(d->selectedRect());
}
void KoCreateShapeStrategy::paint(QPainter &painter, const KoViewConverter &converter)
{
Q_D(KoShapeRubberSelectStrategy);
if (m_outline.isEmpty())
KoShapeRubberSelectStrategy::paint(painter, converter);
else {
painter.save();
painter.setRenderHint(QPainter::Antialiasing, false);
QColor selectColor(Qt::blue); // TODO make configurable
selectColor.setAlphaF(0.5);
QBrush sb(selectColor, Qt::SolidPattern);
painter.setPen(QPen(sb, 0));
painter.setBrush(sb);
QRectF paintRect = converter.documentToView(d->selectedRect());
qreal xscale = paintRect.width() / m_outlineBoundingRect.width();
qreal yscale = paintRect.height() / m_outlineBoundingRect.height();
QTransform matrix;
matrix.translate(-m_outlineBoundingRect.left(), -m_outlineBoundingRect.top());
matrix.scale(xscale, yscale);
painter.translate(paintRect.left(), paintRect.top());
painter.setTransform(matrix, true);
painter.drawPath(m_outline);
painter.restore();
}
}
void KoCreateShapeStrategy::handleMouseMove(const QPointF &point, Qt::KeyboardModifiers modifiers)
{
Q_D(KoShapeRubberSelectStrategy);
KoShapeRubberSelectStrategy::handleMouseMove(point, modifiers);
if (! m_outline.isEmpty())
d->tool->canvas()->updateCanvas(d->selectedRect());
}
diff --git a/libs/ui/actions/KisPasteActionFactory.cpp b/libs/ui/actions/KisPasteActionFactory.cpp
index 5a23f23456..3f5c66c8de 100644
--- a/libs/ui/actions/KisPasteActionFactory.cpp
+++ b/libs/ui/actions/KisPasteActionFactory.cpp
@@ -1,199 +1,199 @@
/*
* Copyright (c) 2017 Dmitry Kazakov
*
* 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 "KisPasteActionFactory.h"
#include "kis_image.h"
#include "KisViewManager.h"
#include "kis_tool_proxy.h"
#include "kis_canvas2.h"
#include "kis_canvas_controller.h"
#include "kis_paint_device.h"
#include "kis_paint_layer.h"
#include "kis_shape_layer.h"
#include "kis_import_catcher.h"
#include "kis_clipboard.h"
#include "commands/kis_image_layer_add_command.h"
#include "kis_processing_applicator.h"
#include
#include
#include
#include
#include
#include "kis_algebra_2d.h"
#include
namespace {
QPointF getFittingOffset(QList shapes,
const QPointF &shapesOffset,
const QRectF &documentRect,
const qreal fitRatio)
{
QPointF accumulatedFitOffset;
Q_FOREACH (KoShape *shape, shapes) {
const QRectF bounds = shape->boundingRect();
const QPointF center = bounds.center() + shapesOffset;
const qreal wMargin = (0.5 - fitRatio) * bounds.width();
const qreal hMargin = (0.5 - fitRatio) * bounds.height();
const QRectF allowedRect = documentRect.adjusted(-wMargin, -hMargin, wMargin, hMargin);
const QPointF fittedCenter = KisAlgebra2D::clampPoint(center, allowedRect);
accumulatedFitOffset += fittedCenter - center;
}
return accumulatedFitOffset;
}
bool tryPasteShapes(bool pasteAtCursorPosition, KisViewManager *view)
{
bool result = false;
KoSvgPaste paste;
if (paste.hasShapes()) {
KoCanvasBase *canvas = view->canvasBase();
QSizeF fragmentSize;
QList shapes =
paste.fetchShapes(canvas->shapeController()->documentRectInPixels(),
canvas->shapeController()->pixelsPerInch(), &fragmentSize);
if (!shapes.isEmpty()) {
KoShapeManager *shapeManager = canvas->shapeManager();
shapeManager->selection()->deselectAll();
KUndo2Command *parentCommand = new KUndo2Command(kundo2_i18n("Paste shapes"));
- canvas->shapeController()->addShapesDirect(shapes, parentCommand);
+ canvas->shapeController()->addShapesDirect(shapes, 0, parentCommand);
QPointF finalShapesOffset;
if (pasteAtCursorPosition) {
QRectF boundingRect = KoShape::boundingRect(shapes);
const QPointF cursorPos = canvas->canvasController()->currentCursorPosition();
finalShapesOffset = cursorPos - boundingRect.center();
} else {
bool foundOverlapping = false;
QRectF boundingRect = KoShape::boundingRect(shapes);
const QPointF offsetStep = 0.1 * QPointF(boundingRect.width(), boundingRect.height());
QPointF offset;
Q_FOREACH (KoShape *shape, shapes) {
QRectF br1 = shape->boundingRect();
bool hasOverlappingShape = false;
do {
hasOverlappingShape = false;
// we cannot use shapesAt() here, because the groups are not
// handled in the shape manager's tree
QList conflicts = shapeManager->shapes();
Q_FOREACH (KoShape *intersectedShape, conflicts) {
if (intersectedShape == shape) continue;
QRectF br2 = intersectedShape->boundingRect();
const qreal tolerance = 2.0; /* pt */
if (KisAlgebra2D::fuzzyCompareRects(br1, br2, tolerance)) {
br1.translate(offsetStep.x(), offsetStep.y());
offset += offsetStep;
hasOverlappingShape = true;
foundOverlapping = true;
break;
}
}
} while (hasOverlappingShape);
if (foundOverlapping) break;
}
if (foundOverlapping) {
finalShapesOffset = offset;
}
}
const QRectF documentRect = canvas->shapeController()->documentRect();
finalShapesOffset += getFittingOffset(shapes, finalShapesOffset, documentRect, 0.1);
if (!finalShapesOffset.isNull()) {
new KoShapeMoveCommand(shapes, finalShapesOffset, parentCommand);
}
canvas->addCommand(parentCommand);
Q_FOREACH (KoShape *shape, shapes) {
canvas->selectedShapesProxy()->selection()->select(shape);
}
result = true;
}
}
return result;
}
}
void KisPasteActionFactory::run(bool pasteAtCursorPosition, KisViewManager *view)
{
KisImageSP image = view->image();
if (!image) return;
if (tryPasteShapes(pasteAtCursorPosition, view)) {
return;
}
const QRect fittingBounds = pasteAtCursorPosition ? QRect() : image->bounds();
KisPaintDeviceSP clip = KisClipboard::instance()->clip(fittingBounds, true);
if (clip) {
if (pasteAtCursorPosition) {
const QPointF docPos = view->canvasBase()->canvasController()->currentCursorPosition();
const QPointF imagePos = view->canvasBase()->coordinatesConverter()->documentToImage(docPos);
const QPointF offset = (imagePos - QRectF(clip->exactBounds()).center()).toPoint();
clip->setX(clip->x() + offset.x());
clip->setY(clip->y() + offset.y());
}
KisImportCatcher::adaptClipToImageColorSpace(clip, image);
KisPaintLayer *newLayer = new KisPaintLayer(image.data(), image->nextLayerName() + i18n("(pasted)"), OPACITY_OPAQUE_U8, clip);
KisNodeSP aboveNode = view->activeLayer();
KisNodeSP parentNode = aboveNode ? aboveNode->parent() : image->root();
KUndo2Command *cmd = new KisImageLayerAddCommand(image, newLayer, parentNode, aboveNode);
KisProcessingApplicator *ap = beginAction(view, cmd->text());
ap->applyCommand(cmd, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::NORMAL);
endAction(ap, KisOperationConfiguration(id()).toXML());
} else {
// XXX: "Add saving of XML data for Paste of shapes"
view->canvasBase()->toolProxy()->paste();
}
}
diff --git a/libs/ui/actions/kis_selection_action_factories.cpp b/libs/ui/actions/kis_selection_action_factories.cpp
index e11d6124cb..b0660b5deb 100644
--- a/libs/ui/actions/kis_selection_action_factories.cpp
+++ b/libs/ui/actions/kis_selection_action_factories.cpp
@@ -1,579 +1,579 @@
/*
* Copyright (c) 2012 Dmitry Kazakov
*
* 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_selection_action_factories.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "KisViewManager.h"
#include "kis_canvas_resource_provider.h"
#include "kis_clipboard.h"
#include "kis_pixel_selection.h"
#include "kis_paint_layer.h"
#include "kis_image.h"
#include "kis_image_barrier_locker.h"
#include "kis_fill_painter.h"
#include "kis_transaction.h"
#include "kis_iterator_ng.h"
#include "kis_processing_applicator.h"
#include "kis_group_layer.h"
#include "commands/kis_selection_commands.h"
#include "commands/kis_image_layer_add_command.h"
#include "kis_tool_proxy.h"
#include "kis_canvas2.h"
#include "kis_canvas_controller.h"
#include "kis_selection_manager.h"
#include "kis_transaction_based_command.h"
#include "kis_selection_filters.h"
#include "kis_shape_selection.h"
#include "KisPart.h"
#include "kis_shape_layer.h"
#include
#include
#include
#include "kis_canvas_resource_provider.h"
#include "kis_figure_painting_tool_helper.h"
namespace ActionHelper {
void copyFromDevice(KisViewManager *view, KisPaintDeviceSP device, bool makeSharpClip = false)
{
KisImageWSP image = view->image();
if (!image) return;
KisSelectionSP selection = view->selection();
QRect rc = (selection) ? selection->selectedExactRect() : image->bounds();
KisPaintDeviceSP clip = new KisPaintDevice(device->colorSpace());
Q_CHECK_PTR(clip);
const KoColorSpace *cs = clip->colorSpace();
// TODO if the source is linked... copy from all linked layers?!?
// Copy image data
KisPainter::copyAreaOptimized(QPoint(), device, clip, rc);
if (selection) {
// Apply selection mask.
KisPaintDeviceSP selectionProjection = selection->projection();
KisHLineIteratorSP layerIt = clip->createHLineIteratorNG(0, 0, rc.width());
KisHLineConstIteratorSP selectionIt = selectionProjection->createHLineIteratorNG(rc.x(), rc.y(), rc.width());
const KoColorSpace *selCs = selection->projection()->colorSpace();
for (qint32 y = 0; y < rc.height(); y++) {
for (qint32 x = 0; x < rc.width(); x++) {
/**
* Sharp method is an exact reverse of COMPOSITE_OVER
* so if you cover the cut/copied piece over its source
* you get an exactly the same image without any seams
*/
if (makeSharpClip) {
qreal dstAlpha = cs->opacityF(layerIt->rawData());
qreal sel = selCs->opacityF(selectionIt->oldRawData());
qreal newAlpha = sel * dstAlpha / (1.0 - dstAlpha + sel * dstAlpha);
float mask = newAlpha / dstAlpha;
cs->applyAlphaNormedFloatMask(layerIt->rawData(), &mask, 1);
} else {
cs->applyAlphaU8Mask(layerIt->rawData(), selectionIt->oldRawData(), 1);
}
layerIt->nextPixel();
selectionIt->nextPixel();
}
layerIt->nextRow();
selectionIt->nextRow();
}
}
KisClipboard::instance()->setClip(clip, rc.topLeft());
}
}
void KisSelectAllActionFactory::run(KisViewManager *view)
{
KisImageWSP image = view->image();
if (!image) return;
KisProcessingApplicator *ap = beginAction(view, kundo2_i18n("Select All"));
if (!image->globalSelection()) {
ap->applyCommand(new KisSetEmptyGlobalSelectionCommand(image),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
}
struct SelectAll : public KisTransactionBasedCommand {
SelectAll(KisImageSP image) : m_image(image) {}
KisImageSP m_image;
KUndo2Command* paint() override {
KisSelectionSP selection = m_image->globalSelection();
KisSelectionTransaction transaction(selection->pixelSelection());
selection->pixelSelection()->select(m_image->bounds());
return transaction.endAndTake();
}
};
ap->applyCommand(new SelectAll(image),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
endAction(ap, KisOperationConfiguration(id()).toXML());
}
void KisDeselectActionFactory::run(KisViewManager *view)
{
KisImageWSP image = view->image();
if (!image) return;
KUndo2Command *cmd = new KisDeselectGlobalSelectionCommand(image);
KisProcessingApplicator *ap = beginAction(view, cmd->text());
ap->applyCommand(cmd, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE);
endAction(ap, KisOperationConfiguration(id()).toXML());
}
void KisReselectActionFactory::run(KisViewManager *view)
{
KisImageWSP image = view->image();
if (!image) return;
KUndo2Command *cmd = new KisReselectGlobalSelectionCommand(image);
KisProcessingApplicator *ap = beginAction(view, cmd->text());
ap->applyCommand(cmd, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE);
endAction(ap, KisOperationConfiguration(id()).toXML());
}
void KisFillActionFactory::run(const QString &fillSource, KisViewManager *view)
{
KisNodeSP node = view->activeNode();
if (!node || !node->hasEditablePaintDevice()) return;
KisSelectionSP selection = view->selection();
QRect selectedRect = selection ?
selection->selectedRect() : view->image()->bounds();
Q_UNUSED(selectedRect);
KisPaintDeviceSP filled = node->paintDevice()->createCompositionSourceDevice();
Q_UNUSED(filled);
bool usePattern = false;
bool useBgColor = false;
if (fillSource.contains("pattern")) {
usePattern = true;
} else if (fillSource.contains("bg")) {
useBgColor = true;
}
KisProcessingApplicator applicator(view->image(), node,
KisProcessingApplicator::NONE,
KisImageSignalVector() << ModifiedSignal,
kundo2_i18n("Flood Fill Layer"));
KisResourcesSnapshotSP resources =
new KisResourcesSnapshot(view->image(), node, view->resourceProvider()->resourceManager());
if (!fillSource.contains("opacity")) {
resources->setOpacity(1.0);
}
KisProcessingVisitorSP visitor =
new FillProcessingVisitor(QPoint(0, 0), // start position
selection,
resources,
false, // fast mode
usePattern,
true, // fill only selection,
0, // feathering radius
0, // sizemod
80, // threshold,
false, // unmerged
useBgColor);
applicator.applyVisitor(visitor,
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
applicator.end();
}
void KisClearActionFactory::run(KisViewManager *view)
{
// XXX: "Add saving of XML data for Clear action"
view->canvasBase()->toolProxy()->deleteSelection();
}
void KisImageResizeToSelectionActionFactory::run(KisViewManager *view)
{
// XXX: "Add saving of XML data for Image Resize To Selection action"
KisSelectionSP selection = view->selection();
if (!selection) return;
view->image()->cropImage(selection->selectedExactRect());
}
void KisCutCopyActionFactory::run(bool willCut, bool makeSharpClip, KisViewManager *view)
{
KisImageSP image = view->image();
if (!image) return;
bool haveShapesSelected = view->selectionManager()->haveShapesSelected();
if (haveShapesSelected) {
// XXX: "Add saving of XML data for Cut/Copy of shapes"
KisImageBarrierLocker locker(image);
if (willCut) {
view->canvasBase()->toolProxy()->cut();
} else {
view->canvasBase()->toolProxy()->copy();
}
} else {
KisNodeSP node = view->activeNode();
if (!node) return;
KisSelectionSP selection = view->selection();
if (selection.isNull()) return;
{
KisImageBarrierLocker locker(image);
KisPaintDeviceSP dev = node->paintDevice();
if (!dev) {
dev = node->projection();
}
if (!dev) {
view->showFloatingMessage(
i18nc("floating message when cannot copy from a node",
"Cannot copy pixels from this type of layer "),
QIcon(), 3000, KisFloatingMessage::Medium);
return;
}
if (dev->exactBounds().isEmpty()) {
view->showFloatingMessage(
i18nc("floating message when copying empty selection",
"Selection is empty: no pixels were copied "),
QIcon(), 3000, KisFloatingMessage::Medium);
return;
}
ActionHelper::copyFromDevice(view, dev, makeSharpClip);
}
if (willCut) {
KUndo2Command *command = 0;
if (node->hasEditablePaintDevice()) {
struct ClearSelection : public KisTransactionBasedCommand {
ClearSelection(KisNodeSP node, KisSelectionSP sel)
: m_node(node), m_sel(sel) {}
KisNodeSP m_node;
KisSelectionSP m_sel;
KUndo2Command* paint() override {
KisSelectionSP cutSelection = m_sel;
// Shrinking the cutting area was previously used
// for getting seamless cut-paste. Now we use makeSharpClip
// instead.
// QRect originalRect = cutSelection->selectedExactRect();
// static const int preciseSelectionThreshold = 16;
//
// if (originalRect.width() > preciseSelectionThreshold ||
// originalRect.height() > preciseSelectionThreshold) {
// cutSelection = new KisSelection(*m_sel);
// delete cutSelection->flatten();
//
// KisSelectionFilter* filter = new KisShrinkSelectionFilter(1, 1, false);
//
// QRect processingRect = filter->changeRect(originalRect);
// filter->process(cutSelection->pixelSelection(), processingRect);
// }
KisTransaction transaction(m_node->paintDevice());
m_node->paintDevice()->clearSelection(cutSelection);
m_node->setDirty(cutSelection->selectedRect());
return transaction.endAndTake();
}
};
command = new ClearSelection(node, selection);
}
KUndo2MagicString actionName = willCut ?
kundo2_i18n("Cut") :
kundo2_i18n("Copy");
KisProcessingApplicator *ap = beginAction(view, actionName);
if (command) {
ap->applyCommand(command,
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::NORMAL);
}
KisOperationConfiguration config(id());
config.setProperty("will-cut", willCut);
endAction(ap, config.toXML());
}
}
}
void KisCopyMergedActionFactory::run(KisViewManager *view)
{
KisImageWSP image = view->image();
if (!image) return;
if (!view->blockUntilOperationsFinished(image)) return;
image->barrierLock();
KisPaintDeviceSP dev = image->root()->projection();
ActionHelper::copyFromDevice(view, dev);
image->unlock();
KisProcessingApplicator *ap = beginAction(view, kundo2_i18n("Copy Merged"));
endAction(ap, KisOperationConfiguration(id()).toXML());
}
void KisPasteNewActionFactory::run(KisViewManager *viewManager)
{
Q_UNUSED(viewManager);
KisPaintDeviceSP clip = KisClipboard::instance()->clip(QRect(), true);
if (!clip) return;
QRect rect = clip->exactBounds();
if (rect.isEmpty()) return;
KisDocument *doc = KisPart::instance()->createDocument();
KisImageSP image = new KisImage(doc->createUndoStore(),
rect.width(),
rect.height(),
clip->colorSpace(),
i18n("Pasted"));
KisPaintLayerSP layer =
new KisPaintLayer(image.data(), image->nextLayerName() + i18n("(pasted)"),
OPACITY_OPAQUE_U8, clip->colorSpace());
KisPainter::copyAreaOptimized(QPoint(), clip, layer->paintDevice(), rect);
image->addNode(layer.data(), image->rootLayer());
doc->setCurrentImage(image);
KisPart::instance()->addDocument(doc);
KisMainWindow *win = viewManager->mainWindow();
win->addViewAndNotifyLoadingCompleted(doc);
}
void KisInvertSelectionOperation::runFromXML(KisViewManager* view, const KisOperationConfiguration& config)
{
KisSelectionFilter* filter = new KisInvertSelectionFilter();
runFilter(filter, view, config);
}
void KisSelectionToVectorActionFactory::run(KisViewManager *view)
{
KisSelectionSP selection = view->selection();
if (selection->hasShapeSelection() ||
!selection->outlineCacheValid()) {
return;
}
QPainterPath selectionOutline = selection->outlineCache();
QTransform transform = view->canvasBase()->coordinatesConverter()->imageToDocumentTransform();
KoShape *shape = KoPathShape::createShapeFromPainterPath(transform.map(selectionOutline));
shape->setShapeId(KoPathShapeId);
/**
* Mark a shape that it belongs to a shape selection
*/
if(!shape->userData()) {
shape->setUserData(new KisShapeSelectionMarker);
}
KisProcessingApplicator *ap = beginAction(view, kundo2_i18n("Convert to Vector Selection"));
- ap->applyCommand(view->canvasBase()->shapeController()->addShape(shape),
+ ap->applyCommand(view->canvasBase()->shapeController()->addShape(shape, 0),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
endAction(ap, KisOperationConfiguration(id()).toXML());
}
void KisShapesToVectorSelectionActionFactory::run(KisViewManager* view)
{
const QList originalShapes = view->canvasBase()->shapeManager()->selection()->selectedShapes();
QList clonedShapes;
Q_FOREACH (KoShape *shape, originalShapes) {
clonedShapes << shape->cloneShape();
}
KisSelectionToolHelper helper(view->canvasBase(), kundo2_i18n("Convert shapes to vector selection"));
helper.addSelectionShapes(clonedShapes);
}
void KisSelectionToShapeActionFactory::run(KisViewManager *view)
{
KisSelectionSP selection = view->selection();
if (!selection->outlineCacheValid()) {
return;
}
QPainterPath selectionOutline = selection->outlineCache();
QTransform transform = view->canvasBase()->coordinatesConverter()->imageToDocumentTransform();
KoShape *shape = KoPathShape::createShapeFromPainterPath(transform.map(selectionOutline));
shape->setShapeId(KoPathShapeId);
KoColor fgColor = view->canvasBase()->resourceManager()->resource(KoCanvasResourceManager::ForegroundColor).value();
KoShapeStrokeSP border(new KoShapeStroke(1.0, fgColor.toQColor()));
shape->setStroke(border);
view->document()->shapeController()->addShape(shape);
}
void KisStrokeSelectionActionFactory::run(KisViewManager *view, StrokeSelectionOptions params)
{
KisImageWSP image = view->image();
if (!image) {
return;
}
KisSelectionSP selection = view->selection();
if (!selection) {
return;
}
int size = params.lineSize;
KisPixelSelectionSP pixelSelection = selection->projection();
if (!pixelSelection->outlineCacheValid()) {
pixelSelection->recalculateOutlineCache();
}
QPainterPath outline = pixelSelection->outlineCache();
QColor color = params.color.toQColor();
KisNodeSP currentNode = view->resourceProvider()->resourceManager()->resource(KisCanvasResourceProvider::CurrentKritaNode).value();
if (!currentNode->inherits("KisShapeLayer") && currentNode->childCount() == 0) {
KoCanvasResourceManager * rManager = view->resourceProvider()->resourceManager();
KisPainter::StrokeStyle strokeStyle = KisPainter::StrokeStyleBrush;
KisPainter::FillStyle fillStyle = params.fillStyle();
KisFigurePaintingToolHelper helper(kundo2_i18n("Draw Polyline"),
image,
currentNode,
rManager ,
strokeStyle,
fillStyle);
helper.setFGColorOverride(params.color);
helper.setSelectionOverride(0);
QPen pen(Qt::red, size);
pen.setJoinStyle(Qt::RoundJoin);
if (fillStyle != KisPainter::FillStyleNone) {
helper.paintPainterPathQPenFill(outline, pen, params.fillColor);
}
else {
helper.paintPainterPathQPen(outline, pen, params.fillColor);
}
}
else {
QTransform transform = view->canvasBase()->coordinatesConverter()->imageToDocumentTransform();
KoShape *shape = KoPathShape::createShapeFromPainterPath(transform.map(outline));
shape->setShapeId(KoPathShapeId);
KoShapeStrokeSP border(new KoShapeStroke(size, color));
shape->setStroke(border);
view->document()->shapeController()->addShape(shape);
}
image->setModified();
}
void KisStrokeBrushSelectionActionFactory::run(KisViewManager *view, StrokeSelectionOptions params)
{
KisImageWSP image = view->image();
if (!image) {
return;
}
KisSelectionSP selection = view->selection();
if (!selection) {
return;
}
KisPixelSelectionSP pixelSelection = selection->projection();
if (!pixelSelection->outlineCacheValid()) {
pixelSelection->recalculateOutlineCache();
}
KisNodeSP currentNode = view->resourceProvider()->resourceManager()->resource(KisCanvasResourceProvider::CurrentKritaNode).value();
if (!currentNode->inherits("KisShapeLayer") && currentNode->childCount() == 0)
{
KoCanvasResourceManager * rManager = view->resourceProvider()->resourceManager();
QPainterPath outline = pixelSelection->outlineCache();
KisPainter::StrokeStyle strokeStyle = KisPainter::StrokeStyleBrush;
KisPainter::FillStyle fillStyle = KisPainter::FillStyleNone;
KoColor color = params.color;
KisFigurePaintingToolHelper helper(kundo2_i18n("Draw Polyline"),
image,
currentNode,
rManager ,
strokeStyle,
fillStyle);
helper.setFGColorOverride(color);
helper.setSelectionOverride(0);
helper.paintPainterPath(outline);
image->setModified();
}
}
diff --git a/libs/ui/tool/kis_selection_tool_helper.cpp b/libs/ui/tool/kis_selection_tool_helper.cpp
index db14dc4223..d39f241b45 100644
--- a/libs/ui/tool/kis_selection_tool_helper.cpp
+++ b/libs/ui/tool/kis_selection_tool_helper.cpp
@@ -1,263 +1,263 @@
/*
* Copyright (c) 2007 Sven Langkamp
*
* 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_selection_tool_helper.h"
#include
#include
#include
#include "kis_pixel_selection.h"
#include "kis_shape_selection.h"
#include "kis_image.h"
#include "canvas/kis_canvas2.h"
#include "KisViewManager.h"
#include "kis_selection_manager.h"
#include "kis_transaction.h"
#include "commands/kis_selection_commands.h"
#include "kis_shape_controller.h"
#include
#include "kis_processing_applicator.h"
#include "kis_transaction_based_command.h"
#include "kis_gui_context_command.h"
#include "kis_command_utils.h"
#include "commands/kis_deselect_global_selection_command.h"
#include "kis_algebra_2d.h"
#include "kis_config.h"
KisSelectionToolHelper::KisSelectionToolHelper(KisCanvas2* canvas, const KUndo2MagicString& name)
: m_canvas(canvas)
, m_name(name)
{
m_image = m_canvas->viewManager()->image();
}
KisSelectionToolHelper::~KisSelectionToolHelper()
{
}
struct LazyInitGlobalSelection : public KisTransactionBasedCommand {
LazyInitGlobalSelection(KisViewManager *view) : m_view(view) {}
KisViewManager *m_view;
KUndo2Command* paint() override {
return !m_view->selection() ?
new KisSetEmptyGlobalSelectionCommand(m_view->image()) : 0;
}
};
void KisSelectionToolHelper::selectPixelSelection(KisPixelSelectionSP selection, SelectionAction action)
{
KisViewManager* view = m_canvas->viewManager();
if (selection->selectedExactRect().isEmpty()) {
m_canvas->viewManager()->selectionManager()->deselect();
return;
}
KisProcessingApplicator applicator(view->image(),
0 /* we need no automatic updates */,
KisProcessingApplicator::SUPPORTS_WRAPAROUND_MODE,
KisImageSignalVector() << ModifiedSignal,
m_name);
applicator.applyCommand(new LazyInitGlobalSelection(view));
struct ApplyToPixelSelection : public KisTransactionBasedCommand {
ApplyToPixelSelection(KisViewManager *view,
KisPixelSelectionSP selection,
SelectionAction action) : m_view(view),
m_selection(selection),
m_action(action) {}
KisViewManager *m_view;
KisPixelSelectionSP m_selection;
SelectionAction m_action;
KUndo2Command* paint() override {
KisPixelSelectionSP pixelSelection = m_view->selection()->pixelSelection();
KIS_ASSERT_RECOVER(pixelSelection) { return 0; }
bool hasSelection = !pixelSelection->isEmpty();
KisSelectionTransaction transaction(pixelSelection);
if (!hasSelection && m_action == SELECTION_SUBTRACT) {
pixelSelection->invert();
}
pixelSelection->applySelection(m_selection, m_action);
const QRect imageBounds = m_view->image()->bounds();
QRect selectionExactRect = m_view->selection()->selectedExactRect();
if (!imageBounds.contains(selectionExactRect)) {
pixelSelection->crop(imageBounds);
if (pixelSelection->outlineCacheValid()) {
QPainterPath cache = pixelSelection->outlineCache();
QPainterPath imagePath;
imagePath.addRect(imageBounds);
cache &= imagePath;
pixelSelection->setOutlineCache(cache);
}
selectionExactRect &= imageBounds;
}
QRect dirtyRect = imageBounds;
if (hasSelection && m_action != SELECTION_REPLACE && m_action != SELECTION_INTERSECT) {
dirtyRect = m_selection->selectedRect();
}
m_view->selection()->updateProjection(dirtyRect);
KUndo2Command *savedCommand = transaction.endAndTake();
pixelSelection->setDirty(dirtyRect);
if (selectionExactRect.isEmpty()) {
KisCommandUtils::CompositeCommand *cmd = new KisCommandUtils::CompositeCommand();
cmd->addCommand(savedCommand);
cmd->addCommand(new KisDeselectGlobalSelectionCommand(m_view->image()));
savedCommand = cmd;
}
return savedCommand;
}
};
applicator.applyCommand(new ApplyToPixelSelection(view, selection, action));
applicator.end();
}
void KisSelectionToolHelper::addSelectionShape(KoShape* shape)
{
QList shapes;
shapes.append(shape);
addSelectionShapes(shapes);
}
void KisSelectionToolHelper::addSelectionShapes(QList< KoShape* > shapes)
{
KisViewManager* view = m_canvas->viewManager();
if (view->image()->wrapAroundModePermitted()) {
view->showFloatingMessage(
i18n("Shape selection does not fully "
"support wraparound mode. Please "
"use pixel selection instead"),
KisIconUtils::loadIcon("selection-info"));
}
KisProcessingApplicator applicator(view->image(),
0 /* we need no automatic updates */,
KisProcessingApplicator::NONE,
KisImageSignalVector() << ModifiedSignal,
m_name);
applicator.applyCommand(new LazyInitGlobalSelection(view));
struct ClearPixelSelection : public KisTransactionBasedCommand {
ClearPixelSelection(KisViewManager *view) : m_view(view) {}
KisViewManager *m_view;
KUndo2Command* paint() override {
KisPixelSelectionSP pixelSelection = m_view->selection()->pixelSelection();
KIS_ASSERT_RECOVER(pixelSelection) { return 0; }
KisSelectionTransaction transaction(pixelSelection);
pixelSelection->clear();
return transaction.endAndTake();
}
};
applicator.applyCommand(new ClearPixelSelection(view));
struct AddSelectionShape : public KisTransactionBasedCommand {
AddSelectionShape(KisViewManager *view, KoShape* shape) : m_view(view),
m_shape(shape) {}
KisViewManager *m_view;
KoShape* m_shape;
KUndo2Command* paint() override {
/**
* Mark a shape that it belongs to a shape selection
*/
if(!m_shape->userData()) {
m_shape->setUserData(new KisShapeSelectionMarker);
}
- return m_view->canvasBase()->shapeController()->addShape(m_shape);
+ return m_view->canvasBase()->shapeController()->addShape(m_shape, 0);
}
};
Q_FOREACH (KoShape* shape, shapes) {
applicator.applyCommand(
new KisGuiContextCommand(new AddSelectionShape(view, shape), view));
}
applicator.end();
}
void KisSelectionToolHelper::cropRectIfNeeded(QRect *rect, SelectionAction action)
{
KisImageWSP image = m_canvas->viewManager()->image();
if (!image->wrapAroundModePermitted() && action != SELECTION_SUBTRACT) {
*rect &= image->bounds();
}
}
bool KisSelectionToolHelper::canShortcutToDeselect(const QRect &rect, SelectionAction action)
{
return rect.isEmpty() && (action == SELECTION_INTERSECT || action == SELECTION_REPLACE);
}
bool KisSelectionToolHelper::canShortcutToNoop(const QRect &rect, SelectionAction action)
{
return rect.isEmpty() && action == SELECTION_ADD;
}
void KisSelectionToolHelper::cropPathIfNeeded(QPainterPath *path)
{
KisImageWSP image = m_canvas->viewManager()->image();
if (!image->wrapAroundModePermitted()) {
QPainterPath cropPath;
cropPath.addRect(image->bounds());
*path &= cropPath;
}
}
bool KisSelectionToolHelper::tryDeselectCurrentSelection(const QRectF selectionViewRect, SelectionAction action)
{
bool result = false;
if (KisAlgebra2D::maxDimension(selectionViewRect) < KisConfig().selectionViewSizeMinimum() &&
(action == SELECTION_INTERSECT || action == SELECTION_REPLACE)) {
// Queueing this action to ensure we avoid a race condition when unlocking the node system
QTimer::singleShot(0, m_canvas->viewManager()->selectionManager(), SLOT(deselect()));
result = true;
}
return result;
}
diff --git a/libs/ui/tool/kis_tool_shape.cc b/libs/ui/tool/kis_tool_shape.cc
index 306a84a914..34c6238354 100644
--- a/libs/ui/tool/kis_tool_shape.cc
+++ b/libs/ui/tool/kis_tool_shape.cc
@@ -1,255 +1,255 @@
/*
* Copyright (c) 2005 Adrian Page
*
* 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_shape.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 "kis_figure_painting_tool_helper.h"
#include
#include
KisToolShape::KisToolShape(KoCanvasBase * canvas, const QCursor & cursor)
: KisToolPaint(canvas, cursor)
{
m_shapeOptionsWidget = 0;
}
KisToolShape::~KisToolShape()
{
// in case the widget hasn't been shown
if (m_shapeOptionsWidget && !m_shapeOptionsWidget->parent()) {
delete m_shapeOptionsWidget;
}
}
void KisToolShape::activate(ToolActivation toolActivation, const QSet &shapes)
{
KisToolPaint::activate(toolActivation, shapes);
m_configGroup = KSharedConfig::openConfig()->group(toolId());
}
int KisToolShape::flags() const
{
return KisTool::FLAG_USES_CUSTOM_COMPOSITEOP|KisTool::FLAG_USES_CUSTOM_PRESET
|KisTool::FLAG_USES_CUSTOM_SIZE;
}
QWidget * KisToolShape::createOptionWidget()
{
m_shapeOptionsWidget = new WdgGeometryOptions(0);
m_shapeOptionsWidget->cmbOutline->setCurrentIndex(KisPainter::StrokeStyleBrush);
//connect two combo box event. Inherited classes can call the slots to make appropriate changes
connect(m_shapeOptionsWidget->cmbOutline, SIGNAL(currentIndexChanged(int)), this, SLOT(outlineSettingChanged(int)));
connect(m_shapeOptionsWidget->cmbFill, SIGNAL(currentIndexChanged(int)), this, SLOT(fillSettingChanged(int)));
m_shapeOptionsWidget->cmbOutline->setCurrentIndex(m_configGroup.readEntry("outlineType", 0));
m_shapeOptionsWidget->cmbFill->setCurrentIndex(m_configGroup.readEntry("fillType", 0));
//if both settings are empty, force the outline to brush so the tool will work when first activated
if ( m_shapeOptionsWidget->cmbFill->currentIndex() == 0 &&
m_shapeOptionsWidget->cmbOutline->currentIndex() == 0)
{
m_shapeOptionsWidget->cmbOutline->setCurrentIndex(1); // brush
}
return m_shapeOptionsWidget;
}
void KisToolShape::outlineSettingChanged(int value)
{
m_configGroup.writeEntry("outlineType", value);
}
void KisToolShape::fillSettingChanged(int value)
{
m_configGroup.writeEntry("fillType", value);
}
KisPainter::FillStyle KisToolShape::fillStyle(void)
{
if (m_shapeOptionsWidget) {
return static_cast(m_shapeOptionsWidget->cmbFill->currentIndex());
} else {
return KisPainter::FillStyleNone;
}
}
KisPainter::StrokeStyle KisToolShape::strokeStyle(void)
{
if (m_shapeOptionsWidget) {
return static_cast(m_shapeOptionsWidget->cmbOutline->currentIndex());
} else {
return KisPainter::StrokeStyleNone;
}
}
qreal KisToolShape::currentStrokeWidth() const
{
const qreal sizeInPx =
canvas()->resourceManager()->resource(KisCanvasResourceProvider::Size).toReal();
return canvas()->unit().fromUserValue(sizeInPx);
}
void KisToolShape::setupPaintAction(KisRecordedPaintAction* action)
{
KisToolPaint::setupPaintAction(action);
action->setFillStyle(fillStyle());
action->setStrokeStyle(strokeStyle());
action->setGenerator(currentGenerator());
action->setPattern(currentPattern());
action->setGradient(currentGradient());
}
void KisToolShape::addShape(KoShape* shape)
{
KoImageCollection* imageCollection = canvas()->shapeController()->resourceManager()->imageCollection();
switch(fillStyle()) {
case KisPainter::FillStyleForegroundColor:
shape->setBackground(QSharedPointer(new KoColorBackground(currentFgColor().toQColor())));
break;
case KisPainter::FillStyleBackgroundColor:
shape->setBackground(QSharedPointer(new KoColorBackground(currentBgColor().toQColor())));
break;
case KisPainter::FillStylePattern:
if (imageCollection) {
QSharedPointer fill(new KoPatternBackground(imageCollection));
if (currentPattern()) {
fill->setPattern(currentPattern()->pattern());
shape->setBackground(fill);
}
} else {
shape->setBackground(QSharedPointer(0));
}
break;
case KisPainter::FillStyleGradient:
{
QLinearGradient *gradient = new QLinearGradient(QPointF(0, 0), QPointF(1, 1));
gradient->setCoordinateMode(QGradient::ObjectBoundingMode);
gradient->setStops(currentGradient()->toQGradient()->stops());
QSharedPointer gradientFill(new KoGradientBackground(gradient));
shape->setBackground(gradientFill);
}
break;
case KisPainter::FillStyleNone:
default:
shape->setBackground(QSharedPointer(0));
break;
}
- KUndo2Command * cmd = canvas()->shapeController()->addShape(shape);
+ KUndo2Command * cmd = canvas()->shapeController()->addShape(shape, 0);
canvas()->addCommand(cmd);
}
void KisToolShape::addPathShape(KoPathShape* pathShape, const KUndo2MagicString& name)
{
KisNodeSP node = currentNode();
if (!node || !blockUntilOperationsFinished()) {
return;
}
// Get painting options
KisPaintOpPresetSP preset = currentPaintOpPreset();
// Compute the outline
KisImageSP image = this->image();
QTransform matrix;
matrix.scale(image->xRes(), image->yRes());
matrix.translate(pathShape->position().x(), pathShape->position().y());
QPainterPath mapedOutline = matrix.map(pathShape->outline());
// Recorde the paint action
KisRecordedPathPaintAction bezierCurvePaintAction(
KisNodeQueryPath::absolutePath(node),
preset );
bezierCurvePaintAction.setPaintColor(currentFgColor());
QPointF lastPoint, nextPoint;
int elementCount = mapedOutline.elementCount();
for (int i = 0; i < elementCount; i++) {
QPainterPath::Element element = mapedOutline.elementAt(i);
switch (element.type) {
case QPainterPath::MoveToElement:
if (i != 0) {
qFatal("Unhandled"); // XXX: I am not sure the tool can produce such element, deal with it when it can
}
lastPoint = QPointF(element.x, element.y);
break;
case QPainterPath::LineToElement:
nextPoint = QPointF(element.x, element.y);
bezierCurvePaintAction.addLine(KisPaintInformation(lastPoint), KisPaintInformation(nextPoint));
lastPoint = nextPoint;
break;
case QPainterPath::CurveToElement:
nextPoint = QPointF(mapedOutline.elementAt(i + 2).x, mapedOutline.elementAt(i + 2).y);
bezierCurvePaintAction.addCurve(KisPaintInformation(lastPoint),
QPointF(mapedOutline.elementAt(i).x,
mapedOutline.elementAt(i).y),
QPointF(mapedOutline.elementAt(i + 1).x,
mapedOutline.elementAt(i + 1).y),
KisPaintInformation(nextPoint));
lastPoint = nextPoint;
break;
default:
continue;
}
}
image->actionRecorder()->addAction(bezierCurvePaintAction);
if (node->hasEditablePaintDevice()) {
KisFigurePaintingToolHelper helper(name,
image,
node,
canvas()->resourceManager(),
strokeStyle(),
fillStyle());
helper.paintPainterPath(mapedOutline);
} else if (node->inherits("KisShapeLayer")) {
pathShape->normalize();
addShape(pathShape);
}
notifyModified();
}
diff --git a/plugins/flake/artistictextshape/ArtisticTextTool.cpp b/plugins/flake/artistictextshape/ArtisticTextTool.cpp
index a502f8695b..4e207d75e7 100644
--- a/plugins/flake/artistictextshape/ArtisticTextTool.cpp
+++ b/plugins/flake/artistictextshape/ArtisticTextTool.cpp
@@ -1,1009 +1,1008 @@
/* This file is part of the KDE project
* Copyright (C) 2007,2011 Jan Hambrecht
* Copyright (C) 2008 Rob Buis
*
* 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 "ArtisticTextTool.h"
#include "ArtisticTextToolSelection.h"
#include "AttachTextToPathCommand.h"
#include "DetachTextFromPathCommand.h"
#include "AddTextRangeCommand.h"
#include "RemoveTextRangeCommand.h"
#include "ArtisticTextShapeConfigWidget.h"
#include "ArtisticTextShapeOnPathWidget.h"
#include "MoveStartOffsetStrategy.h"
#include "SelectTextStrategy.h"
#include "ChangeTextOffsetCommand.h"
#include "ChangeTextFontCommand.h"
#include "ChangeTextAnchorCommand.h"
#include "ReplaceTextRangeCommand.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include