diff --git a/krita/kritamenu.action b/krita/kritamenu.action
index 41e9438754..cdfdfa9470 100644
--- a/krita/kritamenu.action
+++ b/krita/kritamenu.action
@@ -1,1806 +1,1806 @@
File
document-new
&New
Create new document
New
0
0
Ctrl+N
false
document-open
&Open...
Open an existing document
Open
0
0
Ctrl+O
false
document-open-recent
Open &Recent
Open a document which was recently opened
Open Recent
1
0
false
document-save
&Save
Save
Save
1
0
Ctrl+S
false
document-save-as
Save &As...
Save document under a new name
Save As
1
0
Ctrl+Shift+S
false
Sessions...
Open session manager
Sessions
0
0
false
document-import
Open ex&isting Document as Untitled Document...
Open existing Document as Untitled Document
Open existing Document as Untitled Document
0
0
false
document-export
E&xport...
Export
Export
1
0
false
application-pdf
&Export as PDF...
Export as PDF
Export as PDF
1
0
false
Import animation frames...
Import animation frames
Import animation frames
1
0
false
&Render Animation...
Render Animation to GIF, Image Sequence or Video
Render Animation
1000
0
false
&Render Image Sequence Again
Render Animation to Image Sequence Again
Render Animation
1000
0
false
Save Incremental &Version
Save Incremental Version
Save Incremental Version
1
0
Ctrl+Alt+S
false
Save Incremental &Backup
Save Incremental Backup
Save Incremental Backup
1
0
F4
false
&Create Template From Image...
Create Template From Image
Create Template From Image
1
0
false
Create Copy &From Current Image
Create Copy From Current Image
Create Copy From Current Image
1
0
false
document-print
&Print...
Print document
Print
1
0
Ctrl+P
false
document-print-preview
Print Previe&w
Show a print preview of document
Print Preview
1
0
false
configure
&Document Information
Document Information
Document Information
1
0
false
&Close All
Close All
Close All
1
0
Ctrl+Shift+W
false
C&lose
Close
Close
1
0
false
&Quit
Quit application
Quit
0
0
Ctrl+Q
false
Edit
edit-undo
Undo
Undo last action
Undo
1
0
Ctrl+Z
false
edit-redo
Redo
Redo last undone action
Redo
1
0
Ctrl+Shift+Z
false
edit-cut
Cu&t
Cut selection to clipboard
Cut
0
0
Ctrl+X
false
edit-copy
&Copy
Copy selection to clipboard
Copy
0
0
Ctrl+C
false
C&opy (sharp)
Copy (sharp)
Copy (sharp)
100000000
0
false
Cut (&sharp)
Cut (sharp)
Cut (sharp)
100000000
0
false
Copy &merged
Copy merged
Copy merged
100000000
0
Ctrl+Shift+C
false
edit-paste
&Paste
Paste clipboard content
Paste
0
0
Ctrl+V
false
Paste at Cursor
Paste at cursor
Paste at cursor
0
0
Ctrl+Alt+V
false
Paste into &New Image
Paste into New Image
Paste into New Image
0
0
Ctrl+Shift+N
false
edit-clear
C&lear
Clear
Clear
1
0
Del
false
&Fill with Foreground Color
Fill with Foreground Color
Fill with Foreground Color
10000
1
Shift+Backspace
false
Fill &with Background Color
Fill with Background Color
Fill with Background Color
10000
1
Backspace
false
F&ill with Pattern
Fill with Pattern
Fill with Pattern
10000
1
false
Fill Special
Fill with Foreground Color (Opacity)
Fill with Foreground Color (Opacity)
Fill with Foreground Color (Opacity)
10000
1
Ctrl+Shift+Backspace
false
Fill with Background Color (Opacity)
Fill with Background Color (Opacity)
Fill with Background Color (Opacity)
10000
1
Ctrl+Backspace
false
Fill with Pattern (Opacity)
Fill with Pattern (Opacity)
Fill with Pattern (Opacity)
10000
1
false
Stro&ke selected shapes
Stroke selected shapes
Stroke selected shapes
1000000000
0
false
Stroke Selec&tion...
Stroke selection
Stroke selection
10000000000
0
false
Delete keyframe
Delete keyframe
Delete keyframe
100000
0
false
Window
window-new
&New Window
New Window
New Window
0
0
false
N&ext
Next
Next
10
0
false
Previous
Previous
Previous
false
View
&Show Canvas Only
Show just the canvas or the whole window
Show Canvas Only
0
0
Tab
true
view-fullscreen
F&ull Screen Mode
Display the window in full screen
Full Screen Mode
0
0
Ctrl+Shift+F
true
&Wrap Around Mode
Wrap Around Mode
Wrap Around Mode
1
0
W
true
&Instant Preview Mode
Instant Preview Mode
Instant Preview Mode
1
0
Shift+L
true
Soft Proofing
Turns on Soft Proofing
Turns on Soft Proofing
Ctrl+Y
true
Out of Gamut Warnings
Turns on warnings for colors out of proofed gamut, needs soft proofing to be turned on.
Turns on warnings for colors out of proofed gamut, needs soft proofing to be turned on.
Ctrl+Shift+Y
true
mirror-view
Mirror View
Mirror View
Mirror View
M
false
zoom-original
&Reset zoom
Reset zoom
Reset zoom
1
0
Ctrl+0
false
zoom-in
Zoom &In
Zoom In
0
0
Ctrl++
false
zoom-out
Zoom &Out
Zoom Out
0
0
Ctrl+-
false
rotate-canvas-right
Rotate &Canvas Right
Rotate Canvas Right
Rotate Canvas Right
1
0
Ctrl+]
false
rotate-canvas-left
Rotate Canvas &Left
Rotate Canvas Left
Rotate Canvas Left
1
0
Ctrl+[
false
rotation-reset
Reset Canvas Rotation
Reset Canvas Rotation
Reset Canvas Rotation
1
0
false
Show &Rulers
The rulers show the horizontal and vertical positions of the mouse on the image and can be used to position your mouse at the right place on the canvas. <p>Uncheck this to hide the rulers.</p>
Show Rulers
Show Rulers
1
0
true
Rulers Track Pointer
The rulers will track current mouse position and show it on screen. It can cause suptle performance slowdown
Rulers Track Pointer
Rulers Track Pointer
1
0
true
Show Guides
Show or hide guides
Show Guides
1
0
true
Lock Guides
Lock or unlock guides
Lock Guides
1
0
true
Snap to Guides
Snap cursor to guides position
Snap to Guides
1
0
true
Show Status &Bar
Show or hide the status bar
Show Status Bar
0
0
true
Show Pixel Grid
Show Pixel Grid
Show Pixel Grid
1000
0
true
view-grid
Show &Grid
Show Grid
Show Grid
1000
0
Ctrl+Shift+'
true
Snap To Grid
Snap To Grid
Snap To Grid
1000
Ctrl+Shift+;
true
Show Snap Options Popup
Show Snap Options Popup
Show Snap Options Popup
1000
Shift+s
false
Snap Orthogonal
Snap Orthogonal
Snap Orthogonal
1000
true
Snap Node
Snap Node
Snap Node
1000
true
Snap Extension
Snap Extension
Snap Extension
1000
true
Snap Intersection
Snap Intersection
Snap Intersection
1000
true
Snap Bounding Box
Snap Bounding Box
Snap Bounding Box
1000
true
Snap Image Bounds
Snap Image Bounds
Snap Image Bounds
1000
true
Snap Image Center
Snap Image Center
Snap Image Center
1000
true
S&how Painting Assistants
Show Painting Assistants
Show Painting Assistants
1000
0
true
Show &Assistant Previews
Show Assistant Previews
Show Assistant Previews
1000
0
true
S&how Reference Images
Show Reference Images
Show Reference Images
1000
0
true
Image
document-properties
&Properties...
Properties
Properties
1000
0
false
format-stroke-color
&Image Background Color and Transparency...
Change the background color of the image
Image Background Color and Transparency
1000
0
false
&Convert Image Color Space...
Convert Image Color Space
Convert Image Color Space
1000
0
false
trim-to-image
&Trim to Image Size
Trim to Image Size
Trim to Image Size
1
0
false
Trim to Current &Layer
Trim to Current Layer
Trim to Current Layer
100000
0
false
Trim to S&election
Trim to Selection
Trim to Selection
100000000
0
false
&Rotate Image...
Rotate Image
Rotate Image
1000
0
false
object-rotate-right
Rotate &Image 90° to the Right
Rotate Image 90° to the Right
Rotate Image 90° to the Right
1000
0
false
object-rotate-left
Rotate Image &90° to the Left
Rotate Image 90° to the Left
Rotate Image 90° to the Left
1000
0
false
Rotate Image &180°
Rotate Image 180°
Rotate Image 180°
1000
0
false
&Shear Image...
Shear Image
Shear Image
1000
0
false
symmetry-horizontal
&Mirror Image Horizontally
Mirror Image Horizontally
Mirror Image Horizontally
1000
0
false
symmetry-vertical
Mirror Image &Vertically
Mirror Image Vertically
Mirror Image Vertically
1000
0
false
Scale Image To &New Size...
Scale Image To New Size
Scale Image To New Size
1000
0
Ctrl+Alt+I
false
&Offset Image...
Offset Image
Offset Image
1000
0
false
R&esize Canvas...
Resize Canvas
Resize Canvas
1000
0
Ctrl+Alt+C
false
Im&age Split
Image Split
Image Split
1000
0
false
Separate Ima&ge...
Separate Image
Separate Image
1000
0
false
Select
edit-select-all
Select &All
Select All
Select All
0
0
Ctrl+A
false
edit-select-all
&Deselect
Deselect
Deselect
1100000000
0
Ctrl+Shift+A
false
&Reselect
Reselect
Reselect
0
0
Ctrl+Shift+D
false
&Invert Selection
Invert Selection
Invert Selection
10000
0
Ctrl+I
false
&Convert to Vector Selection
Convert to Vector Selection
Convert to Vector Selection
10000000000
0
false
Convert Shapes to &Vector Selection
Convert Shapes to Vector Selection
Convert Shapes to Vector Selection
1000000000
0
false
&Feather Selection...
Feather Selection
Feather Selection
10000000000
100
Shift+F6
false
Dis&play Selection
Display Selection
Display Selection
1000
0
Ctrl+H
true
Sca&le...
Scale
Scale
100000000
100
false
S&elect from Color Range...
Select from Color Range
Select from Color Range
10000
100
false
- Select &Opaque
+ Select &Opaque (Replace)
Select Opaque
Select Opaque
10000
100
false
Select Opaque (&Add)
Select Opaque (Add)
Select Opaque (Add)
10000
100
false
Select Opaque (&Subtract)
Select Opaque (Subtract)
Select Opaque (Subtract)
10000
100
false
Select Opaque (&Intersect)
Select Opaque (Intersect)
Select Opaque (Intersect)
10000
100
false
&Grow Selection...
Grow Selection
Grow Selection
10000000000
100
false
S&hrink Selection...
Shrink Selection
Shrink Selection
10000000000
100
false
&Border Selection...
Border Selection
Border Selection
10000000000
100
false
S&mooth
Smooth
Smooth
10000000000
100
false
Filter
&Apply Filter Again
Apply Filter Again
Apply Filter Again
0
0
Ctrl+F
false
Adjust
Adjust
Adjust
false
Artistic
Artistic
Artistic
false
Blur
Blur
Blur
false
Colors
Colors
Colors
false
Edge Detection
Edge Detection
Edge Detection
false
Enhance
Enhance
Enhance
false
Emboss
Emboss
Emboss
false
Map
Map
Map
false
Other
Other
Other
false
gmic
Start G'MIC-Qt
Start G'Mic-Qt
Start G'Mic-Qt
false
gmic
Re-apply the last G'MIC filter
Apply the last G'Mic-Qt action again
Apply the last G'Mic-Qt action again
false
Settings
configure
&Configure Krita...
Configure Krita
Configure Krita
0
0
false
&Manage Resources...
Manage Resources
Manage Resources
0
0
false
preferences-desktop-locale
Switch Application &Language...
Switch Application Language
Switch Application Language
false
&Show Dockers
Show Dockers
Show Dockers
0
0
true
configure
Configure Tool&bars...
Configure Toolbars
Configure Toolbars
0
0
false
Dockers
Dockers
Dockers
false
&Themes
Themes
Themes
false
im-user
Active Author Profile
Active Author Profile
Active Author Profile
configure-shortcuts
Configure S&hortcuts...
Configure Shortcuts
Configure Shortcuts
0
0
false
&Window
Window
Window
false
Help
help-contents
Krita &Handbook
Krita Handbook
Krita Handbook
F1
false
tools-report-bug
&Report Bug...
Report Bug
Report Bug
false
calligrakrita
&About Krita
About Krita
About Krita
false
kde
About &KDE
About KDE
About KDE
false
Brushes and Stuff
&Gradients
Gradients
Gradients
false
&Patterns
Patterns
Patterns
false
&Color
Color
Color
false
&Painter's Tools
Painter's Tools
Painter's Tools
false
Brush composite
Brush composite
Brush composite
false
Brush option slider 1
Brush option slider 1
Brush option slider 1
false
Brush option slider 2
Brush option slider 2
Brush option slider 2
false
Brush option slider 3
Brush option slider 3
Brush option slider 3
false
Mirror
Mirror
Mirror
false
Layouts
Select layout
false
Workspaces
Workspaces
Workspaces
false
diff --git a/libs/basicflakes/tools/KoCreatePathTool.cpp b/libs/basicflakes/tools/KoCreatePathTool.cpp
index c2063193bb..fa54cfdd5e 100644
--- a/libs/basicflakes/tools/KoCreatePathTool.cpp
+++ b/libs/basicflakes/tools/KoCreatePathTool.cpp
@@ -1,533 +1,581 @@
/* 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 "KoPathPointTypeCommand.h"
#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);
}
d->dragStartPoint = event->point;
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);
}
bool KoCreatePathTool::tryMergeInPathShape(KoPathShape *pathShape)
{
return addPathShapeImpl(pathShape, true);
}
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) {
if (d->pointIsDragged ||
!handleGrabRect(d->dragStartPoint).contains(event->point)) {
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);
+
+ if (!d->prevPointWasDragged && d->autoSmoothCurves) {
+ KoPathPointIndex index = d->shape->pathPointIndex(d->activePoint);
+ if (index.second > 0) {
+
+ KoPathPointIndex prevIndex(index.first, index.second - 1);
+ KoPathPoint *prevPoint = d->shape->pointByIndex(prevIndex);
+
+ if (prevPoint) {
+ KoPathPoint *prevPrevPoint = 0;
+
+ if (index.second > 1) {
+ KoPathPointIndex prevPrevIndex(index.first, index.second - 2);
+ prevPrevPoint = d->shape->pointByIndex(prevPrevIndex);
+ }
+
+ if (prevPrevPoint) {
+ const QPointF control1 = prevPoint->point() + 0.3 * (prevPrevPoint->point() - prevPoint->point());
+ prevPoint->setControlPoint1(control1);
+ }
+
+ const QPointF control2 = prevPoint->point() + 0.3 * (d->activePoint->point() - prevPoint->point());
+ prevPoint->setControlPoint2(control2);
+
+ const QPointF activeControl = d->activePoint->point() + 0.3 * (prevPoint->point() - d->activePoint->point());
+ d->activePoint->setControlPoint1(activeControl);
+
+ KoPathPointTypeCommand::makeCubicPointSmooth(prevPoint);
+ }
+ }
+ }
+
}
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->prevPointWasDragged = d->pointIsDragged;
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;
+
+ if (!d->prevPointWasDragged && d->autoSmoothCurves) {
+ KoPathPointTypeCommand::makeCubicPointSmooth(d->activePoint);
+ }
+
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();
+ d->loadAutoSmoothValueFromConfig();
// 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;
}
}
bool KoCreatePathTool::addPathShapeImpl(KoPathShape *pathShape, bool tryMergeOnly)
{
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;
}
}
if (tryMergeOnly && !startShape && !endShape) {
return false;
}
KUndo2Command *cmd = canvas()->shapeController()->addShape(pathShape, 0);
KIS_SAFE_ASSERT_RECOVER(cmd) {
canvas()->updateCanvas(pathShape->boundingRect());
delete pathShape;
return true;
}
KoSelection *selection = canvas()->shapeManager()->selection();
selection->deselectAll();
selection->select(pathShape);
if (startShape) {
pathShape->setBackground(startShape->background());
pathShape->setStroke(startShape->stroke());
} else if (endShape) {
pathShape->setBackground(endShape->background());
pathShape->setStroke(endShape->stroke());
}
if (startShape) {
canvas()->shapeController()->removeShape(startShape, cmd);
}
if (endShape && startShape != endShape) {
canvas()->shapeController()->removeShape(endShape, cmd);
}
canvas()->addCommand(cmd);
return true;
}
void KoCreatePathTool::addPathShape(KoPathShape *pathShape)
{
addPathShapeImpl(pathShape, false);
}
QList > KoCreatePathTool::createOptionWidgets()
{
Q_D(KoCreatePathTool);
QList > list;
+ QCheckBox *smoothCurves = new QCheckBox(i18n("Autosmooth curve"));
+ smoothCurves->setObjectName("smooth-curves-widget");
+ smoothCurves->setChecked(d->autoSmoothCurves);
+ connect(smoothCurves, SIGNAL(toggled(bool)), this, SLOT(autoSmoothCurvesChanged(bool)));
+ connect(this, SIGNAL(sigUpdateAutoSmoothCurvesGUI(bool)), smoothCurves, SLOT(setChecked(bool)));
+
+ list.append(smoothCurves);
+
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/KoCreatePathTool.h b/libs/basicflakes/tools/KoCreatePathTool.h
index 3924e354f1..a1c5a437e9 100644
--- a/libs/basicflakes/tools/KoCreatePathTool.h
+++ b/libs/basicflakes/tools/KoCreatePathTool.h
@@ -1,113 +1,117 @@
/* This file is part of the KDE project
*
* Copyright (C) 2006 Thorsten Zachmann
* Copyright (C) 2008-2009 Jan Hambrecht
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOCREATEPATHTOOL_H
#define KOCREATEPATHTOOL_H
#include "kritabasicflakes_export.h"
#include
#include
#include
class KoPathShape;
class KoShapeStroke;
class KoCreatePathToolPrivate;
#define KoCreatePathTool_ID "CreatePathTool"
/**
* Tool for creating path shapes.
*/
class KRITABASICFLAKES_EXPORT KoCreatePathTool : public KoToolBase
{
Q_OBJECT
public:
/**
* Constructor for the tool that allows you to create new paths by hand.
* @param canvas the canvas this tool will be working for.
*/
explicit KoCreatePathTool(KoCanvasBase * canvas);
~KoCreatePathTool() override;
/// reimplemented
void paint(QPainter &painter, const KoViewConverter &converter) override;
/// reimplemented
void mousePressEvent(KoPointerEvent *event) override;
/// reimplemented
void mouseDoubleClickEvent(KoPointerEvent *event) override;
/// reimplemented
void mouseMoveEvent(KoPointerEvent *event) override;
/// reimplemented
void mouseReleaseEvent(KoPointerEvent *event) override;
/// reimplemented
void keyPressEvent(QKeyEvent *event) override;
/// For behavior as selection tool and with initial shift-key
virtual bool listeningToModifiers();
/**
* Returns true if path has been started
*/
bool pathStarted();
bool tryMergeInPathShape(KoPathShape *pathShape);
public Q_SLOTS:
/// reimplemented
void activate(ToolActivation activation, const QSet &shapes) override;
/// reimplemented
void deactivate() override;
/// reimplemented
void documentResourceChanged(int key, const QVariant & res) override;
+Q_SIGNALS:
+ void sigUpdateAutoSmoothCurvesGUI(bool value);
+
protected:
/**
* Add path shape to document.
* This method can be overridden and change the behaviour of the tool. In that case the subclass takes ownership of pathShape.
* It gets only called if there are two or more points in the path.
*/
virtual void addPathShape(KoPathShape* pathShape);
protected:
/**
* This method is called to paint the path. Decorations are drawn by KoCreatePathTool afterwards.
*/
virtual void paintPath(KoPathShape& pathShape, QPainter &painter, const KoViewConverter &converter);
void endPath();
void endPathWithoutLastPoint();
void cancelPath();
void removeLastPoint();
bool addPathShapeImpl(KoPathShape* pathShape, bool tryMergeOnly);
/// reimplemented
QList > createOptionWidgets() override;
private:
Q_DECLARE_PRIVATE(KoCreatePathTool)
Q_PRIVATE_SLOT(d_func(), void angleDeltaChanged(int))
Q_PRIVATE_SLOT(d_func(), void angleSnapChanged(int))
+ Q_PRIVATE_SLOT(d_func(), void autoSmoothCurvesChanged(bool))
};
#endif
diff --git a/libs/basicflakes/tools/KoCreatePathTool_p.h b/libs/basicflakes/tools/KoCreatePathTool_p.h
index 2f68d9f479..c6b863a56c 100644
--- a/libs/basicflakes/tools/KoCreatePathTool_p.h
+++ b/libs/basicflakes/tools/KoCreatePathTool_p.h
@@ -1,429 +1,446 @@
/* 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.
*/
#ifndef KOCREATEPATHTOOL_P_H
#define KOCREATEPATHTOOL_P_H
#include "KoCreatePathTool.h"
#include "KoPathPoint.h"
#include "KoPathPointData.h"
#include "KoPathPointMergeCommand.h"
#include "KoParameterShape.h"
#include "KoShapeManager.h"
#include "KoSnapStrategy.h"
#include "KoToolBase_p.h"
#include
+#include "kis_config.h"
#include "math.h"
class KoStrokeConfigWidget;
class KoConverter;
/// Small helper to keep track of a path point and its parent path shape
struct PathConnectionPoint {
PathConnectionPoint()
: path(0), point(0) {
}
// reset state to invalid
void reset() {
path = 0;
point = 0;
}
PathConnectionPoint& operator =(KoPathPoint * pathPoint) {
if (!pathPoint || ! pathPoint->parent()) {
reset();
} else {
path = pathPoint->parent();
point = pathPoint;
}
return *this;
}
bool operator != (const PathConnectionPoint &rhs) const {
return rhs.path != path || rhs.point != point;
}
bool operator == (const PathConnectionPoint &rhs) const {
return rhs.path == path && rhs.point == point;
}
bool isValid() const {
return path && point;
}
// checks if the path and point are still valid
void validate(KoCanvasBase *canvas) {
// no point in validating an already invalid state
if (!isValid()) {
return;
}
// we need canvas to validate
if (!canvas) {
reset();
return;
}
// check if path is still part of the docment
if (!canvas->shapeManager()->shapes().contains(path)) {
reset();
return;
}
// check if point is still part of the path
if (path->pathPointIndex(point) == KoPathPointIndex(-1, -1)) {
reset();
return;
}
}
KoPathShape * path;
KoPathPoint * point;
};
inline qreal squareDistance(const QPointF &p1, const QPointF &p2)
{
qreal dx = p1.x() - p2.x();
qreal dy = p1.y() - p2.y();
return dx * dx + dy * dy;
}
class AngleSnapStrategy : public KoSnapStrategy
{
public:
explicit AngleSnapStrategy(qreal angleStep, bool active)
: KoSnapStrategy(KoSnapGuide::CustomSnapping), m_angleStep(angleStep), m_active(active) {
}
void setStartPoint(const QPointF &startPoint) {
m_startPoint = startPoint;
}
void setAngleStep(qreal angleStep) {
m_angleStep = qAbs(angleStep);
}
bool snap(const QPointF &mousePosition, KoSnapProxy * proxy, qreal maxSnapDistance) override {
Q_UNUSED(proxy);
if (!m_active)
return false;
QLineF line(m_startPoint, mousePosition);
qreal currentAngle = line.angle();
int prevStep = qAbs(currentAngle / m_angleStep);
int nextStep = prevStep + 1;
qreal prevAngle = prevStep * m_angleStep;
qreal nextAngle = nextStep * m_angleStep;
if (qAbs(currentAngle - prevAngle) <= qAbs(currentAngle - nextAngle)) {
line.setAngle(prevAngle);
} else {
line.setAngle(nextAngle);
}
qreal maxSquareSnapDistance = maxSnapDistance * maxSnapDistance;
qreal snapDistance = squareDistance(mousePosition, line.p2());
if (snapDistance > maxSquareSnapDistance)
return false;
setSnappedPosition(line.p2());
return true;
}
QPainterPath decoration(const KoViewConverter &converter) const override {
Q_UNUSED(converter);
QPainterPath decoration;
decoration.moveTo(m_startPoint);
decoration.lineTo(snappedPosition());
return decoration;
}
void deactivate() {
m_active = false;
}
void activate() {
m_active = true;
}
private:
QPointF m_startPoint;
qreal m_angleStep;
bool m_active;
};
class KoCreatePathToolPrivate : public KoToolBasePrivate
{
KoCreatePathTool * const q;
public:
KoCreatePathToolPrivate(KoCreatePathTool * const qq, KoCanvasBase* canvas)
: KoToolBasePrivate(qq, canvas),
q(qq),
shape(0),
activePoint(0),
firstPoint(0),
handleRadius(3),
mouseOverFirstPoint(false),
pointIsDragged(false),
finishAfterThisPoint(false),
hoveredPoint(0),
listeningToModifiers(false),
angleSnapStrategy(0),
angleSnappingDelta(15),
angleSnapStatus(false)
{
}
KoPathShape *shape;
KoPathPoint *activePoint;
KoPathPoint *firstPoint;
int handleRadius;
bool mouseOverFirstPoint;
bool pointIsDragged;
bool finishAfterThisPoint;
PathConnectionPoint existingStartPoint; ///< an existing path point we started a new path at
PathConnectionPoint existingEndPoint; ///< an existing path point we finished a new path at
KoPathPoint *hoveredPoint; ///< an existing path end point the mouse is hovering on
bool listeningToModifiers; // Fine tune when to begin processing modifiers at the beginning of a stroke.
+ bool prevPointWasDragged = false;
+ bool autoSmoothCurves = false;
QPointF dragStartPoint;
AngleSnapStrategy *angleSnapStrategy;
int angleSnappingDelta;
bool angleSnapStatus;
void repaintActivePoint() const {
const bool isFirstPoint = (activePoint == firstPoint);
if (!isFirstPoint && !pointIsDragged)
return;
QRectF rect = activePoint->boundingRect(false);
// make sure that we have the second control point inside our
// update rect, as KoPathPoint::boundingRect will not include
// the second control point of the last path point if the path
// is not closed
const QPointF &point = activePoint->point();
const QPointF &controlPoint = activePoint->controlPoint2();
rect = rect.united(QRectF(point, controlPoint).normalized());
// when painting the first point we want the
// first control point to be painted as well
if (isFirstPoint) {
const QPointF &controlPoint = activePoint->controlPoint1();
rect = rect.united(QRectF(point, controlPoint).normalized());
}
QPointF border = q->canvas()->viewConverter()
->viewToDocument(QPointF(handleRadius, handleRadius));
rect.adjust(-border.x(), -border.y(), border.x(), border.y());
q->canvas()->updateCanvas(rect);
}
/// returns the nearest existing path point
KoPathPoint* endPointAtPosition(const QPointF &position) const {
QRectF roi = q->handleGrabRect(position);
QList shapes = q->canvas()->shapeManager()->shapesAt(roi);
KoPathPoint * nearestPoint = 0;
qreal minDistance = HUGE_VAL;
uint grabSensitivity = q->grabSensitivity();
qreal maxDistance = q->canvas()->viewConverter()->viewToDocumentX(grabSensitivity);
Q_FOREACH(KoShape * s, shapes) {
KoPathShape * path = dynamic_cast(s);
if (!path)
continue;
KoParameterShape *paramShape = dynamic_cast(s);
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;
}
/// Connects given path with the ones we hit when starting/finishing
bool connectPaths(KoPathShape *pathShape, const PathConnectionPoint &pointAtStart, const PathConnectionPoint &pointAtEnd) const {
KoPathShape * startShape = 0;
KoPathShape * endShape = 0;
KoPathPoint * startPoint = 0;
KoPathPoint * endPoint = 0;
if (pointAtStart.isValid()) {
startShape = pointAtStart.path;
startPoint = pointAtStart.point;
}
if (pointAtEnd.isValid()) {
endShape = pointAtEnd.path;
endPoint = pointAtEnd.point;
}
// at least one point must be valid
if (!startPoint && !endPoint)
return false;
// do not allow connecting to the same point twice
if (startPoint == endPoint)
endPoint = 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);
// combine with the path we hit on start
KoPathPointIndex startIndex(-1, -1);
if (startShape && startPoint) {
startIndex = startShape->pathPointIndex(startPoint);
pathShape->combine(startShape);
pathShape->moveSubpath(0, pathShape->subpathCount() - 1);
}
// combine with the path we hit on finish
KoPathPointIndex endIndex(-1, -1);
if (endShape && endPoint) {
endIndex = endShape->pathPointIndex(endPoint);
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;
}
void addPathShape() {
if (!shape) return;
if (shape->pointCount() < 2) {
cleanUp();
return;
}
// this is done so that nothing happens when the mouseReleaseEvent for the this event is received
KoPathShape *pathShape = shape;
shape = 0;
q->addPathShape(pathShape);
cleanUp();
return;
}
void cleanUp() {
// reset snap guide
q->canvas()->updateCanvas(q->canvas()->snapGuide()->boundingRect());
q->canvas()->snapGuide()->reset();
angleSnapStrategy = 0;
delete shape;
shape = 0;
existingStartPoint = 0;
existingEndPoint = 0;
hoveredPoint = 0;
listeningToModifiers = false;
}
void angleDeltaChanged(int value) {
angleSnappingDelta = value;
if (angleSnapStrategy)
angleSnapStrategy->setAngleStep(angleSnappingDelta);
}
+ void autoSmoothCurvesChanged(bool value) {
+ autoSmoothCurves = value;
+
+ KisConfig cfg(false);
+ cfg.setAutoSmoothBezierCurves(value);
+ }
+
+ void loadAutoSmoothValueFromConfig() {
+ KisConfig cfg(true);
+ autoSmoothCurves = cfg.autoSmoothBezierCurves();
+
+ emit q->sigUpdateAutoSmoothCurvesGUI(autoSmoothCurves);
+ }
+
void angleSnapChanged(int angleSnap) {
angleSnapStatus = ! angleSnapStatus;
if (angleSnapStrategy) {
if (angleSnap == Qt::Checked)
angleSnapStrategy->activate();
else
angleSnapStrategy->deactivate();
}
}
};
#endif // KOCREATEPATHTOOL_P_H
diff --git a/libs/flake/commands/KoPathPointTypeCommand.cpp b/libs/flake/commands/KoPathPointTypeCommand.cpp
index d61ac53e7b..57d4351bd7 100644
--- a/libs/flake/commands/KoPathPointTypeCommand.cpp
+++ b/libs/flake/commands/KoPathPointTypeCommand.cpp
@@ -1,224 +1,235 @@
/* This file is part of the KDE project
* Copyright (C) 2006,2008 Jan Hambrecht
* Copyright (C) 2006,2007 Thorsten Zachmann
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoPathPointTypeCommand.h"
#include
#include
#include "KoPathSegment.h"
KoPathPointTypeCommand::KoPathPointTypeCommand(
const QList & pointDataList,
PointType pointType,
KUndo2Command *parent)
: KoPathBaseCommand(parent)
, m_pointType(pointType)
{
QList::const_iterator it(pointDataList.begin());
for (; it != pointDataList.end(); ++it) {
KoPathPoint *point = it->pathShape->pointByIndex(it->pointIndex);
if (point) {
PointData pointData(*it);
pointData.m_oldControlPoint1 = it->pathShape->shapeToDocument(point->controlPoint1());
pointData.m_oldControlPoint2 = it->pathShape->shapeToDocument(point->controlPoint2());
pointData.m_oldProperties = point->properties();
pointData.m_hadControlPoint1 = point->activeControlPoint1();
pointData.m_hadControlPoint2 = point->activeControlPoint2();
m_oldPointData.append(pointData);
m_shapes.insert(it->pathShape);
}
}
setText(kundo2_i18n("Set point type"));
}
KoPathPointTypeCommand::~KoPathPointTypeCommand()
{
}
void KoPathPointTypeCommand::redo()
{
KUndo2Command::redo();
repaint(false);
m_additionalPointData.clear();
QList::iterator it(m_oldPointData.begin());
for (; it != m_oldPointData.end(); ++it) {
KoPathPoint *point = it->m_pointData.pathShape->pointByIndex(it->m_pointData.pointIndex);
KoPathPoint::PointProperties properties = point->properties();
switch (m_pointType) {
case Line: {
point->removeControlPoint1();
point->removeControlPoint2();
break;
}
case Curve: {
KoPathPointIndex pointIndex = it->m_pointData.pointIndex;
KoPathPointIndex prevIndex;
KoPathPointIndex nextIndex;
KoPathShape * path = it->m_pointData.pathShape;
// get previous path node
if (pointIndex.second > 0)
prevIndex = KoPathPointIndex(pointIndex.first, pointIndex.second - 1);
else if (pointIndex.second == 0 && path->isClosedSubpath(pointIndex.first))
prevIndex = KoPathPointIndex(pointIndex.first, path->subpathPointCount(pointIndex.first) - 1);
// get next node
if (pointIndex.second < path->subpathPointCount(pointIndex.first) - 1)
nextIndex = KoPathPointIndex(pointIndex.first, pointIndex.second + 1);
else if (pointIndex.second < path->subpathPointCount(pointIndex.first) - 1
&& path->isClosedSubpath(pointIndex.first))
nextIndex = KoPathPointIndex(pointIndex.first, 0);
KoPathPoint * prevPoint = path->pointByIndex(prevIndex);
KoPathPoint * nextPoint = path->pointByIndex(nextIndex);
if (prevPoint && ! point->activeControlPoint1() && appendPointData(KoPathPointData(path, prevIndex))) {
KoPathSegment cubic = KoPathSegment(prevPoint, point).toCubic();
if (prevPoint->activeControlPoint2()) {
prevPoint->setControlPoint2(cubic.first()->controlPoint2());
point->setControlPoint1(cubic.second()->controlPoint1());
} else
point->setControlPoint1(cubic.second()->controlPoint1());
}
if (nextPoint && ! point->activeControlPoint2() && appendPointData(KoPathPointData(path, nextIndex))) {
KoPathSegment cubic = KoPathSegment(point, nextPoint).toCubic();
if (nextPoint->activeControlPoint1()) {
point->setControlPoint2(cubic.first()->controlPoint2());
nextPoint->setControlPoint1(cubic.second()->controlPoint1());
} else
point->setControlPoint2(cubic.first()->controlPoint2());
}
+ point->setProperties(properties);
break;
}
case Symmetric: {
properties &= ~KoPathPoint::IsSmooth;
properties |= KoPathPoint::IsSymmetric;
// calculate vector from node point to first control point and normalize it
QPointF directionC1 = point->controlPoint1() - point->point();
qreal dirLengthC1 = sqrt(directionC1.x() * directionC1.x() + directionC1.y() * directionC1.y());
directionC1 /= dirLengthC1;
// calculate vector from node point to second control point and normalize it
QPointF directionC2 = point->controlPoint2() - point->point();
qreal dirLengthC2 = sqrt(directionC2.x() * directionC2.x() + directionC2.y() * directionC2.y());
directionC2 /= dirLengthC2;
// calculate the average distance of the control points to the node point
qreal averageLength = 0.5 * (dirLengthC1 + dirLengthC2);
// compute position of the control points so that they lie on a line going through the node point
// the new distance of the control points is the average distance to the node point
point->setControlPoint1(point->point() + 0.5 * averageLength * (directionC1 - directionC2));
point->setControlPoint2(point->point() + 0.5 * averageLength * (directionC2 - directionC1));
+ point->setProperties(properties);
}
break;
case Smooth: {
- properties &= ~KoPathPoint::IsSymmetric;
- properties |= KoPathPoint::IsSmooth;
-
- // calculate vector from node point to first control point and normalize it
- QPointF directionC1 = point->controlPoint1() - point->point();
- qreal dirLengthC1 = sqrt(directionC1.x() * directionC1.x() + directionC1.y() * directionC1.y());
- directionC1 /= dirLengthC1;
- // calculate vector from node point to second control point and normalize it
- QPointF directionC2 = point->controlPoint2() - point->point();
- qreal dirLengthC2 = sqrt(directionC2.x() * directionC2.x() + directionC2.y() * directionC2.y());
- directionC2 /= dirLengthC2;
- // compute position of the control points so that they lie on a line going through the node point
- // the new distance of the control points is the average distance to the node point
- point->setControlPoint1(point->point() + 0.5 * dirLengthC1 * (directionC1 - directionC2));
- point->setControlPoint2(point->point() + 0.5 * dirLengthC2 * (directionC2 - directionC1));
+ makeCubicPointSmooth(point);
}
break;
case Corner:
default:
properties &= ~KoPathPoint::IsSymmetric;
properties &= ~KoPathPoint::IsSmooth;
+ point->setProperties(properties);
break;
}
- point->setProperties(properties);
}
repaint(true);
}
void KoPathPointTypeCommand::undo()
{
KUndo2Command::undo();
repaint(false);
/*
QList::iterator it(m_oldPointData.begin());
for (; it != m_oldPointData.end(); ++it)
{
KoPathShape *pathShape = it->m_pointData.pathShape;
KoPathPoint *point = pathShape->pointByIndex(it->m_pointData.pointIndex);
point->setProperties(it->m_oldProperties);
if (it->m_hadControlPoint1)
point->setControlPoint1(pathShape->documentToShape(it->m_oldControlPoint1));
else
point->removeControlPoint1();
if (it->m_hadControlPoint2)
point->setControlPoint2(pathShape->documentToShape(it->m_oldControlPoint2));
else
point->removeControlPoint2();
}
*/
undoChanges(m_oldPointData);
undoChanges(m_additionalPointData);
repaint(true);
}
+void KoPathPointTypeCommand::makeCubicPointSmooth(KoPathPoint *point)
+{
+ KoPathPoint::PointProperties properties = point->properties();
+
+ properties &= ~KoPathPoint::IsSymmetric;
+ properties |= KoPathPoint::IsSmooth;
+
+ // calculate vector from node point to first control point and normalize it
+ QPointF directionC1 = point->controlPoint1() - point->point();
+ qreal dirLengthC1 = sqrt(directionC1.x() * directionC1.x() + directionC1.y() * directionC1.y());
+ directionC1 /= dirLengthC1;
+ // calculate vector from node point to second control point and normalize it
+ QPointF directionC2 = point->controlPoint2() - point->point();
+ qreal dirLengthC2 = sqrt(directionC2.x() * directionC2.x() + directionC2.y() * directionC2.y());
+ directionC2 /= dirLengthC2;
+ // compute position of the control points so that they lie on a line going through the node point
+ // the new distance of the control points is the average distance to the node point
+ point->setControlPoint1(point->point() + 0.5 * dirLengthC1 * (directionC1 - directionC2));
+ point->setControlPoint2(point->point() + 0.5 * dirLengthC2 * (directionC2 - directionC1));
+
+ point->setProperties(properties);
+}
+
void KoPathPointTypeCommand::undoChanges(const QList &data)
{
QList::const_iterator it(data.begin());
for (; it != data.end(); ++it) {
KoPathShape *pathShape = it->m_pointData.pathShape;
KoPathPoint *point = pathShape->pointByIndex(it->m_pointData.pointIndex);
point->setProperties(it->m_oldProperties);
if (it->m_hadControlPoint1)
point->setControlPoint1(pathShape->documentToShape(it->m_oldControlPoint1));
else
point->removeControlPoint1();
if (it->m_hadControlPoint2)
point->setControlPoint2(pathShape->documentToShape(it->m_oldControlPoint2));
else
point->removeControlPoint2();
}
}
bool KoPathPointTypeCommand::appendPointData(KoPathPointData data)
{
KoPathPoint *point = data.pathShape->pointByIndex(data.pointIndex);
if (! point)
return false;
PointData pointData(data);
pointData.m_oldControlPoint1 = data.pathShape->shapeToDocument(point->controlPoint1());
pointData.m_oldControlPoint2 = data.pathShape->shapeToDocument(point->controlPoint2());
pointData.m_oldProperties = point->properties();
pointData.m_hadControlPoint1 = point->activeControlPoint1();
pointData.m_hadControlPoint2 = point->activeControlPoint2();
m_additionalPointData.append(pointData);
return true;
}
diff --git a/libs/flake/commands/KoPathPointTypeCommand.h b/libs/flake/commands/KoPathPointTypeCommand.h
index 849ec8b804..f025a2314f 100644
--- a/libs/flake/commands/KoPathPointTypeCommand.h
+++ b/libs/flake/commands/KoPathPointTypeCommand.h
@@ -1,79 +1,81 @@
/* This file is part of the KDE project
* Copyright (C) 2006,2008 Jan Hambrecht
* Copyright (C) 2006,2007 Thorsten Zachmann
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOPATHPOINTTYPECOMMAND_H
#define KOPATHPOINTTYPECOMMAND_H
#include
#include
#include "KoPathBaseCommand.h"
#include "KoPathPoint.h"
#include "KoPathPointData.h"
#include "kritaflake_export.h"
/// The undo / redo command for changing the path point type.
class KRITAFLAKE_EXPORT KoPathPointTypeCommand : public KoPathBaseCommand
{
public:
/// The type of the point
enum PointType {
Corner,
Smooth,
Symmetric,
Line,
Curve
};
/**
* Command to change the type of the given points
* @param pointDataList List of point for changing the points
* @param pointType the new point type to set
* @param parent the parent command used for macro commands
*/
KoPathPointTypeCommand(const QList &pointDataList, PointType pointType, KUndo2Command *parent = 0);
~KoPathPointTypeCommand() override;
/// redo the command
void redo() override;
/// revert the actions done in redo
void undo() override;
+ static void makeCubicPointSmooth(KoPathPoint *point);
+
private:
// used for storing the data for undo
struct PointData {
PointData(const KoPathPointData pointData)
: m_pointData(pointData) {}
KoPathPointData m_pointData;
// old control points in document coordinates
QPointF m_oldControlPoint1;
QPointF m_oldControlPoint2;
KoPathPoint::PointProperties m_oldProperties;
bool m_hadControlPoint1;
bool m_hadControlPoint2;
};
bool appendPointData(KoPathPointData data);
void undoChanges(const QList &data);
PointType m_pointType;
QList m_oldPointData;
QList m_additionalPointData;
};
#endif // KOPATHPOINTTYPECOMMAND_H
diff --git a/libs/flake/text/KoSvgTextShape.cpp b/libs/flake/text/KoSvgTextShape.cpp
index df2fb9b035..321a00ed38 100644
--- a/libs/flake/text/KoSvgTextShape.cpp
+++ b/libs/flake/text/KoSvgTextShape.cpp
@@ -1,628 +1,628 @@
/*
* 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 "KoSvgTextShape.h"
#include
#include
#include "KoSvgText.h"
#include "KoSvgTextProperties.h"
#include
#include
#include
#include
#include
#include "kis_debug.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
-struct KoSvgTextShapePrivate : public KoSvgTextChunkShapePrivate
+class KoSvgTextShapePrivate : public KoSvgTextChunkShapePrivate
{
KoSvgTextShapePrivate(KoSvgTextShape *_q)
: KoSvgTextChunkShapePrivate(_q)
{
}
KoSvgTextShapePrivate(const KoSvgTextShapePrivate &rhs, KoSvgTextShape *q)
: KoSvgTextChunkShapePrivate(rhs, q)
{
}
std::vector> cachedLayouts;
std::vector cachedLayoutsOffsets;
QThread *cachedLayoutsWorkingThread = 0;
void clearAssociatedOutlines(KoShape *rootShape);
Q_DECLARE_PUBLIC(KoSvgTextShape)
};
KoSvgTextShape::KoSvgTextShape()
: KoSvgTextChunkShape(new KoSvgTextShapePrivate(this))
{
setShapeId(KoSvgTextShape_SHAPEID);
}
KoSvgTextShape::KoSvgTextShape(const KoSvgTextShape &rhs)
: KoSvgTextChunkShape(new KoSvgTextShapePrivate(*rhs.d_func(), this))
{
setShapeId(KoSvgTextShape_SHAPEID);
// QTextLayout has no copy-ctor, so just relayout everything!
relayout();
}
KoSvgTextShape::~KoSvgTextShape()
{
}
KoShape *KoSvgTextShape::cloneShape() const
{
return new KoSvgTextShape(*this);
}
void KoSvgTextShape::shapeChanged(ChangeType type, KoShape *shape)
{
KoSvgTextChunkShape::shapeChanged(type, shape);
if (type == StrokeChanged || type == BackgroundChanged || type == ContentChanged) {
relayout();
}
}
void KoSvgTextShape::paintComponent(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext)
{
Q_D(KoSvgTextShape);
Q_UNUSED(paintContext);
/**
* HACK ALERT:
* QTextLayout should only be accessed from the thread it has been created in.
* If the cached layout has been created in a different thread, we should just
* recreate the layouts in the current thread to be able to render them.
*/
if (QThread::currentThread() != d->cachedLayoutsWorkingThread) {
relayout();
}
applyConversion(painter, converter);
for (int i = 0; i < (int)d->cachedLayouts.size(); i++) {
d->cachedLayouts[i]->draw(&painter, d->cachedLayoutsOffsets[i]);
}
/**
* HACK ALERT:
* The layouts of non-gui threads must be destroyed in the same thread
* they have been created. Because the thread might be restarted in the
* meantime or just destroyed, meaning that the per-thread freetype data
* will not be available.
*/
if (QThread::currentThread() != qApp->thread()) {
d->cachedLayouts.clear();
d->cachedLayoutsOffsets.clear();
d->cachedLayoutsWorkingThread = 0;
}
}
void KoSvgTextShape::paintStroke(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext)
{
Q_UNUSED(painter);
Q_UNUSED(converter);
Q_UNUSED(paintContext);
// do nothing! everything is painted in paintComponent()
}
QPainterPath KoSvgTextShape::textOutline()
{
Q_D(KoSvgTextShape);
QPainterPath result;
result.setFillRule(Qt::WindingFill);
for (int i = 0; i < (int)d->cachedLayouts.size(); i++) {
const QPointF layoutOffset = d->cachedLayoutsOffsets[i];
const QTextLayout *layout = d->cachedLayouts[i].get();
for (int j = 0; j < layout->lineCount(); j++) {
QTextLine line = layout->lineAt(j);
Q_FOREACH (const QGlyphRun &run, line.glyphRuns()) {
const QVector indexes = run.glyphIndexes();
const QVector positions = run.positions();
const QRawFont font = run.rawFont();
KIS_SAFE_ASSERT_RECOVER(indexes.size() == positions.size()) { continue; }
for (int k = 0; k < indexes.size(); k++) {
QPainterPath glyph = font.pathForGlyph(indexes[k]);
glyph.translate(positions[k] + layoutOffset);
result += glyph;
}
const qreal thickness = font.lineThickness();
const QRectF runBounds = run.boundingRect();
if (run.overline()) {
// the offset is calculated to be consistent with the way how Qt renders the text
const qreal y = line.y();
QRectF overlineBlob(runBounds.x(), y, runBounds.width(), thickness);
overlineBlob.translate(layoutOffset);
QPainterPath path;
path.addRect(overlineBlob);
// don't use direct addRect, because it doesn't care about Qt::WindingFill
result += path;
}
if (run.strikeOut()) {
// the offset is calculated to be consistent with the way how Qt renders the text
const qreal y = line.y() + 0.5 * line.height();
QRectF strikeThroughBlob(runBounds.x(), y, runBounds.width(), thickness);
strikeThroughBlob.translate(layoutOffset);
QPainterPath path;
path.addRect(strikeThroughBlob);
// don't use direct addRect, because it doesn't care about Qt::WindingFill
result += path;
}
if (run.underline()) {
const qreal y = line.y() + line.ascent() + font.underlinePosition();
QRectF underlineBlob(runBounds.x(), y, runBounds.width(), thickness);
underlineBlob.translate(layoutOffset);
QPainterPath path;
path.addRect(underlineBlob);
// don't use direct addRect, because it doesn't care about Qt::WindingFill
result += path;
}
}
}
}
return result;
}
void KoSvgTextShape::resetTextShape()
{
KoSvgTextChunkShape::resetTextShape();
relayout();
}
struct TextChunk {
QString text;
QVector formats;
Qt::LayoutDirection direction = Qt::LeftToRight;
Qt::Alignment alignment = Qt::AlignLeading;
struct SubChunkOffset {
QPointF offset;
int start = 0;
};
QVector offsets;
boost::optional xStartPos;
boost::optional yStartPos;
QPointF applyStartPosOverride(const QPointF &pos) const {
QPointF result = pos;
if (xStartPos) {
result.rx() = *xStartPos;
}
if (yStartPos) {
result.ry() = *yStartPos;
}
return result;
}
};
QVector mergeIntoChunks(const QVector &subChunks)
{
QVector chunks;
for (auto it = subChunks.begin(); it != subChunks.end(); ++it) {
if (it->transformation.startsNewChunk() || it == subChunks.begin()) {
TextChunk newChunk = TextChunk();
newChunk.direction = it->format.layoutDirection();
newChunk.alignment = it->format.calculateAlignment();
newChunk.xStartPos = it->transformation.xPos;
newChunk.yStartPos = it->transformation.yPos;
chunks.append(newChunk);
}
TextChunk ¤tChunk = chunks.last();
if (it->transformation.hasRelativeOffset()) {
TextChunk::SubChunkOffset o;
o.start = currentChunk.text.size();
o.offset = it->transformation.relativeOffset();
KIS_SAFE_ASSERT_RECOVER_NOOP(!o.offset.isNull());
currentChunk.offsets.append(o);
}
QTextLayout::FormatRange formatRange;
formatRange.start = currentChunk.text.size();
formatRange.length = it->text.size();
formatRange.format = it->format;
currentChunk.formats.append(formatRange);
currentChunk.text += it->text;
}
return chunks;
}
/**
* Qt's QTextLayout has a weird trait, it doesn't count space characters as
* distinct characters in QTextLayout::setNumColumns(), that is, if we want to
* position a block of text that starts with a space character in a specific
* position, QTextLayout will drop this space and will move the text to the left.
*
* That is why we have a special wrapper object that ensures that no spaces are
* dropped and their horizontal advance parameter is taken into account.
*/
struct LayoutChunkWrapper
{
LayoutChunkWrapper(QTextLayout *layout)
: m_layout(layout)
{
}
QPointF addTextChunk(int startPos, int length, const QPointF &textChunkStartPos)
{
QPointF currentTextPos = textChunkStartPos;
const int lastPos = startPos + length - 1;
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(startPos == m_addedChars, currentTextPos);
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(lastPos < m_layout->text().size(), currentTextPos);
QTextLine line;
std::swap(line, m_danglingLine);
if (!line.isValid()) {
line = m_layout->createLine();
}
// skip all the space characters that were not included into the Qt's text line
const int currentLineStart = line.isValid() ? line.textStart() : startPos + length;
while (startPos < currentLineStart && startPos <= lastPos) {
currentTextPos.rx() += skipSpaceCharacter(startPos);
startPos++;
}
if (startPos <= lastPos) {
const int numChars = lastPos - startPos + 1;
line.setNumColumns(numChars);
line.setPosition(currentTextPos - QPointF(0, line.ascent()));
currentTextPos.rx() += line.horizontalAdvance();
// skip all the space characters that were not included into the Qt's text line
for (int i = line.textStart() + line.textLength(); i < lastPos; i++) {
currentTextPos.rx() += skipSpaceCharacter(i);
}
} else {
// keep the created but unused line for future use
std::swap(line, m_danglingLine);
}
m_addedChars += length;
return currentTextPos;
}
private:
qreal skipSpaceCharacter(int pos) {
const QTextCharFormat format =
formatForPos(pos, m_layout->formats());
const QChar skippedChar = m_layout->text()[pos];
KIS_SAFE_ASSERT_RECOVER_NOOP(skippedChar.isSpace() || !skippedChar.isPrint());
QFontMetrics metrics(format.font());
return metrics.width(skippedChar);
}
static QTextCharFormat formatForPos(int pos, const QVector &formats)
{
Q_FOREACH (const QTextLayout::FormatRange &range, formats) {
if (pos >= range.start && pos < range.start + range.length) {
return range.format;
}
}
KIS_SAFE_ASSERT_RECOVER_NOOP(0 && "pos should be within the bounds of the layouted text");
return QTextCharFormat();
}
private:
int m_addedChars = 0;
QTextLayout *m_layout;
QTextLine m_danglingLine;
};
void KoSvgTextShape::relayout()
{
Q_D(KoSvgTextShape);
d->cachedLayouts.clear();
d->cachedLayoutsOffsets.clear();
d->cachedLayoutsWorkingThread = QThread::currentThread();
QPointF currentTextPos;
QVector textChunks = mergeIntoChunks(layoutInterface()->collectSubChunks());
Q_FOREACH (const TextChunk &chunk, textChunks) {
std::unique_ptr layout(new QTextLayout());
QTextOption option;
// WARNING: never activate this option! It breaks the RTL text layout!
//option.setFlags(QTextOption::ShowTabsAndSpaces);
option.setWrapMode(QTextOption::WrapAnywhere);
option.setUseDesignMetrics(true); // TODO: investigate if it is needed?
option.setTextDirection(chunk.direction);
layout->setText(chunk.text);
layout->setTextOption(option);
layout->setFormats(chunk.formats);
layout->setCacheEnabled(true);
layout->beginLayout();
currentTextPos = chunk.applyStartPosOverride(currentTextPos);
const QPointF anchorPointPos = currentTextPos;
int lastSubChunkStart = 0;
QPointF lastSubChunkOffset;
LayoutChunkWrapper wrapper(layout.get());
for (int i = 0; i <= chunk.offsets.size(); i++) {
const bool isFinalPass = i == chunk.offsets.size();
const int length =
!isFinalPass ?
chunk.offsets[i].start - lastSubChunkStart :
chunk.text.size() - lastSubChunkStart;
if (length > 0) {
currentTextPos += lastSubChunkOffset;
currentTextPos = wrapper.addTextChunk(lastSubChunkStart,
length,
currentTextPos);
}
if (!isFinalPass) {
lastSubChunkOffset = chunk.offsets[i].offset;
lastSubChunkStart = chunk.offsets[i].start;
}
}
layout->endLayout();
QPointF diff;
if (chunk.alignment & Qt::AlignTrailing || chunk.alignment & Qt::AlignHCenter) {
if (chunk.alignment & Qt::AlignTrailing) {
diff = currentTextPos - anchorPointPos;
} else if (chunk.alignment & Qt::AlignHCenter) {
diff = 0.5 * (currentTextPos - anchorPointPos);
}
// TODO: fix after t2b text implemented
diff.ry() = 0;
}
d->cachedLayouts.push_back(std::move(layout));
d->cachedLayoutsOffsets.push_back(-diff);
}
d->clearAssociatedOutlines(this);
for (int i = 0; i < int(d->cachedLayouts.size()); i++) {
const QTextLayout &layout = *d->cachedLayouts[i];
const QPointF layoutOffset = d->cachedLayoutsOffsets[i];
using namespace KoSvgText;
Q_FOREACH (const QTextLayout::FormatRange &range, layout.formats()) {
const KoSvgCharChunkFormat &format =
static_cast(range.format);
AssociatedShapeWrapper wrapper = format.associatedShapeWrapper();
const int rangeStart = range.start;
const int safeRangeLength = range.length > 0 ? range.length : layout.text().size() - rangeStart;
if (safeRangeLength <= 0) continue;
const int rangeEnd = range.start + safeRangeLength - 1;
const int firstLineIndex = layout.lineForTextPosition(rangeStart).lineNumber();
const int lastLineIndex = layout.lineForTextPosition(rangeEnd).lineNumber();
for (int i = firstLineIndex; i <= lastLineIndex; i++) {
const QTextLine line = layout.lineAt(i);
// It might happen that the range contains only one (or two)
// symbol that is a whitespace symbol. In such a case we should
// just skip this (invalid) line.
if (!line.isValid()) continue;
const int posStart = qMax(line.textStart(), rangeStart);
const int posEnd = qMin(line.textStart() + line.textLength() - 1, rangeEnd);
const QList glyphRuns = line.glyphRuns(posStart, posEnd - posStart + 1);
Q_FOREACH (const QGlyphRun &run, glyphRuns) {
const QPointF firstPosition = run.positions().first();
const quint32 firstGlyphIndex = run.glyphIndexes().first();
const QPointF lastPosition = run.positions().last();
const quint32 lastGlyphIndex = run.glyphIndexes().last();
const QRawFont rawFont = run.rawFont();
const QRectF firstGlyphRect = rawFont.boundingRect(firstGlyphIndex).translated(firstPosition);
const QRectF lastGlyphRect = rawFont.boundingRect(lastGlyphIndex).translated(lastPosition);
QRectF rect = run.boundingRect();
/**
* HACK ALERT: there is a bug in a way how Qt calculates boundingRect()
* of the glyph run. It doesn't care about left and right bearings
* of the border chars in the run, therefore it becomes cropped.
*
* Here we just add a half-char width margin to both sides
* of the glyph run to make sure the glyphs are fully painted.
*
* BUG: 389528
* BUG: 392068
*/
rect.setLeft(qMin(rect.left(), lastGlyphRect.left()) - 0.5 * firstGlyphRect.width());
rect.setRight(qMax(rect.right(), lastGlyphRect.right()) + 0.5 * lastGlyphRect.width());
wrapper.addCharacterRect(rect.translated(layoutOffset));
}
}
}
}
}
void KoSvgTextShapePrivate::clearAssociatedOutlines(KoShape *rootShape)
{
KoSvgTextChunkShape *chunkShape = dynamic_cast(rootShape);
KIS_SAFE_ASSERT_RECOVER_RETURN(chunkShape);
chunkShape->layoutInterface()->clearAssociatedOutline();
Q_FOREACH (KoShape *child, chunkShape->shapes()) {
clearAssociatedOutlines(child);
}
}
bool KoSvgTextShape::isRootTextNode() const
{
return true;
}
KoSvgTextShapeFactory::KoSvgTextShapeFactory()
: KoShapeFactoryBase(KoSvgTextShape_SHAPEID, i18n("Text"))
{
setToolTip(i18n("SVG Text Shape"));
setIconName(koIconNameCStr("x-shape-text"));
setLoadingPriority(5);
setXmlElementNames(KoXmlNS::svg, QStringList("text"));
KoShapeTemplate t;
t.name = i18n("SVG Text");
t.iconName = koIconName("x-shape-text");
t.toolTip = i18n("SVG Text Shape");
addTemplate(t);
}
KoShape *KoSvgTextShapeFactory::createDefaultShape(KoDocumentResourceManager *documentResources) const
{
debugFlake << "Create default svg text shape";
KoSvgTextShape *shape = new KoSvgTextShape();
shape->setShapeId(KoSvgTextShape_SHAPEID);
KoSvgTextShapeMarkupConverter converter(shape);
converter.convertFromSvg("Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
"",
QRectF(0, 0, 200, 60),
documentResources->shapeController()->pixelsPerInch());
debugFlake << converter.errors() << converter.warnings();
return shape;
}
KoShape *KoSvgTextShapeFactory::createShape(const KoProperties *params, KoDocumentResourceManager *documentResources) const
{
KoSvgTextShape *shape = new KoSvgTextShape();
shape->setShapeId(KoSvgTextShape_SHAPEID);
QString svgText = params->stringProperty("svgText", "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
QString defs = params->stringProperty("defs" , "");
QRectF shapeRect = QRectF(0, 0, 200, 60);
QVariant rect = params->property("shapeRect");
if (rect.type()==QVariant::RectF) {
shapeRect = rect.toRectF();
}
KoSvgTextShapeMarkupConverter converter(shape);
converter.convertFromSvg(svgText,
defs,
shapeRect,
documentResources->shapeController()->pixelsPerInch());
shape->setBackground(QSharedPointer(new KoColorBackground(QColor(Qt::black))));
shape->setPosition(shapeRect.topLeft());
return shape;
}
bool KoSvgTextShapeFactory::supports(const KoXmlElement &/*e*/, KoShapeLoadingContext &/*context*/) const
{
return false;
}
diff --git a/libs/image/CMakeLists.txt b/libs/image/CMakeLists.txt
index 1fa68ecb61..06d757f161 100644
--- a/libs/image/CMakeLists.txt
+++ b/libs/image/CMakeLists.txt
@@ -1,377 +1,379 @@
add_subdirectory( tests )
add_subdirectory( tiles3 )
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/metadata
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty
${CMAKE_CURRENT_SOURCE_DIR}/brushengine
${CMAKE_CURRENT_SOURCE_DIR}/commands
${CMAKE_CURRENT_SOURCE_DIR}/commands_new
${CMAKE_CURRENT_SOURCE_DIR}/filter
${CMAKE_CURRENT_SOURCE_DIR}/floodfill
${CMAKE_CURRENT_SOURCE_DIR}/generator
${CMAKE_CURRENT_SOURCE_DIR}/layerstyles
${CMAKE_CURRENT_SOURCE_DIR}/processing
${CMAKE_SOURCE_DIR}/sdk/tests
)
include_directories(SYSTEM
${EIGEN3_INCLUDE_DIR}
)
if(FFTW3_FOUND)
include_directories(${FFTW3_INCLUDE_DIR})
endif()
if(HAVE_VC)
include_directories(SYSTEM ${Vc_INCLUDE_DIR} ${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS})
ko_compile_for_all_implementations(__per_arch_circle_mask_generator_objs kis_brush_mask_applicator_factories.cpp)
else()
set(__per_arch_circle_mask_generator_objs kis_brush_mask_applicator_factories.cpp)
endif()
set(kritaimage_LIB_SRCS
tiles3/kis_tile.cc
tiles3/kis_tile_data.cc
tiles3/kis_tile_data_store.cc
tiles3/kis_tile_data_pooler.cc
tiles3/kis_tiled_data_manager.cc
tiles3/KisTiledExtentManager.cpp
tiles3/kis_memento_manager.cc
tiles3/kis_hline_iterator.cpp
tiles3/kis_vline_iterator.cpp
tiles3/kis_random_accessor.cc
tiles3/swap/kis_abstract_compression.cpp
tiles3/swap/kis_lzf_compression.cpp
tiles3/swap/kis_abstract_tile_compressor.cpp
tiles3/swap/kis_legacy_tile_compressor.cpp
tiles3/swap/kis_tile_compressor_2.cpp
tiles3/swap/kis_chunk_allocator.cpp
tiles3/swap/kis_memory_window.cpp
tiles3/swap/kis_swapped_data_store.cpp
tiles3/swap/kis_tile_data_swapper.cpp
kis_distance_information.cpp
kis_painter.cc
kis_painter_blt_multi_fixed.cpp
kis_marker_painter.cpp
KisPrecisePaintDeviceWrapper.cpp
kis_progress_updater.cpp
brushengine/kis_paint_information.cc
brushengine/kis_random_source.cpp
brushengine/KisPerStrokeRandomSource.cpp
brushengine/kis_stroke_random_source.cpp
brushengine/kis_paintop.cc
brushengine/kis_paintop_factory.cpp
brushengine/kis_paintop_preset.cpp
brushengine/kis_paintop_registry.cc
brushengine/kis_paintop_settings.cpp
brushengine/kis_paintop_settings_update_proxy.cpp
brushengine/kis_paintop_utils.cpp
brushengine/kis_no_size_paintop_settings.cpp
brushengine/kis_locked_properties.cc
brushengine/kis_locked_properties_proxy.cpp
brushengine/kis_locked_properties_server.cpp
brushengine/kis_paintop_config_widget.cpp
brushengine/kis_uniform_paintop_property.cpp
brushengine/kis_combo_based_paintop_property.cpp
brushengine/kis_slider_based_paintop_property.cpp
brushengine/kis_standard_uniform_properties_factory.cpp
brushengine/KisStrokeSpeedMeasurer.cpp
brushengine/KisPaintopSettingsIds.cpp
commands/kis_deselect_global_selection_command.cpp
+ commands/KisDeselectActiveSelectionCommand.cpp
commands/kis_image_change_layers_command.cpp
commands/kis_image_change_visibility_command.cpp
commands/kis_image_command.cpp
commands/kis_image_set_projection_color_space_command.cpp
commands/kis_image_layer_add_command.cpp
commands/kis_image_layer_move_command.cpp
commands/kis_image_layer_remove_command.cpp
commands/kis_image_layer_remove_command_impl.cpp
commands/kis_image_lock_command.cpp
commands/kis_node_command.cpp
commands/kis_node_compositeop_command.cpp
commands/kis_node_opacity_command.cpp
commands/kis_node_property_list_command.cpp
commands/kis_reselect_global_selection_command.cpp
+ commands/KisReselectActiveSelectionCommand.cpp
commands/kis_set_global_selection_command.cpp
commands_new/kis_saved_commands.cpp
commands_new/kis_processing_command.cpp
commands_new/kis_image_resize_command.cpp
commands_new/kis_image_set_resolution_command.cpp
commands_new/kis_node_move_command2.cpp
commands_new/kis_set_layer_style_command.cpp
commands_new/kis_selection_move_command2.cpp
commands_new/kis_update_command.cpp
commands_new/kis_switch_current_time_command.cpp
commands_new/kis_change_projection_color_command.cpp
commands_new/kis_activate_selection_mask_command.cpp
processing/kis_do_nothing_processing_visitor.cpp
processing/kis_simple_processing_visitor.cpp
processing/kis_crop_processing_visitor.cpp
processing/kis_crop_selections_processing_visitor.cpp
processing/kis_transform_processing_visitor.cpp
processing/kis_mirror_processing_visitor.cpp
filter/kis_filter.cc
filter/kis_filter_category_ids.cpp
filter/kis_filter_configuration.cc
filter/kis_color_transformation_configuration.cc
filter/kis_filter_registry.cc
filter/kis_color_transformation_filter.cc
generator/kis_generator.cpp
generator/kis_generator_layer.cpp
generator/kis_generator_registry.cpp
floodfill/kis_fill_interval_map.cpp
floodfill/kis_scanline_fill.cpp
lazybrush/kis_min_cut_worker.cpp
lazybrush/kis_lazy_fill_tools.cpp
lazybrush/kis_multiway_cut.cpp
lazybrush/KisWatershedWorker.cpp
lazybrush/kis_colorize_mask.cpp
lazybrush/kis_colorize_stroke_strategy.cpp
KisDelayedUpdateNodeInterface.cpp
kis_adjustment_layer.cc
kis_selection_based_layer.cpp
kis_node_filter_interface.cpp
kis_base_accessor.cpp
kis_base_node.cpp
kis_base_processor.cpp
kis_bookmarked_configuration_manager.cc
kis_node_uuid_info.cpp
kis_clone_layer.cpp
kis_colorspace_convert_visitor.cpp
kis_config_widget.cpp
kis_convolution_kernel.cc
kis_convolution_painter.cc
kis_gaussian_kernel.cpp
kis_edge_detection_kernel.cpp
kis_cubic_curve.cpp
kis_default_bounds.cpp
kis_default_bounds_base.cpp
kis_effect_mask.cc
kis_fast_math.cpp
kis_fill_painter.cc
kis_filter_mask.cpp
kis_filter_strategy.cc
kis_transform_mask.cpp
kis_transform_mask_params_interface.cpp
kis_recalculate_transform_mask_job.cpp
kis_recalculate_generator_layer_job.cpp
kis_transform_mask_params_factory_registry.cpp
kis_safe_transform.cpp
kis_gradient_painter.cc
kis_gradient_shape_strategy.cpp
kis_cached_gradient_shape_strategy.cpp
kis_polygonal_gradient_shape_strategy.cpp
kis_iterator_ng.cpp
kis_async_merger.cpp
kis_merge_walker.cc
kis_updater_context.cpp
kis_update_job_item.cpp
kis_stroke_strategy_undo_command_based.cpp
kis_simple_stroke_strategy.cpp
KisRunnableBasedStrokeStrategy.cpp
KisRunnableStrokeJobData.cpp
KisRunnableStrokeJobsInterface.cpp
KisFakeRunnableStrokeJobsExecutor.cpp
kis_stroke_job_strategy.cpp
kis_stroke_strategy.cpp
kis_stroke.cpp
kis_strokes_queue.cpp
KisStrokesQueueMutatedJobInterface.cpp
kis_simple_update_queue.cpp
kis_update_scheduler.cpp
kis_queues_progress_updater.cpp
kis_composite_progress_proxy.cpp
kis_sync_lod_cache_stroke_strategy.cpp
kis_lod_capable_layer_offset.cpp
kis_update_time_monitor.cpp
KisImageConfigNotifier.cpp
kis_group_layer.cc
kis_count_visitor.cpp
kis_histogram.cc
kis_image_interfaces.cpp
kis_image_animation_interface.cpp
kis_time_range.cpp
kis_node_graph_listener.cpp
kis_image.cc
kis_image_signal_router.cpp
kis_image_config.cpp
kis_projection_updates_filter.cpp
kis_suspend_projection_updates_stroke_strategy.cpp
kis_regenerate_frame_stroke_strategy.cpp
kis_switch_time_stroke_strategy.cpp
kis_crop_saved_extra_data.cpp
kis_timed_signal_threshold.cpp
kis_layer.cc
kis_indirect_painting_support.cpp
kis_abstract_projection_plane.cpp
kis_layer_projection_plane.cpp
kis_layer_utils.cpp
kis_mask_projection_plane.cpp
kis_projection_leaf.cpp
kis_mask.cc
kis_base_mask_generator.cpp
kis_rect_mask_generator.cpp
kis_circle_mask_generator.cpp
kis_gauss_circle_mask_generator.cpp
kis_gauss_rect_mask_generator.cpp
${__per_arch_circle_mask_generator_objs}
kis_curve_circle_mask_generator.cpp
kis_curve_rect_mask_generator.cpp
kis_math_toolbox.cpp
kis_memory_statistics_server.cpp
kis_name_server.cpp
kis_node.cpp
kis_node_facade.cpp
kis_node_progress_proxy.cpp
kis_busy_progress_indicator.cpp
kis_node_visitor.cpp
kis_paint_device.cc
kis_paint_device_debug_utils.cpp
kis_fixed_paint_device.cpp
KisOptimizedByteArray.cpp
kis_paint_layer.cc
kis_perspective_math.cpp
kis_pixel_selection.cpp
kis_processing_information.cpp
kis_properties_configuration.cc
kis_random_accessor_ng.cpp
kis_random_generator.cc
kis_random_sub_accessor.cpp
kis_wrapped_random_accessor.cpp
kis_selection.cc
KisSelectionUpdateCompressor.cpp
kis_selection_mask.cpp
kis_update_outline_job.cpp
kis_update_selection_job.cpp
kis_serializable_configuration.cc
kis_transaction_data.cpp
kis_transform_worker.cc
kis_perspectivetransform_worker.cpp
bsplines/kis_bspline_1d.cpp
bsplines/kis_bspline_2d.cpp
bsplines/kis_nu_bspline_2d.cpp
kis_warptransform_worker.cc
kis_cage_transform_worker.cpp
kis_liquify_transform_worker.cpp
kis_green_coordinates_math.cpp
kis_transparency_mask.cc
kis_undo_adapter.cpp
kis_macro_based_undo_store.cpp
kis_surrogate_undo_adapter.cpp
kis_legacy_undo_adapter.cpp
kis_post_execution_undo_adapter.cpp
kis_processing_visitor.cpp
kis_processing_applicator.cpp
krita_utils.cpp
kis_outline_generator.cpp
kis_layer_composition.cpp
kis_selection_filters.cpp
KisProofingConfiguration.h
metadata/kis_meta_data_entry.cc
metadata/kis_meta_data_filter.cc
metadata/kis_meta_data_filter_p.cc
metadata/kis_meta_data_filter_registry.cc
metadata/kis_meta_data_filter_registry_model.cc
metadata/kis_meta_data_io_backend.cc
metadata/kis_meta_data_merge_strategy.cc
metadata/kis_meta_data_merge_strategy_p.cc
metadata/kis_meta_data_merge_strategy_registry.cc
metadata/kis_meta_data_parser.cc
metadata/kis_meta_data_schema.cc
metadata/kis_meta_data_schema_registry.cc
metadata/kis_meta_data_store.cc
metadata/kis_meta_data_type_info.cc
metadata/kis_meta_data_validator.cc
metadata/kis_meta_data_value.cc
kis_keyframe.cpp
kis_keyframe_channel.cpp
kis_keyframe_commands.cpp
kis_scalar_keyframe_channel.cpp
kis_raster_keyframe_channel.cpp
kis_onion_skin_compositor.cpp
kis_onion_skin_cache.cpp
kis_idle_watcher.cpp
kis_psd_layer_style.cpp
kis_layer_properties_icons.cpp
layerstyles/kis_multiple_projection.cpp
layerstyles/kis_layer_style_filter.cpp
layerstyles/kis_layer_style_filter_environment.cpp
layerstyles/kis_layer_style_filter_projection_plane.cpp
layerstyles/kis_layer_style_projection_plane.cpp
layerstyles/kis_ls_drop_shadow_filter.cpp
layerstyles/kis_ls_satin_filter.cpp
layerstyles/kis_ls_stroke_filter.cpp
layerstyles/kis_ls_bevel_emboss_filter.cpp
layerstyles/kis_ls_overlay_filter.cpp
layerstyles/kis_ls_utils.cpp
layerstyles/gimp_bump_map.cpp
KisProofingConfiguration.cpp
kis_node_query_path.cc
)
set(einspline_SRCS
3rdparty/einspline/bspline_create.cpp
3rdparty/einspline/bspline_data.cpp
3rdparty/einspline/multi_bspline_create.cpp
3rdparty/einspline/nubasis.cpp
3rdparty/einspline/nubspline_create.cpp
3rdparty/einspline/nugrid.cpp
)
add_library(kritaimage SHARED ${kritaimage_LIB_SRCS} ${einspline_SRCS})
generate_export_header(kritaimage BASE_NAME kritaimage)
target_link_libraries(kritaimage
PUBLIC
kritaversion
kritawidgets
kritaglobal kritapsd
kritaodf kritapigment
kritacommand
kritawidgetutils
Qt5::Concurrent
)
target_link_libraries(kritaimage PUBLIC ${Boost_SYSTEM_LIBRARY})
if(OPENEXR_FOUND)
target_link_libraries(kritaimage PUBLIC ${OPENEXR_LIBRARIES})
endif()
if(FFTW3_FOUND)
target_link_libraries(kritaimage PRIVATE ${FFTW3_LIBRARIES})
endif()
if(HAVE_VC)
target_link_libraries(kritaimage PUBLIC ${Vc_LIBRARIES})
endif()
if (NOT GSL_FOUND)
message (WARNING "KRITA WARNING! No GNU Scientific Library was found! Krita's Shaped Gradients might be non-normalized! Please install GSL library.")
else ()
target_link_libraries(kritaimage PRIVATE ${GSL_LIBRARIES} ${GSL_CBLAS_LIBRARIES})
endif ()
target_include_directories(kritaimage
PUBLIC
$
$
$
$
$
$
)
set_target_properties(kritaimage PROPERTIES
VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION}
)
install(TARGETS kritaimage ${INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/libs/image/KisLazyStorage.h b/libs/image/KisLazyStorage.h
index 347621da5f..3b3f91e941 100644
--- a/libs/image/KisLazyStorage.h
+++ b/libs/image/KisLazyStorage.h
@@ -1,70 +1,88 @@
+/*
+ * Copyright (c) 2018 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 KISLAZYSTORAGE_H
#define KISLAZYSTORAGE_H
#include
#include
#include
#include
#include
template
class KisLazyStorage
{
public:
template
explicit KisLazyStorage(P1 p1)
: m_builder([=]() { return new T(p1); }),
m_data(0)
{
}
template
explicit KisLazyStorage(P1 p1, P2 p2)
: m_builder([=]() { return new T(p1, p2); }),
m_data(0)
{
}
template
explicit KisLazyStorage(P1 p1, P2 p2, P2 p3)
: m_builder([=]() { return new T(p1, p2, p3); }),
m_data(0)
{
}
KisLazyStorage(const KisLazyStorage &rgh) = delete;
KisLazyStorage(KisLazyStorage &&rgh) = delete;
~KisLazyStorage() {
delete m_data.load();
}
T* operator->() {
return getPointer();
}
T& operator*() {
return *getPointer();
}
private:
T* getPointer() {
if(!m_data) {
QMutexLocker l(&m_mutex);
if(!m_data) {
m_data = m_builder();
}
}
return m_data;
}
private:
std::function m_builder;
std::atomic m_data;
QMutex m_mutex;
};
#endif // KISLAZYSTORAGE_H
diff --git a/libs/image/commands/kis_selection_commands.h b/libs/image/KisSelectionTags.h
similarity index 68%
copy from libs/image/commands/kis_selection_commands.h
copy to libs/image/KisSelectionTags.h
index 2cdc0fb095..12f0e67859 100644
--- a/libs/image/commands/kis_selection_commands.h
+++ b/libs/image/KisSelectionTags.h
@@ -1,27 +1,36 @@
/*
- * Copyright (c) 2008 Boudewijn Rempt
+ * Copyright (c) 2018 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 KIS_SELECTION_COMMANDS_H
-#define KIS_SELECTION_COMMANDS_H
+#ifndef KISSELECTIONTAGS_H
+#define KISSELECTIONTAGS_H
-#include "kis_deselect_global_selection_command.h"
-#include "kis_reselect_global_selection_command.h"
-#include "kis_set_global_selection_command.h"
-#endif
+enum SelectionMode {
+ PIXEL_SELECTION,
+ SHAPE_PROTECTION
+};
+enum SelectionAction {
+ SELECTION_REPLACE,
+ SELECTION_ADD,
+ SELECTION_SUBTRACT,
+ SELECTION_INTERSECT,
+ SELECTION_DEFAULT
+};
+
+#endif // KISSELECTIONTAGS_H
diff --git a/libs/image/commands/KisDeselectActiveSelectionCommand.cpp b/libs/image/commands/KisDeselectActiveSelectionCommand.cpp
new file mode 100644
index 0000000000..195a6edd7e
--- /dev/null
+++ b/libs/image/commands/KisDeselectActiveSelectionCommand.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2018 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 "KisDeselectActiveSelectionCommand.h"
+#include "kis_image.h"
+#include "kis_selection.h"
+#include "kis_selection_mask.h"
+
+KisDeselectActiveSelectionCommand::KisDeselectActiveSelectionCommand(KisSelectionSP activeSelection, KisImageWSP image, KUndo2Command *parent)
+ : KisDeselectGlobalSelectionCommand(image, parent),
+ m_activeSelection(activeSelection)
+{
+}
+
+KisDeselectActiveSelectionCommand::~KisDeselectActiveSelectionCommand()
+{
+}
+
+void KisDeselectActiveSelectionCommand::redo()
+{
+ KisImageSP image = m_image.toStrongRef();
+ KIS_SAFE_ASSERT_RECOVER_RETURN(image);
+
+ if (m_activeSelection && m_activeSelection == image->globalSelection()) {
+ KisDeselectGlobalSelectionCommand::redo();
+ } else if (m_activeSelection) {
+ KisNodeSP parentNode = m_activeSelection->parentNode();
+ if (!parentNode) return;
+
+ m_deselectedMask = dynamic_cast(parentNode.data());
+ if (m_deselectedMask) {
+ KIS_SAFE_ASSERT_RECOVER(m_deselectedMask->active()) {
+ m_deselectedMask.clear();
+ return;
+ }
+
+ m_deselectedMask->setActive(false);
+ }
+ }
+}
+
+void KisDeselectActiveSelectionCommand::undo()
+{
+ if (m_deselectedMask) {
+ m_deselectedMask->setActive(true);
+ m_deselectedMask.clear();
+ } else {
+ KisDeselectGlobalSelectionCommand::undo();
+ }
+}
diff --git a/libs/image/commands/kis_selection_commands.h b/libs/image/commands/KisDeselectActiveSelectionCommand.h
similarity index 56%
copy from libs/image/commands/kis_selection_commands.h
copy to libs/image/commands/KisDeselectActiveSelectionCommand.h
index 2cdc0fb095..c9742ccb72 100644
--- a/libs/image/commands/kis_selection_commands.h
+++ b/libs/image/commands/KisDeselectActiveSelectionCommand.h
@@ -1,27 +1,38 @@
/*
- * Copyright (c) 2008 Boudewijn Rempt
+ * Copyright (c) 2018 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 KIS_SELECTION_COMMANDS_H
-#define KIS_SELECTION_COMMANDS_H
+#ifndef KISDESELECTACTIVESELECTIONCOMMAND_H
+#define KISDESELECTACTIVESELECTIONCOMMAND_H
#include "kis_deselect_global_selection_command.h"
-#include "kis_reselect_global_selection_command.h"
-#include "kis_set_global_selection_command.h"
-#endif
+class KRITAIMAGE_EXPORT KisDeselectActiveSelectionCommand : public KisDeselectGlobalSelectionCommand
+{
+public:
+ KisDeselectActiveSelectionCommand(KisSelectionSP activeSelection, KisImageWSP image, KUndo2Command * parent = 0);
+ ~KisDeselectActiveSelectionCommand() override;
+ void redo() override;
+ void undo() override;
+
+private:
+ KisSelectionSP m_activeSelection;
+ KisSelectionMaskSP m_deselectedMask;
+};
+
+#endif // KISDESELECTACTIVESELECTIONCOMMAND_H
diff --git a/libs/image/commands/KisReselectActiveSelectionCommand.cpp b/libs/image/commands/KisReselectActiveSelectionCommand.cpp
new file mode 100644
index 0000000000..4af691f9c5
--- /dev/null
+++ b/libs/image/commands/KisReselectActiveSelectionCommand.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2018 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 "KisReselectActiveSelectionCommand.h"
+
+#include "kis_image.h"
+#include "kis_node.h"
+#include "kis_layer.h"
+#include "kis_selection_mask.h"
+#include
+
+
+KisReselectActiveSelectionCommand::KisReselectActiveSelectionCommand(KisNodeSP activeNode, KisImageWSP image, KUndo2Command *parent)
+ : KisReselectGlobalSelectionCommand(image, parent),
+ m_activeNode(activeNode)
+{
+}
+
+void KisReselectActiveSelectionCommand::redo()
+{
+ bool shouldReselectFGlobalSelection = true;
+
+ if (m_activeNode) {
+ KisSelectionMaskSP mask = dynamic_cast(m_activeNode.data());
+
+ if (!mask) {
+
+ KisLayerSP layer;
+ KisNodeSP node = m_activeNode;
+ while (node && !(layer = dynamic_cast(node.data()))) {
+ node = node->parent();
+ }
+
+ if (layer && !layer->selectionMask()) {
+ KoProperties properties;
+ properties.setProperty("active", false);
+ properties.setProperty("visible", true);
+ QList masks = layer->childNodes(QStringList("KisSelectionMask"), properties);
+
+ if (!masks.isEmpty()) {
+ mask = dynamic_cast(masks.first().data());
+ }
+ } else if (layer && layer->selectionMask()) {
+ shouldReselectFGlobalSelection = false;
+ }
+ }
+
+ if (mask) {
+ mask->setActive(true);
+ shouldReselectFGlobalSelection = false;
+ m_reselectedMask = mask;
+ }
+ }
+
+ if (shouldReselectFGlobalSelection) {
+ KisReselectGlobalSelectionCommand::redo();
+ }
+}
+
+void KisReselectActiveSelectionCommand::undo()
+{
+ if (m_reselectedMask) {
+ m_reselectedMask->setActive(false);
+ m_reselectedMask.clear();
+ } else {
+ KisReselectGlobalSelectionCommand::undo();
+ }
+}
diff --git a/libs/image/commands/kis_selection_commands.h b/libs/image/commands/KisReselectActiveSelectionCommand.h
similarity index 59%
copy from libs/image/commands/kis_selection_commands.h
copy to libs/image/commands/KisReselectActiveSelectionCommand.h
index 2cdc0fb095..2ffb565a41 100644
--- a/libs/image/commands/kis_selection_commands.h
+++ b/libs/image/commands/KisReselectActiveSelectionCommand.h
@@ -1,27 +1,38 @@
/*
- * Copyright (c) 2008 Boudewijn Rempt
+ * Copyright (c) 2018 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 KIS_SELECTION_COMMANDS_H
-#define KIS_SELECTION_COMMANDS_H
+#ifndef KISRESELECTACTIVESELECTIONCOMMAND_H
+#define KISRESELECTACTIVESELECTIONCOMMAND_H
-#include "kis_deselect_global_selection_command.h"
#include "kis_reselect_global_selection_command.h"
-#include "kis_set_global_selection_command.h"
-#endif
+class KRITAIMAGE_EXPORT KisReselectActiveSelectionCommand : public KisReselectGlobalSelectionCommand
+{
+public:
+ KisReselectActiveSelectionCommand(KisNodeSP activeNode, KisImageWSP image, KUndo2Command * parent = 0);
+
+ void redo() override;
+ void undo() override;
+
+private:
+ KisNodeSP m_activeNode;
+ KisSelectionMaskSP m_reselectedMask;
+};
+
+#endif // KISRESELECTACTIVESELECTIONCOMMAND_H
diff --git a/libs/image/commands/kis_deselect_global_selection_command.h b/libs/image/commands/kis_deselect_global_selection_command.h
index 39030beb8f..64982eaace 100644
--- a/libs/image/commands/kis_deselect_global_selection_command.h
+++ b/libs/image/commands/kis_deselect_global_selection_command.h
@@ -1,47 +1,47 @@
/*
* 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.
*/
#ifndef KIS_DESELECT_GLOBAL_SELECTION_COMMAND_H_
#define KIS_DESELECT_GLOBAL_SELECTION_COMMAND_H_
#include
#include
#include "kis_types.h"
/// The command for deselection the global selection of KisImage
class KRITAIMAGE_EXPORT KisDeselectGlobalSelectionCommand : public KUndo2Command
{
public:
/**
* Constructor
* @param image the image
* @param parent the parent command
*/
KisDeselectGlobalSelectionCommand(KisImageWSP image, KUndo2Command * parent = 0);
~KisDeselectGlobalSelectionCommand() override;
void redo() override;
void undo() override;
-private:
+protected:
KisImageWSP m_image;
KisSelectionSP m_oldSelection;
};
#endif
diff --git a/libs/image/commands/kis_reselect_global_selection_command.h b/libs/image/commands/kis_reselect_global_selection_command.h
index c27a7b848c..0e5c9dad02 100644
--- a/libs/image/commands/kis_reselect_global_selection_command.h
+++ b/libs/image/commands/kis_reselect_global_selection_command.h
@@ -1,47 +1,47 @@
/*
* 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.
*/
#ifndef KIS_RESELECT_GLOBAL_SELECTION_COMMAND_H
#define KIS_RESELECT_GLOBAL_SELECTION_COMMAND_H
#include
#include
#include "kis_types.h"
/// The command for deselection the global selection of KisImage
class KRITAIMAGE_EXPORT KisReselectGlobalSelectionCommand : public KUndo2Command
{
public:
/**
* Constructor
* @param image the image
* @param parent the parent command
*/
KisReselectGlobalSelectionCommand(KisImageWSP image, KUndo2Command * parent = 0);
~KisReselectGlobalSelectionCommand() override;
void redo() override;
void undo() override;
-private:
+protected:
KisImageWSP m_image;
bool m_canReselect;
};
#endif
diff --git a/libs/image/commands/kis_selection_commands.h b/libs/image/commands/kis_selection_commands.h
index 2cdc0fb095..78f3125284 100644
--- a/libs/image/commands/kis_selection_commands.h
+++ b/libs/image/commands/kis_selection_commands.h
@@ -1,27 +1,29 @@
/*
* Copyright (c) 2008 Boudewijn Rempt
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_SELECTION_COMMANDS_H
#define KIS_SELECTION_COMMANDS_H
#include "kis_deselect_global_selection_command.h"
+#include "KisDeselectActiveSelectionCommand.h"
#include "kis_reselect_global_selection_command.h"
+#include "KisReselectActiveSelectionCommand.h"
#include "kis_set_global_selection_command.h"
#endif
diff --git a/libs/image/kis_selection.h b/libs/image/kis_selection.h
index e72a7f028a..97ce6f2c25 100644
--- a/libs/image/kis_selection.h
+++ b/libs/image/kis_selection.h
@@ -1,228 +1,218 @@
/*
* Copyright (c) 2004 Boudewijn Rempt
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_SELECTION_H_
#define KIS_SELECTION_H_
#include
#include "kis_types.h"
#include "kritaimage_export.h"
#include "kis_default_bounds.h"
#include "kis_image.h"
-enum SelectionMode {
- PIXEL_SELECTION,
- SHAPE_PROTECTION
-};
-
-enum SelectionAction {
- SELECTION_REPLACE,
- SELECTION_ADD,
- SELECTION_SUBTRACT,
- SELECTION_INTERSECT,
- SELECTION_DEFAULT
-};
+#include "KisSelectionTags.h"
#include "kis_pixel_selection.h"
class KisSelectionComponent;
class QPainterPath;
/**
* KisSelection is a composite object. It may contain an instance
* of KisPixelSelection and a KisShapeSelection object. Both these
* selections are merged into a projection of the KisSelection.
*
* Every pixel in the paint device can indicate a degree of selectedness, varying
* between MIN_SELECTED and MAX_SELECTED.
*
* The projection() paint device itself is only a projection: you can
* read from it, but not write to it. You need to keep track of
* the need for updating the projection yourself: there is no
* automatic updating after changing the contents of one or more
* of the selection components.
*/
class KRITAIMAGE_EXPORT KisSelection : public KisShared
{
public:
/**
* Create a new KisSelection.
*
* @param defaultBounds defines the bounds of the selection when
* Select All is initiated.
*/
KisSelection(KisDefaultBoundsBaseSP defaultBounds = KisDefaultBoundsBaseSP());
/**
* Copy the selection. The selection components are copied, too.
*/
KisSelection(const KisSelection& rhs);
KisSelection& operator=(const KisSelection &rhs);
/**
* Delete the selection. The shape selection component is deleted, the
* pixel selection component is contained in a shared pointer, so that
* may still be valid.
*/
virtual ~KisSelection();
/**
* The paint device of the pixel selection should report
* about it's setDirty events to its parent. The creator
* should set the parent manually if it wants to get the
* signals
*/
void setParentNode(KisNodeWSP node);
bool hasPixelSelection() const;
bool hasShapeSelection() const;
bool outlineCacheValid() const;
QPainterPath outlineCache() const;
void recalculateOutlineCache();
/**
* Tells whether the cached thumbnail of the selection is still valid
*/
bool thumbnailImageValid() const;
/**
* Recalculates the thumbnail of the selection
*/
void recalculateThumbnailImage(const QColor &maskColor);
/**
* Returns the thumbnail of the selection.
*/
QImage thumbnailImage() const;
/**
* Returns the transformation which should be applied to the thumbnail before
* being painted over the image
*/
QTransform thumbnailImageTransform() const;
/**
* return the pixel selection component of this selection. Pixel
* selection component is always present in the selection. In case
* the user wants a vector selection, pixel selection will store
* the pixelated version of it.
*
* NOTE: use pixelSelection() for changing the selection only. For
* reading the selection and passing the data to bitBlt function use
* projection(). Although projection() and pixelSelection() currently
* point ot the same paint device, this behavior may change in the
* future.
*/
KisPixelSelectionSP pixelSelection() const;
/**
* return the vector selection component of this selection or zero
* if hasShapeSelection() returns false.
*/
KisSelectionComponent* shapeSelection() const;
void setShapeSelection(KisSelectionComponent* shapeSelection);
/**
* Returns the projection of the selection. It may be the same
* as pixel selection. You must read selection data from this
* paint device only
*/
KisPixelSelectionSP projection() const;
/**
* Updates the projection of the selection. You should call this
* method after the every change of the selection components.
* There is no automatic updates framework present
*/
void updateProjection(const QRect& rect);
void updateProjection();
void setVisible(bool visible);
bool isVisible();
/**
* Convenience functions. Just call the corresponding methods
* of the underlying projection
*/
bool isTotallyUnselected(const QRect & r) const;
QRect selectedRect() const;
/**
* @brief Slow, but exact way of determining the rectangle
* that encloses the selection.
*
* Default pixel of the selection device may vary and you would get wrong bounds.
* selectedExactRect() handles all these cases.
*
*/
QRect selectedExactRect() const;
void setX(qint32 x);
void setY(qint32 y);
qint32 x() const;
qint32 y() const;
void setDefaultBounds(KisDefaultBoundsBaseSP bounds);
void clear();
/**
* @brief flatten creates a new pixel selection component from the shape selection
* and throws away the shape selection. This has no effect if there is no
* shape selection.
*/
KUndo2Command* flatten();
void notifySelectionChanged();
/**
* Request rerendering of the shape selection component in a
* compressed way. Usually, you don't need to call it manually,
* because all the work is done by KisShapeSelectionModel.
*/
void requestCompressedProjectionUpdate(const QRect &rc);
/// XXX: This method was marked KDE_DEPRECATED but without information on what to
/// replace it with. Undeprecate, therefore.
quint8 selected(qint32 x, qint32 y) const;
private:
friend class KisSelectionTest;
friend class KisMaskTest;
friend class KisAdjustmentLayerTest;
friend class KisUpdateSelectionJob;
friend class KisSelectionUpdateCompressor;
+ friend class KisDeselectActiveSelectionCommand;
KisNodeWSP parentNode() const;
void copyFrom(const KisSelection &rhs);
private:
struct Private;
Private * const m_d;
};
#endif // KIS_SELECTION_H_
diff --git a/libs/image/kis_updater_context.h b/libs/image/kis_updater_context.h
index e6cb8705b6..2e8f69dd0f 100644
--- a/libs/image/kis_updater_context.h
+++ b/libs/image/kis_updater_context.h
@@ -1,212 +1,210 @@
/*
* Copyright (c) 2010 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 __KIS_UPDATER_CONTEXT_H
#define __KIS_UPDATER_CONTEXT_H
#include
#include
#include
#include
#include "kis_base_rects_walker.h"
#include "kis_async_merger.h"
#include "kis_lock_free_lod_counter.h"
#include "KisUpdaterContextSnapshotEx.h"
#include "kis_update_scheduler.h"
class KisUpdateJobItem;
class KisSpontaneousJob;
class KisStrokeJob;
class KRITAIMAGE_EXPORT KisUpdaterContext : public QObject
{
Q_OBJECT
public:
static const int useIdealThreadCountTag;
public:
KisUpdaterContext(qint32 threadCount = useIdealThreadCountTag, QObject *parent = 0);
~KisUpdaterContext() override;
/**
* Returns the number of currently running jobs of each type.
* To use this information you should lock the context beforehand.
*
* \see lock()
*/
void getJobsSnapshot(qint32 &numMergeJobs, qint32 &numStrokeJobs);
KisUpdaterContextSnapshotEx getContextSnapshotEx() const;
/**
* Returns the current level of detail of all the running jobs in the
* context. If there are no jobs, returns -1.
*/
int currentLevelOfDetail() const;
/**
* Check whether there is a spare thread for running
* one more job
*/
bool hasSpareThread();
/**
* Checks whether the walker intersects with any
* of currently executing walkers. If it does,
* it is not allowed to go in. It should be called
* with the lock held.
*
* \see lock()
*/
bool isJobAllowed(KisBaseRectsWalkerSP walker);
/**
* Registers the job and starts executing it.
* The caller must ensure that the context is locked
* with lock(), job is allowed with isWalkerAllowed() and
* there is a spare thread for running it with hasSpareThread()
*
* \see lock()
* \see isWalkerAllowed()
* \see hasSpareThread()
*/
virtual void addMergeJob(KisBaseRectsWalkerSP walker);
/**
* Adds a stroke job to the context. The prerequisites are
* the same as for addMergeJob()
* \see addMergeJob()
*/
virtual void addStrokeJob(KisStrokeJob *strokeJob);
/**
* Adds a spontaneous job to the context. The prerequisites are
* the same as for addMergeJob()
* \see addMergeJob()
*/
virtual void addSpontaneousJob(KisSpontaneousJob *spontaneousJob);
/**
* Block execution of the caller until all the jobs are finished
*/
void waitForDone();
/**
* Locks the context to guarantee an exclusive access
* to the context
*/
void lock();
/**
* Unlocks the context
*
* \see lock()
*/
void unlock();
/**
* Set the number of threads available for this updater context
* WARNING: one cannot change the number of threads if there is
* at least one job running in the context! So before
* calling this method make sure you do two things:
* 1) barrierLock() the update scheduler
* 2) lock() the context
*/
void setThreadsLimit(int value);
/**
* Return the number of available threads in the context. Make sure you
* lock the context before calling this function!
*/
int threadsLimit() const;
void continueUpdate(const QRect& rc);
void doSomeUsefulWork();
void jobFinished();
- friend class KisUpdateJobItem;
-
protected:
static bool walkerIntersectsJob(KisBaseRectsWalkerSP walker,
const KisUpdateJobItem* job);
qint32 findSpareThread();
protected:
/**
* The lock is shared by all the child update job items.
* When an item wants to run a usual (non-exclusive) job,
* it locks the lock for read access. When an exclusive
* access is requested, it locks it for write
*/
QReadWriteLock m_exclusiveJobLock;
QMutex m_lock;
QVector m_jobs;
QThreadPool m_threadPool;
KisLockFreeLodCounter m_lodCounter;
KisUpdateScheduler *m_scheduler;
private:
friend class KisUpdaterContextTest;
friend class KisUpdateSchedulerTest;
friend class KisStrokesQueueTest;
friend class KisSimpleUpdateQueueTest;
friend class KisUpdateJobItem;
void addMergeJobTest(KisBaseRectsWalkerSP walker);
void addStrokeJobTest(KisStrokeJob *strokeJob);
void addSpontaneousJobTest(KisSpontaneousJob *spontaneousJob);
const QVector getJobs();
void clear();
};
class KRITAIMAGE_EXPORT KisTestableUpdaterContext : public KisUpdaterContext
{
public:
/**
* Creates an explicit number of threads
*/
KisTestableUpdaterContext(qint32 threadCount);
~KisTestableUpdaterContext() override;
/**
* The only difference - it doesn't start execution
* of the job
*/
void addMergeJob(KisBaseRectsWalkerSP walker) override;
void addStrokeJob(KisStrokeJob *strokeJob) override;
void addSpontaneousJob(KisSpontaneousJob *spontaneousJob) override;
const QVector getJobs();
void clear();
friend class KisUpdateJobItem;
};
#endif /* __KIS_UPDATER_CONTEXT_H */
diff --git a/libs/image/tests/scheduler_utils.h b/libs/image/tests/scheduler_utils.h
index 6b5d683969..3c28f6e0de 100644
--- a/libs/image/tests/scheduler_utils.h
+++ b/libs/image/tests/scheduler_utils.h
@@ -1,281 +1,281 @@
/*
* Copyright (c) 2011 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 __SCHEDULER_UTILS_H
#define __SCHEDULER_UTILS_H
#include
#include "kis_merge_walker.h"
#include "kis_stroke_strategy.h"
#include "kis_stroke_job.h"
#include "kis_spontaneous_job.h"
#include "kis_stroke.h"
#include "kis_image.h"
#define SCOMPARE(s1, s2) QCOMPARE(QString(s1), QString(s2))
#define COMPARE_WALKER(item, walker) \
QCOMPARE(item->walker(), walker)
#define COMPARE_NAME(item, name) \
QCOMPARE(getJobName(item->strokeJob()), QString(name))
#define VERIFY_EMPTY(item) \
QVERIFY(!item->isRunning())
void executeStrokeJobs(KisStroke *stroke) {
KisStrokeJob *job;
while((job = stroke->popOneJob())) {
job->run();
delete job;
}
}
bool checkWalker(KisBaseRectsWalkerSP walker, const QRect &rect, int lod = 0) {
if(walker->requestedRect() == rect && walker->levelOfDetail() == lod) {
return true;
}
else {
dbgKrita << "walker rect:" << walker->requestedRect();
dbgKrita << "expected rect:" << rect;
dbgKrita << "walker lod:" << walker->levelOfDetail();
dbgKrita << "expected lod:" << lod;
return false;
}
}
class KisNoopSpontaneousJob : public KisSpontaneousJob
{
public:
KisNoopSpontaneousJob(bool overridesEverything = false, int lod = 0)
: m_overridesEverything(overridesEverything),
m_lod(lod)
{
}
void run() override {
}
bool overrides(const KisSpontaneousJob *otherJob) override {
Q_UNUSED(otherJob);
return m_overridesEverything;
}
int levelOfDetail() const override {
return m_lod;
}
private:
bool m_overridesEverything;
int m_lod;
};
static QStringList globalExecutedDabs;
class KisNoopDabStrategy : public KisStrokeJobStrategy
{
public:
KisNoopDabStrategy(QString name)
: m_name(name),
m_isMarked(false)
{}
void run(KisStrokeJobData *data) override {
Q_UNUSED(data);
globalExecutedDabs << m_name;
}
virtual QString name(KisStrokeJobData *data) const {
Q_UNUSED(data);
return m_name;
}
void setMarked() {
m_isMarked = true;
}
bool isMarked() const {
return m_isMarked;
}
private:
QString m_name;
bool m_isMarked;
};
class KisTestingStrokeJobData : public KisStrokeJobData
{
public:
KisTestingStrokeJobData(Sequentiality sequentiality = SEQUENTIAL,
Exclusivity exclusivity = NORMAL,
bool addMutatedJobs = false,
const QString &customSuffix = QString())
: KisStrokeJobData(sequentiality, exclusivity),
m_addMutatedJobs(addMutatedJobs),
m_customSuffix(customSuffix)
{
}
KisTestingStrokeJobData(const KisTestingStrokeJobData &rhs)
: KisStrokeJobData(rhs),
m_addMutatedJobs(rhs.m_addMutatedJobs)
{
}
KisStrokeJobData* createLodClone(int levelOfDetail) override {
Q_UNUSED(levelOfDetail);
return new KisTestingStrokeJobData(*this);
}
bool m_addMutatedJobs = false;
bool m_isMutated = false;
QString m_customSuffix;
};
class KisMutatableDabStrategy : public KisNoopDabStrategy
{
public:
KisMutatableDabStrategy(const QString &name, KisStrokeStrategy *parentStrokeStrategy)
: KisNoopDabStrategy(name),
m_parentStrokeStrategy(parentStrokeStrategy)
{
}
void run(KisStrokeJobData *data) override {
KisTestingStrokeJobData *td = dynamic_cast(data);
if (td && td->m_isMutated) {
globalExecutedDabs << QString("%1_mutated").arg(name(data));
} else if (td && td->m_addMutatedJobs) {
globalExecutedDabs << name(data);
for (int i = 0; i < 3; i++) {
KisTestingStrokeJobData *newData =
new KisTestingStrokeJobData(td->sequentiality(), td->exclusivity(), false);
newData->m_isMutated = true;
m_parentStrokeStrategy->addMutatedJob(newData);
}
} else {
globalExecutedDabs << name(data);
}
}
- virtual QString name(KisStrokeJobData *data) const {
+ virtual QString name(KisStrokeJobData *data) const override {
const QString baseName = KisNoopDabStrategy::name(data);
KisTestingStrokeJobData *td = dynamic_cast(data);
return !td || td->m_customSuffix.isEmpty() ? baseName : QString("%1_%2").arg(baseName).arg(td->m_customSuffix);
}
private:
KisStrokeStrategy *m_parentStrokeStrategy = 0;
};
class KisTestingStrokeStrategy : public KisStrokeStrategy
{
public:
KisTestingStrokeStrategy(const QString &prefix = QString(),
bool exclusive = false,
bool inhibitServiceJobs = false,
bool forceAllowInitJob = false,
bool forceAllowCancelJob = false)
: KisStrokeStrategy(prefix, kundo2_noi18n(prefix)),
m_prefix(prefix),
m_inhibitServiceJobs(inhibitServiceJobs),
m_forceAllowInitJob(forceAllowInitJob),
m_forceAllowCancelJob(forceAllowCancelJob),
m_cancelSeqNo(0)
{
setExclusive(exclusive);
}
KisTestingStrokeStrategy(const KisTestingStrokeStrategy &rhs, int levelOfDetail)
: KisStrokeStrategy(rhs),
m_prefix(rhs.m_prefix),
m_inhibitServiceJobs(rhs.m_inhibitServiceJobs),
m_forceAllowInitJob(rhs.m_forceAllowInitJob),
m_cancelSeqNo(rhs.m_cancelSeqNo)
{
m_prefix = QString("clone%1_%2").arg(levelOfDetail).arg(m_prefix);
}
KisStrokeJobStrategy* createInitStrategy() override {
return m_forceAllowInitJob || !m_inhibitServiceJobs ?
new KisNoopDabStrategy(m_prefix + "init") : 0;
}
KisStrokeJobStrategy* createFinishStrategy() override {
return !m_inhibitServiceJobs ?
new KisNoopDabStrategy(m_prefix + "finish") : 0;
}
KisStrokeJobStrategy* createCancelStrategy() override {
return m_forceAllowCancelJob || !m_inhibitServiceJobs ?
new KisNoopDabStrategy(m_prefix + "cancel") : 0;
}
KisStrokeJobStrategy* createDabStrategy() override {
return new KisMutatableDabStrategy(m_prefix + "dab", this);
}
KisStrokeStrategy* createLodClone(int levelOfDetail) override {
return new KisTestingStrokeStrategy(*this, levelOfDetail);
}
class CancelData : public KisStrokeJobData
{
public:
CancelData(int seqNo) : m_seqNo(seqNo) {}
int seqNo() const { return m_seqNo; }
private:
int m_seqNo;
};
KisStrokeJobData* createCancelData() override {
return new CancelData(m_cancelSeqNo++);
}
private:
QString m_prefix;
bool m_inhibitServiceJobs;
int m_forceAllowInitJob;
bool m_forceAllowCancelJob;
int m_cancelSeqNo;
};
inline QString getJobName(KisStrokeJob *job) {
KisNoopDabStrategy *pointer =
dynamic_cast(job->testingGetDabStrategy());
KIS_ASSERT(pointer);
return pointer->name(job->testingGetDabData());
}
inline int cancelSeqNo(KisStrokeJob *job) {
KisTestingStrokeStrategy::CancelData *pointer =
dynamic_cast
(job->testingGetDabData());
Q_ASSERT(pointer);
return pointer->seqNo();
}
#endif /* __SCHEDULER_UTILS_H */
diff --git a/libs/libqml/plugins/kritasketchplugin/models/LayerModel.cpp b/libs/libqml/plugins/kritasketchplugin/models/LayerModel.cpp
index 2c13f720b1..6ce5a76e8b 100644
--- a/libs/libqml/plugins/kritasketchplugin/models/LayerModel.cpp
+++ b/libs/libqml/plugins/kritasketchplugin/models/LayerModel.cpp
@@ -1,973 +1,983 @@
/* This file is part of the KDE project
* Copyright (C) 2012 Dan Leinir Turthra Jensen
*
* 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 "LayerModel.h"
#include "LayerThumbProvider.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 "KisSelectionActionsAdapter.h"
struct LayerModelMetaInfo {
LayerModelMetaInfo()
: canMoveUp(false)
, canMoveRight(false)
, canMoveDown(false)
, canMoveLeft(false)
, depth(-1)
{}
bool canMoveUp;
bool canMoveRight;
bool canMoveDown;
bool canMoveLeft;
int depth;
};
class LayerModel::Private {
public:
Private(LayerModel* qq)
: q(qq)
, nodeModel(new KisNodeModel(qq))
, aboutToRemoveRoots(false)
, canvas(0)
, nodeManager(0)
, image(0)
, activeNode(0)
, declarativeEngine(0)
, thumbProvider(0)
, updateActiveLayerWithNewFilterConfigTimer(new QTimer(qq))
, imageChangedTimer(new QTimer(qq))
{
QList tmpFilters = KisFilterRegistry::instance()->values();
Q_FOREACH (const KisFilterSP& filter, tmpFilters)
{
filters[filter.data()->id()] = filter.data();
}
updateActiveLayerWithNewFilterConfigTimer->setInterval(0);
updateActiveLayerWithNewFilterConfigTimer->setSingleShot(true);
connect(updateActiveLayerWithNewFilterConfigTimer, SIGNAL(timeout()), qq, SLOT(updateActiveLayerWithNewFilterConfig()));
imageChangedTimer->setInterval(250);
imageChangedTimer->setSingleShot(true);
connect(imageChangedTimer, SIGNAL(timeout()), qq, SLOT(imageHasChanged()));
}
LayerModel* q;
QList layers;
QHash layerMeta;
KisNodeModel* nodeModel;
bool aboutToRemoveRoots;
KisViewManager* view;
KisCanvas2* canvas;
+ QScopedPointer selectionActionsAdapter;
QPointer nodeManager;
KisImageWSP image;
KisNodeSP activeNode;
QQmlEngine* declarativeEngine;
LayerThumbProvider* thumbProvider;
QHash filters;
KisFilterConfigurationSP newConfig;
QTimer* updateActiveLayerWithNewFilterConfigTimer;
QTimer* imageChangedTimer;
static int counter()
{
static int count = 0;
return count++;
}
static QStringList layerClassNames()
{
QStringList list;
list << "KisGroupLayer";
list << "KisPaintLayer";
list << "KisFilterMask";
list << "KisAdjustmentLayer";
return list;
}
int deepChildCount(KisNodeSP layer)
{
quint32 childCount = layer->childCount();
QList children = layer->childNodes(layerClassNames(), KoProperties());
for(quint32 i = 0; i < childCount; ++i)
childCount += deepChildCount(children.at(i));
return childCount;
}
void rebuildLayerList(KisNodeSP layer = 0)
{
bool refreshingFromRoot = false;
if (!image)
{
layers.clear();
return;
}
if (layer == 0)
{
refreshingFromRoot = true;
layers.clear();
layer = image->rootLayer();
}
// implementation node: The root node is not a visible node, and so
// is never added to the list of layers
QList children = layer->childNodes(layerClassNames(), KoProperties());
if (children.count() == 0)
return;
for(quint32 i = children.count(); i > 0; --i)
{
layers << children.at(i-1);
rebuildLayerList(children.at(i-1));
}
if (refreshingFromRoot)
refreshLayerMovementAbilities();
}
void refreshLayerMovementAbilities()
{
layerMeta.clear();
if (layers.count() == 0)
return;
for(int i = 0; i < layers.count(); ++i)
{
const KisNodeSP layer = layers.at(i);
LayerModelMetaInfo ability;
if (i > 0)
ability.canMoveUp = true;
if (i < layers.count() - 1)
ability.canMoveDown = true;
KisNodeSP parent = layer;
while(parent)
{
++ability.depth;
parent = parent->parent();
}
if (ability.depth > 1)
ability.canMoveLeft = true;
if (i < layers.count() - 1 && qobject_cast(layers.at(i + 1).constData()))
ability.canMoveRight = true;
layerMeta[layer] = ability;
}
}
};
LayerModel::LayerModel(QObject* parent)
: QAbstractListModel(parent)
, d(new Private(this))
{
connect(d->nodeModel, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)),
this, SLOT(source_rowsAboutToBeInserted(QModelIndex, int, int)));
connect(d->nodeModel, SIGNAL(rowsInserted(QModelIndex, int, int)),
this, SLOT(source_rowsInserted(QModelIndex, int, int)));
connect(d->nodeModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)),
this, SLOT(source_rowsAboutToBeRemoved(QModelIndex, int, int)));
connect(d->nodeModel, SIGNAL(rowsRemoved(QModelIndex, int, int)),
this, SLOT(source_rowsRemoved(QModelIndex, int, int)));
connect(d->nodeModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
this, SLOT(source_dataChanged(QModelIndex,QModelIndex)));
connect(d->nodeModel, SIGNAL(modelReset()),
this, SLOT(source_modelReset()));
connect(d->nodeModel, SIGNAL(layoutAboutToBeChanged()), this, SIGNAL(layoutAboutToBeChanged()));
connect(d->nodeModel, SIGNAL(layoutChanged()), this, SIGNAL(layoutChanged()));
}
LayerModel::~LayerModel()
{
delete d;
}
QHash LayerModel::roleNames() const
{
QHash roles;
roles[IconRole] = "icon";
roles[NameRole] = "name";
roles[ActiveLayerRole] = "activeLayer";
roles[OpacityRole] = "opacity";
roles[PercentOpacityRole] = "percentOpacity";
roles[VisibleRole] = "visible";
roles[CompositeDetailsRole] = "compositeDetails";
roles[FilterRole] = "filter";
roles[ChildCountRole] = "childCount";
roles[DeepChildCountRole] = "deepChildCount";
roles[DepthRole] = "depth";
roles[PreviousItemDepthRole] = "previousItemDepth";
roles[NextItemDepthRole] = "nextItemDepth";
roles[CanMoveDownRole] = "canMoveDown";
roles[CanMoveLeftRole] = "canMoveLeft";
roles[CanMoveRightRole] = "canMoveRight";
roles[CanMoveUpRole] = "canMoveUp";
return roles;
}
QObject* LayerModel::view() const
{
return d->view;
}
void LayerModel::setView(QObject *newView)
{
KisViewManager* view = qobject_cast(newView);
// This has to happen very early, and we will be filling it back up again soon anyway...
if (d->canvas) {
d->canvas->disconnectCanvasObserver(this);
disconnect(d->image, 0, this, 0);
disconnect(d->nodeManager, 0, this, 0);
disconnect(d->nodeModel, 0, d->nodeManager, 0);
disconnect(d->nodeModel, SIGNAL(nodeActivated(KisNodeSP)), this, SLOT(currentNodeChanged(KisNodeSP)));
d->image = 0;
d->nodeManager = 0;
d->layers.clear();
d->activeNode.clear();
d->canvas = 0;
- d->nodeModel->setDummiesFacade(0, 0, 0, 0, 0);
+ d->nodeModel->setDummiesFacade(0, 0, 0, 0, 0, 0);
+ d->selectionActionsAdapter.reset();
}
d->view = view;
if (!d->view) {
return;
}
d->canvas = view->canvasBase();
d->thumbProvider = new LayerThumbProvider();
d->thumbProvider->setLayerModel(this);
d->thumbProvider->setLayerID(Private::counter());
// QT5TODO: results in a crash
d->declarativeEngine->addImageProvider(QString("layerthumb%1").arg(d->thumbProvider->layerID()), d->thumbProvider);
if (d->canvas) {
d->image = d->canvas->imageView()->image();
d->nodeManager = d->canvas->viewManager()->nodeManager();
KisDummiesFacadeBase *kritaDummiesFacade = dynamic_cast(d->canvas->imageView()->document()->shapeController());
KisShapeController *shapeController = dynamic_cast(d->canvas->imageView()->document()->shapeController());
- d->nodeModel->setDummiesFacade(kritaDummiesFacade, d->image, shapeController, d->nodeManager->nodeSelectionAdapter(), d->nodeManager->nodeInsertionAdapter());
+
+ d->selectionActionsAdapter.reset(new KisSelectionActionsAdapter(d->canvas->viewManager()->selectionManager()));
+ d->nodeModel->setDummiesFacade(kritaDummiesFacade,
+ d->image,
+ shapeController,
+ d->nodeManager->nodeSelectionAdapter(),
+ d->nodeManager->nodeInsertionAdapter(),
+ d->selectionActionsAdapter.data());
connect(d->image, SIGNAL(sigAboutToBeDeleted()), SLOT(notifyImageDeleted()));
connect(d->image, SIGNAL(sigNodeChanged(KisNodeSP)), SLOT(nodeChanged(KisNodeSP)));
connect(d->image, SIGNAL(sigImageUpdated(QRect)), SLOT(imageChanged()));
connect(d->image, SIGNAL(sigRemoveNodeAsync(KisNodeSP)), SLOT(aboutToRemoveNode(KisNodeSP)));
// cold start
currentNodeChanged(d->nodeManager->activeNode());
// Connection KisNodeManager -> KisLayerBox
connect(d->nodeManager, SIGNAL(sigUiNeedChangeActiveNode(KisNodeSP)), this, SLOT(currentNodeChanged(KisNodeSP)));
d->rebuildLayerList();
beginResetModel();
endResetModel();
}
}
QObject* LayerModel::engine() const
{
return d->declarativeEngine;
}
void LayerModel::setEngine(QObject* newEngine)
{
d->declarativeEngine = qobject_cast(newEngine);
emit engineChanged();
}
QString LayerModel::fullImageThumbUrl() const
{
return QString("image://layerthumb%1/fullimage/%2").arg(d->thumbProvider->layerID()).arg(QDateTime::currentMSecsSinceEpoch());
}
void LayerModel::currentNodeChanged(KisNodeSP newActiveNode)
{
if (!d->activeNode.isNull()) {
QModelIndex oldIndex = d->nodeModel->indexFromNode(d->activeNode);
source_dataChanged(oldIndex, oldIndex);
}
d->activeNode = newActiveNode;
emitActiveChanges();
if (!d->activeNode.isNull()) {
QModelIndex oldIndex = d->nodeModel->indexFromNode(d->activeNode);
source_dataChanged(oldIndex, oldIndex);
}
}
QVariant LayerModel::data(const QModelIndex& index, int role) const
{
QVariant data;
if (index.isValid()) {
index.internalPointer();
KisNodeSP node = d->layers.at(index.row());
if (node.isNull())
return data;
KisNodeSP parent;
switch(role)
{
case IconRole:
if (dynamic_cast(node.constData()))
data = QLatin1String("../images/svg/icon-layer_group-black.svg");
else if (dynamic_cast(node.constData()))
data = QLatin1String("../images/svg/icon-layer_filter-black.svg");
else if (dynamic_cast(node.constData()))
data = QLatin1String("../images/svg/icon-layer_filter-black.svg");
else
// We add the currentMSecsSinceEpoch to ensure we force an update (even with cache turned
// off, we still apparently get some caching behaviour on delegates in QML)
data = QString("image://layerthumb%1/%2/%3").arg(d->thumbProvider->layerID()).arg(index.row()).arg(QDateTime::currentMSecsSinceEpoch());
break;
case NameRole:
data = node->name();
break;
case ActiveLayerRole:
data = (node == d->activeNode);
break;
case OpacityRole:
data = node->opacity();
break;
case PercentOpacityRole:
data = node->percentOpacity();
break;
case VisibleRole:
data = node->visible();
break;
case CompositeDetailsRole:
// composite op goes here...
if (node->compositeOp())
data = node->compositeOp()->description();
break;
case FilterRole:
break;
case ChildCountRole:
data = node->childNodes(d->layerClassNames(), KoProperties()).count();
break;
case DeepChildCountRole:
data = d->deepChildCount(d->layers.at(index.row()));
break;
case DepthRole:
data = d->layerMeta[node.data()].depth;
break;
case PreviousItemDepthRole:
if (index.row() == 0)
data = -1;
else
data = d->layerMeta[d->layers[index.row() - 1].data()].depth;
break;
case NextItemDepthRole:
if (index.row() == d->layers.count() - 1)
data = -1;
else
data = d->layerMeta[d->layers[index.row() + 1].data()].depth;
break;
case CanMoveDownRole:
data = (node == d->activeNode) && d->layerMeta[node.data()].canMoveDown;
break;
case CanMoveLeftRole:
data = (node == d->activeNode) && d->layerMeta[node.data()].canMoveLeft;
break;
case CanMoveRightRole:
data = (node == d->activeNode) && d->layerMeta[node.data()].canMoveRight;
break;
case CanMoveUpRole:
data = (node == d->activeNode) && d->layerMeta[node.data()].canMoveUp;
break;
default:
break;
}
}
return data;
}
int LayerModel::rowCount(const QModelIndex& parent) const
{
if ( parent.isValid() ) {
return 0;
}
return d->layers.count();
}
QVariant LayerModel::headerData(int section, Qt::Orientation orientation, int role) const
{
return QAbstractItemModel::headerData(section, orientation, role);
}
void LayerModel::setActive(int index)
{
if (index > -1 && index < d->layers.count()) {
KisNodeSP newNode = d->layers.at(index);
d->nodeManager->slotUiActivatedNode(newNode);
currentNodeChanged(newNode);
}
}
void LayerModel::moveUp()
{
KisNodeSP node = d->nodeManager->activeNode();
KisNodeSP parent = node->parent();
KisNodeSP grandParent = parent->parent();
if (!d->nodeManager->activeNode()->nextSibling()) {
//dbgKrita << "Active node apparently has no next sibling, however that has happened...";
if (!grandParent)
return;
//dbgKrita << "Node has grandparent";
if (!grandParent->parent() && node->inherits("KisMask"))
return;
//dbgKrita << "Node isn't a mask";
d->nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent) + 1);
}
else {
//dbgKrita << "Move node directly";
d->nodeManager->lowerNode();
}
}
void LayerModel::moveDown()
{
KisNodeSP node = d->nodeManager->activeNode();
KisNodeSP parent = node->parent();
KisNodeSP grandParent = parent->parent();
if (!d->nodeManager->activeNode()->prevSibling()) {
//dbgKrita << "Active node apparently has no previous sibling, however that has happened...";
if (!grandParent)
return;
//dbgKrita << "Node has grandparent";
if (!grandParent->parent() && node->inherits("KisMask"))
return;
//dbgKrita << "Node isn't a mask";
d->nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent));
} else
{
//dbgKrita << "Move node directly";
d->nodeManager->raiseNode();
}
}
void LayerModel::moveLeft()
{
KisNodeSP node = d->nodeManager->activeNode();
KisNodeSP parent = node->parent();
KisNodeSP grandParent = parent->parent();
quint16 nodeIndex = parent->index(node);
if (!grandParent)
return;
if (!grandParent->parent() && node->inherits("KisMask"))
return;
if (nodeIndex <= parent->childCount() / 2) {
d->nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent));
}
else {
d->nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent) + 1);
}
}
void LayerModel::moveRight()
{
KisNodeSP node = d->nodeManager->activeNode();
KisNodeSP parent = d->nodeManager->activeNode()->parent();
KisNodeSP newParent;
int nodeIndex = parent->index(node);
int indexAbove = nodeIndex + 1;
int indexBelow = nodeIndex - 1;
if (parent->at(indexBelow) && parent->at(indexBelow)->allowAsChild(node)) {
newParent = parent->at(indexBelow);
d->nodeManager->moveNodeAt(node, newParent, newParent->childCount());
}
else if (parent->at(indexAbove) && parent->at(indexAbove)->allowAsChild(node)) {
newParent = parent->at(indexAbove);
d->nodeManager->moveNodeAt(node, newParent, 0);
}
else {
return;
}
}
void LayerModel::clear()
{
d->canvas->viewManager()->selectionManager()->clear();
}
void LayerModel::clone()
{
d->nodeManager->duplicateActiveNode();
}
void LayerModel::setLocked(int index, bool newLocked)
{
if (index > -1 && index < d->layers.count()) {
if(d->layers[index]->userLocked() == newLocked)
return;
d->layers[index]->setUserLocked(newLocked);
QModelIndex idx = createIndex(index, 0);
dataChanged(idx, idx);
}
}
void LayerModel::setOpacity(int index, float newOpacity)
{
if (index > -1 && index < d->layers.count()) {
if(qFuzzyCompare(d->layers[index]->opacity() + 1, newOpacity + 1))
return;
d->layers[index]->setOpacity(newOpacity);
d->layers[index]->setDirty();
QModelIndex idx = createIndex(index, 0);
dataChanged(idx, idx);
}
}
void LayerModel::setVisible(int index, bool newVisible)
{
if (index > -1 && index < d->layers.count()) {
KisBaseNode::PropertyList props = d->layers[index]->sectionModelProperties();
if(props[0].state == newVisible)
return;
KisBaseNode::Property prop = props[0];
prop.state = newVisible;
props[0] = prop;
d->nodeModel->setData( d->nodeModel->indexFromNode(d->layers[index]), QVariant::fromValue(props), KisNodeModel::PropertiesRole );
d->layers[index]->setDirty(d->layers[index]->extent());
QModelIndex idx = createIndex(index, 0);
dataChanged(idx, idx);
}
}
QImage LayerModel::layerThumbnail(QString layerID) const
{
// So, yeah, this is a complete cheatery hack. However, it ensures
// we actually get updates when we want them (every time the image is supposed
// to be changed). Had hoped we could avoid it, but apparently not.
int index = layerID.section(QChar('/'), 0, 0).toInt();
QImage thumb;
if (index > -1 && index < d->layers.count()) {
if (d->thumbProvider)
thumb = d->layers[index]->createThumbnail(120, 120);
}
return thumb;
}
void LayerModel::deleteCurrentLayer()
{
d->activeNode.clear();
d->nodeManager->removeNode();
}
void LayerModel::deleteLayer(int index)
{
if (index > -1 && index < d->layers.count()) {
if (d->activeNode == d->layers.at(index))
d->activeNode.clear();
d->nodeManager->slotUiActivatedNode(d->layers.at(index));
d->nodeManager->removeNode();
d->rebuildLayerList();
beginResetModel();
endResetModel();
}
}
void LayerModel::addLayer(int layerType)
{
switch(layerType) {
case 0:
d->nodeManager->createNode("KisPaintLayer");
break;
case 1:
d->nodeManager->createNode("KisGroupLayer");
break;
case 2:
d->nodeManager->createNode("KisFilterMask", true);
break;
default:
break;
}
}
void LayerModel::source_rowsAboutToBeInserted(QModelIndex /*p*/, int /*from*/, int /*to*/)
{
beginResetModel();
}
void LayerModel::source_rowsInserted(QModelIndex /*p*/, int, int)
{
d->rebuildLayerList();
emit countChanged();
endResetModel();
}
void LayerModel::source_rowsAboutToBeRemoved(QModelIndex /*p*/, int /*from*/, int /*to*/)
{
beginResetModel();
}
void LayerModel::source_rowsRemoved(QModelIndex, int, int)
{
d->rebuildLayerList();
emit countChanged();
endResetModel();
}
void LayerModel::source_dataChanged(QModelIndex /*tl*/, QModelIndex /*br*/)
{
QModelIndex top = createIndex(0, 0);
QModelIndex bottom = createIndex(d->layers.count() - 1, 0);
dataChanged(top, bottom);
}
void LayerModel::source_modelReset()
{
beginResetModel();
d->rebuildLayerList();
d->activeNode.clear();
if (d->layers.count() > 0)
{
d->nodeManager->slotUiActivatedNode(d->layers.at(0));
currentNodeChanged(d->layers.at(0));
}
emit countChanged();
endResetModel();
}
void LayerModel::notifyImageDeleted()
{
}
void LayerModel::nodeChanged(KisNodeSP node)
{
QModelIndex index = createIndex(d->layers.indexOf(node), 0);
dataChanged(index, index);
}
void LayerModel::imageChanged()
{
d->imageChangedTimer->start();
}
void LayerModel::imageHasChanged()
{
QModelIndex top = createIndex(0, 0);
QModelIndex bottom = createIndex(d->layers.count() - 1, 0);
dataChanged(top, bottom);
}
void LayerModel::aboutToRemoveNode(KisNodeSP node)
{
Q_UNUSED(node)
QTimer::singleShot(0, this, SLOT(source_modelReset()));
}
void LayerModel::emitActiveChanges()
{
emit activeFilterConfigChanged();
emit activeNameChanged();
emit activeTypeChanged();
emit activeCompositeOpChanged();
emit activeOpacityChanged();
emit activeVisibleChanged();
emit activeLockedChanged();
emit activeRChannelActiveChanged();
emit activeGChannelActiveChanged();
emit activeBChannelActiveChanged();
emit activeAChannelActiveChanged();
}
QString LayerModel::activeName() const
{
if (d->activeNode.isNull())
return QString();
return d->activeNode->name();
}
void LayerModel::setActiveName(QString newName)
{
if (d->activeNode.isNull())
return;
d->activeNode->setName(newName);
emit activeNameChanged();
}
QString LayerModel::activeType() const
{
return d->activeNode->metaObject()->className();
}
int LayerModel::activeCompositeOp() const
{
if (d->activeNode.isNull())
return 0;
KoID entry(d->activeNode->compositeOp()->id());
QModelIndex idx = KisCompositeOpListModel::sharedInstance()->indexOf(entry);
if (idx.isValid())
return idx.row();
return 0;
}
void LayerModel::setActiveCompositeOp(int newOp)
{
if (d->activeNode.isNull())
return;
KoID entry;
if (KisCompositeOpListModel::sharedInstance()->entryAt(entry, KisCompositeOpListModel::sharedInstance()->index(newOp)))
{
d->activeNode->setCompositeOpId(entry.id());
d->activeNode->setDirty();
emit activeCompositeOpChanged();
}
}
int LayerModel::activeOpacity() const
{
if (d->activeNode.isNull())
return 0;
return d->activeNode->opacity();
}
void LayerModel::setActiveOpacity(int newOpacity)
{
d->activeNode->setOpacity(newOpacity);
d->activeNode->setDirty();
emit activeOpacityChanged();
}
bool LayerModel::activeVisible() const
{ if (d->activeNode.isNull())
return false;
return d->activeNode->visible();
}
void LayerModel::setActiveVisible(bool newVisible)
{
if (d->activeNode.isNull())
return;
setVisible(d->layers.indexOf(d->activeNode), newVisible);
emit activeVisibleChanged();
}
bool LayerModel::activeLocked() const
{
if (d->activeNode.isNull())
return false;
return d->activeNode->userLocked();
}
void LayerModel::setActiveLocked(bool newLocked)
{
if (d->activeNode.isNull())
return;
d->activeNode->setUserLocked(newLocked);
emit activeLockedChanged();
}
bool LayerModel::activeAChannelActive() const
{
KisLayer* layer = qobject_cast(d->activeNode.data());
bool state = false;
if (layer)
state = !layer->alphaChannelDisabled();
return state;
}
void LayerModel::setActiveAChannelActive(bool newActive)
{
KisLayer* layer = qobject_cast(d->activeNode.data());
if (layer)
{
layer->disableAlphaChannel(!newActive);
layer->setDirty();
emit activeAChannelActiveChanged();
}
}
bool getActiveChannel(KisNodeSP node, int channelIndex)
{
KisLayer* layer = qobject_cast(node.data());
bool flag = false;
if (layer)
{
QBitArray flags = layer->channelFlags();
if (channelIndex < flags.size()) {
flag = flags[channelIndex];
}
}
return flag;
}
void setChannelActive(KisNodeSP node, int channelIndex, bool newActive)
{
KisLayer* layer = qobject_cast(node.data());
if (layer) {
QBitArray flags = layer->channelFlags();
flags.setBit(channelIndex, newActive);
layer->setChannelFlags(flags);
layer->setDirty();
}
}
bool LayerModel::activeBChannelActive() const
{
return getActiveChannel(d->activeNode, 2);
}
void LayerModel::setActiveBChannelActive(bool newActive)
{
setChannelActive(d->activeNode, 2, newActive);
emit activeBChannelActiveChanged();
}
bool LayerModel::activeGChannelActive() const
{
return getActiveChannel(d->activeNode, 1);
}
void LayerModel::setActiveGChannelActive(bool newActive)
{
setChannelActive(d->activeNode, 1, newActive);
emit activeGChannelActiveChanged();
}
bool LayerModel::activeRChannelActive() const
{
return getActiveChannel(d->activeNode, 0);
}
void LayerModel::setActiveRChannelActive(bool newActive)
{
setChannelActive(d->activeNode, 0, newActive);
emit activeRChannelActiveChanged();
}
QObject* LayerModel::activeFilterConfig() const
{
QMap props;
QString filterId;
KisFilterMask* filterMask = qobject_cast(d->activeNode.data());
if (filterMask) {
props = filterMask->filter()->getProperties();
filterId = filterMask->filter()->name();
}
else {
KisAdjustmentLayer* adjustmentLayer = qobject_cast(d->activeNode.data());
if (adjustmentLayer)
{
props = adjustmentLayer->filter()->getProperties();
filterId = adjustmentLayer->filter()->name();
}
}
PropertyContainer* config = new PropertyContainer(filterId, 0);
QMap::const_iterator i;
for(i = props.constBegin(); i != props.constEnd(); ++i) {
config->setProperty(i.key().toLatin1(), i.value());
//dbgKrita << "Getting active config..." << i.key() << i.value();
}
return config;
}
void LayerModel::setActiveFilterConfig(QObject* newConfig)
{
if (d->activeNode.isNull())
return;
PropertyContainer* config = qobject_cast(newConfig);
if (!config)
return;
//dbgKrita << "Attempting to set new config" << config->name();
KisFilterConfigurationSP realConfig = d->filters.value(config->name())->factoryConfiguration();
QMap::const_iterator i;
for(i = realConfig->getProperties().constBegin(); i != realConfig->getProperties().constEnd(); ++i)
{
realConfig->setProperty(i.key(), config->property(i.key().toLatin1()));
//dbgKrita << "Creating config..." << i.key() << i.value();
}
// The following code causes sporadic crashes, and disabling causes leaks. So, leaks it must be, for now.
// The cause is the lack of a smart pointer interface for passing filter configs around
// Must be remedied, but for now...
// if (d->newConfig)
// delete(d->newConfig);
d->newConfig = realConfig;
//d->updateActiveLayerWithNewFilterConfigTimer->start();
updateActiveLayerWithNewFilterConfig();
}
void LayerModel::updateActiveLayerWithNewFilterConfig()
{
if (!d->newConfig)
return;
//dbgKrita << "Setting new config..." << d->newConfig->name();
KisFilterMask* filterMask = qobject_cast(d->activeNode.data());
if (filterMask)
{
//dbgKrita << "Filter mask";
if (filterMask->filter() == d->newConfig)
return;
//dbgKrita << "Setting filter mask";
filterMask->setFilter(d->newConfig);
}
else
{
KisAdjustmentLayer* adjustmentLayer = qobject_cast(d->activeNode.data());
if (adjustmentLayer)
{
//dbgKrita << "Adjustment layer";
if (adjustmentLayer->filter() == d->newConfig)
return;
//dbgKrita << "Setting filter on adjustment layer";
adjustmentLayer->setFilter(d->newConfig);
}
else
{
//dbgKrita << "UNKNOWN, BAIL OUT!";
}
}
d->newConfig = 0;
d->activeNode->setDirty(d->activeNode->extent());
d->image->setModified();
QTimer::singleShot(100, this, SIGNAL(activeFilterConfigChanged()));
}
diff --git a/libs/pigment/KoColorSpaceRegistry.h b/libs/pigment/KoColorSpaceRegistry.h
index 3bee4e3a26..edb5908ab1 100644
--- a/libs/pigment/KoColorSpaceRegistry.h
+++ b/libs/pigment/KoColorSpaceRegistry.h
@@ -1,361 +1,361 @@
/*
* Copyright (c) 2003 Patrick Julien
* Copyright (c) 2004,2010 Cyrille Berger
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOCOLORSPACEREGISTRY_H
#define KOCOLORSPACEREGISTRY_H
#include
#include
#include
#include "kritapigment_export.h"
#include
#include
#include
class KoColorProfile;
class KoColorConversionSystem;
class KoColorConversionCache;
class KoColorConversionTransformation;
/**
* The registry for colorspaces and profiles.
* This class contains:
* - a registry of colorspace instantiated with specific profiles.
* - a registry of singleton colorspace factories.
* - a registry of icc profiles
*
* Locking policy details:
*
* Basically, we have two levels of locks in the registry:
* 1) (outer level) is Private::registrylock, which controls the structures
* of the color space registry itself
* 2) (inner level) is KoColorProfileStorage::Private::lock controls
* the structures related to profiles.
*
* The locks can be taken individually, but if you are going to take both
* of them, you should always follow the order 1) registry; 2) profiles.
* Otherwise you'll get a deadlock.
*
* To avoid recursive deadlocks, all the dependent classes
* (KoColorConversionSystem and KoColorSpaceFactory) now do not use the direct
* links to the registry. Instead, they use special private interfaces that
* skip recursive locking and ensure we take a lock twice.
*/
class KRITAPIGMENT_EXPORT KoColorSpaceRegistry
{
public:
KoColorSpaceRegistry();
enum ColorSpaceListVisibility {
OnlyUserVisible = 1, ///< Only user visible color space
AllColorSpaces = 4 ///< All color space even those not visible to the user
};
enum ColorSpaceListProfilesSelection {
OnlyDefaultProfile = 1, ///< Only add the default profile
AllProfiles = 4 ///< Add all profiles
};
/**
* Return an instance of the KoColorSpaceRegistry
* Creates an instance if that has never happened before and returns the singleton instance.
*/
static KoColorSpaceRegistry * instance();
virtual ~KoColorSpaceRegistry();
public:
/**
* add a color space to the registry
* @param item the color space factory to add
*/
void add(KoColorSpaceFactory* item);
/**
* Remove a color space factory from the registry. Note that it is the
* responsibility of the caller to ensure that the colorspaces are not
* used anymore.
*/
void remove(KoColorSpaceFactory* item);
/**
* Add a profile to the profile map but do not add it to the
* color conversion system yet.
* @param profile the new profile to be registered.
*/
void addProfileToMap(KoColorProfile *p);
/**
* register the profile with the color space registry
* @param profile the new profile to be registered so it can be combined with
* colorspaces.
*/
void addProfile(KoColorProfile* profile);
void addProfile(const KoColorProfile* profile); // TODO why ?
void removeProfile(KoColorProfile* profile);
/**
* Create an alias to a profile with a different name. Then @ref profileByName
* will return the profile @p to when passed @p name as a parameter.
*/
void addProfileAlias(const QString& name, const QString& to);
/**
* @return the profile alias, or name if not aliased
*/
QString profileAlias(const QString& name) const;
/**
* create a profile of the specified type.
*/
const KoColorProfile *createColorProfile(const QString & colorModelId, const QString & colorDepthId, const QByteArray& rawData);
/**
* Return a profile by its given name, or 0 if none registered.
* @return a profile by its given name, or 0 if none registered.
* @param name the product name as set on the profile.
* @see addProfile()
* @see KoColorProfile::productName()
*/
const KoColorProfile * profileByName(const QString & name) const ;
/**
* Returns a profile by its unique id stored/calculated in the header.
* The first call to this function might take long, because the map is
* created on the first use only (atm used by SVG only)
* @param id unique ProfileID of the profile (MD5 sum of its header)
* @return the profile or 0 if not found
*/
const KoColorProfile *profileByUniqueId(const QByteArray &id) const;
bool profileIsCompatible(const KoColorProfile* profile, const QString &colorSpaceId);
/**
* Return the list of profiles for a colorspace with the argument id.
* Profiles will not work with any color space, you can query which profiles
* that are registered with this registry can be used in combination with the
* argument factory.
* @param colorSpaceId the colorspace-id with which all the returned profiles will work.
* @return a list of profiles for the factory
*/
QList profilesFor(const QString& csID) const;
QString defaultProfileForColorSpace(const QString &colorSpaceId) const;
/**
* This function is called by the color space to create a color conversion
* between two color space. This function search in the graph of transformations
* the best possible path between the two color space.
*/
KoColorConversionTransformation* createColorConverter(const KoColorSpace * srcColorSpace, const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const;
/**
* This function creates two transformations, one from the color space and one to the
* color space. The destination color space is picked from a list of color space, such
* as the conversion between the two color space is of the best quality.
*
* The typical use case of this function is for KoColorTransformationFactory which
* doesn't support all color spaces, so unsupported color space have to find an
* acceptable conversion in order to use that KoColorTransformationFactory.
*
* @param colorSpace the source color space
* @param possibilities a list of color space among which we need to find the best
* conversion
* @param fromCS the conversion from the source color space will be affected to this
* variable
* @param toCS the revert conversion to the source color space will be affected to this
* variable
*/
void createColorConverters(const KoColorSpace* colorSpace, const QList< QPair >& possibilities, KoColorConversionTransformation*& fromCS, KoColorConversionTransformation*& toCS) const;
/**
* Return a colorspace that works with the parameter profile.
* @param colorSpaceId the ID string of the colorspace that you want to have returned
* @param profile the profile be combined with the colorspace
* @return the wanted colorspace, or 0 when the cs and profile can not be combined.
*/
const KoColorSpace * colorSpace(const QString & colorModelId, const QString & colorDepthId, const KoColorProfile *profile);
/**
* Return a colorspace that works with the parameter profile.
* @param profileName the name of the KoColorProfile to be combined with the colorspace
* @return the wanted colorspace, or 0 when the cs and profile can not be combined.
*/
const KoColorSpace * colorSpace(const QString & colorModelId, const QString & colorDepthId, const QString &profileName);
const KoColorSpace * colorSpace(const QString & colorModelId, const QString & colorDepthId);
/**
* Return the id of the colorspace that have the defined colorModelId with colorDepthId.
* @param colorModelId id of the color model
* @param colorDepthId id of the color depth
* @return the id of the wanted colorspace, or "" if no colorspace correspond to those ids
*/
QString colorSpaceId(const QString & colorModelId, const QString & colorDepthId) const;
/**
* It's a convenient function that behave like the above.
* Return the id of the colorspace that have the defined colorModelId with colorDepthId.
* @param colorModelId id of the color model
* @param colorDepthId id of the color depth
* @return the id of the wanted colorspace, or "" if no colorspace correspond to those ids
*/
QString colorSpaceId(const KoID& colorModelId, const KoID& colorDepthId) const;
/**
* @return a the identifiant of the color model for the given color space id.
*
* This function is a compatibility function used to get the color space from
* all kra files.
*/
KoID colorSpaceColorModelId(const QString & _colorSpaceId) const;
/**
* @return a the identifiant of the color depth for the given color space id.
*
* This function is a compatibility function used to get the color space from
* all kra files.
*/
KoID colorSpaceColorDepthId(const QString & _colorSpaceId) const;
/**
* Convenience methods to get the often used alpha colorspaces
*/
const KoColorSpace *alpha8();
const KoColorSpace *alpha16();
#include
#ifdef HAVE_OPENEXR
const KoColorSpace *alpha16f();
#endif
const KoColorSpace *alpha32f();
/**
* Convenience method to get an RGBA 8bit colorspace. If a profile is not specified,
* an sRGB profile will be used.
* @param profileName the name of an RGB color profile
* @return the wanted colorspace, or 0 if the color space and profile can not be combined.
*/
const KoColorSpace * rgb8(const QString &profileName = QString());
/**
* Convenience method to get an RGBA 8bit colorspace with the given profile.
* @param profile an RGB profile
* @return the wanted colorspace, or 0 if the color space and profile can not be combined.
*/
const KoColorSpace * rgb8(const KoColorProfile * profile);
/**
* Convenience method to get an RGBA 16bit colorspace. If a profile is not specified,
* an sRGB profile will be used.
* @param profileName the name of an RGB color profile
* @return the wanted colorspace, or 0 if the color space and profile can not be combined.
*/
const KoColorSpace * rgb16(const QString &profileName = QString());
/**
* Convenience method to get an RGBA 16bit colorspace with the given profile.
* @param profile an RGB profile
* @return the wanted colorspace, or 0 if the color space and profile can not be combined.
*/
const KoColorSpace * rgb16(const KoColorProfile * profile);
/**
* Convenience method to get an Lab 16bit colorspace. If a profile is not specified,
* an Lab profile with a D50 whitepoint will be used.
* @param profileName the name of an Lab color profile
* @return the wanted colorspace, or 0 if the color space and profile can not be combined.
*/
const KoColorSpace * lab16(const QString &profileName = QString());
/**
* Convenience method to get an Lab 16bit colorspace with the given profile.
* @param profile an Lab profile
* @return the wanted colorspace, or 0 if the color space and profile can not be combined.
*/
const KoColorSpace * lab16(const KoColorProfile * profile);
/**
* @return the list of available color models
*/
QList colorModelsList(ColorSpaceListVisibility option) const;
/**
* @return the list of available color models for the given colorModelId
*/
QList colorDepthList(const KoID& colorModelId, ColorSpaceListVisibility option) const;
/**
* @return the list of available color models for the given colorModelId
*/
QList colorDepthList(const QString & colorModelId, ColorSpaceListVisibility option) const;
/**
* @return the cache of color conversion transformation to be use by KoColorSpace
*/
KoColorConversionCache* colorConversionCache() const;
/**
* @return a permanent colorspace owned by the registry, of the same type and profile
* as the one given in argument
*/
const KoColorSpace* permanentColorspace(const KoColorSpace* _colorSpace);
/**
* This function return a list of all the keys in KoID format by using the name() method
* on the objects stored in the registry.
*/
QList listKeys() const;
private:
friend class KisCsConversionTest;
friend class KisIteratorTest;
friend class KisIteratorNGTest;
friend class KisPainterTest;
friend class KisCrashFilterTest;
friend class KoColorSpacesBenchmark;
friend class TestKoColorSpaceSanity;
friend class TestColorConversionSystem;
- friend class FriendOfColorSpaceRegistry;
+ friend struct FriendOfColorSpaceRegistry;
/**
* @return a list with an instance of all color space with their default profile.
*/
QList allColorSpaces(ColorSpaceListVisibility visibility, ColorSpaceListProfilesSelection pSelection);
/**
* @return the color conversion system use by the registry and the color
* spaces to create color conversion transformation.
*
* WARNING: conversion system is guared by the registry locks, don't
* use it anywhere other than unttests!
*/
const KoColorConversionSystem* colorConversionSystem() const;
private:
KoColorSpaceRegistry(const KoColorSpaceRegistry&);
KoColorSpaceRegistry operator=(const KoColorSpaceRegistry&);
void init();
private:
struct Private;
Private * const d;
};
#endif // KOCOLORSPACEREGISTRY_H
diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt
index dbee566cdd..af24770d85 100644
--- a/libs/ui/CMakeLists.txt
+++ b/libs/ui/CMakeLists.txt
@@ -1,577 +1,578 @@
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/qtlockedfile
${EXIV2_INCLUDE_DIR}
)
include_directories(SYSTEM
${EIGEN3_INCLUDE_DIR}
${OCIO_INCLUDE_DIR}
)
add_subdirectory( tests )
if (APPLE)
find_library(FOUNDATION_LIBRARY Foundation)
find_library(APPKIT_LIBRARY AppKit)
endif ()
set(kritaui_LIB_SRCS
canvas/kis_canvas_widget_base.cpp
canvas/kis_canvas2.cpp
canvas/kis_canvas_updates_compressor.cpp
canvas/kis_canvas_controller.cpp
canvas/kis_paintop_transformation_connector.cpp
canvas/kis_display_color_converter.cpp
canvas/kis_display_filter.cpp
canvas/kis_exposure_gamma_correction_interface.cpp
canvas/kis_tool_proxy.cpp
canvas/kis_canvas_decoration.cc
canvas/kis_coordinates_converter.cpp
canvas/kis_grid_manager.cpp
canvas/kis_grid_decoration.cpp
canvas/kis_grid_config.cpp
canvas/kis_prescaled_projection.cpp
canvas/kis_qpainter_canvas.cpp
canvas/kis_projection_backend.cpp
canvas/kis_update_info.cpp
canvas/kis_image_patch.cpp
canvas/kis_image_pyramid.cpp
canvas/kis_infinity_manager.cpp
canvas/kis_change_guides_command.cpp
canvas/kis_guides_decoration.cpp
canvas/kis_guides_manager.cpp
canvas/kis_guides_config.cpp
canvas/kis_snap_config.cpp
canvas/kis_snap_line_strategy.cpp
canvas/KisSnapPointStrategy.cpp
dialogs/kis_about_application.cpp
dialogs/kis_dlg_adj_layer_props.cc
dialogs/kis_dlg_adjustment_layer.cc
dialogs/kis_dlg_filter.cpp
dialogs/kis_dlg_generator_layer.cpp
dialogs/kis_dlg_file_layer.cpp
dialogs/kis_dlg_filter.cpp
dialogs/kis_dlg_stroke_selection_properties.cpp
dialogs/kis_dlg_image_properties.cc
dialogs/kis_dlg_layer_properties.cc
dialogs/kis_dlg_preferences.cc
dialogs/slider_and_spin_box_sync.cpp
dialogs/kis_dlg_blacklist_cleanup.cpp
dialogs/kis_dlg_layer_style.cpp
dialogs/kis_dlg_png_import.cpp
dialogs/kis_dlg_import_image_sequence.cpp
dialogs/kis_delayed_save_dialog.cpp
dialogs/KisSessionManagerDialog.cpp
dialogs/KisNewWindowLayoutDialog.cpp
flake/kis_node_dummies_graph.cpp
flake/kis_dummies_facade_base.cpp
flake/kis_dummies_facade.cpp
flake/kis_node_shapes_graph.cpp
flake/kis_node_shape.cpp
flake/kis_shape_controller.cpp
flake/kis_shape_layer.cc
flake/kis_shape_layer_canvas.cpp
flake/kis_shape_selection.cpp
flake/kis_shape_selection_canvas.cpp
flake/kis_shape_selection_model.cpp
flake/kis_take_all_shapes_command.cpp
brushhud/kis_uniform_paintop_property_widget.cpp
brushhud/kis_brush_hud.cpp
brushhud/kis_round_hud_button.cpp
brushhud/kis_dlg_brush_hud_config.cpp
brushhud/kis_brush_hud_properties_list.cpp
brushhud/kis_brush_hud_properties_config.cpp
kis_aspect_ratio_locker.cpp
kis_autogradient.cc
kis_bookmarked_configurations_editor.cc
kis_bookmarked_configurations_model.cc
kis_bookmarked_filter_configurations_model.cc
KisPaintopPropertiesBase.cpp
kis_canvas_resource_provider.cpp
kis_derived_resources.cpp
kis_categories_mapper.cpp
kis_categorized_list_model.cpp
kis_categorized_item_delegate.cpp
kis_clipboard.cc
kis_config.cc
kis_control_frame.cpp
kis_composite_ops_model.cc
kis_paint_ops_model.cpp
kis_cursor.cc
kis_cursor_cache.cpp
kis_custom_pattern.cc
kis_file_layer.cpp
kis_change_file_layer_command.h
kis_safe_document_loader.cpp
kis_splash_screen.cpp
kis_filter_manager.cc
kis_filters_model.cc
kis_histogram_view.cc
KisImageBarrierLockerWithFeedback.cpp
kis_image_manager.cc
kis_image_view_converter.cpp
kis_import_catcher.cc
kis_layer_manager.cc
kis_mask_manager.cc
kis_mimedata.cpp
kis_node_commands_adapter.cpp
kis_node_manager.cpp
kis_node_juggler_compressed.cpp
kis_node_selection_adapter.cpp
kis_node_insertion_adapter.cpp
kis_node_model.cpp
kis_node_filter_proxy_model.cpp
kis_model_index_converter_base.cpp
kis_model_index_converter.cpp
kis_model_index_converter_show_all.cpp
kis_painting_assistant.cc
kis_painting_assistants_decoration.cpp
KisDecorationsManager.cpp
kis_paintop_box.cc
kis_paintop_option.cpp
kis_paintop_options_model.cpp
kis_paintop_settings_widget.cpp
kis_popup_palette.cpp
kis_png_converter.cpp
kis_preference_set_registry.cpp
KisResourceServerProvider.cpp
KisResourceBundleServerProvider.cpp
KisSelectedShapesProxy.cpp
kis_selection_decoration.cc
kis_selection_manager.cc
+ KisSelectionActionsAdapter.cpp
kis_statusbar.cc
kis_zoom_manager.cc
kis_favorite_resource_manager.cpp
kis_workspace_resource.cpp
kis_action.cpp
kis_action_manager.cpp
KisActionPlugin.cpp
kis_canvas_controls_manager.cpp
kis_tooltip_manager.cpp
kis_multinode_property.cpp
kis_stopgradient_editor.cpp
KisWelcomePageWidget.cpp
kisexiv2/kis_exif_io.cpp
kisexiv2/kis_exiv2.cpp
kisexiv2/kis_iptc_io.cpp
kisexiv2/kis_xmp_io.cpp
opengl/kis_opengl.cpp
opengl/kis_opengl_canvas2.cpp
opengl/kis_opengl_canvas_debugger.cpp
opengl/kis_opengl_image_textures.cpp
opengl/kis_texture_tile.cpp
opengl/kis_opengl_shader_loader.cpp
opengl/kis_texture_tile_info_pool.cpp
opengl/KisOpenGLUpdateInfoBuilder.cpp
kis_fps_decoration.cpp
tool/kis_selection_tool_helper.cpp
tool/kis_selection_tool_config_widget_helper.cpp
tool/kis_rectangle_constraint_widget.cpp
tool/kis_shape_tool_helper.cpp
tool/kis_tool.cc
tool/kis_delegated_tool_policies.cpp
tool/kis_tool_freehand.cc
tool/kis_speed_smoother.cpp
tool/kis_painting_information_builder.cpp
tool/kis_stabilized_events_sampler.cpp
tool/kis_tool_freehand_helper.cpp
tool/kis_tool_multihand_helper.cpp
tool/kis_figure_painting_tool_helper.cpp
tool/kis_tool_paint.cc
tool/kis_tool_shape.cc
tool/kis_tool_ellipse_base.cpp
tool/kis_tool_rectangle_base.cpp
tool/kis_tool_polyline_base.cpp
tool/kis_tool_utils.cpp
tool/kis_resources_snapshot.cpp
tool/kis_smoothing_options.cpp
tool/KisStabilizerDelayedPaintHelper.cpp
tool/KisStrokeSpeedMonitor.cpp
tool/strokes/freehand_stroke.cpp
tool/strokes/KisStrokeEfficiencyMeasurer.cpp
tool/strokes/kis_painter_based_stroke_strategy.cpp
tool/strokes/kis_filter_stroke_strategy.cpp
tool/strokes/kis_color_picker_stroke_strategy.cpp
tool/strokes/KisFreehandStrokeInfo.cpp
tool/strokes/KisMaskedFreehandStrokePainter.cpp
tool/strokes/KisMaskingBrushRenderer.cpp
tool/strokes/KisMaskingBrushCompositeOpFactory.cpp
widgets/kis_cmb_composite.cc
widgets/kis_cmb_contour.cpp
widgets/kis_cmb_gradient.cpp
widgets/kis_paintop_list_widget.cpp
widgets/kis_cmb_idlist.cc
widgets/kis_color_space_selector.cc
widgets/kis_advanced_color_space_selector.cc
widgets/kis_cie_tongue_widget.cpp
widgets/kis_tone_curve_widget.cpp
widgets/kis_curve_widget.cpp
widgets/kis_custom_image_widget.cc
widgets/kis_image_from_clipboard_widget.cpp
widgets/kis_double_widget.cc
widgets/kis_filter_selector_widget.cc
widgets/kis_gradient_chooser.cc
widgets/kis_iconwidget.cc
widgets/kis_mask_widgets.cpp
widgets/kis_meta_data_merge_strategy_chooser_widget.cc
widgets/kis_multi_bool_filter_widget.cc
widgets/kis_multi_double_filter_widget.cc
widgets/kis_multi_integer_filter_widget.cc
widgets/kis_multipliers_double_slider_spinbox.cpp
widgets/kis_paintop_presets_popup.cpp
widgets/kis_tool_options_popup.cpp
widgets/kis_paintop_presets_chooser_popup.cpp
widgets/kis_paintop_presets_save.cpp
widgets/kis_paintop_preset_icon_library.cpp
widgets/kis_pattern_chooser.cc
widgets/kis_preset_chooser.cpp
widgets/kis_progress_widget.cpp
widgets/kis_selection_options.cc
widgets/kis_scratch_pad.cpp
widgets/kis_scratch_pad_event_filter.cpp
widgets/kis_preset_selector_strip.cpp
widgets/kis_slider_spin_box.cpp
widgets/KisSelectionPropertySlider.cpp
widgets/kis_size_group.cpp
widgets/kis_size_group_p.cpp
widgets/kis_wdg_generator.cpp
widgets/kis_workspace_chooser.cpp
widgets/kis_categorized_list_view.cpp
widgets/kis_widget_chooser.cpp
widgets/kis_tool_button.cpp
widgets/kis_floating_message.cpp
widgets/kis_lod_availability_widget.cpp
widgets/kis_color_label_selector_widget.cpp
widgets/kis_color_filter_combo.cpp
widgets/kis_elided_label.cpp
widgets/kis_stopgradient_slider_widget.cpp
widgets/kis_preset_live_preview_view.cpp
widgets/KisScreenColorPicker.cpp
widgets/KoDualColorButton.cpp
widgets/KoStrokeConfigWidget.cpp
widgets/KoFillConfigWidget.cpp
utils/kis_document_aware_spin_box_unit_manager.cpp
input/kis_input_manager.cpp
input/kis_input_manager_p.cpp
input/kis_extended_modifiers_mapper.cpp
input/kis_abstract_input_action.cpp
input/kis_tool_invocation_action.cpp
input/kis_pan_action.cpp
input/kis_alternate_invocation_action.cpp
input/kis_rotate_canvas_action.cpp
input/kis_zoom_action.cpp
input/kis_change_frame_action.cpp
input/kis_gamma_exposure_action.cpp
input/kis_show_palette_action.cpp
input/kis_change_primary_setting_action.cpp
input/kis_abstract_shortcut.cpp
input/kis_native_gesture_shortcut.cpp
input/kis_single_action_shortcut.cpp
input/kis_stroke_shortcut.cpp
input/kis_shortcut_matcher.cpp
input/kis_select_layer_action.cpp
input/KisQtWidgetsTweaker.cpp
input/KisInputActionGroup.cpp
operations/kis_operation.cpp
operations/kis_operation_configuration.cpp
operations/kis_operation_registry.cpp
operations/kis_operation_ui_factory.cpp
operations/kis_operation_ui_widget.cpp
operations/kis_filter_selection_operation.cpp
actions/kis_selection_action_factories.cpp
actions/KisPasteActionFactory.cpp
input/kis_touch_shortcut.cpp
kis_document_undo_store.cpp
kis_transaction_based_command.cpp
kis_gui_context_command.cpp
kis_gui_context_command_p.cpp
input/kis_tablet_debugger.cpp
input/kis_input_profile_manager.cpp
input/kis_input_profile.cpp
input/kis_shortcut_configuration.cpp
input/config/kis_input_configuration_page.cpp
input/config/kis_edit_profiles_dialog.cpp
input/config/kis_input_profile_model.cpp
input/config/kis_input_configuration_page_item.cpp
input/config/kis_action_shortcuts_model.cpp
input/config/kis_input_type_delegate.cpp
input/config/kis_input_mode_delegate.cpp
input/config/kis_input_button.cpp
input/config/kis_input_editor_delegate.cpp
input/config/kis_mouse_input_editor.cpp
input/config/kis_wheel_input_editor.cpp
input/config/kis_key_input_editor.cpp
processing/fill_processing_visitor.cpp
kis_asl_layer_style_serializer.cpp
kis_psd_layer_style_resource.cpp
canvas/kis_mirror_axis.cpp
kis_abstract_perspective_grid.cpp
KisApplication.cpp
KisAutoSaveRecoveryDialog.cpp
KisDetailsPane.cpp
KisDocument.cpp
KisCloneDocumentStroke.cpp
KisNodeDelegate.cpp
kis_node_view_visibility_delegate.cpp
KisNodeToolTip.cpp
KisNodeView.cpp
kis_node_view_color_scheme.cpp
KisImportExportFilter.cpp
KisFilterEntry.cpp
KisImportExportManager.cpp
KisImportExportUtils.cpp
kis_async_action_feedback.cpp
KisMainWindow.cpp
KisOpenPane.cpp
KisPart.cpp
KisPrintJob.cpp
KisTemplate.cpp
KisTemplateCreateDia.cpp
KisTemplateGroup.cpp
KisTemplates.cpp
KisTemplatesPane.cpp
KisTemplateTree.cpp
KisUndoActionsUpdateManager.cpp
KisView.cpp
thememanager.cpp
kis_mainwindow_observer.cpp
KisViewManager.cpp
kis_mirror_manager.cpp
qtlockedfile/qtlockedfile.cpp
qtsingleapplication/qtlocalpeer.cpp
qtsingleapplication/qtsingleapplication.cpp
KisResourceBundle.cpp
KisResourceBundleManifest.cpp
kis_md5_generator.cpp
KisApplicationArguments.cpp
KisNetworkAccessManager.cpp
KisMultiFeedRSSModel.cpp
KisRemoteFileFetcher.cpp
KisSaveGroupVisitor.cpp
KisWindowLayoutResource.cpp
KisWindowLayoutManager.cpp
KisSessionResource.cpp
KisReferenceImagesDecoration.cpp
KisReferenceImage.cpp
flake/KisReferenceImagesLayer.cpp
flake/KisReferenceImagesLayer.h
)
if(WIN32)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
input/kis_tablet_event.cpp
input/wintab/kis_tablet_support_win.cpp
input/wintab/kis_screen_size_choice_dialog.cpp
qtlockedfile/qtlockedfile_win.cpp
input/wintab/kis_tablet_support_win8.cpp
opengl/kis_opengl_win.cpp
)
endif()
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
kis_animation_frame_cache.cpp
kis_animation_cache_populator.cpp
KisAsyncAnimationRendererBase.cpp
KisAsyncAnimationCacheRenderer.cpp
KisAsyncAnimationFramesSavingRenderer.cpp
dialogs/KisAsyncAnimationRenderDialogBase.cpp
dialogs/KisAsyncAnimationCacheRenderDialog.cpp
dialogs/KisAsyncAnimationFramesSaveDialog.cpp
canvas/kis_animation_player.cpp
kis_animation_importer.cpp
KisSyncedAudioPlayback.cpp
KisFrameDataSerializer.cpp
KisFrameCacheStore.cpp
KisFrameCacheSwapper.cpp
KisAbstractFrameCacheSwapper.cpp
KisInMemoryFrameCacheSwapper.cpp
input/wintab/drawpile_tablettester/tablettester.cpp
input/wintab/drawpile_tablettester/tablettest.cpp
)
if(UNIX)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
input/kis_tablet_event.cpp
input/wintab/kis_tablet_support.cpp
qtlockedfile/qtlockedfile_unix.cpp
)
if(NOT APPLE)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
input/wintab/qxcbconnection_xi2.cpp
input/wintab/qxcbconnection.cpp
input/wintab/kis_xi2_event_filter.cpp
)
endif()
endif()
if(APPLE)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
osx.mm
)
endif()
ki18n_wrap_ui(kritaui_LIB_SRCS
widgets/KoFillConfigWidget.ui
widgets/KoStrokeConfigWidget.ui
forms/wdgdlgpngimport.ui
forms/wdgfullscreensettings.ui
forms/wdgautogradient.ui
forms/wdggeneralsettings.ui
forms/wdgperformancesettings.ui
forms/wdggenerators.ui
forms/wdgbookmarkedconfigurationseditor.ui
forms/wdgapplyprofile.ui
forms/wdgcustompattern.ui
forms/wdglayerproperties.ui
forms/wdgcolorsettings.ui
forms/wdgtabletsettings.ui
forms/wdgcolorspaceselector.ui
forms/wdgcolorspaceselectoradvanced.ui
forms/wdgdisplaysettings.ui
forms/kis_previewwidgetbase.ui
forms/kis_matrix_widget.ui
forms/wdgselectionoptions.ui
forms/wdggeometryoptions.ui
forms/wdgnewimage.ui
forms/wdgimageproperties.ui
forms/wdgmaskfromselection.ui
forms/wdgmasksource.ui
forms/wdgfilterdialog.ui
forms/wdgmetadatamergestrategychooser.ui
forms/wdgpaintoppresets.ui
forms/wdgpaintopsettings.ui
forms/wdgdlggeneratorlayer.ui
forms/wdgdlgfilelayer.ui
forms/wdgfilterselector.ui
forms/wdgfilternodecreation.ui
forms/wdgmultipliersdoublesliderspinbox.ui
forms/wdgnodequerypatheditor.ui
forms/wdgpresetselectorstrip.ui
forms/wdgsavebrushpreset.ui
forms/wdgpreseticonlibrary.ui
forms/wdgdlgblacklistcleanup.ui
forms/wdgrectangleconstraints.ui
forms/wdgimportimagesequence.ui
forms/wdgstrokeselectionproperties.ui
forms/KisDetailsPaneBase.ui
forms/KisOpenPaneBase.ui
forms/wdgstopgradienteditor.ui
forms/wdgsessionmanager.ui
forms/wdgnewwindowlayout.ui
forms/KisWelcomePage.ui
brushhud/kis_dlg_brush_hud_config.ui
dialogs/kis_delayed_save_dialog.ui
input/config/kis_input_configuration_page.ui
input/config/kis_edit_profiles_dialog.ui
input/config/kis_input_configuration_page_item.ui
input/config/kis_mouse_input_editor.ui
input/config/kis_wheel_input_editor.ui
input/config/kis_key_input_editor.ui
layerstyles/wdgBevelAndEmboss.ui
layerstyles/wdgblendingoptions.ui
layerstyles/WdgColorOverlay.ui
layerstyles/wdgContour.ui
layerstyles/wdgdropshadow.ui
layerstyles/WdgGradientOverlay.ui
layerstyles/wdgInnerGlow.ui
layerstyles/wdglayerstyles.ui
layerstyles/WdgPatternOverlay.ui
layerstyles/WdgSatin.ui
layerstyles/WdgStroke.ui
layerstyles/wdgstylesselector.ui
layerstyles/wdgTexture.ui
wdgsplash.ui
input/wintab/kis_screen_size_choice_dialog.ui
input/wintab/drawpile_tablettester/tablettest.ui
)
QT5_WRAP_CPP(kritaui_HEADERS_MOC KisNodePropertyAction_p.h)
add_library(kritaui SHARED ${kritaui_HEADERS_MOC} ${kritaui_LIB_SRCS} )
generate_export_header(kritaui BASE_NAME kritaui)
target_link_libraries(kritaui KF5::CoreAddons KF5::Completion KF5::I18n KF5::ItemViews Qt5::Network
kritaimpex kritacolor kritaimage kritalibbrush kritawidgets kritawidgetutils kritaresources ${PNG_LIBRARIES} ${EXIV2_LIBRARIES}
)
if (HAVE_QT_MULTIMEDIA)
target_link_libraries(kritaui Qt5::Multimedia)
endif()
if (HAVE_KIO)
target_link_libraries(kritaui KF5::KIOCore)
endif()
if (NOT WIN32 AND NOT APPLE)
target_link_libraries(kritaui ${X11_X11_LIB}
${X11_Xinput_LIB}
${XCB_LIBRARIES})
endif()
if(APPLE)
target_link_libraries(kritaui ${FOUNDATION_LIBRARY})
target_link_libraries(kritaui ${APPKIT_LIBRARY})
endif ()
target_link_libraries(kritaui ${OPENEXR_LIBRARIES})
# Add VSync disable workaround
if(NOT WIN32 AND NOT APPLE)
target_link_libraries(kritaui ${CMAKE_DL_LIBS} Qt5::X11Extras)
endif()
if(X11_FOUND)
target_link_libraries(kritaui Qt5::X11Extras ${X11_LIBRARIES})
endif()
target_include_directories(kritaui
PUBLIC
$
$
$
$
$
$
$
)
set_target_properties(kritaui
PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION}
)
install(TARGETS kritaui ${INSTALL_TARGETS_DEFAULT_ARGS})
if (APPLE)
install(FILES osx.stylesheet DESTINATION ${DATA_INSTALL_DIR}/krita)
endif ()
diff --git a/libs/ui/KisNodeDelegate.cpp b/libs/ui/KisNodeDelegate.cpp
index 328e38abc4..0e73f9ba79 100644
--- a/libs/ui/KisNodeDelegate.cpp
+++ b/libs/ui/KisNodeDelegate.cpp
@@ -1,918 +1,956 @@
/*
Copyright (c) 2006 Gábor Lehel
Copyright (c) 2008 Cyrille Berger
Copyright (c) 2011 José Luis Vergara
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 "kis_config.h"
#include "KisNodeDelegate.h"
#include "kis_node_model.h"
#include "KisNodeToolTip.h"
#include "KisNodeView.h"
#include "KisPart.h"
#include "input/kis_input_manager.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kis_node_view_color_scheme.h"
#include "kis_icon_utils.h"
#include "kis_layer_properties_icons.h"
#include "krita_utils.h"
#include "kis_config_notifier.h"
typedef KisBaseNode::Property* OptionalProperty;
#include
class KisNodeDelegate::Private
{
public:
Private() : view(0), edit(0) { }
KisNodeView *view;
QPointer edit;
KisNodeToolTip tip;
QColor checkersColor1;
QColor checkersColor2;
QList rightmostProperties(const KisBaseNode::PropertyList &props) const;
int numProperties(const QModelIndex &index) const;
OptionalProperty findProperty(KisBaseNode::PropertyList &props, const OptionalProperty &refProp) const;
OptionalProperty findVisibilityProperty(KisBaseNode::PropertyList &props) const;
void toggleProperty(KisBaseNode::PropertyList &props, OptionalProperty prop, bool controlPressed, const QModelIndex &index);
};
KisNodeDelegate::KisNodeDelegate(KisNodeView *view, QObject *parent)
: QAbstractItemDelegate(parent)
, d(new Private)
{
d->view = view;
QApplication::instance()->installEventFilter(this);
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
slotConfigChanged();
}
KisNodeDelegate::~KisNodeDelegate()
{
delete d;
}
QSize KisNodeDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(index);
KisNodeViewColorScheme scm;
return QSize(option.rect.width(), scm.rowHeight());
}
void KisNodeDelegate::paint(QPainter *p, const QStyleOptionViewItem &o, const QModelIndex &index) const
{
p->save();
{
QStyleOptionViewItem option = getOptions(o, index);
QStyle *style = option.widget ? option.widget->style() : QApplication::style();
style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, p, option.widget);
bool shouldGrayOut = index.data(KisNodeModel::ShouldGrayOutRole).toBool();
if (shouldGrayOut) {
option.state &= ~QStyle::State_Enabled;
}
p->setFont(option.font);
drawColorLabel(p, option, index);
drawFrame(p, option, index);
drawThumbnail(p, option, index);
drawText(p, option, index);
drawIcons(p, option, index);
drawVisibilityIconHijack(p, option, index);
drawDecoration(p, option, index);
drawExpandButton(p, option, index);
drawBranch(p, option, index);
drawProgressBar(p, option, index);
}
p->restore();
}
void KisNodeDelegate::drawBranch(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const {
Q_UNUSED(index);
KisNodeViewColorScheme scm;
const QPoint base = scm.relThumbnailRect().translated(option.rect.topLeft()).topLeft() - QPoint( scm.indentation(), 0);
// there is no indention if we are starting negative, so don't draw a branch
if (base.x() < 0) {
return;
}
QPen oldPen = p->pen();
const qreal oldOpacity = p->opacity(); // remember previous opacity
p->setOpacity(1.0);
QColor color = scm.gridColor(option, d->view);
QColor bgColor = option.state & QStyle::State_Selected ?
qApp->palette().color(QPalette::Base) :
qApp->palette().color(QPalette::Text);
color = KritaUtils::blendColors(color, bgColor, 0.9);
// TODO: if we are a mask type, use dotted lines for the branch style
// p->setPen(QPen(p->pen().color(), 2, Qt::DashLine, Qt::RoundCap, Qt::RoundJoin));
p->setPen(QPen(color, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
QPoint p2 = base + QPoint(scm.iconSize() - scm.decorationMargin()*2, scm.iconSize()*0.45);
QPoint p3 = base + QPoint(scm.iconSize() - scm.decorationMargin()*2, scm.iconSize());
QPoint p4 = base + QPoint(scm.iconSize()*1.4, scm.iconSize());
p->drawLine(p2, p3);
p->drawLine(p3, p4);
// draw parent lines (keep drawing until x position is less than 0
QPoint p5 = p2 - QPoint(scm.indentation(), 0);
QPoint p6 = p3 - QPoint(scm.indentation(), 0);
QPoint parentBase1 = p5;
QPoint parentBase2 = p6;
// indent lines needs to be very subtle to avoid making the docker busy looking
color = KritaUtils::blendColors(color, bgColor, 0.9); // makes it a little lighter than L lines
p->setPen(QPen(color, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
while (parentBase1.x() > scm.visibilityColumnWidth()) {
p->drawLine(parentBase1, parentBase2);
parentBase1 = parentBase1 - QPoint(scm.indentation(), 0);
parentBase2 = parentBase2 - QPoint(scm.indentation(), 0);
}
p->setPen(oldPen);
p->setOpacity(oldOpacity);
}
void KisNodeDelegate::drawColorLabel(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
KisNodeViewColorScheme scm;
const int label = index.data(KisNodeModel::ColorLabelIndexRole).toInt();
QColor color = scm.colorLabel(label);
if (color.alpha() <= 0) return;
QColor bgColor = qApp->palette().color(QPalette::Base);
color = KritaUtils::blendColors(color, bgColor, 0.3);
const QRect rect = option.state & QStyle::State_Selected ?
iconsRect(option, index) :
option.rect.adjusted(-scm.indentation(), 0, 0, 0);
p->fillRect(rect, color);
}
void KisNodeDelegate::drawFrame(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
KisNodeViewColorScheme scm;
QPen oldPen = p->pen();
p->setPen(scm.gridColor(option, d->view));
const QPoint base = option.rect.topLeft();
QPoint p2 = base + QPoint(-scm.indentation() - 1, 0);
QPoint p3 = base + QPoint(2 * scm.decorationMargin() + scm.decorationSize(), 0);
QPoint p4 = base + QPoint(-1, 0);
QPoint p5(iconsRect(option,
index).left() - 1, base.y());
QPoint p6(option.rect.right(), base.y());
QPoint v(0, option.rect.height());
// draw a line that goes the length of the entire frame. one for the
// top, and one for the bottom
QPoint pTopLeft(0, option.rect.topLeft().y());
QPoint pTopRight(option.rect.bottomRight().x(),option.rect.topLeft().y() );
p->drawLine(pTopLeft, pTopRight);
QPoint pBottomLeft(0, option.rect.topLeft().y() + scm.rowHeight());
QPoint pBottomRight(option.rect.bottomRight().x(),option.rect.topLeft().y() + scm.rowHeight() );
p->drawLine(pBottomLeft, pBottomRight);
const bool paintForParent =
index.parent().isValid() &&
!index.row();
if (paintForParent) {
QPoint p1(-2 * scm.indentation() - 1, 0);
p1 += base;
p->drawLine(p1, p2);
}
QPoint k0(0, base.y());
QPoint k1(1 * scm.border() + 2 * scm.visibilityMargin() + scm.visibilitySize(), base.y());
p->drawLine(k0, k1);
p->drawLine(k0 + v, k1 + v);
p->drawLine(k0, k0 + v);
p->drawLine(k1, k1 + v);
p->drawLine(p2, p6);
p->drawLine(p2 + v, p6 + v);
p->drawLine(p2, p2 + v);
p->drawLine(p3, p3 + v);
p->drawLine(p4, p4 + v);
p->drawLine(p5, p5 + v);
p->drawLine(p6, p6 + v);
//// For debugging purposes only
//p->setPen(Qt::blue);
//KritaUtils::renderExactRect(p, iconsRect(option, index));
//KritaUtils::renderExactRect(p, textRect(option, index));
//KritaUtils::renderExactRect(p, scm.relThumbnailRect().translated(option.rect.topLeft()));
p->setPen(oldPen);
}
-void KisNodeDelegate::drawThumbnail(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
+QRect KisNodeDelegate::thumbnailClickRect(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
- KisNodeViewColorScheme scm;
+ Q_UNUSED(index);
+ const int steps = 0;
+ KisNodeViewColorScheme scm;
+ return QRect(scm.border() +
+ 2 * scm.visibilityMargin() + scm.visibilitySize() +
+ scm.border() + steps * scm.indentation(),
+ scm.border() + option.rect.top(),
+ 2 * scm.thumbnailMargin() + scm.thumbnailSize(),
+ scm.rowHeight() - scm.border());
+}
+
+void KisNodeDelegate::drawThumbnail(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ KisNodeViewColorScheme scm;
const int thumbSize = scm.thumbnailSize();
const qreal oldOpacity = p->opacity(); // remember previous opacity
QImage img = index.data(int(KisNodeModel::BeginThumbnailRole) + thumbSize).value();
if (!(option.state & QStyle::State_Enabled)) {
p->setOpacity(0.35);
}
QRect fitRect = scm.relThumbnailRect().translated(option.rect.topLeft());
QPoint offset;
offset.setX((fitRect.width() - img.width()) / 2);
offset.setY((fitRect.height() - img.height()) / 2);
offset += fitRect.topLeft();
// paint in a checkerboard pattern behind the layer contents to represent transparent
const int step = scm.thumbnailSize() / 6;
QImage checkers(2 * step, 2 * step, QImage::Format_ARGB32);
QPainter gc(&checkers);
gc.fillRect(QRect(0, 0, step, step), d->checkersColor1);
gc.fillRect(QRect(step, 0, step, step), d->checkersColor2);
gc.fillRect(QRect(step, step, step, step), d->checkersColor1);
gc.fillRect(QRect(0, step, step, step), d->checkersColor2);
QBrush brush(checkers);
p->setBrushOrigin(offset);
p->fillRect(img.rect().translated(offset), brush);
p->drawImage(offset, img);
p->setOpacity(oldOpacity); // restore old opacity
QRect borderRect = kisGrowRect(img.rect(), 1).translated(offset);
KritaUtils::renderExactRect(p, borderRect, scm.gridColor(option, d->view));
}
QRect KisNodeDelegate::iconsRect(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
KisNodeViewColorScheme scm;
int propCount = d->numProperties(index);
const int iconsWidth =
propCount * (scm.iconSize() + 2 * scm.iconMargin()) +
(propCount - 1) * scm.border();
const int x = option.rect.x() + option.rect.width()
- (iconsWidth + scm.border());
const int y = option.rect.y() + scm.border();
return QRect(x, y,
iconsWidth,
scm.rowHeight() - scm.border());
}
QRect KisNodeDelegate::textRect(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
KisNodeViewColorScheme scm;
static QFont f;
static int minbearing = 1337 + 666; //can be 0 or negative, 2003 is less likely
if (minbearing == 2003 || f != option.font) {
f = option.font; //getting your bearings can be expensive, so we cache them
minbearing = option.fontMetrics.minLeftBearing() + option.fontMetrics.minRightBearing();
}
const int decorationOffset =
2 * scm.border() +
2 * scm.decorationMargin() +
scm.decorationSize();
const int width =
iconsRect(option, index).left() - option.rect.x() -
scm.border() + minbearing - decorationOffset;
return QRect(option.rect.x() - minbearing + decorationOffset,
option.rect.y() + scm.border(),
width,
scm.rowHeight() - scm.border());
}
void KisNodeDelegate::drawText(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
KisNodeViewColorScheme scm;
const QRect rc = textRect(option, index)
.adjusted(scm.textMargin(), 0, -scm.textMargin(), 0);
QPen oldPen = p->pen();
const qreal oldOpacity = p->opacity(); // remember previous opacity
p->setPen(option.palette.color(QPalette::Active,QPalette::Text ));
if (!(option.state & QStyle::State_Enabled)) {
p->setOpacity(0.55);
}
const QString text = index.data(Qt::DisplayRole).toString();
const QString elided = elidedText(p->fontMetrics(), rc.width(), Qt::ElideRight, text);
p->drawText(rc, Qt::AlignLeft | Qt::AlignVCenter, elided);
p->setPen(oldPen); // restore pen settings
p->setOpacity(oldOpacity);
}
QList KisNodeDelegate::Private::rightmostProperties(const KisBaseNode::PropertyList &props) const
{
QList list;
QList prependList;
list << OptionalProperty(0);
list << OptionalProperty(0);
list << OptionalProperty(0);
KisBaseNode::PropertyList::const_iterator it = props.constBegin();
KisBaseNode::PropertyList::const_iterator end = props.constEnd();
for (; it != end; ++it) {
if (!it->isMutable) continue;
if (it->id == KisLayerPropertiesIcons::visible.id()) {
// noop...
} else if (it->id == KisLayerPropertiesIcons::locked.id()) {
list[0] = OptionalProperty(&(*it));
} else if (it->id == KisLayerPropertiesIcons::inheritAlpha.id()) {
list[1] = OptionalProperty(&(*it));
} else if (it->id == KisLayerPropertiesIcons::alphaLocked.id()) {
list[2] = OptionalProperty(&(*it));
} else {
prependList.prepend(OptionalProperty(&(*it)));
}
}
{
QMutableListIterator i(prependList);
i.toBack();
while (i.hasPrevious()) {
OptionalProperty val = i.previous();
int emptyIndex = list.lastIndexOf(0);
if (emptyIndex < 0) break;
list[emptyIndex] = val;
i.remove();
}
}
return prependList + list;
}
int KisNodeDelegate::Private::numProperties(const QModelIndex &index) const
{
KisBaseNode::PropertyList props = index.data(KisNodeModel::PropertiesRole).value();
QList realProps = rightmostProperties(props);
return realProps.size();
}
OptionalProperty KisNodeDelegate::Private::findProperty(KisBaseNode::PropertyList &props, const OptionalProperty &refProp) const
{
KisBaseNode::PropertyList::iterator it = props.begin();
KisBaseNode::PropertyList::iterator end = props.end();
for (; it != end; ++it) {
if (it->id == refProp->id) {
return &(*it);
}
}
return 0;
}
OptionalProperty KisNodeDelegate::Private::findVisibilityProperty(KisBaseNode::PropertyList &props) const
{
KisBaseNode::PropertyList::iterator it = props.begin();
KisBaseNode::PropertyList::iterator end = props.end();
for (; it != end; ++it) {
if (it->id == KisLayerPropertiesIcons::visible.id()) {
return &(*it);
}
}
return 0;
}
void KisNodeDelegate::drawIcons(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
KisNodeViewColorScheme scm;
const QRect r = iconsRect(option, index);
QTransform oldTransform = p->transform();
QPen oldPen = p->pen();
p->setTransform(QTransform::fromTranslate(r.x(), r.y()));
p->setPen(scm.gridColor(option, d->view));
int x = 0;
const int y = (scm.rowHeight() - scm.border() - scm.iconSize()) / 2;
KisBaseNode::PropertyList props = index.data(KisNodeModel::PropertiesRole).value();
QList realProps = d->rightmostProperties(props);
Q_FOREACH (OptionalProperty prop, realProps) {
x += scm.iconMargin();
if (prop) {
QIcon icon = prop->state.toBool() ? prop->onIcon : prop->offIcon;
bool fullColor = prop->state.toBool() && option.state & QStyle::State_Enabled;
const qreal oldOpacity = p->opacity(); // remember previous opacity
if (fullColor) {
p->setOpacity(1.0);
}
else {
p->setOpacity(0.35);
}
p->drawPixmap(x, y, icon.pixmap(scm.iconSize(), QIcon::Normal));
p->setOpacity(oldOpacity); // restore old opacity
}
x += scm.iconSize() + scm.iconMargin();
p->drawLine(x, 0, x, scm.rowHeight() - scm.border());
x += scm.border();
}
p->setTransform(oldTransform);
p->setPen(oldPen);
}
QRect KisNodeDelegate::visibilityClickRect(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(index);
KisNodeViewColorScheme scm;
return QRect(scm.border(), scm.border() + option.rect.top(),
2 * scm.visibilityMargin() + scm.visibilitySize(),
scm.rowHeight() - scm.border());
}
QRect KisNodeDelegate::decorationClickRect(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(option);
Q_UNUSED(index);
KisNodeViewColorScheme scm;
QRect realVisualRect = d->view->originalVisualRect(index);
return QRect(realVisualRect.left(), scm.border() + realVisualRect.top(),
2 * scm.decorationMargin() + scm.decorationSize(),
scm.rowHeight() - scm.border());
}
void KisNodeDelegate::drawVisibilityIconHijack(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
/**
* Small hack Alert:
*
* Here wepaint over the area that sits basically outside our layer's
* row. Anyway, just update it later...
*/
KisNodeViewColorScheme scm;
KisBaseNode::PropertyList props = index.data(KisNodeModel::PropertiesRole).value();
OptionalProperty prop = d->findVisibilityProperty(props);
if (!prop) return;
const int x = scm.border() + scm.visibilityMargin();
const int y = option.rect.top() + (scm.rowHeight() - scm.border() - scm.visibilitySize()) / 2;
QIcon icon = prop->state.toBool() ? prop->onIcon : prop->offIcon;
// if we are not showing the layer, make the icon slightly transparent like other inactive icons
const qreal oldOpacity = p->opacity();
if (prop->state.toBool() == true) {
p->setOpacity(1.0);
}
else {
p->setOpacity(0.35);
}
p->drawPixmap(x, y, icon.pixmap(scm.visibilitySize(), QIcon::Normal));
p->setOpacity(oldOpacity);
//// For debugging purposes only
// p->save();
// p->setPen(Qt::blue);
// KritaUtils::renderExactRect(p, visibilityClickRect(option, index));
// p->restore();
}
void KisNodeDelegate::drawDecoration(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
KisNodeViewColorScheme scm;
QIcon icon = index.data(Qt::DecorationRole).value();
if (!icon.isNull()) {
QPixmap pixmap =
icon.pixmap(scm.decorationSize(),
(option.state & QStyle::State_Enabled) ?
QIcon::Normal : QIcon::Disabled);
const QRect rc = scm.relDecorationRect().translated(option.rect.topLeft());
const qreal oldOpacity = p->opacity(); // remember previous opacity
if (!(option.state & QStyle::State_Enabled)) {
p->setOpacity(0.35);
}
p->drawPixmap(rc.topLeft(), pixmap);
p->setOpacity(oldOpacity); // restore old opacity
}
}
void KisNodeDelegate::drawExpandButton(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(index);
KisNodeViewColorScheme scm;
QRect rc = scm.relExpandButtonRect().translated(option.rect.topLeft());
rc = kisGrowRect(rc, 0);
if (!(option.state & QStyle::State_Children)) {
return;
}
QString iconName = option.state & QStyle::State_Open ? "arrow-down" : "arrow-right";
QIcon icon = KisIconUtils::loadIcon(iconName);
QPixmap pixmap = icon.pixmap(rc.width(),
(option.state & QStyle::State_Enabled) ?
QIcon::Normal : QIcon::Disabled);
p->drawPixmap(rc.topLeft(), pixmap);
}
void KisNodeDelegate::Private::toggleProperty(KisBaseNode::PropertyList &props, OptionalProperty clickedProperty, bool controlPressed, const QModelIndex &index)
{
QAbstractItemModel *model = view->model();
// Using Ctrl+click to enter stasis
if (controlPressed && clickedProperty->canHaveStasis) {
// STEP 0: Prepare to Enter or Leave control key stasis
quint16 numberOfLeaves = model->rowCount(index.parent());
QModelIndex eachItem;
// STEP 1: Go.
if (clickedProperty->isInStasis == false) { // Enter
/* Make every leaf of this node go State = False, saving the old property value to stateInStasis */
for (quint16 i = 0; i < numberOfLeaves; ++i) { // Foreach leaf in the node (index.parent())
eachItem = model->index(i, 1, index.parent());
// The entire property list has to be altered because model->setData cannot set individual properties
KisBaseNode::PropertyList eachPropertyList = eachItem.data(KisNodeModel::PropertiesRole).value