diff --git a/krita/krita.action b/krita/krita.action
index b909c360e6..0a9057c509 100644
--- a/krita/krita.action
+++ b/krita/krita.action
@@ -1,2926 +1,2949 @@
General
Open Resources Folder
Opens a file browser at the location Krita saves resources such as brushes to.
Opens a file browser at the location Krita saves resources such as brushes to.
Open Resources Folder
0
0
false
Cleanup removed files...
Cleanup removed files
Cleanup removed files
0
0
false
C&ascade
Cascade
Cascade
10
0
false
&Tile
Tile
Tile
10
0
false
Import Resources or Bundles...
Import Resources or Bundles
Import Resources or Bundles
0
0
false
Create Resource Bundle...
Create Resource Bundle
Create Resource Bundle
0
0
false
Show File Toolbar
Show File Toolbar
Show File Toolbar
false
Show color selector
Show color selector
Show color selector
Shift+I
false
Show MyPaint shade selector
Show MyPaint shade selector
Show MyPaint shade selector
Shift+M
false
Show minimal shade selector
Show minimal shade selector
Show minimal shade selector
Shift+N
false
Show color history
Show color history
Show color history
H
false
Show common colors
Show common colors
Show common colors
U
false
Show Tool Options
Show Tool Options
Show Tool Options
\
false
Show Brush Editor
Show Brush Editor
Show Brush Editor
F5
false
Show Brush Presets
Show Brush Presets
Show Brush Presets
F6
false
Toggle Tablet Debugger
Toggle Tablet Debugger
Toggle Tablet Debugger
0
0
Ctrl+Shift+T
false
Rename Composition...
Rename Composition
Rename Composition
0
0
false
Painting
Make brush color lighter
Make brush color lighter
Make brush color lighter
0
0
L
false
Make brush color darker
Make brush color darker
Make brush color darker
0
0
K
false
Make brush color more saturated
Make brush color more saturated
Make brush color more saturated
false
Make brush color more desaturated
Make brush color more desaturated
Make brush color more desaturated
false
Shift brush color hue clockwise
Shift brush color hue clockwise
Shift brush color hue clockwise
false
Shift brush color hue counter-clockwise
Shift brush color hue counter-clockwise
Shift brush color hue counter-clockwise
false
Make brush color more red
Make brush color more red
Make brush color more red
false
Make brush color more green
Make brush color more green
Make brush color more green
false
Make brush color more blue
Make brush color more blue
Make brush color more blue
false
Make brush color more yellow
Make brush color more yellow
Make brush color more yellow
false
Increase opacity
Increase opacity
Increase opacity
0
0
O
false
Decrease opacity
Decrease opacity
Decrease opacity
0
0
I
false
draw-eraser
Set eraser mode
Set eraser mode
Set eraser mode
10000
0
E
true
view-refresh
Reload Original Preset
Reload Original Preset
Reload Original Preset
10000
false
transparency-unlocked
Preserve Alpha
Preserve Alpha
Preserve Alpha
10000
true
+
+ transform_icons_penPressure
+ Use Pen Pressure
+
+ Use Pen Pressure
+ Use Pen Pressure
+ 10000
+
+ true
+
+
symmetry-horizontal
Horizontal Mirror Tool
Horizontal Mirror Tool
Horizontal Mirror Tool
10000
true
symmetry-vertical
Vertical Mirror Tool
Vertical Mirror Tool
Vertical Mirror Tool
10000
true
Paste at cursor
Paste at cursor
Paste at cursor
0
0
false
&Invert Selection
Invert current selection
Invert Selection
10000000000
100
Ctrl+Shift+I
false
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
&Toggle Selection Display Mode
Toggle Selection Display Mode
Toggle Selection Display Mode
0
0
false
Next Favourite Preset
Next Favourite Preset
Next Favourite Preset
,
false
Previous Favourite Preset
Previous Favourite Preset
Previous Favourite Preset
.
false
Switch to Previous Preset
Switch to Previous Preset
Switch to Previous Preset
/
false
Hide Brushes and Stuff Toolbar
Hide Brushes and Stuff Toolbar
Hide Brushes and Stuff Toolbar
true
zoom-in
Zoom &In
Zoom In
Zoom In
Ctrl++; Ctrl+=
false
zoom-out
Zoom &Out
Zoom Out
Zoom Out
Ctrl+-
false
Reset Foreground and Background Color
Reset Foreground and Background Color
Reset Foreground and Background Color
D
false
Swap Foreground and Background Color
Swap Foreground and Background Color
Swap Foreground and Background Color
X
false
Brush Smoothing: Weighted
Brush Smoothing: Weighted
Brush Smoothing: Weighted
false
Brush Smoothing: Disabled
Brush Smoothing: Disabled
Brush Smoothing: Disabled
false
Brush Smoothing: Stabilizer
Brush Smoothing: Stabilizer
Brush Smoothing: Stabilizer
false
Decrease Brush Size
Decrease Brush Size
Decrease Brush Size
0
0
[
false
Brush Smoothing: Basic
Brush Smoothing: Basic
Brush Smoothing: Basic
false
Increase Brush Size
Increase Brush Size
Increase Brush Size
0
0
]
false
Toggle Assistant
Toggle Assistant
ToggleAssistant
Ctrl+Shift+L
true
Undo Polygon Selection Points
Undo Polygon Selection Points
Undo Polygon Selection Points
Shift+Z
false
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
Convert &to Shape
Convert to Shape
Convert to Shape
10000000000
0
false
&Select Opaque
Select Opaque
Select Opaque
100000
100
false
&Show Global Selection Mask
Shows global selection as a usual selection mask in <interface>Layers</interface> docker
Show Global Selection Mask
0
0
true
Filters
&Color to Alpha...
Color to Alpha
Color to Alpha
10000
0
false
&Top Edge Detection
Top Edge Detection
Top Edge Detection
10000
0
false
&Index Colors...
Index Colors
Index Colors
10000
0
false
Emboss Horizontal &Only
Emboss Horizontal Only
Emboss Horizontal Only
10000
0
false
D&odge
Dodge
Dodge
10000
0
false
&Sharpen
Sharpen
Sharpen
10000
0
false
B&urn
Burn
Burn
10000
0
false
&Mean Removal
Mean Removal
Mean Removal
10000
0
false
&Gaussian Blur...
Gaussian Blur
Gaussian Blur
10000
0
false
Emboss &in All Directions
Emboss in All Directions
Emboss in All Directions
10000
0
false
&Small Tiles...
Small Tiles
Small Tiles
10000
0
false
&Levels...
Levels
Levels
10000
0
Ctrl+L
false
&Sobel...
Sobel
Sobel
10000
0
false
&Wave...
Wave
Wave
10000
0
false
&Motion Blur...
Motion Blur
Motion Blur
10000
0
false
&Color Adjustment curves...
Color Adjustment curves
Color Adjustment curves
10000
0
Ctrl+M
false
Pi&xelize...
Pixelize
Pixelize
10000
0
false
Emboss (&Laplacian)
Emboss (Laplacian)
Emboss (Laplacian)
10000
0
false
&Left Edge Detection
Left Edge Detection
Left Edge Detection
10000
0
false
&Blur...
Blur
Blur
10000
0
false
&Raindrops...
Raindrops
Raindrops
10000
0
false
&Bottom Edge Detection
Bottom Edge Detection
Bottom Edge Detection
10000
0
false
&Random Noise...
Random Noise
Random Noise
10000
0
false
&Brightness/Contrast curve...
Brightness/Contrast curve
Brightness/Contrast curve
10000
0
false
Colo&r Balance..
Color Balance..
Color Balance..
10000
0
Ctrl+B
false
&PhongBumpmap...
PhongBumpmap
PhongBumpmap
10000
0
false
&Desaturate
Desaturate
Desaturate
10000
0
Ctrl+Shift+U
false
Color &Transfer...
Color Transfer
Color Transfer
10000
0
false
Emboss &Vertical Only
Emboss Vertical Only
Emboss Vertical Only
10000
0
false
&Lens Blur...
Lens Blur
Lens Blur
10000
0
false
M&inimize Channel
Minimize Channel
Minimize Channel
10000
0
false
M&aximize Channel
Maximize Channel
Maximize Channel
10000
0
false
&Oilpaint...
Oilpaint
Oilpaint
10000
0
false
&Right Edge Detection
Right Edge Detection
Right Edge Detection
10000
0
false
&Auto Contrast
Auto Contrast
Auto Contrast
10000
0
false
&Round Corners...
Round Corners
Round Corners
10000
0
false
&Unsharp Mask...
Unsharp Mask
Unsharp Mask
10000
0
false
&Emboss with Variable Depth...
Emboss with Variable Depth
Emboss with Variable Depth
10000
0
false
Emboss &Horizontal && Vertical
Emboss Horizontal & Vertical
Emboss Horizontal & Vertical
10000
0
false
Random &Pick...
Random Pick
Random Pick
10000
0
false
&Gaussian Noise Reduction...
Gaussian Noise Reduction
Gaussian Noise Reduction
10000
0
false
&Posterize...
Posterize
Posterize
10000
0
false
&Wavelet Noise Reducer...
Wavelet Noise Reducer
Wavelet Noise Reducer
10000
0
false
&HSV Adjustment...
HSV Adjustment
HSV Adjustment
10000
0
Ctrl+U
false
Tool Shortcuts
Dynamic Brush Tool
Dynamic Brush Tool
Dynamic Brush Tool
false
Crop Tool
Crop the image to an area
Crop the image to an area
C
false
Polygon Tool
Polygon Tool. Shift-mouseclick ends the polygon.
Polygon Tool. Shift-mouseclick ends the polygon.
false
References
References
References
false
Rectangle Tool
Rectangle Tool
Rectangle Tool
false
Multibrush Tool
Multibrush Tool
Multibrush Tool
Q
false
Shape Manipulation Tool
Shape Manipulation Tool
Shape Manipulation Tool
false
Color Picker
Select a color from the image or current layer
Select a color from the image or current layer
P
false
Text Editing Tool
Text editing
Text editing
false
Outline Selection Tool
Outline Selection Tool
Outline Selection Tool
false
Artistic Text Tool
Artistic text editing
Artistic text editing
false
Bezier Curve Selection Tool
Select a
Bezier Curve Selection Tool
false
Similar Color Selection Tool
Select a
Similar Color Selection Tool
false
Fill Tool
Fill a contiguous area of color with a color, or fill a selection.
Fill a contiguous area of color with a color, or fill a selection.
F
false
Line Tool
Line Tool
Line Tool
false
Freehand Path Tool
Freehand Path Tool
Freehand Path Tool
false
Bezier Curve Tool
Bezier Curve Tool. Shift-mouseclick ends the curve.
Bezier Curve Tool. Shift-mouseclick ends the curve.
false
Ellipse Tool
Ellipse Tool
Ellipse Tool
false
Freehand Brush Tool
Freehand Brush Tool
Freehand Brush Tool
B
false
Create object
Create object
Create object
false
Elliptical Selection Tool
Elliptical Selection Tool
Elliptical Selection Tool
J
false
Contiguous Selection Tool
Contiguous Selection Tool
Contiguous Selection Tool
false
Pattern editing
Pattern editing
Pattern editing
false
Review
Review
Review
false
Draw a gradient.
Draw a gradient.
Draw a gradient.
G
false
Polygonal Selection Tool
Polygonal Selection Tool
Polygonal Selection Tool
false
Measurement Tool
Measure the distance between two points
Measure the distance between two points
false
Rectangular Selection Tool
Rectangular Selection Tool
Rectangular Selection Tool
Ctrl+R
false
Move Tool
Move a layer
Move a layer
T
false
Vector Image Tool
Vector Image (EMF/WMF/SVM/SVG) tool
Vector Image (EMF/WMF/SVM/SVG) tool
false
Calligraphy
Calligraphy
Calligraphy
false
Path editing
Path editing
Path editing
false
Polyline Tool
Polyline Tool. Shift-mouseclick ends the polyline.
Polyline Tool. Shift-mouseclick ends the polyline.
false
Transform Tool
Transform a layer or a selection
Transform a layer or a selection
Ctrl+T
false
Ruler assistant editor tool
Ruler assistant editor tool
Ruler assistant editor tool
false
Text tool
Text tool
Text tool
false
Gradient Editing Tool
Gradient editing
Gradient editing
false
Brush Selection Tool
Brush Selection Tool
Brush Selection Tool
false
Blending Modes
Next Blending Mode
Next Blending Mode
Next Blending Mode
Alt+Shift++
false
Previous Blending Mode
Previous Blending Mode
Previous Blending Mode
Alt+Shift+-
false
Select Normal Blending Mode
Select Normal Blending Mode
Select Normal Blending Mode
0
0
Alt+Shift+N
false
Select Dissolve Blending Mode
Select Dissolve Blending Mode
Select Dissolve Blending Mode
0
0
Alt+Shift+I
false
Select Behind Blending Mode
Select Behind Blending Mode
Select Behind Blending Mode
0
0
Alt+Shift+Q
false
Select Clear Blending Mode
Select Clear Blending Mode
Select Clear Blending Mode
0
0
Alt+Shift+R
false
Select Darken Blending Mode
Select Darken Blending Mode
Select Darken Blending Mode
0
0
Alt+Shift+K
false
Select Multiply Blending Mode
Select Multiply Blending Mode
Select Multiply Blending Mode
0
0
Alt+Shift+M
false
Select Color Burn Blending Mode
Select Color Burn Blending Mode
Select Color Burn Blending Mode
0
0
Alt+Shift+B
false
Select Linear Burn Blending Mode
Select Linear Burn Blending Mode
Select Linear Burn Blending Mode
0
0
Alt+Shift+A
false
Select Lighten Blending Mode
Select Lighten Blending Mode
Select Lighten Blending Mode
0
0
Alt+Shift+G
false
Select Screen Blending Mode
Select Screen Blending Mode
Select Screen Blending Mode
0
0
Alt+Shift+S
false
Select Color Dodge Blending Mode
Select Color Dodge Blending Mode
Select Color Dodge Blending Mode
0
0
Alt+Shift+D
false
Select Linear Dodge Blending Mode
Select Linear Dodge Blending Mode
Select Linear Dodge Blending Mode
0
0
Alt+Shift+W
false
Select Overlay Blending Mode
Select Overlay Blending Mode
Select Overlay Blending Mode
0
0
Alt+Shift+O
false
Select Soft Light Blending Mode
Select Soft Light Blending Mode
Select Soft Light Blending Mode
0
0
Alt+Shift+F
false
Select Hard Light Blending Mode
Select Hard Light Blending Mode
Select Hard Light Blending Mode
0
0
Alt+Shift+H
false
Select Vivid Light Blending Mode
Select Vivid Light Blending Mode
Select Vivid Light Blending Mode
0
0
Alt+Shift+V
false
Select Linear Light Blending Mode
Select Linear Light Blending Mode
Select Linear Light Blending Mode
0
0
Alt+Shift+J
false
Select Pin Light Blending Mode
Select Pin Light Blending Mode
Select Pin Light Blending Mode
0
0
Alt+Shift+Z
false
Select Hard Mix Blending Mode
Select Hard Mix Blending Mode
Select Hard Mix Blending Mode
0
0
Alt+Shift+L
false
Select Difference Blending Mode
Select Difference Blending Mode
Select Difference Blending Mode
0
0
Alt+Shift+E
false
Select Exclusion Blending Mode
Select Exclusion Blending Mode
Select Exclusion Blending Mode
0
0
Alt+Shift+X
false
Select Hue Blending Mode
Select Hue Blending Mode
Select Hue Blending Mode
0
0
Alt+Shift+U
false
Select Saturation Blending Mode
Select Saturation Blending Mode
Select Saturation Blending Mode
0
0
Alt+Shift+T
false
Select Color Blending Mode
Select Color Blending Mode
Select Color Blending Mode
0
0
Alt+Shift+C
false
Select Luminosity Blending Mode
Select Luminosity Blending Mode
Select Luminosity Blending Mode
0
0
Alt+Shift+Y
false
Animation
Previous frame
Move to previous frame
Move to previous frame
1
0
false
Next frame
Move to next frame
Move to next frame
1
0
false
Play / pause animation
Play / pause animation
Play / pause animation
1
0
false
Add blank frame
Add blank frame
Add blank frame
100000
0
false
Copy Frame
Add duplicate frame
Add duplicate frame
100000
0
false
Toggle onion skin
Toggle onion skin
Toggle onion skin
100000
0
false
Previous Keyframe
false
Next Keyframe
false
First Frame
false
Last Frame
false
Auto Frame Mode
true
true
Add blank frame
Add blank frame
Add blank frame
100000
0
false
Show in Timeline
true
Layers
Activate next layer
Activate next layer
Activate next layer
100000
0
PgUp
false
Activate previous layer
Activate previous layer
Activate previous layer
100000
0
PgDown
false
groupLayer
&Group Layer
Group Layer
Group Layer
1000
0
false
cloneLayer
&Clone Layer
Clone Layer
Clone Layer
1000
0
false
vectorLayer
&Vector Layer
Vector Layer
Vector Layer
1000
0
false
filterLayer
&Filter Layer...
Filter Layer
Filter Layer
1000
0
false
fillLayer
&Fill Layer...
Fill Layer
Fill Layer
1000
0
false
fileLayer
&File Layer...
File Layer
File Layer
1000
0
false
transparencyMask
&Transparency Mask
Transparency Mask
Transparency Mask
100000
0
false
filterMask
&Filter Mask...
Filter Mask
Filter Mask
100000
0
false
transformMask
&Transform Mask...
Transform Mask
Transform Mask
100000
0
false
selectionMask
&Local Selection
Local Selection
Local Selection
100000
0
false
view-filter
&Isolate Layer
Isolate Layer
Isolate Layer
1000
0
true
paintLayer
&Paint Layer
Paint Layer
Paint Layer
1000
0
Insert
false
duplicatelayer
&Duplicate Layer or Mask
Duplicate Layer or Mask
Duplicate Layer or Mask
1000
0
Ctrl+J
false
&Cut Selection to New Layer
Cut Selection to New Layer
Cut Selection to New Layer
100000000
1
Ctrl+Shift+J
false
Copy &Selection to New Layer
Copy Selection to New Layer
Copy Selection to New Layer
100000000
0
Ctrl+Alt+J
false
Copy Layer
Copy layer to clipboard
Copy layer to clipboard
0
0
false
Cut Layer
Cut layer to clipboard
Cut layer to clipboard
0
0
false
Paste Layer
Paste layer from clipboard
Paste layer from clipboard
100000000000000
0
false
Quick Group
Create a group layer containing selected layers
Quick Group
0
0
Ctrl+G
false
Quick Ungroup
Remove grouping of the layers or remove one layer out of the group
Quick Ungroup
0
0
Ctrl+Alt+G
false
Quick Clipping Group
Group selected layers and add a layer with clipped alpha channel
Quick Clipping Group
0
0
Ctrl+Shift+G
false
All Layers
Select all layers
Select all layers
0
0
false
Visible Layers
Select all visible layers
Select all visible layers
0
0
false
Locked Layers
Select all locked layers
Select all locked layers
0
0
false
Invisible Layers
Select all invisible layers
Select all invisible layers
0
0
false
Unlocked Layers
Select all unlocked layers
Select all unlocked layers
0
0
false
document-save
&Save Layer/Mask...
Save Layer/Mask
Save Layer/Mask
1000
0
false
document-save
Save &Group Layers...
Save Group Layers
Save Group Layers
100000
0
false
Convert group to &animated layer
Convert child layers into animation frames
Convert child layers into animation frames
100000
0
false
I&mport Layer...
Import Layer
Import Layer
100000
0
false
paintLayer
&as Paint Layer...
as Paint Layer
as Paint Layer
1000
0
false
transparencyMask
as &Transparency Mask...
as Transparency Mask
as Transparency Mask
1000
0
false
filterMask
as &Filter Mask...
as Filter Mask
as Filter Mask
1000
0
false
selectionMask
as &Selection Mask...
as Selection Mask
as Selection Mask
1000
0
false
paintLayer
to &Paint Layer
to Paint Layer
to Paint Layer
1000
0
false
transparencyMask
to &Transparency Mask
to Transparency Mask
to Transparency Mask
1000
0
false
filterMask
to &Filter Mask...
to Filter Mask
to Filter Mask
1000
0
false
selectionMask
to &Selection Mask
to Selection Mask
to Selection Mask
1000
0
false
transparencyMask
&Alpha into Mask
Alpha into Mask
Alpha into Mask
100000
10
false
transparency-enabled
&Write as Alpha
Write as Alpha
Write as Alpha
1000000
1
false
document-save
&Save Merged...
Save Merged
Save Merged
1000000
0
false
Split Layer...
Split Layer
Split Layer
1000
0
false
+
+
+ Wavelet Decompose ...
+
+ Wavelet Decompose
+ Wavelet Decompose
+ 1000
+ 1
+
+ false
+
+
symmetry-horizontal
Mirror Layer Hori&zontally
Mirror Layer Horizontally
Mirror Layer Horizontally
1000
1
false
symmetry-vertical
Mirror Layer &Vertically
Mirror Layer Vertically
Mirror Layer Vertically
1000
1
false
&Rotate Layer...
Rotate Layer
Rotate Layer
100000
1
false
object-rotate-right
Rotate &Layer 90° to the Right
Rotate Layer 90° to the Right
Rotate Layer 90° to the Right
100000
1
false
object-rotate-left
Rotate Layer &90° to the Left
Rotate Layer 90° to the Left
Rotate Layer 90° to the Left
100000
1
false
Rotate Layer &180°
Rotate Layer 180°
Rotate Layer 180°
100000
1
false
Scale &Layer to new Size...
Scale Layer to new Size
Scale Layer to new Size
100000
1
false
&Shear Layer...
Shear Layer
Shear Layer
100000
1
false
&Offset Layer...
Offset Layer
Offset Layer
100000
1
false
Clones &Array...
Clones Array
Clones Array
100000
0
false
&Edit metadata...
Edit metadata
Edit metadata
100000
1
false
&Histogram...
Histogram
Histogram
100000
0
false
&Convert Layer Color Space...
Convert Layer Color Space
Convert Layer Color Space
100000
1
false
&Merge with Layer Below
Merge with Layer Below
Merge with Layer Below
100000
0
Ctrl+E
false
&Flatten Layer
Flatten Layer
Flatten Layer
100000
0
false
Ras&terize Layer
Rasterize Layer
Rasterize Layer
10000000
1
false
Flatten ima&ge
Flatten image
Flatten image
100000
0
Ctrl+Shift+E
false
&Merge Selected Layers
Merge Selected Layers
Merge Selected Layers
Ctrl+Alt+E
false
La&yer Style...
Layer Style
Layer Style
100000
1
false
Move into previous group
Move into previous group
Move into previous group
0
0
false
Move into next group
Move into next group
Move into next group
0
0
false
Rename current layer
Rename current layer
Rename current layer
100000
0
F2
false
deletelayer
&Remove Layer
Remove Layer
Remove Layer
1000
1
Shift+Delete
false
arrowupblr
Move Layer or Mask Up
Move Layer or Mask Up
Ctrl+PgUp
false
arrowdown
Move Layer or Mask Down
Move Layer or Mask Down
Ctrl+PgDown
false
properties
&Properties...
Properties
Properties
1000
1
F3
false
diff --git a/krita/krita.xmlgui b/krita/krita.xmlgui
index ad9fbd5d6a..b3de40898e 100644
--- a/krita/krita.xmlgui
+++ b/krita/krita.xmlgui
@@ -1,360 +1,369 @@
&Image
&Rotate
+
&Layer
+
+
+
+
New
&Import/Export
Import
&Convert
+
&Select
-
-
-
-
-
-
- S&plit Alpha
-
-
-
+
+ &Group
+
+
+
-
-
-
-
-
- &Rotate
-
-
-
-
-
+
+ &Transform
+
+
+
+
+ &Rotate
+
+
+
+
+
+
+
+
+
+
+ S&plit
+
+ S&plit Alpha
+
+
+
+
+
+
-
-
-
-
+
-
-
-
-
-
+
&Select
Filte&r
&Tools
Recording
Macros
Setti&ngs
&Help
File
Brushes and Stuff
diff --git a/krita/pics/tool_transform/dark_transform_icons_penPressure_locked.png b/krita/pics/tool_transform/dark_transform_icons_penPressure_locked.png
new file mode 100644
index 0000000000..f18f35ffec
Binary files /dev/null and b/krita/pics/tool_transform/dark_transform_icons_penPressure_locked.png differ
diff --git a/krita/pics/tool_transform/light_transform_icons_penPressure_locked.png b/krita/pics/tool_transform/light_transform_icons_penPressure_locked.png
new file mode 100644
index 0000000000..14b0a62405
Binary files /dev/null and b/krita/pics/tool_transform/light_transform_icons_penPressure_locked.png differ
diff --git a/krita/pics/tool_transform/tool-transform-icons.qrc b/krita/pics/tool_transform/tool-transform-icons.qrc
index ae594e1ee3..023352cce6 100644
--- a/krita/pics/tool_transform/tool-transform-icons.qrc
+++ b/krita/pics/tool_transform/tool-transform-icons.qrc
@@ -1,40 +1,40 @@
-
-
-
+
+
dark_transform_icons_cage.png
dark_transform_icons_liquify_erase.png
dark_transform_icons_liquify_main.png
dark_transform_icons_liquify_move.png
dark_transform_icons_liquify_offset.png
dark_transform_icons_liquify_resize.png
dark_transform_icons_liquify_rotate.png
dark_transform_icons_liquify_rotateCCW.png
dark_transform_icons_main.png
dark_transform_icons_perspective.png
dark_transform_icons_warp.png
dark_transform_icons_penPressure.png
dark_krita_tool_transform_recursive.png
dark_transform_icons_mirror_x.svg
dark_transform_icons_mirror_y.svg
dark_transform_icons_rotate_cw.svg
dark_transform_icons_rotate_ccw.svg
-
light_transform_icons_cage.png
light_transform_icons_liquify_erase.png
light_transform_icons_liquify_main.png
light_transform_icons_liquify_move.png
light_transform_icons_liquify_offset.png
light_transform_icons_liquify_resize.png
light_transform_icons_liquify_rotate.png
light_transform_icons_liquify_rotateCCW.png
light_transform_icons_main.png
light_transform_icons_perspective.png
light_transform_icons_warp.png
light_transform_icons_penPressure.png
light_krita_tool_transform_recursive.png
light_transform_icons_mirror_x.svg
light_transform_icons_mirror_y.svg
light_transform_icons_rotate_cw.svg
light_transform_icons_rotate_ccw.svg
+ dark_transform_icons_penPressure_locked.png
+ light_transform_icons_penPressure_locked.png
diff --git a/libs/koplugin/KisMimeDatabase.cpp b/libs/koplugin/KisMimeDatabase.cpp
index 72eb8f1097..2346bbb89e 100644
--- a/libs/koplugin/KisMimeDatabase.cpp
+++ b/libs/koplugin/KisMimeDatabase.cpp
@@ -1,264 +1,264 @@
/*
* Copyright (c) 2016 Boudewijn Rempt
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisMimeDatabase.h"
#include
#include
#include
#include
#include
QList KisMimeDatabase::s_mimeDatabase;
QString KisMimeDatabase::mimeTypeForFile(const QString &file)
{
fillMimeData();
QFileInfo fi(file);
- QString suffix = fi.suffix();
+ QString suffix = fi.suffix().toLower();
Q_FOREACH(const KisMimeDatabase::KisMimeType &mimeType, s_mimeDatabase) {
if (mimeType.suffixes.contains("*." + suffix)) {
debugPlugin << "mimeTypeForFile(). KisMimeDatabase returned" << mimeType.mimeType << "for" << file;
return mimeType.mimeType;
}
}
QMimeDatabase db;
QMimeType mime = db.mimeTypeForFile(file);
if (mime.name() != "application/octet-stream") {
debugPlugin << "mimeTypeForFile(). QMimeDatabase returned" << mime.name() << "for" << file;
return mime.name();
}
return "";
}
QString KisMimeDatabase::mimeTypeForSuffix(const QString &suffix)
{
fillMimeData();
QMimeDatabase db;
- QString s = suffix;
+ QString s = suffix.toLower();
if (!s.startsWith("*.")) {
s = "*." + s;
}
Q_FOREACH(const KisMimeDatabase::KisMimeType &mimeType, s_mimeDatabase) {
if (mimeType.suffixes.contains(s)) {
debugPlugin << "mimeTypeForSuffix(). KisMimeDatabase returned" << mimeType.mimeType << "for" << s;
return mimeType.mimeType;
}
}
QMimeType mime = db.mimeTypeForFile(s);
if (mime.name() != "application/octet-stream") {
debugPlugin << "mimeTypeForSuffix(). QMimeDatabase returned" << mime.name() << "for" << s;
return mime.name();
}
return "";
}
QString KisMimeDatabase::mimeTypeForData(const QByteArray ba)
{
QMimeDatabase db;
QMimeType mtp = db.mimeTypeForData(ba);
debugPlugin << "mimeTypeForData(). QMimeDatabase returned" << mtp.name();
return mtp.name();
}
QString KisMimeDatabase::descriptionForMimeType(const QString &mimeType)
{
fillMimeData();
Q_FOREACH(const KisMimeDatabase::KisMimeType &m, s_mimeDatabase) {
if (m.mimeType == mimeType) {
debugPlugin << "descriptionForMimeType. KisMimeDatabase returned" << m.description << "for" << mimeType;
return m.description;
}
}
QMimeDatabase db;
QMimeType mime = db.mimeTypeForName(mimeType);
if (mime.name() != "application/octet-stream") {
debugPlugin << "descriptionForMimeType. QMimeDatabase returned" << mime.comment() << "for" << mimeType;
return mime.comment();
}
return mimeType;
}
QStringList KisMimeDatabase::suffixesForMimeType(const QString &mimeType)
{
fillMimeData();
Q_FOREACH(const KisMimeDatabase::KisMimeType &m, s_mimeDatabase) {
if (m.mimeType == mimeType) {
debugPlugin << "suffixesForMimeType. KisMimeDatabase returned" << m.suffixes;
return m.suffixes;
}
}
QMimeDatabase db;
QMimeType mime = db.mimeTypeForName(mimeType);
if (mime.name() != "application/octet-stream" && !mime.suffixes().isEmpty()) {
QString preferredSuffix = mime.preferredSuffix();
QStringList suffixes = mime.suffixes();
if (preferredSuffix != suffixes.first()) {
suffixes.removeAll(preferredSuffix);
suffixes.prepend(preferredSuffix);
}
debugPlugin << "suffixesForMimeType. QMimeDatabase returned" << suffixes;
return suffixes;
}
return QStringList();
}
QString KisMimeDatabase::iconNameForMimeType(const QString &mimeType)
{
QMimeDatabase db;
QMimeType mime = db.mimeTypeForName(mimeType);
debugPlugin << "iconNameForMimeType" << mime.iconName();
return mime.iconName();
}
void KisMimeDatabase::fillMimeData()
{
// This should come from the import/export plugins, but the json files aren't translated,
// which is bad for the description field
if (s_mimeDatabase.isEmpty()) {
KisMimeType mimeType;
mimeType.mimeType = "image/x-gimp-brush";
mimeType.description = i18nc("description of a file type", "Gimp Brush");
mimeType.suffixes = QStringList() << "*.gbr" << "*.vbr";
s_mimeDatabase << mimeType;
mimeType.mimeType = "image/x-gimp-brush-animated";
mimeType.description = i18nc("description of a file type", "Gimp Image Hose Brush");
mimeType.suffixes = QStringList() << "*.gih";
s_mimeDatabase << mimeType;
mimeType.mimeType = "image/x-adobe-brushlibrary";
mimeType.description = i18nc("description of a file type", "Adobe Brush Library");
mimeType.suffixes = QStringList() << "*.abr";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-krita-paintoppreset";
mimeType.description = i18nc("description of a file type", "Krita Brush Preset");
mimeType.suffixes = QStringList() << "*.kpp";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-krita-assistant";
mimeType.description = i18nc("description of a file type", "Krita Assistant");
mimeType.suffixes = QStringList() << "*.paintingassistant";
s_mimeDatabase << mimeType;
mimeType.mimeType = "image/x-r16";
mimeType.description = i18nc("description of a file type", "R16 Heightmap");
mimeType.suffixes = QStringList() << "*.r16";
s_mimeDatabase << mimeType;
mimeType.mimeType = "image/x-r8";
mimeType.description = i18nc("description of a file type", "R8 Heightmap");
mimeType.suffixes = QStringList() << "*.r8";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-spriter";
mimeType.description = i18nc("description of a file type", "Spriter SCML");
mimeType.suffixes = QStringList() << "*.scml";
s_mimeDatabase << mimeType;
mimeType.mimeType = "image/x-svm";
mimeType.description = i18nc("description of a file type", "Starview Metafile");
mimeType.suffixes = QStringList() << "*.svm";
s_mimeDatabase << mimeType;
mimeType.mimeType = "image/openraster";
mimeType.description = i18nc("description of a file type", "OpenRaster Image");
mimeType.suffixes = QStringList() << "*.ora";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-photoshop-style-library";
mimeType.description = i18nc("description of a file type", "Photoshop Layer Style Library");
mimeType.suffixes = QStringList() << "*.asl";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-gimp-color-palette";
mimeType.description = i18nc("description of a file type", "Gimp Color Palette");
mimeType.suffixes = QStringList() << "*.gpl" << "*.pal" << "*.act" << "*.aco" << "*.colors";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-opencolorio-configuration";
mimeType.description = i18nc("description of a file type", "OpenColorIO Configuration");
mimeType.suffixes = QStringList() << "*.ocio";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-krita-recorded-macro";
mimeType.description = i18nc("description of a file type", "Krita Recorded Action");
mimeType.suffixes = QStringList() << "*.krarec";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-gimp-gradient";
mimeType.description = i18nc("description of a file type", "GIMP Gradients");
mimeType.suffixes = QStringList() << "*.ggr";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-gimp-pattern";
mimeType.description = i18nc("description of a file type", "GIMP Patterns");
mimeType.suffixes = QStringList() << "*.pat";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-karbon-gradient";
mimeType.description = i18nc("description of a file type", "Karbon Gradients");
mimeType.suffixes = QStringList() << "*.kgr";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-krita-bundle";
mimeType.description = i18nc("description of a file type", "Krita Resource Bundle");
mimeType.suffixes = QStringList() << "*.bundle";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-krita-workspace";
mimeType.description = i18nc("description of a file type", "Krita Workspace");
mimeType.suffixes = QStringList() << "*.kws";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-krita-taskset";
mimeType.description = i18nc("description of a file type", "Krita Taskset");
mimeType.suffixes = QStringList() << "*.kts";
s_mimeDatabase << mimeType;
mimeType.mimeType = "image/x-krita-raw";
mimeType.description = i18nc("description of a file type", "Camera Raw Files");
mimeType.suffixes = QStringList() << "*.nef" << "*.cr2" << "*.sr2" << "*.crw" << "*.pef" << "*.x3f" << "*.kdc" << "*.mrw" << "*.arw" << "*.k25" << "*.dcr" << "*.orf" << "*.raw" << "*.raw" << "*.raf" << "*.srf" << "*.dng";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-extension-exr";
mimeType.description = i18nc("description of a file type", "OpenEXR (Extended)");
mimeType.suffixes = QStringList() << "*.exr";
s_mimeDatabase << mimeType;
debugPlugin << "Filled mimedatabase with" << s_mimeDatabase.count() << "special mimetypes";
}
}
diff --git a/libs/ui/kis_canvas_resource_provider.cpp b/libs/ui/kis_canvas_resource_provider.cpp
index 84a4ba7ea9..6eb7a20e7a 100644
--- a/libs/ui/kis_canvas_resource_provider.cpp
+++ b/libs/ui/kis_canvas_resource_provider.cpp
@@ -1,544 +1,554 @@
/*
* Copyright (c) 2006 Boudewijn Rempt
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_canvas_resource_provider.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kis_favorite_resource_manager.h"
#include "kis_config.h"
#include "KisViewManager.h"
#include "canvas/kis_canvas2.h"
KisCanvasResourceProvider::KisCanvasResourceProvider(KisViewManager * view)
: m_view(view)
{
m_fGChanged = true;
m_enablefGChange = true; // default to true, so that colour history is working without popup palette
}
KisCanvasResourceProvider::~KisCanvasResourceProvider()
{
disconnect(); // in case Qt gets confused
}
KoCanvasResourceManager* KisCanvasResourceProvider::resourceManager()
{
return m_resourceManager;
}
void KisCanvasResourceProvider::setResourceManager(KoCanvasResourceManager *resourceManager)
{
m_resourceManager = resourceManager;
QVariant v;
v.setValue(KoColor(Qt::black, KoColorSpaceRegistry::instance()->rgb8()));
m_resourceManager->setResource(KoCanvasResourceManager::ForegroundColor, v);
v.setValue(KoColor(Qt::white, KoColorSpaceRegistry::instance()->rgb8()));
m_resourceManager->setResource(KoCanvasResourceManager::BackgroundColor, v);
setCurrentCompositeOp(COMPOSITE_OVER);
setMirrorHorizontal(false);
setMirrorVertical(false);
m_resourceManager->setResource(HdrExposure, 0.0);
m_resourceManager->setResource(HdrGamma, 1.0);
m_resourceManager->setResource(EffectiveZoom, 1.0);
connect(m_resourceManager, SIGNAL(canvasResourceChanged(int,QVariant)),
this, SLOT(slotCanvasResourceChanged(int,QVariant)));
m_resourceManager->setResource(KoCanvasResourceManager::ApplicationSpeciality, KoCanvasResourceManager::NoAdvancedText);
}
KoCanvasBase * KisCanvasResourceProvider::canvas() const
{
return m_view->canvasBase();
}
KoColor KisCanvasResourceProvider::bgColor() const
{
return m_resourceManager->resource(KoCanvasResourceManager::BackgroundColor).value();
}
KoColor KisCanvasResourceProvider::fgColor() const
{
return m_resourceManager->resource(KoCanvasResourceManager::ForegroundColor).value();
}
float KisCanvasResourceProvider::HDRExposure() const
{
return static_cast(m_resourceManager->resource(HdrExposure).toDouble());
}
void KisCanvasResourceProvider::setHDRExposure(float exposure)
{
m_resourceManager->setResource(HdrExposure, static_cast(exposure));
}
float KisCanvasResourceProvider::HDRGamma() const
{
return static_cast(m_resourceManager->resource(HdrGamma).toDouble());
}
void KisCanvasResourceProvider::setHDRGamma(float gamma)
{
m_resourceManager->setResource(HdrGamma, static_cast(gamma));
}
KoPattern * KisCanvasResourceProvider::currentPattern() const
{
if (m_resourceManager->hasResource(CurrentPattern)) {
return m_resourceManager->resource(CurrentPattern).value();
}
else {
return 0;
}
}
KisFilterConfiguration * KisCanvasResourceProvider::currentGeneratorConfiguration() const
{
return m_resourceManager->resource(CurrentGeneratorConfiguration).value();
}
KoAbstractGradient* KisCanvasResourceProvider::currentGradient() const
{
if (m_resourceManager->hasResource(CurrentGradient)) {
return m_resourceManager->resource(CurrentGradient).value();
}
else {
return 0;
}
}
KisImageWSP KisCanvasResourceProvider::currentImage() const
{
return m_view->image();
}
KisNodeSP KisCanvasResourceProvider::currentNode() const
{
return m_view->activeNode();
}
KisPaintOpPresetSP KisCanvasResourceProvider::currentPreset() const
{
KisPaintOpPresetSP preset = m_resourceManager->resource(CurrentPaintOpPreset).value();
return preset;
}
void KisCanvasResourceProvider::setPaintOpPreset(const KisPaintOpPresetSP preset)
{
Q_ASSERT(preset->valid());
Q_ASSERT(!preset->paintOp().id().isEmpty());
Q_ASSERT(preset->settings());
if (!preset) return;
dbgUI << "setPaintOpPreset" << preset->paintOp();
QVariant v;
v.setValue(preset);
m_resourceManager->setResource(CurrentPaintOpPreset, v);
}
KisPaintOpPresetSP KisCanvasResourceProvider::previousPreset() const
{
KisPaintOpPresetSP preset = m_resourceManager->resource(PreviousPaintOpPreset).value();
return preset;
}
void KisCanvasResourceProvider::setPreviousPaintOpPreset(const KisPaintOpPresetSP preset)
{
Q_ASSERT(preset->valid());
Q_ASSERT(!preset->paintOp().id().isEmpty());
Q_ASSERT(preset->settings());
if (!preset) return;
dbgUI << "setPreviousPaintOpPreset" << preset->paintOp();
QVariant v;
v.setValue(preset);
m_resourceManager->setResource(PreviousPaintOpPreset, v);
}
void KisCanvasResourceProvider::slotPatternActivated(KoResource * res)
{
KoPattern *pattern = dynamic_cast(res);
QVariant v;
v.setValue(pattern);
m_resourceManager->setResource(CurrentPattern, v);
emit sigPatternChanged(pattern);
}
void KisCanvasResourceProvider::slotGeneratorConfigurationActivated(KisFilterConfiguration * res)
{
KisFilterConfiguration * generatorConfiguration = dynamic_cast(res);
QVariant v;
v.setValue(generatorConfiguration);
m_resourceManager->setResource(CurrentGeneratorConfiguration, v);
}
void KisCanvasResourceProvider::slotGradientActivated(KoResource *res)
{
KoAbstractGradient * gradient = dynamic_cast(res);
QVariant v;
v.setValue(gradient);
m_resourceManager->setResource(CurrentGradient, v);
emit sigGradientChanged(gradient);
}
void KisCanvasResourceProvider::setBGColor(const KoColor& c)
{
QVariant v;
v.setValue(c);
m_resourceManager->setResource(KoCanvasResourceManager::BackgroundColor, v);
emit sigBGColorChanged(c);
}
void KisCanvasResourceProvider::setFGColor(const KoColor& c)
{
m_fGChanged = true;
QVariant v;
v.setValue(c);
m_resourceManager->setResource(KoCanvasResourceManager::ForegroundColor, v);
emit sigFGColorChanged(c);
}
void KisCanvasResourceProvider::slotSetFGColor(const KoColor& c)
{
setFGColor(c);
}
void KisCanvasResourceProvider::slotSetBGColor(const KoColor& c)
{
setBGColor(c);
}
void KisCanvasResourceProvider::slotNodeActivated(const KisNodeSP node)
{
QVariant v;
v.setValue(KisNodeWSP(node));
m_resourceManager->setResource(CurrentKritaNode, v);
emit sigNodeChanged(currentNode());
}
void KisCanvasResourceProvider::slotImageSizeChanged()
{
if (KisImageWSP image = m_view->image()) {
float fw = image->width() / image->xRes();
float fh = image->height() / image->yRes();
QSizeF postscriptSize(fw, fh);
m_resourceManager->setResource(KoCanvasResourceManager::PageSize, postscriptSize);
}
}
void KisCanvasResourceProvider::slotOnScreenResolutionChanged()
{
KisImageWSP image = m_view->image();
KisCanvas2 *canvas = m_view->canvasBase();
if(!image || !canvas) return;
qreal zoomX, zoomY;
canvas->coordinatesConverter()->zoom(&zoomX, &zoomY);
qreal scaleX = zoomX / image->xRes();
qreal scaleY = zoomY / image->yRes();
emit sigOnScreenResolutionChanged(scaleX, scaleY);
}
void KisCanvasResourceProvider::slotCanvasResourceChanged(int key, const QVariant & res)
{
if(key == KoCanvasResourceManager::ForegroundColor || key == KoCanvasResourceManager::BackgroundColor) {
KoAbstractGradient* resource = KoResourceServerProvider::instance()->gradientServer()->resources()[0];
KoStopGradient* stopGradient = dynamic_cast(resource);
if(stopGradient) {
QList stops;
stops << KoGradientStop(0.0, fgColor()) << KoGradientStop(1.0, KoColor(QColor(0, 0, 0, 0), fgColor().colorSpace()));
stopGradient->setStops(stops);
KoResourceServerProvider::instance()->gradientServer()->updateResource(resource);
}
resource = KoResourceServerProvider::instance()->gradientServer()->resources()[1];
stopGradient = dynamic_cast(resource);
if(stopGradient) {
QList stops;
stops << KoGradientStop(0.0, fgColor()) << KoGradientStop(1.0, bgColor());
stopGradient->setStops(stops);
KoResourceServerProvider::instance()->gradientServer()->updateResource(resource);
}
}
switch (key) {
case(KoCanvasResourceManager::ForegroundColor):
m_fGChanged = true;
emit sigFGColorChanged(res.value());
break;
case(KoCanvasResourceManager::BackgroundColor):
emit sigBGColorChanged(res.value());
break;
case(CurrentPattern):
emit sigPatternChanged(static_cast(res.value()));
break;
case(CurrentGradient):
emit sigGradientChanged(static_cast(res.value()));
break;
case(CurrentKritaNode) :
emit sigNodeChanged(currentNode());
break;
case (Opacity):
{
emit sigOpacityChanged(res.toDouble());
}
default:
;
// Do nothing
};
}
void KisCanvasResourceProvider::setCurrentCompositeOp(const QString& compositeOp)
{
m_resourceManager->setResource(CurrentCompositeOp,
QVariant::fromValue(compositeOp));
}
QString KisCanvasResourceProvider::currentCompositeOp() const
{
return m_resourceManager->resource(CurrentCompositeOp).value();
}
bool KisCanvasResourceProvider::eraserMode() const
{
return m_resourceManager->resource(EraserMode).toBool();
}
void KisCanvasResourceProvider::setEraserMode(bool value)
{
m_resourceManager->setResource(EraserMode,
QVariant::fromValue(value));
}
void KisCanvasResourceProvider::slotPainting()
{
if (m_fGChanged && m_enablefGChange) {
emit sigFGColorUsed(fgColor());
m_fGChanged = false;
}
}
void KisCanvasResourceProvider::slotResetEnableFGChange(bool b)
{
m_enablefGChange = b;
}
QList > KisCanvasResourceProvider::perspectiveGrids() const
{
return m_perspectiveGrids;
}
void KisCanvasResourceProvider::addPerspectiveGrid(KisAbstractPerspectiveGrid* grid)
{
m_perspectiveGrids.append(grid);
}
void KisCanvasResourceProvider::removePerspectiveGrid(KisAbstractPerspectiveGrid* grid)
{
m_perspectiveGrids.removeOne(grid);
}
void KisCanvasResourceProvider::clearPerspectiveGrids()
{
m_perspectiveGrids.clear();
}
void KisCanvasResourceProvider::setMirrorHorizontal(bool mirrorHorizontal)
{
m_resourceManager->setResource(MirrorHorizontal, mirrorHorizontal);
emit mirrorModeChanged();
}
bool KisCanvasResourceProvider::mirrorHorizontal() const
{
return m_resourceManager->resource(MirrorHorizontal).toBool();
}
void KisCanvasResourceProvider::setMirrorVertical(bool mirrorVertical)
{
m_resourceManager->setResource(MirrorVertical, mirrorVertical);
emit mirrorModeChanged();
}
bool KisCanvasResourceProvider::mirrorVertical() const
{
return m_resourceManager->resource(MirrorVertical).toBool();
}
void KisCanvasResourceProvider::setMirrorHorizontalLock(bool isLocked)
{
m_resourceManager->setResource(MirrorHorizontalLock, isLocked);
emit mirrorModeChanged();
}
bool KisCanvasResourceProvider::mirrorHorizontalLock() {
return m_resourceManager->resource(MirrorHorizontalLock).toBool();
}
void KisCanvasResourceProvider::setMirrorVerticalLock(bool isLocked)
{
m_resourceManager->setResource(MirrorVerticalLock, isLocked);
emit mirrorModeChanged();
}
bool KisCanvasResourceProvider::mirrorVerticalHideDecorations() {
return m_resourceManager->resource(MirrorVerticalHideDecorations).toBool();
}
void KisCanvasResourceProvider::setMirrorVerticalHideDecorations(bool hide)
{
m_resourceManager->setResource(MirrorVerticalHideDecorations, hide);
emit mirrorModeChanged();
}
bool KisCanvasResourceProvider::mirrorHorizontalHideDecorations() {
return m_resourceManager->resource(MirrorHorizontalHideDecorations).toBool();
}
void KisCanvasResourceProvider::setMirrorHorizontalHideDecorations(bool hide)
{
m_resourceManager->setResource(MirrorHorizontalHideDecorations, hide);
emit mirrorModeChanged();
}
bool KisCanvasResourceProvider::mirrorVerticalLock() {
return m_resourceManager->resource(MirrorVerticalLock).toBool();
}
void KisCanvasResourceProvider::mirrorVerticalMoveCanvasToCenter() {
emit moveMirrorVerticalCenter();
}
void KisCanvasResourceProvider::mirrorHorizontalMoveCanvasToCenter() {
emit moveMirrorHorizontalCenter();
}
void KisCanvasResourceProvider::setOpacity(qreal opacity)
{
m_resourceManager->setResource(Opacity, opacity);
}
qreal KisCanvasResourceProvider::opacity() const
{
return m_resourceManager->resource(Opacity).toReal();
}
void KisCanvasResourceProvider::setFlow(qreal flow)
{
m_resourceManager->setResource(Flow, flow);
}
qreal KisCanvasResourceProvider::flow() const
{
return m_resourceManager->resource(Flow).toReal();
}
void KisCanvasResourceProvider::setSize(qreal size)
{
m_resourceManager->setResource(Size, size);
}
qreal KisCanvasResourceProvider::size() const
{
return m_resourceManager->resource(Size).toReal();
}
void KisCanvasResourceProvider::setSelectionAction(int action)
{
m_resourceManager->setResource(SelectionAction, action);
emit sigSelectionActionChanged(action);
}
int KisCanvasResourceProvider::selectionAction()
{
return m_resourceManager->resource(SelectionAction).toInt();
}
void KisCanvasResourceProvider::setSelectionMode(int mode)
{
m_resourceManager->setResource(SelectionMode, mode);
emit sigSelectionModeChanged(mode);
}
int KisCanvasResourceProvider::selectionMode()
{
return m_resourceManager->resource(SelectionMode).toInt();
}
void KisCanvasResourceProvider::setGlobalAlphaLock(bool lock)
{
m_resourceManager->setResource(GlobalAlphaLock, lock);
}
bool KisCanvasResourceProvider::globalAlphaLock() const
{
return m_resourceManager->resource(GlobalAlphaLock).toBool();
}
+void KisCanvasResourceProvider::setDisablePressure(bool value)
+{
+ m_resourceManager->setResource(DisablePressure, value);
+}
+
+bool KisCanvasResourceProvider::disablePressure() const
+{
+ return m_resourceManager->resource(DisablePressure).toBool();
+}
+
void KisCanvasResourceProvider::notifyLoadingWorkspace(KisWorkspaceResource* workspace)
{
emit sigLoadingWorkspace(workspace);
}
void KisCanvasResourceProvider::notifySavingWorkspace(KisWorkspaceResource* workspace)
{
emit sigSavingWorkspace(workspace);
}
diff --git a/libs/ui/kis_canvas_resource_provider.h b/libs/ui/kis_canvas_resource_provider.h
index a219beab27..4d1c4a1883 100644
--- a/libs/ui/kis_canvas_resource_provider.h
+++ b/libs/ui/kis_canvas_resource_provider.h
@@ -1,247 +1,250 @@
/*
* Copyright (c) 2006 Boudewijn Rempt
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_CANVAS_RESOURCE_PROVIDER_H_
#define KIS_CANVAS_RESOURCE_PROVIDER_H_
#include
#include
#include
#include
#include "kis_types.h"
#include "kritaui_export.h"
class KisWorkspaceResource;
class KoColorProfile;
class KoAbstractGradient;
class KoResource;
class KoCanvasBase;
class KisViewManager;
class KoPattern;
class KisFilterConfiguration;
#include
/**
* KisCanvasResourceProvider contains the per-window current settings that
* influence painting, like paintop, color, gradients and so on.
*/
class KRITAUI_EXPORT KisCanvasResourceProvider : public QObject
{
Q_OBJECT
public:
enum Resources {
HdrExposure = KoCanvasResourceManager::KritaStart + 1,
CurrentPattern,
CurrentGradient,
CurrentDisplayProfile,
CurrentImage,
CurrentKritaNode,
CurrentPaintOpPreset,
CurrentGeneratorConfiguration,
CurrentCompositeOp,
CurrentEffectiveCompositeOp,
LodAvailability,
EraserMode,
MirrorHorizontal,
MirrorVertical,
MirrorHorizontalLock,
MirrorVerticalLock,
MirrorVerticalHideDecorations,
MirrorHorizontalHideDecorations,
MirrorAxesCenter,
Opacity,
Flow,
Size,
HdrGamma,
GlobalAlphaLock,
+ DisablePressure,
PreviousPaintOpPreset,
EffectiveZoom, ///<-Used only by painting tools for non-displaying purposes
PresetAllowsLod,
SelectionAction,
SelectionMode
};
KisCanvasResourceProvider(KisViewManager * view);
~KisCanvasResourceProvider();
void setResourceManager(KoCanvasResourceManager *resourceManager);
KoCanvasResourceManager* resourceManager();
KoCanvasBase * canvas() const;
KoColor bgColor() const;
void setBGColor(const KoColor& c);
KoColor fgColor() const;
void setFGColor(const KoColor& c);
float HDRExposure() const;
void setHDRExposure(float exposure);
float HDRGamma() const;
void setHDRGamma(float gamma);
bool eraserMode() const;
void setEraserMode(bool value);
KoPattern *currentPattern() const;
KoAbstractGradient *currentGradient() const;
KisImageWSP currentImage() const;
KisNodeSP currentNode() const;
KisPaintOpPresetSP currentPreset() const;
void setPaintOpPreset(const KisPaintOpPresetSP preset);
KisPaintOpPresetSP previousPreset() const;
void setPreviousPaintOpPreset(const KisPaintOpPresetSP preset);
KisFilterConfiguration* currentGeneratorConfiguration() const;
void setCurrentCompositeOp(const QString& compositeOp);
QString currentCompositeOp() const;
QList > perspectiveGrids() const;
void addPerspectiveGrid(KisAbstractPerspectiveGrid*);
void removePerspectiveGrid(KisAbstractPerspectiveGrid*);
void clearPerspectiveGrids();
void setMirrorHorizontal(bool mirrorHorizontal);
bool mirrorHorizontal() const;
void setMirrorVertical(bool mirrorVertical);
bool mirrorVertical() const;
// options for horizontal and vertical mirror toolbar
void setMirrorHorizontalLock(bool isLocked);
bool mirrorHorizontalLock();
void setMirrorVerticalLock(bool isLocked);
bool mirrorVerticalLock();
void setMirrorVerticalHideDecorations(bool hide);
bool mirrorVerticalHideDecorations();
void setMirrorHorizontalHideDecorations(bool hide);
bool mirrorHorizontalHideDecorations();
void mirrorVerticalMoveCanvasToCenter();
void mirrorHorizontalMoveCanvasToCenter();
void setOpacity(qreal opacity);
qreal opacity() const;
void setFlow(qreal opacity);
qreal flow() const;
void setSize(qreal size);
qreal size() const;
void setGlobalAlphaLock(bool lock);
bool globalAlphaLock() const;
+ void setDisablePressure(bool value);
+ bool disablePressure() const;
///Notify that the workspace is saved and settings should be saved to it
void notifySavingWorkspace(KisWorkspaceResource* workspace);
///Notify that the workspace is loaded and settings can be read
void notifyLoadingWorkspace(KisWorkspaceResource* workspace);
int selectionAction();
void setSelectionAction(int action);
int selectionMode();
void setSelectionMode(int mode);
public Q_SLOTS:
void slotSetFGColor(const KoColor& c);
void slotSetBGColor(const KoColor& c);
void slotPatternActivated(KoResource *pattern);
void slotGradientActivated(KoResource *gradient);
void slotNodeActivated(const KisNodeSP node);
void slotGeneratorConfigurationActivated(KisFilterConfiguration * generatorConfiguration);
void slotPainting();
/**
* Set the image size in pixels. The resource provider will store
* the image size in postscript points.
*/
// FIXME: this slot doesn't catch the case when image resolution is changed
void slotImageSizeChanged();
void slotOnScreenResolutionChanged();
// This is a flag to handle a bug:
// If pop up palette is visible and a new colour is selected, the new colour
// will be added when the user clicks on the canvas to hide the palette
// In general, we want to be able to store recent color if the pop up palette
// is not visible
void slotResetEnableFGChange(bool);
private Q_SLOTS:
void slotCanvasResourceChanged(int key, const QVariant & res);
Q_SIGNALS:
void sigFGColorChanged(const KoColor &);
void sigBGColorChanged(const KoColor &);
void sigGradientChanged(KoAbstractGradient *);
void sigPatternChanged(KoPattern *);
void sigNodeChanged(const KisNodeSP);
void sigDisplayProfileChanged(const KoColorProfile *);
void sigFGColorUsed(const KoColor&);
void sigOnScreenResolutionChanged(qreal scaleX, qreal scaleY);
void sigOpacityChanged(qreal);
void sigSavingWorkspace(KisWorkspaceResource* workspace);
void sigLoadingWorkspace(KisWorkspaceResource* workspace);
void sigSelectionActionChanged(const int);
void sigSelectionModeChanged(const int);
void mirrorModeChanged();
void moveMirrorVerticalCenter();
void moveMirrorHorizontalCenter();
private:
KisViewManager * m_view;
KoCanvasResourceManager *m_resourceManager;
bool m_fGChanged;
QList > m_perspectiveGrids;
// This is a flag to handle a bug:
// If pop up palette is visible and a new colour is selected, the new colour
// will be added when the user clicks on the canvas to hide the palette
// In general, we want to be able to store recent color if the pop up palette
// is not visible
bool m_enablefGChange;
};
#endif
diff --git a/libs/ui/kis_paintop_box.cc b/libs/ui/kis_paintop_box.cc
index 05f3f174f1..b3c56ef894 100644
--- a/libs/ui/kis_paintop_box.cc
+++ b/libs/ui/kis_paintop_box.cc
@@ -1,1197 +1,1237 @@
/*
* kis_paintop_box.cc - part of KImageShop/Krayon/Krita
*
* Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org)
* Copyright (c) 2009-2011 Sven Langkamp (sven.langkamp@gmail.com)
* Copyright (c) 2010 Lukáš Tvrdý
* Copyright (C) 2011 Silvio Heinrich
* Copyright (C) 2011 Srikanth Tiyyagura
* Copyright (c) 2014 Mohit Goyal
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_paintop_box.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kis_canvas2.h"
#include "kis_node_manager.h"
#include "KisViewManager.h"
#include "kis_canvas_resource_provider.h"
#include "kis_resource_server_provider.h"
#include "kis_favorite_resource_manager.h"
#include "kis_config.h"
#include "widgets/kis_popup_button.h"
#include "widgets/kis_tool_options_popup.h"
#include "widgets/kis_paintop_presets_popup.h"
#include "widgets/kis_tool_options_popup.h"
#include "widgets/kis_paintop_presets_chooser_popup.h"
#include "widgets/kis_workspace_chooser.h"
#include "widgets/kis_paintop_list_widget.h"
#include "widgets/kis_slider_spin_box.h"
#include "widgets/kis_cmb_composite.h"
#include "widgets/kis_widget_chooser.h"
#include "tool/kis_tool.h"
#include "kis_signals_blocker.h"
#include "kis_action_manager.h"
#include "kis_highlighted_button.h"
typedef KoResourceServerSimpleConstruction > KisPaintOpPresetResourceServer;
typedef KoResourceServerAdapter > KisPaintOpPresetResourceServerAdapter;
KisPaintopBox::KisPaintopBox(KisViewManager *view, QWidget *parent, const char *name)
: QWidget(parent)
, m_resourceProvider(view->resourceProvider())
, m_optionWidget(0)
, m_toolOptionsPopupButton(0)
, m_brushEditorPopupButton(0)
, m_presetSelectorPopupButton(0)
, m_toolOptionsPopup(0)
, m_viewManager(view)
, m_previousNode(0)
, m_currTabletToolID(KoInputDevice::invalid())
, m_presetsEnabled(true)
, m_blockUpdate(false)
, m_dirtyPresetsEnabled(false)
, m_eraserBrushSizeEnabled(false)
, m_presetUpdateCompressor(200, KisSignalCompressor::FIRST_ACTIVE)
{
Q_ASSERT(view != 0);
setObjectName(name);
KisConfig cfg;
m_dirtyPresetsEnabled = cfg.useDirtyPresets();
m_eraserBrushSizeEnabled = cfg.useEraserBrushSize();
KAcceleratorManager::setNoAccel(this);
setWindowTitle(i18n("Painter's Toolchest"));
KConfigGroup grp = KSharedConfig::openConfig()->group("krita").group("Toolbar BrushesAndStuff");
int iconsize = grp.readEntry("IconSize", 32);
if (!cfg.toolOptionsInDocker()) {
m_toolOptionsPopupButton = new KisPopupButton(this);
m_toolOptionsPopupButton->setIcon(KisIconUtils::loadIcon("configure"));
m_toolOptionsPopupButton->setToolTip(i18n("Tool Settings"));
m_toolOptionsPopupButton->setFixedSize(iconsize, iconsize);
}
m_brushEditorPopupButton = new KisPopupButton(this);
m_brushEditorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_02"));
m_brushEditorPopupButton->setToolTip(i18n("Edit brush settings"));
m_brushEditorPopupButton->setFixedSize(iconsize, iconsize);
m_presetSelectorPopupButton = new KisPopupButton(this);
m_presetSelectorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_01"));
m_presetSelectorPopupButton->setToolTip(i18n("Choose brush preset"));
m_presetSelectorPopupButton->setFixedSize(iconsize, iconsize);
m_eraseModeButton = new KisHighlightedToolButton(this);
m_eraseModeButton->setFixedSize(iconsize, iconsize);
m_eraseModeButton->setCheckable(true);
m_eraseAction = m_viewManager->actionManager()->createAction("erase_action");
m_eraseModeButton->setDefaultAction(m_eraseAction);
m_reloadButton = new QToolButton(this);
m_reloadButton->setFixedSize(iconsize, iconsize);
m_reloadAction = m_viewManager->actionManager()->createAction("reload_preset_action");
m_reloadButton->setDefaultAction(m_reloadAction);
m_alphaLockButton = new KisHighlightedToolButton(this);
m_alphaLockButton->setFixedSize(iconsize, iconsize);
m_alphaLockButton->setCheckable(true);
KisAction* alphaLockAction = m_viewManager->actionManager()->createAction("preserve_alpha");
m_alphaLockButton->setDefaultAction(alphaLockAction);
+ // pen pressure
+ m_disablePressureButton = new KisHighlightedToolButton(this);
+ m_disablePressureButton->setFixedSize(iconsize, iconsize);
+ m_disablePressureButton->setCheckable(true);
+ m_disablePressureAction = m_viewManager->actionManager()->createAction("disable_pressure");
+ m_disablePressureButton->setDefaultAction(m_disablePressureAction);
// horizontal and vertical mirror toolbar buttons
// mirror tool options for the X Mirror
QMenu *toolbarMenuXMirror = new QMenu();
KisAction* hideCanvasDecorationsX = m_viewManager->actionManager()->createAction("mirrorX-hideDecorations");
hideCanvasDecorationsX->setCheckable(true);
hideCanvasDecorationsX->setText(i18n("Hide Mirror Line"));
toolbarMenuXMirror->addAction(hideCanvasDecorationsX);
KisAction* lockActionX = m_viewManager->actionManager()->createAction("mirrorX-lock");
lockActionX->setText(i18n("Lock"));
lockActionX->setCheckable(true);
toolbarMenuXMirror->addAction(lockActionX);
KisAction* moveToCenterActionX = m_viewManager->actionManager()->createAction("mirrorX-moveToCenter");
moveToCenterActionX->setCheckable(false);
moveToCenterActionX->setText(i18n("Move to Canvas Center"));
toolbarMenuXMirror->addAction(moveToCenterActionX);
// mirror tool options for the Y Mirror
QMenu *toolbarMenuYMirror = new QMenu();
KisAction* hideCanvasDecorationsY = m_viewManager->actionManager()->createAction("mirrorY-hideDecorations");
hideCanvasDecorationsY->setCheckable(true);
hideCanvasDecorationsY->setText(i18n("Hide Mirror Line"));
toolbarMenuYMirror->addAction(hideCanvasDecorationsY);
KisAction* lockActionY = m_viewManager->actionManager()->createAction("mirrorY-lock");
lockActionY->setText(i18n("Lock"));
lockActionY->setCheckable(true);
toolbarMenuYMirror->addAction(lockActionY);
KisAction* moveToCenterActionY = m_viewManager->actionManager()->createAction("mirrorY-moveToCenter");
moveToCenterActionY->setCheckable(false);
moveToCenterActionY->setText(i18n("Move to Canvas Center"));
toolbarMenuYMirror->addAction(moveToCenterActionY);
// create horizontal and vertical mirror buttons
m_hMirrorButton = new KisHighlightedToolButton(this);
int menuPadding = 10;
m_hMirrorButton->setFixedSize(iconsize + menuPadding, iconsize);
m_hMirrorButton->setCheckable(true);
m_hMirrorAction = m_viewManager->actionManager()->createAction("hmirror_action");
m_hMirrorButton->setDefaultAction(m_hMirrorAction);
m_hMirrorButton->setMenu(toolbarMenuXMirror);
m_hMirrorButton->setPopupMode(QToolButton::MenuButtonPopup);
m_vMirrorButton = new KisHighlightedToolButton(this);
m_vMirrorButton->setFixedSize(iconsize + menuPadding, iconsize);
m_vMirrorButton->setCheckable(true);
m_vMirrorAction = m_viewManager->actionManager()->createAction("vmirror_action");
m_vMirrorButton->setDefaultAction(m_vMirrorAction);
m_vMirrorButton->setMenu(toolbarMenuYMirror);
m_vMirrorButton->setPopupMode(QToolButton::MenuButtonPopup);
// add connections for horizontal and mirrror buttons
connect(lockActionX, SIGNAL(toggled(bool)), this, SLOT(slotLockXMirrorToggle(bool)));
connect(lockActionY, SIGNAL(toggled(bool)), this, SLOT(slotLockYMirrorToggle(bool)));
connect(moveToCenterActionX, SIGNAL(triggered(bool)), this, SLOT(slotMoveToCenterMirrorX()));
connect(moveToCenterActionY, SIGNAL(triggered(bool)), this, SLOT(slotMoveToCenterMirrorY()));
connect(hideCanvasDecorationsX, SIGNAL(toggled(bool)), this, SLOT(slotHideDecorationMirrorX(bool)));
connect(hideCanvasDecorationsY, SIGNAL(toggled(bool)), this, SLOT(slotHideDecorationMirrorY(bool)));
const bool sliderLabels = cfg.sliderLabels();
int sliderWidth;
if (sliderLabels) {
sliderWidth = 150 * logicalDpiX() / 96;
}
else {
sliderWidth = 120 * logicalDpiX() / 96;
}
for (int i = 0; i < 3; ++i) {
m_sliderChooser[i] = new KisWidgetChooser(i + 1);
KisDoubleSliderSpinBox* slOpacity;
KisDoubleSliderSpinBox* slFlow;
KisDoubleSliderSpinBox* slSize;
if (sliderLabels) {
slOpacity = m_sliderChooser[i]->addWidget("opacity");
slFlow = m_sliderChooser[i]->addWidget("flow");
slSize = m_sliderChooser[i]->addWidget("size");
slOpacity->setPrefix(QString("%1 ").arg(i18n("Opacity:")));
slFlow->setPrefix(QString("%1 ").arg(i18n("Flow:")));
slSize->setPrefix(QString("%1 ").arg(i18n("Size:")));
}
else {
slOpacity = m_sliderChooser[i]->addWidget("opacity", i18n("Opacity:"));
slFlow = m_sliderChooser[i]->addWidget("flow", i18n("Flow:"));
slSize = m_sliderChooser[i]->addWidget("size", i18n("Size:"));
}
slOpacity->setRange(0.0, 1.0, 2);
slOpacity->setValue(1.0);
slOpacity->setSingleStep(0.05);
slOpacity->setMinimumWidth(qMax(sliderWidth, slOpacity->sizeHint().width()));
slOpacity->setFixedHeight(iconsize);
slOpacity->setBlockUpdateSignalOnDrag(true);
slFlow->setRange(0.0, 1.0, 2);
slFlow->setValue(1.0);
slFlow->setSingleStep(0.05);
slFlow->setMinimumWidth(qMax(sliderWidth, slFlow->sizeHint().width()));
slFlow->setFixedHeight(iconsize);
slFlow->setBlockUpdateSignalOnDrag(true);
slSize->setRange(0, 1000, 2);
slSize->setValue(100);
slSize->setSingleStep(1);
slSize->setExponentRatio(3.0);
slSize->setSuffix(i18n(" px"));
slSize->setMinimumWidth(qMax(sliderWidth, slSize->sizeHint().width()));
slSize->setFixedHeight(iconsize);
slSize->setBlockUpdateSignalOnDrag(true);
m_sliderChooser[i]->chooseWidget(cfg.toolbarSlider(i + 1));
}
m_cmbCompositeOp = new KisCompositeOpComboBox();
m_cmbCompositeOp->setFixedHeight(iconsize);
Q_FOREACH (KisAction * a, m_cmbCompositeOp->blendmodeActions()) {
m_viewManager->actionManager()->addAction(a->text(), a);
}
m_workspaceWidget = new KisPopupButton(this);
m_workspaceWidget->setIcon(KisIconUtils::loadIcon("view-choose"));
m_workspaceWidget->setToolTip(i18n("Choose workspace"));
m_workspaceWidget->setFixedSize(iconsize, iconsize);
m_workspaceWidget->setPopupWidget(new KisWorkspaceChooser(view));
QHBoxLayout* baseLayout = new QHBoxLayout(this);
m_paintopWidget = new QWidget(this);
baseLayout->addWidget(m_paintopWidget);
baseLayout->setSpacing(4);
baseLayout->setContentsMargins(0, 0, 0, 0);
m_layout = new QHBoxLayout(m_paintopWidget);
if (!cfg.toolOptionsInDocker()) {
m_layout->addWidget(m_toolOptionsPopupButton);
}
m_layout->addWidget(m_brushEditorPopupButton);
m_layout->addWidget(m_presetSelectorPopupButton);
m_layout->setSpacing(4);
m_layout->setContentsMargins(0, 0, 0, 0);
QWidget* compositeActions = new QWidget(this);
QHBoxLayout* compositeLayout = new QHBoxLayout(compositeActions);
compositeLayout->addWidget(m_cmbCompositeOp);
compositeLayout->addWidget(m_eraseModeButton);
compositeLayout->addWidget(m_alphaLockButton);
compositeLayout->setSpacing(4);
compositeLayout->setContentsMargins(0, 0, 0, 0);
compositeLayout->addWidget(m_reloadButton);
QWidgetAction * action;
action = new QWidgetAction(this);
view->actionCollection()->addAction("composite_actions", action);
action->setText(i18n("Brush composite"));
action->setDefaultWidget(compositeActions);
+ QWidget* compositePressure = new QWidget(this);
+ QHBoxLayout* pressureLayout = new QHBoxLayout(compositePressure);
+ pressureLayout->addWidget(m_disablePressureButton);
+ pressureLayout->setSpacing(4);
+ pressureLayout->setContentsMargins(0, 0, 0, 0);
+
+ action = new QWidgetAction(this);
+ view->actionCollection()->addAction("pressure_action", action);
+ action->setText(i18n("Pressure usage (small button)"));
+ action->setDefaultWidget(compositePressure);
+
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("brushslider1", action);
view->actionCollection()->addAction("brushslider1", action);
action->setDefaultWidget(m_sliderChooser[0]);
connect(action, SIGNAL(triggered()), m_sliderChooser[0], SLOT(showPopupWidget()));
connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_sliderChooser[0], SLOT(updateThemedIcons()));
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("brushslider2", action);
view->actionCollection()->addAction("brushslider2", action);
action->setDefaultWidget(m_sliderChooser[1]);
connect(action, SIGNAL(triggered()), m_sliderChooser[1], SLOT(showPopupWidget()));
connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_sliderChooser[1], SLOT(updateThemedIcons()));
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("brushslider3", action);
view->actionCollection()->addAction("brushslider3", action);
action->setDefaultWidget(m_sliderChooser[2]);
connect(action, SIGNAL(triggered()), m_sliderChooser[2], SLOT(showPopupWidget()));
connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_sliderChooser[2], SLOT(updateThemedIcons()));
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("next_favorite_preset", action);
view->actionCollection()->addAction("next_favorite_preset", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotNextFavoritePreset()));
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("previous_favorite_preset", action);
view->actionCollection()->addAction("previous_favorite_preset", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotPreviousFavoritePreset()));
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("previous_preset", action);
view->actionCollection()->addAction("previous_preset", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotSwitchToPreviousPreset()));
if (!cfg.toolOptionsInDocker()) {
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("show_tool_options", action);
view->actionCollection()->addAction("show_tool_options", action);
connect(action, SIGNAL(triggered()), m_toolOptionsPopupButton, SLOT(showPopupWidget()));
}
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("show_brush_editor", action);
view->actionCollection()->addAction("show_brush_editor", action);
connect(action, SIGNAL(triggered()), m_brushEditorPopupButton, SLOT(showPopupWidget()));
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("show_brush_presets", action);
view->actionCollection()->addAction("show_brush_presets", action);
connect(action, SIGNAL(triggered()), m_presetSelectorPopupButton, SLOT(showPopupWidget()));
QWidget* mirrorActions = new QWidget(this);
QHBoxLayout* mirrorLayout = new QHBoxLayout(mirrorActions);
mirrorLayout->addWidget(m_hMirrorButton);
mirrorLayout->addWidget(m_vMirrorButton);
mirrorLayout->setSpacing(4);
mirrorLayout->setContentsMargins(0, 0, 0, 0);
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("mirror_actions", action);
action->setDefaultWidget(mirrorActions);
view->actionCollection()->addAction("mirror_actions", action);
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("workspaces", action);
view->actionCollection()->addAction("workspaces", action);
action->setDefaultWidget(m_workspaceWidget);
if (!cfg.toolOptionsInDocker()) {
m_toolOptionsPopup = new KisToolOptionsPopup();
m_toolOptionsPopupButton->setPopupWidget(m_toolOptionsPopup);
m_toolOptionsPopup->switchDetached(false);
}
m_presetsPopup = new KisPaintOpPresetsPopup(m_resourceProvider);
m_brushEditorPopupButton->setPopupWidget(m_presetsPopup);
m_presetsPopup->switchDetached(false);
connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_presetsPopup, SLOT(updateThemedIcons()));
m_presetsChooserPopup = new KisPaintOpPresetsChooserPopup();
m_presetsChooserPopup->setFixedSize(500, 600);
m_presetSelectorPopupButton->setPopupWidget(m_presetsChooserPopup);
m_currCompositeOpID = KoCompositeOpRegistry::instance().getDefaultCompositeOp().id();
slotNodeChanged(view->activeNode());
// Get all the paintops
QList keys = KisPaintOpRegistry::instance()->keys();
QList factoryList;
Q_FOREACH (const QString & paintopId, keys) {
factoryList.append(KisPaintOpRegistry::instance()->get(paintopId));
}
m_presetsPopup->setPaintOpList(factoryList);
connect(m_presetsPopup , SIGNAL(paintopActivated(QString)) , SLOT(slotSetPaintop(QString)));
connect(m_presetsPopup , SIGNAL(savePresetClicked()) , SLOT(slotSaveActivePreset()));
connect(m_presetsPopup , SIGNAL(defaultPresetClicked()) , SLOT(slotSetupDefaultPreset()));
connect(m_presetsPopup , SIGNAL(signalResourceSelected(KoResource*)), SLOT(resourceSelected(KoResource*)));
connect(m_presetsPopup , SIGNAL(reloadPresetClicked()) , SLOT(slotReloadPreset()));
connect(m_presetsPopup , SIGNAL(dirtyPresetToggled(bool)) , SLOT(slotDirtyPresetToggled(bool)));
connect(m_presetsPopup , SIGNAL(eraserBrushSizeToggled(bool)) , SLOT(slotEraserBrushSizeToggled(bool)));
connect(m_presetsChooserPopup, SIGNAL(resourceSelected(KoResource*)) , SLOT(resourceSelected(KoResource*)));
connect(m_resourceProvider , SIGNAL(sigNodeChanged(const KisNodeSP)) , SLOT(slotNodeChanged(const KisNodeSP)));
connect(m_cmbCompositeOp , SIGNAL(currentIndexChanged(int)) , SLOT(slotSetCompositeMode(int)));
connect(m_eraseAction , SIGNAL(toggled(bool)) , SLOT(slotToggleEraseMode(bool)));
connect(alphaLockAction , SIGNAL(toggled(bool)) , SLOT(slotToggleAlphaLockMode(bool)));
+ connect(m_disablePressureAction , SIGNAL(toggled(bool)) , SLOT(slotDisablePressureMode(bool)));
+ m_disablePressureAction->setChecked(true);
connect(m_hMirrorAction , SIGNAL(toggled(bool)) , SLOT(slotHorizontalMirrorChanged(bool)));
connect(m_vMirrorAction , SIGNAL(toggled(bool)) , SLOT(slotVerticalMirrorChanged(bool)));
connect(m_reloadAction , SIGNAL(triggered()) , SLOT(slotReloadPreset()));
connect(m_sliderChooser[0]->getWidget("opacity"), SIGNAL(valueChanged(qreal)), SLOT(slotSlider1Changed()));
connect(m_sliderChooser[0]->getWidget("flow") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider1Changed()));
connect(m_sliderChooser[0]->getWidget("size") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider1Changed()));
connect(m_sliderChooser[1]->getWidget("opacity"), SIGNAL(valueChanged(qreal)), SLOT(slotSlider2Changed()));
connect(m_sliderChooser[1]->getWidget("flow") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider2Changed()));
connect(m_sliderChooser[1]->getWidget("size") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider2Changed()));
connect(m_sliderChooser[2]->getWidget("opacity"), SIGNAL(valueChanged(qreal)), SLOT(slotSlider3Changed()));
connect(m_sliderChooser[2]->getWidget("flow") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider3Changed()));
connect(m_sliderChooser[2]->getWidget("size") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider3Changed()));
//Needed to connect canvas to favorite resource manager
connect(m_viewManager->resourceProvider(), SIGNAL(sigFGColorChanged(KoColor)), SLOT(slotUnsetEraseMode()));
m_favoriteResourceManager = new KisFavoriteResourceManager(this);
connect(m_resourceProvider, SIGNAL(sigFGColorUsed(KoColor)), m_favoriteResourceManager, SLOT(slotAddRecentColor(KoColor)));
connect(m_resourceProvider, SIGNAL(sigFGColorChanged(KoColor)), m_favoriteResourceManager, SLOT(slotChangeFGColorSelector(KoColor)));
connect(m_resourceProvider, SIGNAL(sigBGColorChanged(KoColor)), m_favoriteResourceManager, SLOT(slotSetBGColor(KoColor)));
// cold initialization
m_favoriteResourceManager->slotChangeFGColorSelector(m_resourceProvider->fgColor());
m_favoriteResourceManager->slotSetBGColor(m_resourceProvider->bgColor());
connect(m_favoriteResourceManager, SIGNAL(sigSetFGColor(KoColor)), m_resourceProvider, SLOT(slotSetFGColor(KoColor)));
connect(m_favoriteResourceManager, SIGNAL(sigSetBGColor(KoColor)), m_resourceProvider, SLOT(slotSetBGColor(KoColor)));
connect(m_favoriteResourceManager, SIGNAL(sigEnableChangeColor(bool)), m_resourceProvider, SLOT(slotResetEnableFGChange(bool)));
connect(view->mainWindow(), SIGNAL(themeChanged()), this, SLOT(slotUpdateSelectionIcon()));
slotInputDeviceChanged(KoToolManager::instance()->currentInputDevice());
}
KisPaintopBox::~KisPaintopBox()
{
KisConfig cfg;
QMapIterator iter(m_tabletToolMap);
while (iter.hasNext()) {
iter.next();
if ((iter.key().pointer) == QTabletEvent::Eraser) {
cfg.writeEntry(QString("LastEraser_%1").arg(iter.key().uniqueID) , iter.value().preset->name());
}
else {
cfg.writeEntry(QString("LastPreset_%1").arg(iter.key().uniqueID) , iter.value().preset->name());
}
}
// Do not delete the widget, since it it is global to the application, not owned by the view
m_presetsPopup->setPaintOpSettingsWidget(0);
qDeleteAll(m_paintopOptionWidgets);
delete m_favoriteResourceManager;
for (int i = 0; i < 3; ++i) {
delete m_sliderChooser[i];
}
}
void KisPaintopBox::restoreResource(KoResource* resource)
{
KisPaintOpPreset* preset = dynamic_cast(resource);
if (preset) {
setCurrentPaintop(preset->paintOp(), preset);
m_presetsPopup->setPresetImage(preset->image());
m_presetsPopup->resourceSelected(resource);
}
}
void KisPaintopBox::newOptionWidgets(const QList > &optionWidgetList)
{
if (m_toolOptionsPopup) {
m_toolOptionsPopup->newOptionWidgets(optionWidgetList);
}
}
void KisPaintopBox::resourceSelected(KoResource* resource)
{
KisPaintOpPreset* preset = dynamic_cast(resource);
if (preset) {
if (!preset->settings()->isLoadable())
return;
setCurrentPaintopAndReload(preset->paintOp(), preset);
m_presetsPopup->setPresetImage(preset->image());
m_presetsPopup->resourceSelected(resource);
}
}
void KisPaintopBox::setCurrentPaintopAndReload(const KoID& paintop, KisPaintOpPresetSP preset)
{
if (!m_dirtyPresetsEnabled) {
KisSignalsBlocker blocker(m_optionWidget);
if (!preset->load()) {
warnKrita << "failed to load the preset.";
}
}
setCurrentPaintop(paintop, preset);
}
void KisPaintopBox::setCurrentPaintop(const KoID& paintop, KisPaintOpPresetSP preset)
{
if (m_resourceProvider->currentPreset()) {
m_resourceProvider->setPreviousPaintOpPreset(m_resourceProvider->currentPreset());
if (m_optionWidget) {
m_optionWidget->disconnect(this);
m_optionWidget->hide();
}
m_paintOpPresetMap[m_resourceProvider->currentPreset()->paintOp()] = m_resourceProvider->currentPreset();
m_tabletToolMap[m_currTabletToolID].preset = m_resourceProvider->currentPreset();
m_tabletToolMap[m_currTabletToolID].paintOpID = m_resourceProvider->currentPreset()->paintOp();
}
preset = (!preset) ? activePreset(paintop) : preset;
Q_ASSERT(preset && preset->settings());
if (!m_paintopOptionWidgets.contains(paintop))
m_paintopOptionWidgets[paintop] = KisPaintOpRegistry::instance()->get(paintop.id())->createConfigWidget(this);
m_optionWidget = m_paintopOptionWidgets[paintop];
KisSignalsBlocker b(m_optionWidget);
preset->setOptionsWidget(m_optionWidget);
m_optionWidget->setImage(m_viewManager->image());
m_optionWidget->setNode(m_viewManager->activeNode());
m_presetsPopup->setPaintOpSettingsWidget(m_optionWidget);
m_resourceProvider->setPaintOpPreset(preset);
Q_ASSERT(m_optionWidget && m_presetSelectorPopupButton);
connect(m_optionWidget, SIGNAL(sigConfigurationUpdated()), this, SLOT(slotGuiChangedCurrentPreset()));
connect(&m_presetUpdateCompressor, SIGNAL(timeout()), this, SLOT(slotUpdateOptionsWidget()));
connect(m_optionWidget, SIGNAL(sigSaveLockedConfig(KisPropertiesConfiguration*)), this, SLOT(slotSaveLockedOptionToPreset(KisPropertiesConfiguration*)));
connect(m_optionWidget, SIGNAL(sigDropLockedConfig(KisPropertiesConfiguration*)), this, SLOT(slotDropLockedOption(KisPropertiesConfiguration*)));
// load the current brush engine icon for the brush editor toolbar button
KisPaintOpFactory* paintOp = KisPaintOpRegistry::instance()->get(paintop.id());
QString pixFilename = KoResourcePaths::findResource("kis_images", paintOp->pixmap());
m_brushEditorPopupButton->setIcon(QIcon(pixFilename));
m_presetsPopup->setCurrentPaintOp(paintop.id());
if (m_presetsPopup->currentPaintOp() != paintop.id()) {
// Must change the paintop as the current one is not supported
// by the new colorspace.
dbgKrita << "current paintop " << paintop.name() << " was not set, not supported by colorspace";
}
m_presetConnections.clear();
// preset -> compressor
m_presetConnections.addConnection(
preset->updateProxy(), SIGNAL(sigSettingsChanged()),
&m_presetUpdateCompressor, SLOT(start()));
}
void KisPaintopBox::slotUpdateOptionsWidget()
{
KisPaintOpPresetSP preset = m_resourceProvider->currentPreset();
KIS_SAFE_ASSERT_RECOVER_RETURN(preset);
KIS_SAFE_ASSERT_RECOVER_RETURN(m_optionWidget);
KisSignalsBlocker b(m_optionWidget);
m_optionWidget->setConfigurationSafe(preset->settings().data());
m_presetsPopup->resourceSelected(preset.data());
m_presetsPopup->updateViewSettings();
}
KisPaintOpPresetSP KisPaintopBox::defaultPreset(const KoID& paintOp)
{
QString defaultName = paintOp.id() + ".kpp";
QString path = KoResourcePaths::findResource("kis_defaultpresets", defaultName);
KisPaintOpPresetSP preset = new KisPaintOpPreset(path);
if (!preset->load()) {
preset = KisPaintOpRegistry::instance()->defaultPreset(paintOp);
}
Q_ASSERT(preset);
Q_ASSERT(preset->valid());
return preset;
}
KisPaintOpPresetSP KisPaintopBox::activePreset(const KoID& paintOp)
{
if (m_paintOpPresetMap[paintOp] == 0) {
m_paintOpPresetMap[paintOp] = defaultPreset(paintOp);
}
return m_paintOpPresetMap[paintOp];
}
void KisPaintopBox::updateCompositeOp(QString compositeOpID)
{
if (!m_optionWidget) return;
KisSignalsBlocker blocker(m_optionWidget);
KisNodeSP node = m_resourceProvider->currentNode();
if (node && node->paintDevice()) {
if (!node->paintDevice()->colorSpace()->hasCompositeOp(compositeOpID))
compositeOpID = KoCompositeOpRegistry::instance().getDefaultCompositeOp().id();
{
KisSignalsBlocker b1(m_cmbCompositeOp);
m_cmbCompositeOp->selectCompositeOp(KoID(compositeOpID));
}
if (compositeOpID != m_currCompositeOpID) {
m_currCompositeOpID = compositeOpID;
}
}
}
void KisPaintopBox::setWidgetState(int flags)
{
if (flags & (ENABLE_COMPOSITEOP | DISABLE_COMPOSITEOP)) {
m_cmbCompositeOp->setEnabled(flags & ENABLE_COMPOSITEOP);
m_eraseModeButton->setEnabled(flags & ENABLE_COMPOSITEOP);
}
if (flags & (ENABLE_PRESETS | DISABLE_PRESETS)) {
m_presetSelectorPopupButton->setEnabled(flags & ENABLE_PRESETS);
m_brushEditorPopupButton->setEnabled(flags & ENABLE_PRESETS);
}
for (int i = 0; i < 3; ++i) {
if (flags & (ENABLE_OPACITY | DISABLE_OPACITY))
m_sliderChooser[i]->getWidget("opacity")->setEnabled(flags & ENABLE_OPACITY);
if (flags & (ENABLE_FLOW | DISABLE_FLOW))
m_sliderChooser[i]->getWidget("flow")->setEnabled(flags & ENABLE_FLOW);
if (flags & (ENABLE_SIZE | DISABLE_SIZE))
m_sliderChooser[i]->getWidget("size")->setEnabled(flags & ENABLE_SIZE);
}
}
void KisPaintopBox::setSliderValue(const QString& sliderID, qreal value)
{
for (int i = 0; i < 3; ++i) {
KisDoubleSliderSpinBox* slider = m_sliderChooser[i]->getWidget(sliderID);
KisSignalsBlocker b(slider);
slider->setValue(value);
}
}
void KisPaintopBox::slotSetPaintop(const QString& paintOpId)
{
if (KisPaintOpRegistry::instance()->get(paintOpId) != 0) {
KoID id(paintOpId, KisPaintOpRegistry::instance()->get(paintOpId)->name());
setCurrentPaintop(id);
}
}
void KisPaintopBox::slotInputDeviceChanged(const KoInputDevice& inputDevice)
{
TabletToolMap::iterator toolData = m_tabletToolMap.find(inputDevice);
if (toolData == m_tabletToolMap.end()) {
KisConfig cfg;
KisPaintOpPresetResourceServer *rserver = KisResourceServerProvider::instance()->paintOpPresetServer(false);
KisPaintOpPresetSP preset;
if (inputDevice.pointer() == QTabletEvent::Eraser) {
preset = rserver->resourceByName(cfg.readEntry(QString("LastEraser_%1").arg(inputDevice.uniqueTabletId()), "Eraser_circle"));
}
else {
preset = rserver->resourceByName(cfg.readEntry(QString("LastPreset_%1").arg(inputDevice.uniqueTabletId()), "Basic_tip_default"));
}
if (!preset) {
preset = rserver->resourceByName("Basic_tip_default");
}
if (preset) {
setCurrentPaintop(preset->paintOp(), preset);
}
}
else {
setCurrentPaintop(toolData->paintOpID, toolData->preset);
}
m_currTabletToolID = TabletToolID(inputDevice);
}
void KisPaintopBox::slotCanvasResourceChanged(int key, const QVariant &value)
{
if (m_viewManager) {
sender()->blockSignals(true);
KisPaintOpPresetSP preset = m_viewManager->resourceProvider()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value();
if (preset && m_resourceProvider->currentPreset()->name() != preset->name()) {
QString compositeOp = preset->settings()->getString("CompositeOp");
updateCompositeOp(compositeOp);
resourceSelected(preset.data());
}
m_presetsChooserPopup->canvasResourceChanged(preset.data(), preset);
if (key == KisCanvasResourceProvider::CurrentCompositeOp) {
if (m_resourceProvider->currentCompositeOp() != m_currCompositeOpID) {
updateCompositeOp(m_resourceProvider->currentCompositeOp());
}
}
if (key == KisCanvasResourceProvider::Size) {
setSliderValue("size", m_resourceProvider->size());
}
if (key == KisCanvasResourceProvider::Opacity) {
setSliderValue("opacity", m_resourceProvider->opacity());
}
if (key == KisCanvasResourceProvider::Flow) {
setSliderValue("flow", m_resourceProvider->flow());
}
if (key == KisCanvasResourceProvider::EraserMode) {
m_eraseAction->setChecked(value.toBool());
}
+ if (key == KisCanvasResourceProvider::DisablePressure) {
+ m_disablePressureAction->setChecked(value.toBool());
+ }
+
sender()->blockSignals(false);
}
}
void KisPaintopBox::slotSaveActivePreset()
{
KisPaintOpPresetSP curPreset = m_resourceProvider->currentPreset();
if (!curPreset)
return;
m_favoriteResourceManager->setBlockUpdates(true);
KisPaintOpPresetSP newPreset = curPreset->clone();
KisPaintOpPresetResourceServer * rServer = KisResourceServerProvider::instance()->paintOpPresetServer();
QString saveLocation = rServer->saveLocation();
QString presetName = m_presetsPopup->getPresetName();
QString presetFilename = saveLocation + presetName + newPreset->defaultFileExtension();
QStringList tags;
KisPaintOpPresetSP resource = rServer->resourceByName(presetName);
if (resource) {
tags = rServer->assignedTagsList(resource.data());
rServer->removeResourceAndBlacklist(resource);
}
newPreset->setImage(m_presetsPopup->cutOutOverlay());
newPreset->setFilename(presetFilename);
newPreset->setName(presetName);
newPreset->setPresetDirty(false);
rServer->addResource(newPreset);
Q_FOREACH (const QString & tag, tags) {
rServer->addTag(newPreset.data(), tag);
}
// HACK ALERT! the server does not notify the observers
// automatically, so we need to call theupdate manually!
rServer->tagCategoryMembersChanged();
restoreResource(newPreset.data());
m_favoriteResourceManager->setBlockUpdates(false);
}
void KisPaintopBox::slotUpdatePreset()
{
if (!m_resourceProvider->currentPreset()) return;
// block updates of avoid some over updating of the option widget
m_blockUpdate = true;
setSliderValue("size", m_resourceProvider->size());
{
qreal opacity = m_resourceProvider->currentPreset()->settings()->paintOpOpacity();
m_resourceProvider->setOpacity(opacity);
setSliderValue("opacity", opacity);
setWidgetState(ENABLE_OPACITY);
}
{
setSliderValue("flow", m_resourceProvider->currentPreset()->settings()->paintOpFlow());
setWidgetState(ENABLE_FLOW);
}
{
updateCompositeOp(m_resourceProvider->currentPreset()->settings()->paintOpCompositeOp());
setWidgetState(ENABLE_COMPOSITEOP);
}
m_blockUpdate = false;
}
void KisPaintopBox::slotSetupDefaultPreset()
{
KisPaintOpPresetSP preset = defaultPreset(m_resourceProvider->currentPreset()->paintOp());
preset->setOptionsWidget(m_optionWidget);
m_resourceProvider->setPaintOpPreset(preset);
}
void KisPaintopBox::slotNodeChanged(const KisNodeSP node)
{
if (m_previousNode.isValid() && m_previousNode->paintDevice())
disconnect(m_previousNode->paintDevice().data(), SIGNAL(colorSpaceChanged(const KoColorSpace*)), this, SLOT(slotColorSpaceChanged(const KoColorSpace*)));
// Reconnect colorspace change of node
if (node && node->paintDevice()) {
connect(node->paintDevice().data(), SIGNAL(colorSpaceChanged(const KoColorSpace*)), this, SLOT(slotColorSpaceChanged(const KoColorSpace*)));
m_resourceProvider->setCurrentCompositeOp(m_currCompositeOpID);
m_previousNode = node;
slotColorSpaceChanged(node->colorSpace());
}
if (m_optionWidget) {
m_optionWidget->setNode(node);
}
}
void KisPaintopBox::slotColorSpaceChanged(const KoColorSpace* colorSpace)
{
m_cmbCompositeOp->validate(colorSpace);
}
void KisPaintopBox::slotToggleEraseMode(bool checked)
{
const bool oldEraserMode = m_resourceProvider->eraserMode();
m_resourceProvider->setEraserMode(checked);
if (oldEraserMode != checked && m_eraserBrushSizeEnabled) {
const qreal currentSize = m_resourceProvider->size();
KisPaintOpSettingsSP settings = m_resourceProvider->currentPreset()->settings();
// remember brush size. set the eraser size to the normal brush size if not set
if (checked) {
settings->setSavedBrushSize(currentSize);
if (qFuzzyIsNull(settings->savedEraserSize())) {
settings->setSavedEraserSize(currentSize);
}
} else {
settings->setSavedEraserSize(currentSize);
if (qFuzzyIsNull(settings->savedBrushSize())) {
settings->setSavedBrushSize(currentSize);
}
}
//update value in UI (this is the main place the value is 'stored' in memory)
qreal newSize = checked ? settings->savedEraserSize() : settings->savedBrushSize();
m_resourceProvider->setSize(newSize);
}
}
void KisPaintopBox::slotSetCompositeMode(int index)
{
Q_UNUSED(index);
QString compositeOp = m_cmbCompositeOp->selectedCompositeOp().id();
m_resourceProvider->setCurrentCompositeOp(compositeOp);
}
void KisPaintopBox::slotHorizontalMirrorChanged(bool value)
{
m_resourceProvider->setMirrorHorizontal(value);
}
void KisPaintopBox::slotVerticalMirrorChanged(bool value)
{
m_resourceProvider->setMirrorVertical(value);
}
void KisPaintopBox::sliderChanged(int n)
{
if (!m_optionWidget) // widget will not exist if the are no documents open
return;
KisSignalsBlocker blocker(m_optionWidget);
qreal opacity = m_sliderChooser[n]->getWidget("opacity")->value();
qreal flow = m_sliderChooser[n]->getWidget("flow")->value();
qreal size = m_sliderChooser[n]->getWidget("size")->value();
setSliderValue("opacity", opacity);
setSliderValue("flow" , flow);
setSliderValue("size" , size);
if (m_presetsEnabled) {
// IMPORTANT: set the PaintOp size before setting the other properties
// it wont work the other way
// TODO: why?!
m_resourceProvider->setSize(size);
m_resourceProvider->setOpacity(opacity);
m_resourceProvider->setFlow(flow);
KisLockedPropertiesProxy *propertiesProxy = KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(m_resourceProvider->currentPreset()->settings());
propertiesProxy->setProperty("OpacityValue", opacity);
propertiesProxy->setProperty("FlowValue", flow);
delete propertiesProxy;
} else {
m_resourceProvider->setOpacity(opacity);
}
m_presetsPopup->resourceSelected(m_resourceProvider->currentPreset().data());
}
void KisPaintopBox::slotSlider1Changed()
{
sliderChanged(0);
}
void KisPaintopBox::slotSlider2Changed()
{
sliderChanged(1);
}
void KisPaintopBox::slotSlider3Changed()
{
sliderChanged(2);
}
void KisPaintopBox::slotToolChanged(KoCanvasController* canvas, int toolId)
{
Q_UNUSED(canvas);
Q_UNUSED(toolId);
if (!m_viewManager->canvasBase()) return;
QString id = KoToolManager::instance()->activeToolId();
KisTool* tool = dynamic_cast(KoToolManager::instance()->toolById(m_viewManager->canvasBase(), id));
if (tool) {
int flags = tool->flags();
if (flags & KisTool::FLAG_USES_CUSTOM_COMPOSITEOP) {
setWidgetState(ENABLE_COMPOSITEOP | ENABLE_OPACITY);
} else {
setWidgetState(DISABLE_COMPOSITEOP | DISABLE_OPACITY);
}
if (flags & KisTool::FLAG_USES_CUSTOM_PRESET) {
setWidgetState(ENABLE_PRESETS | ENABLE_SIZE | ENABLE_FLOW);
slotUpdatePreset();
m_presetsEnabled = true;
} else {
setWidgetState(DISABLE_PRESETS | DISABLE_SIZE | DISABLE_FLOW);
m_presetsEnabled = false;
}
} else setWidgetState(DISABLE_ALL);
}
void KisPaintopBox::slotPreviousFavoritePreset()
{
if (!m_favoriteResourceManager) return;
int i = 0;
Q_FOREACH (KisPaintOpPresetSP preset, m_favoriteResourceManager->favoritePresetList()) {
if (m_resourceProvider->currentPreset() && m_resourceProvider->currentPreset()->name() == preset->name()) {
if (i > 0) {
m_favoriteResourceManager->slotChangeActivePaintop(i - 1);
} else {
m_favoriteResourceManager->slotChangeActivePaintop(m_favoriteResourceManager->numFavoritePresets() - 1);
}
return;
}
i++;
}
}
void KisPaintopBox::slotNextFavoritePreset()
{
if (!m_favoriteResourceManager) return;
int i = 0;
Q_FOREACH (KisPaintOpPresetSP preset, m_favoriteResourceManager->favoritePresetList()) {
if (m_resourceProvider->currentPreset()->name() == preset->name()) {
if (i < m_favoriteResourceManager->numFavoritePresets() - 1) {
m_favoriteResourceManager->slotChangeActivePaintop(i + 1);
} else {
m_favoriteResourceManager->slotChangeActivePaintop(0);
}
return;
}
i++;
}
}
void KisPaintopBox::slotSwitchToPreviousPreset()
{
if (m_resourceProvider->previousPreset()) {
setCurrentPaintop(m_resourceProvider->previousPreset()->paintOp(), m_resourceProvider->previousPreset());
}
}
void KisPaintopBox::slotUnsetEraseMode()
{
m_eraseAction->setChecked(false);
}
void KisPaintopBox::slotToggleAlphaLockMode(bool checked)
{
if (checked) {
m_alphaLockButton->actions()[0]->setIcon(KisIconUtils::loadIcon("transparency-locked"));
} else {
m_alphaLockButton->actions()[0]->setIcon(KisIconUtils::loadIcon("transparency-unlocked"));
}
m_resourceProvider->setGlobalAlphaLock(checked);
}
+void KisPaintopBox::slotDisablePressureMode(bool checked)
+{
+ if (checked) {
+ m_disablePressureButton->actions()[0]->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure"));
+ } else {
+ m_disablePressureButton->actions()[0]->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure_locked"));
+ }
+
+ m_resourceProvider->setDisablePressure(checked);
+}
+
void KisPaintopBox::slotReloadPreset()
{
KisSignalsBlocker blocker(m_optionWidget);
//Here using the name and fetching the preset from the server was the only way the load was working. Otherwise it was not loading.
KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer();
KisPaintOpPresetSP preset = rserver->resourceByName(m_resourceProvider->currentPreset()->name());
if (preset) {
preset->load();
}
}
void KisPaintopBox::slotGuiChangedCurrentPreset() // Called only when UI is changed and not when preset is changed
{
m_optionWidget->writeConfigurationSafe(const_cast(m_resourceProvider->currentPreset()->settings().data()));
//m_presetsPopup->resourceSelected(m_resourceProvider->currentPreset().data());
// TODO!!!!!!!!
//m_presetsPopup->updateViewSettings();
}
void KisPaintopBox::slotSaveLockedOptionToPreset(KisPropertiesConfiguration* p)
{
QMapIterator i(p->getProperties());
while (i.hasNext()) {
i.next();
m_resourceProvider->currentPreset()->settings()->setProperty(i.key(), QVariant(i.value()));
if (m_resourceProvider->currentPreset()->settings()->hasProperty(i.key() + "_previous")) {
m_resourceProvider->currentPreset()->settings()->removeProperty(i.key() + "_previous");
}
}
slotGuiChangedCurrentPreset();
}
void KisPaintopBox::slotDropLockedOption(KisPropertiesConfiguration* p)
{
KisSignalsBlocker blocker(m_optionWidget);
KisPaintOpPresetSP preset = m_resourceProvider->currentPreset();
{
KisPaintOpPreset::DirtyStateSaver dirtySaver(preset.data());
QMapIterator i(p->getProperties());
while (i.hasNext()) {
i.next();
if (preset->settings()->hasProperty(i.key() + "_previous")) {
preset->settings()->setProperty(i.key(), preset->settings()->getProperty(i.key() + "_previous"));
preset->settings()->removeProperty(i.key() + "_previous");
}
}
}
//slotUpdatePreset();
}
void KisPaintopBox::slotDirtyPresetToggled(bool value)
{
if (!value) {
slotReloadPreset();
m_presetsPopup->resourceSelected(m_resourceProvider->currentPreset().data());
m_presetsPopup->updateViewSettings();
}
m_dirtyPresetsEnabled = value;
KisConfig cfg;
cfg.setUseDirtyPresets(m_dirtyPresetsEnabled);
}
void KisPaintopBox::slotEraserBrushSizeToggled(bool value)
{
m_eraserBrushSizeEnabled = value;
KisConfig cfg;
cfg.setUseEraserBrushSize(m_eraserBrushSizeEnabled);
}
void KisPaintopBox::slotUpdateSelectionIcon()
{
m_hMirrorAction->setIcon(KisIconUtils::loadIcon("symmetry-horizontal"));
m_vMirrorAction->setIcon(KisIconUtils::loadIcon("symmetry-vertical"));
KisConfig cfg;
if (!cfg.toolOptionsInDocker()) {
m_toolOptionsPopupButton->setIcon(KisIconUtils::loadIcon("configure"));
}
m_presetSelectorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_01"));
m_brushEditorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_02"));
m_workspaceWidget->setIcon(KisIconUtils::loadIcon("view-choose"));
m_eraseAction->setIcon(KisIconUtils::loadIcon("draw-eraser"));
m_reloadAction->setIcon(KisIconUtils::loadIcon("view-refresh"));
+
+ if (m_disablePressureAction->isChecked()) {
+ m_disablePressureButton->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure"));
+ } else {
+ m_disablePressureButton->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure_locked"));
+ }
}
void KisPaintopBox::slotLockXMirrorToggle(bool toggleLock) {
m_resourceProvider->setMirrorHorizontalLock(toggleLock);
}
void KisPaintopBox::slotLockYMirrorToggle(bool toggleLock) {
m_resourceProvider->setMirrorVerticalLock(toggleLock);
}
void KisPaintopBox::slotHideDecorationMirrorX(bool toggled) {
m_resourceProvider->setMirrorHorizontalHideDecorations(toggled);
}
void KisPaintopBox::slotHideDecorationMirrorY(bool toggled) {
m_resourceProvider->setMirrorVerticalHideDecorations(toggled);
}
void KisPaintopBox::slotMoveToCenterMirrorX() {
m_resourceProvider->mirrorHorizontalMoveCanvasToCenter();
}
void KisPaintopBox::slotMoveToCenterMirrorY() {
m_resourceProvider->mirrorVerticalMoveCanvasToCenter();
}
diff --git a/libs/ui/kis_paintop_box.h b/libs/ui/kis_paintop_box.h
index aa01ec03a8..2e6addfacd 100644
--- a/libs/ui/kis_paintop_box.h
+++ b/libs/ui/kis_paintop_box.h
@@ -1,249 +1,252 @@
/*
* kis_paintop_box.h - part of KImageShop/Krayon/Krita
*
* Copyright (c) 2004-2008 Boudewijn Rempt (boud@valdyas.org)
* Copyright (C) 2011 Silvio Heinrich
*
* 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_PAINTOP_BOX_H_
#define KIS_PAINTOP_BOX_H_
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kritaui_export.h"
#include "kis_signal_auto_connection.h"
#include "kis_signal_compressor.h"
class QToolButton;
class QString;
class QHBoxLayout;
class KoColorSpace;
class KoResource;
class KoCanvasController;
class KisViewManager;
class KisCanvasResourceProvider;
class KisPopupButton;
class KisToolOptionsPopup;
class KisPaintOpPresetsPopup;
class KisPaintOpPresetsChooserPopup;
class KisPaintOpConfigWidget;
class KisCompositeOpComboBox;
class KisWidgetChooser;
class KisFavoriteResourceManager;
class KisAction;
/**
* This widget presents all paintops that a user can paint with.
* Paintops represent real-world tools or the well-known Shoup
* computer equivalents that do nothing but change color.
*
* To incorporate the dirty preset functionality and locked settings
* the following slots are added
* void slotReloadPreset();
void slotGuiChangedCurrentPreset();
void slotSaveLockedOptionToPreset(KisPropertiesConfiguration* p);
void slotDropLockedOption(KisPropertiesConfiguration* p);
void slotDirtyPresetToggled(bool);
Everytime a value is changed in a preset, the preset is made dirty through the onChange() function.
For Locked Settings however, a changed Locked Setting will not cause a preset to become dirty. That is
becuase it borrows its values from the KisLockedPropertiesServer.
Hence the dirty state of the Preset is kept consistent before and after a writeConfiguration operation in most cases.
* XXX: When we have a lot of paintops, replace the listbox
* with a table, and for every category a combobox.
*
* XXX: instead of text, use pretty pictures.
*/
class KRITAUI_EXPORT KisPaintopBox : public QWidget
{
Q_OBJECT
enum {
ENABLE_PRESETS = 0x0001,
DISABLE_PRESETS = 0x0002,
ENABLE_COMPOSITEOP = 0x0004,
DISABLE_COMPOSITEOP = 0x0008,
ENABLE_OPACITY = 0x0010,
DISABLE_OPACITY = 0x0020,
ENABLE_FLOW = 0x0040,
DISABLE_FLOW = 0x0080,
ENABLE_SIZE = 0x0100,
DISABLE_SIZE = 0x0200,
ENABLE_ALL = 0x5555,
DISABLE_ALL = 0xAAAA
};
public:
KisPaintopBox(KisViewManager* view, QWidget* parent, const char* name);
~KisPaintopBox();
void restoreResource(KoResource* resource);
/**
* Update the option widgets to the argument ones, removing the currently set widgets.
*/
void newOptionWidgets(const QList > & optionWidgetList);
KisFavoriteResourceManager *favoriteResourcesManager() { return m_favoriteResourceManager; }
public Q_SLOTS:
void slotColorSpaceChanged(const KoColorSpace* colorSpace);
void slotInputDeviceChanged(const KoInputDevice & inputDevice);
void slotCanvasResourceChanged(int key, const QVariant& v);
void resourceSelected(KoResource* resource);
private:
void setCurrentPaintop(const KoID& paintop, KisPaintOpPresetSP preset = 0);
void setCurrentPaintopAndReload(const KoID& paintop, KisPaintOpPresetSP preset);
KisPaintOpPresetSP defaultPreset(const KoID& paintOp);
KisPaintOpPresetSP activePreset(const KoID& paintOp);
void updateCompositeOp(QString compositeOpID);
void setWidgetState(int flags);
void setSliderValue(const QString& sliderID, qreal value);
void sliderChanged(int n);
private Q_SLOTS:
void slotSaveActivePreset();
void slotUpdatePreset();
void slotSetupDefaultPreset();
void slotNodeChanged(const KisNodeSP node);
void slotToggleEraseMode(bool checked);
void slotSetCompositeMode(int index);
void slotSetPaintop(const QString& paintOpId);
void slotHorizontalMirrorChanged(bool value);
void slotVerticalMirrorChanged(bool value);
void slotSlider1Changed();
void slotSlider2Changed();
void slotSlider3Changed();
void slotToolChanged(KoCanvasController* canvas, int toolId);
void slotPreviousFavoritePreset();
void slotNextFavoritePreset();
void slotSwitchToPreviousPreset();
void slotUnsetEraseMode();
void slotToggleAlphaLockMode(bool);
+ void slotDisablePressureMode(bool);
void slotReloadPreset();
void slotGuiChangedCurrentPreset();
void slotSaveLockedOptionToPreset(KisPropertiesConfiguration* p);
void slotDropLockedOption(KisPropertiesConfiguration* p);
void slotDirtyPresetToggled(bool);
void slotEraserBrushSizeToggled(bool);
void slotUpdateSelectionIcon();
void slotLockXMirrorToggle(bool);
void slotLockYMirrorToggle(bool);
void slotMoveToCenterMirrorX();
void slotMoveToCenterMirrorY();
void slotHideDecorationMirrorX(bool);
void slotHideDecorationMirrorY(bool);
void slotUpdateOptionsWidget();
private:
KisCanvasResourceProvider* m_resourceProvider;
QHBoxLayout* m_layout;
QWidget* m_paintopWidget;
KisPaintOpConfigWidget* m_optionWidget;
KisPopupButton* m_toolOptionsPopupButton;
KisPopupButton* m_brushEditorPopupButton;
KisPopupButton* m_presetSelectorPopupButton;
KisCompositeOpComboBox* m_cmbCompositeOp;
QToolButton* m_eraseModeButton;
QToolButton* m_alphaLockButton;
+ QToolButton* m_disablePressureButton;
QToolButton* m_hMirrorButton;
QToolButton* m_vMirrorButton;
KisToolOptionsPopup* m_toolOptionsPopup;
KisPaintOpPresetsPopup* m_presetsPopup;
KisPaintOpPresetsChooserPopup* m_presetsChooserPopup;
KisViewManager* m_viewManager;
KisPopupButton* m_workspaceWidget;
KisWidgetChooser* m_sliderChooser[3];
QMap m_paintopOptionWidgets;
KisFavoriteResourceManager* m_favoriteResourceManager;
QToolButton* m_reloadButton;
KisAction* m_eraseAction;
KisAction* m_reloadAction;
+ KisAction* m_disablePressureAction;
QString m_currCompositeOpID;
KisNodeWSP m_previousNode;
KisAction* m_hMirrorAction;
KisAction* m_vMirrorAction;
struct TabletToolID {
TabletToolID(const KoInputDevice& dev) {
uniqueID = dev.uniqueTabletId();
// Only the eraser is special, and we don't look at Cursor
pointer = QTabletEvent::Pen;
if (dev.pointer() == QTabletEvent::Eraser) {
pointer = QTabletEvent::Eraser;
}
}
bool operator == (const TabletToolID& id) const {
return pointer == id.pointer && uniqueID == id.uniqueID;
}
bool operator < (const TabletToolID& id) const {
if (uniqueID == id.uniqueID)
return pointer < id.pointer;
return uniqueID < id.uniqueID;
}
QTabletEvent::PointerType pointer;
qint64 uniqueID;
};
struct TabletToolData {
KoID paintOpID;
KisPaintOpPresetSP preset;
};
typedef QMap TabletToolMap;
typedef QMap PaintOpPresetMap;
TabletToolMap m_tabletToolMap;
PaintOpPresetMap m_paintOpPresetMap;
TabletToolID m_currTabletToolID;
bool m_presetsEnabled;
bool m_blockUpdate;
bool m_dirtyPresetsEnabled;
bool m_eraserBrushSizeEnabled;
KisSignalAutoConnectionsStore m_presetConnections;
KisSignalCompressor m_presetUpdateCompressor;
};
#endif //KIS_PAINTOP_BOX_H_
diff --git a/libs/ui/tool/kis_painting_information_builder.cpp b/libs/ui/tool/kis_painting_information_builder.cpp
index b2ce8289d4..f7b3ae9efd 100644
--- a/libs/ui/tool/kis_painting_information_builder.cpp
+++ b/libs/ui/tool/kis_painting_information_builder.cpp
@@ -1,189 +1,198 @@
/*
* 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.
*/
#include "kis_painting_information_builder.h"
#include
#include "kis_config.h"
#include "kis_config_notifier.h"
#include "kis_cubic_curve.h"
#include "kis_speed_smoother.h"
+#include
+#include "kis_canvas_resource_provider.h"
+
/***********************************************************************/
/* KisPaintingInformationBuilder */
/***********************************************************************/
const int KisPaintingInformationBuilder::LEVEL_OF_PRESSURE_RESOLUTION = 1024;
KisPaintingInformationBuilder::KisPaintingInformationBuilder()
- : m_speedSmoother(new KisSpeedSmoother())
+ : m_speedSmoother(new KisSpeedSmoother()),
+ m_pressureDisabled(false)
{
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()),
SLOT(updateSettings()));
updateSettings();
}
KisPaintingInformationBuilder::~KisPaintingInformationBuilder()
{
}
void KisPaintingInformationBuilder::updateSettings()
{
KisConfig cfg;
KisCubicCurve curve;
curve.fromString(cfg.pressureTabletCurve());
m_pressureSamples = curve.floatTransfer(LEVEL_OF_PRESSURE_RESOLUTION + 1);
}
KisPaintInformation KisPaintingInformationBuilder::startStroke(KoPointerEvent *event,
- int timeElapsed)
+ int timeElapsed,
+ const KoCanvasResourceManager *manager)
{
+ if (manager) {
+ m_pressureDisabled = manager->resource(KisCanvasResourceProvider::DisablePressure).toBool();
+ }
+
m_startPoint = event->point;
return createPaintingInformation(event, timeElapsed);
}
KisPaintInformation KisPaintingInformationBuilder::continueStroke(KoPointerEvent *event,
int timeElapsed)
{
return createPaintingInformation(event, timeElapsed);
}
QPointF KisPaintingInformationBuilder::adjustDocumentPoint(const QPointF &point, const QPointF &/*startPoint*/)
{
return point;
}
QPointF KisPaintingInformationBuilder::documentToImage(const QPointF &point)
{
return point;
}
QPointF KisPaintingInformationBuilder::imageToView(const QPointF &point)
{
return point;
}
qreal KisPaintingInformationBuilder::calculatePerspective(const QPointF &documentPoint)
{
Q_UNUSED(documentPoint);
return 1.0;
}
KisPaintInformation KisPaintingInformationBuilder::createPaintingInformation(KoPointerEvent *event,
int timeElapsed)
{
QPointF adjusted = adjustDocumentPoint(event->point, m_startPoint);
QPointF imagePoint = documentToImage(adjusted);
qreal perspective = calculatePerspective(adjusted);
qreal speed = m_speedSmoother->getNextSpeed(imageToView(imagePoint));
return KisPaintInformation(imagePoint,
- pressureToCurve(event->pressure()),
+ !m_pressureDisabled ? 1.0 : pressureToCurve(event->pressure()),
event->xTilt(), event->yTilt(),
event->rotation(),
event->tangentialPressure(),
perspective,
timeElapsed,
speed);
}
KisPaintInformation KisPaintingInformationBuilder::hover(const QPointF &imagePoint,
const KoPointerEvent *event)
{
qreal perspective = calculatePerspective(imagePoint);
qreal speed = m_speedSmoother->getNextSpeed(imageToView(imagePoint));
if (event) {
return KisPaintInformation::createHoveringModeInfo(imagePoint,
PRESSURE_DEFAULT,
event->xTilt(), event->yTilt(),
event->rotation(),
event->tangentialPressure(),
perspective,
speed);
} else {
return KisPaintInformation::createHoveringModeInfo(imagePoint);
}
}
qreal KisPaintingInformationBuilder::pressureToCurve(qreal pressure)
{
return m_pressureSamples.at(qRound(pressure * LEVEL_OF_PRESSURE_RESOLUTION));
}
/***********************************************************************/
/* KisConverterPaintingInformationBuilder */
/***********************************************************************/
#include "kis_coordinates_converter.h"
KisConverterPaintingInformationBuilder::KisConverterPaintingInformationBuilder(const KisCoordinatesConverter *converter)
: m_converter(converter)
{
}
QPointF KisConverterPaintingInformationBuilder::documentToImage(const QPointF &point)
{
return m_converter->documentToImage(point);
}
QPointF KisConverterPaintingInformationBuilder::imageToView(const QPointF &point)
{
return m_converter->documentToWidget(point);
}
/***********************************************************************/
/* KisToolFreehandPaintingInformationBuilder */
/***********************************************************************/
#include "kis_tool_freehand.h"
KisToolFreehandPaintingInformationBuilder::KisToolFreehandPaintingInformationBuilder(KisToolFreehand *tool)
: m_tool(tool)
{
}
QPointF KisToolFreehandPaintingInformationBuilder::documentToImage(const QPointF &point)
{
return m_tool->convertToPixelCoord(point);
}
QPointF KisToolFreehandPaintingInformationBuilder::imageToView(const QPointF &point)
{
return m_tool->pixelToView(point);
}
QPointF KisToolFreehandPaintingInformationBuilder::adjustDocumentPoint(const QPointF &point, const QPointF &startPoint)
{
return m_tool->adjustPosition(point, startPoint);
}
qreal KisToolFreehandPaintingInformationBuilder::calculatePerspective(const QPointF &documentPoint)
{
return m_tool->calculatePerspective(documentPoint);
}
diff --git a/libs/ui/tool/kis_painting_information_builder.h b/libs/ui/tool/kis_painting_information_builder.h
index bde17d7cdc..979bf92566 100644
--- a/libs/ui/tool/kis_painting_information_builder.h
+++ b/libs/ui/tool/kis_painting_information_builder.h
@@ -1,109 +1,111 @@
/*
* 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 __KIS_PAINTING_INFORMATION_BUILDER_H
#define __KIS_PAINTING_INFORMATION_BUILDER_H
#include
#include
#include "kis_types.h"
#include "kritaui_export.h"
#include
class KoPointerEvent;
class KisToolFreehand;
class KisCoordinatesConverter;
class KisSpeedSmoother;
+class KoCanvasResourceManager;
class KRITAUI_EXPORT KisPaintingInformationBuilder : public QObject
{
Q_OBJECT
public:
KisPaintingInformationBuilder();
~KisPaintingInformationBuilder();
- KisPaintInformation startStroke(KoPointerEvent *event, int timeElapsed);
+ KisPaintInformation startStroke(KoPointerEvent *event, int timeElapsed, const KoCanvasResourceManager *manager);
KisPaintInformation continueStroke(KoPointerEvent *event,
int timeElapsed);
KisPaintInformation hover(const QPointF &imagePoint,
const KoPointerEvent *event);
protected Q_SLOTS:
void updateSettings();
protected:
virtual QPointF adjustDocumentPoint(const QPointF &point, const QPointF &startPoint);
virtual QPointF documentToImage(const QPointF &point);
virtual QPointF imageToView(const QPointF &point);
virtual qreal calculatePerspective(const QPointF &documentPoint);
private:
KisPaintInformation createPaintingInformation(KoPointerEvent *event,
int timeElapsed);
/**
* Defines how many discrete samples are stored in a precomputed array
* of different pressures.
*/
static const int LEVEL_OF_PRESSURE_RESOLUTION;
qreal pressureToCurve(qreal pressure);
private:
QVector m_pressureSamples;
QPointF m_startPoint;
QScopedPointer m_speedSmoother;
+ bool m_pressureDisabled;
};
class KRITAUI_EXPORT KisConverterPaintingInformationBuilder : public KisPaintingInformationBuilder
{
Q_OBJECT
public:
KisConverterPaintingInformationBuilder(const KisCoordinatesConverter *converter);
protected:
virtual QPointF documentToImage(const QPointF &point);
virtual QPointF imageToView(const QPointF &point);
private:
const KisCoordinatesConverter *m_converter;
};
class KRITAUI_EXPORT KisToolFreehandPaintingInformationBuilder : public KisPaintingInformationBuilder
{
Q_OBJECT
public:
KisToolFreehandPaintingInformationBuilder(KisToolFreehand *tool);
protected:
virtual QPointF documentToImage(const QPointF &point);
virtual QPointF imageToView(const QPointF &point);
virtual QPointF adjustDocumentPoint(const QPointF &point, const QPointF &startPoint);
virtual qreal calculatePerspective(const QPointF &documentPoint);
private:
KisToolFreehand *m_tool;
};
#endif /* __KIS_PAINTING_INFORMATION_BUILDER_H */
diff --git a/libs/ui/tool/kis_tool_freehand_helper.cpp b/libs/ui/tool/kis_tool_freehand_helper.cpp
index dde3424b5d..cd7eca0c03 100644
--- a/libs/ui/tool/kis_tool_freehand_helper.cpp
+++ b/libs/ui/tool/kis_tool_freehand_helper.cpp
@@ -1,859 +1,859 @@
/*
* 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.
*/
#include "kis_tool_freehand_helper.h"
#include
#include
#include
#include
#include
#include
#include "kis_painting_information_builder.h"
#include "kis_recording_adapter.h"
#include "kis_image.h"
#include "kis_painter.h"
#include
#include
#include "kis_update_time_monitor.h"
#include "kis_stabilized_events_sampler.h"
#include "kis_config.h"
#include
//#define DEBUG_BEZIER_CURVES
struct KisToolFreehandHelper::Private
{
KisPaintingInformationBuilder *infoBuilder;
KisRecordingAdapter *recordingAdapter;
KisStrokesFacade *strokesFacade;
KUndo2MagicString transactionText;
bool haveTangent;
QPointF previousTangent;
bool hasPaintAtLeastOnce;
QTime strokeTime;
QTimer strokeTimeoutTimer;
QVector painterInfos;
KisResourcesSnapshotSP resources;
KisStrokeId strokeId;
KisPaintInformation previousPaintInformation;
KisPaintInformation olderPaintInformation;
KisSmoothingOptionsSP smoothingOptions;
QTimer airbrushingTimer;
QList history;
QList distanceHistory;
KisPaintOpUtils::PositionHistory lastOutlinePos;
// Stabilizer data
QQueue stabilizerDeque;
QTimer stabilizerPollTimer;
KisStabilizedEventsSampler stabilizedSampler;
int canvasRotation;
bool canvasMirroredH;
KisPaintInformation
getStabilizedPaintInfo(const QQueue &queue,
const KisPaintInformation &lastPaintInfo);
qreal effectiveSmoothnessDistance() const;
};
KisToolFreehandHelper::KisToolFreehandHelper(KisPaintingInformationBuilder *infoBuilder,
const KUndo2MagicString &transactionText,
KisRecordingAdapter *recordingAdapter)
: m_d(new Private())
{
m_d->infoBuilder = infoBuilder;
m_d->recordingAdapter = recordingAdapter;
m_d->transactionText = transactionText;
m_d->smoothingOptions = KisSmoothingOptionsSP(new KisSmoothingOptions());
m_d->canvasRotation = 0;
m_d->strokeTimeoutTimer.setSingleShot(true);
connect(&m_d->strokeTimeoutTimer, SIGNAL(timeout()), SLOT(finishStroke()));
connect(&m_d->airbrushingTimer, SIGNAL(timeout()), SLOT(doAirbrushing()));
connect(&m_d->stabilizerPollTimer, SIGNAL(timeout()), SLOT(stabilizerPollAndPaint()));
}
KisToolFreehandHelper::~KisToolFreehandHelper()
{
delete m_d;
}
void KisToolFreehandHelper::setSmoothness(KisSmoothingOptionsSP smoothingOptions)
{
m_d->smoothingOptions = smoothingOptions;
}
KisSmoothingOptionsSP KisToolFreehandHelper::smoothingOptions() const
{
return m_d->smoothingOptions;
}
QPainterPath KisToolFreehandHelper::paintOpOutline(const QPointF &savedCursorPos,
const KoPointerEvent *event,
const KisPaintOpSettings *globalSettings,
KisPaintOpSettings::OutlineMode mode) const
{
const KisPaintOpSettings *settings = globalSettings;
KisPaintInformation info = m_d->infoBuilder->hover(savedCursorPos, event);
info.setCanvasRotation(m_d->canvasRotation);
info.setCanvasHorizontalMirrorState( m_d->canvasMirroredH );
KisDistanceInformation distanceInfo(m_d->lastOutlinePos.pushThroughHistory(savedCursorPos), 0);
if (!m_d->painterInfos.isEmpty()) {
settings = m_d->resources->currentPaintOpPreset()->settings();
info = m_d->previousPaintInformation;
/**
* When LoD mode is active it may happen that the helper has
* already started a stroke, but it painted noting, because
* all the work is being calculated by the scaled-down LodN
* stroke. So at first we try to fetch the data from the lodN
* stroke ("buddy") and then check if there is at least
* something has been painted with this distance information
* object.
*/
KisDistanceInformation *buddyDistance =
m_d->painterInfos.first()->buddyDragDistance();
if (buddyDistance) {
/**
* Tiny hack alert: here we fetch the distance information
* directly from the LodN stroke. Ideally, we should
* upscale its data, but here we just override it with our
* local copy of the coordinates.
*/
distanceInfo = *buddyDistance;
distanceInfo.overrideLastValues(m_d->lastOutlinePos.pushThroughHistory(savedCursorPos), 0);
} else if (m_d->painterInfos.first()->dragDistance->isStarted()) {
distanceInfo = *m_d->painterInfos.first()->dragDistance;
}
}
KisPaintInformation::DistanceInformationRegistrar registrar =
info.registerDistanceInformation(&distanceInfo);
QPainterPath outline = settings->brushOutline(info, mode);
if (m_d->resources &&
m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER &&
m_d->smoothingOptions->useDelayDistance()) {
const qreal R = m_d->smoothingOptions->delayDistance() /
m_d->resources->effectiveZoom();
outline.addEllipse(info.pos(), R, R);
}
return outline;
}
void KisToolFreehandHelper::initPaint(KoPointerEvent *event,
KoCanvasResourceManager *resourceManager,
KisImageWSP image, KisNodeSP currentNode,
KisStrokesFacade *strokesFacade,
KisPostExecutionUndoAdapter *undoAdapter,
KisNodeSP overrideNode,
KisDefaultBoundsBaseSP bounds)
{
KisPaintInformation pi =
- m_d->infoBuilder->startStroke(event, elapsedStrokeTime());
+ m_d->infoBuilder->startStroke(event, elapsedStrokeTime(), resourceManager);
initPaintImpl(pi,
resourceManager,
image,
currentNode,
strokesFacade,
undoAdapter,
overrideNode,
bounds);
}
bool KisToolFreehandHelper::isRunning() const
{
return m_d->strokeId;
}
void KisToolFreehandHelper::initPaintImpl(const KisPaintInformation &previousPaintInformation,
KoCanvasResourceManager *resourceManager,
KisImageWSP image,
KisNodeSP currentNode,
KisStrokesFacade *strokesFacade,
KisPostExecutionUndoAdapter *undoAdapter,
KisNodeSP overrideNode,
KisDefaultBoundsBaseSP bounds)
{
Q_UNUSED(overrideNode);
m_d->strokesFacade = strokesFacade;
m_d->haveTangent = false;
m_d->previousTangent = QPointF();
m_d->hasPaintAtLeastOnce = false;
m_d->strokeTime.start();
m_d->previousPaintInformation = previousPaintInformation;
createPainters(m_d->painterInfos,
m_d->previousPaintInformation.pos(),
m_d->previousPaintInformation.currentTime());
m_d->resources = new KisResourcesSnapshot(image,
currentNode,
undoAdapter,
resourceManager,
bounds);
if(overrideNode) {
m_d->resources->setCurrentNode(overrideNode);
}
if(m_d->recordingAdapter) {
m_d->recordingAdapter->startStroke(image, m_d->resources);
}
KisStrokeStrategy *stroke =
new FreehandStrokeStrategy(m_d->resources->needsIndirectPainting(),
m_d->resources->indirectPaintingCompositeOp(),
m_d->resources, m_d->painterInfos, m_d->transactionText);
m_d->strokeId = m_d->strokesFacade->startStroke(stroke);
m_d->history.clear();
m_d->distanceHistory.clear();
if(m_d->resources->needsAirbrushing()) {
m_d->airbrushingTimer.setInterval(m_d->resources->airbrushingRate());
m_d->airbrushingTimer.start();
}
if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
stabilizerStart(m_d->previousPaintInformation);
}
}
void KisToolFreehandHelper::paintBezierSegment(KisPaintInformation pi1, KisPaintInformation pi2,
QPointF tangent1, QPointF tangent2)
{
if (tangent1.isNull() || tangent2.isNull()) return;
const qreal maxSanePoint = 1e6;
QPointF controlTarget1;
QPointF controlTarget2;
// Shows the direction in which control points go
QPointF controlDirection1 = pi1.pos() + tangent1;
QPointF controlDirection2 = pi2.pos() - tangent2;
// Lines in the direction of the control points
QLineF line1(pi1.pos(), controlDirection1);
QLineF line2(pi2.pos(), controlDirection2);
// Lines to check whether the control points lay on the opposite
// side of the line
QLineF line3(controlDirection1, controlDirection2);
QLineF line4(pi1.pos(), pi2.pos());
QPointF intersection;
if (line3.intersect(line4, &intersection) == QLineF::BoundedIntersection) {
qreal controlLength = line4.length() / 2;
line1.setLength(controlLength);
line2.setLength(controlLength);
controlTarget1 = line1.p2();
controlTarget2 = line2.p2();
} else {
QLineF::IntersectType type = line1.intersect(line2, &intersection);
if (type == QLineF::NoIntersection ||
intersection.manhattanLength() > maxSanePoint) {
intersection = 0.5 * (pi1.pos() + pi2.pos());
// dbgKrita << "WARINING: there is no intersection point "
// << "in the basic smoothing algoriths";
}
controlTarget1 = intersection;
controlTarget2 = intersection;
}
// shows how near to the controlTarget the value raises
qreal coeff = 0.8;
qreal velocity1 = QLineF(QPointF(), tangent1).length();
qreal velocity2 = QLineF(QPointF(), tangent2).length();
if (velocity1 == 0.0 || velocity2 == 0.0) {
velocity1 = 1e-6;
velocity2 = 1e-6;
warnKrita << "WARNING: Basic Smoothing: Velocity is Zero! Please report a bug:" << ppVar(velocity1) << ppVar(velocity2);
}
qreal similarity = qMin(velocity1/velocity2, velocity2/velocity1);
// the controls should not differ more than 50%
similarity = qMax(similarity, qreal(0.5));
// when the controls are symmetric, their size should be smaller
// to avoid corner-like curves
coeff *= 1 - qMax(qreal(0.0), similarity - qreal(0.8));
Q_ASSERT(coeff > 0);
QPointF control1;
QPointF control2;
if (velocity1 > velocity2) {
control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1;
coeff *= similarity;
control2 = pi2.pos() * (1.0 - coeff) + coeff * controlTarget2;
} else {
control2 = pi2.pos() * (1.0 - coeff) + coeff * controlTarget2;
coeff *= similarity;
control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1;
}
paintBezierCurve(pi1,
control1,
control2,
pi2);
}
qreal KisToolFreehandHelper::Private::effectiveSmoothnessDistance() const
{
const qreal effectiveSmoothnessDistance =
!smoothingOptions->useScalableDistance() ?
smoothingOptions->smoothnessDistance() :
smoothingOptions->smoothnessDistance() / resources->effectiveZoom();
return effectiveSmoothnessDistance;
}
void KisToolFreehandHelper::paint(KoPointerEvent *event)
{
KisPaintInformation info =
m_d->infoBuilder->continueStroke(event,
elapsedStrokeTime());
info.setCanvasRotation( m_d->canvasRotation );
info.setCanvasHorizontalMirrorState( m_d->canvasMirroredH );
KisUpdateTimeMonitor::instance()->reportMouseMove(info.pos());
/**
* Smooth the coordinates out using the history and the
* distance. This is a heavily modified version of an algo used in
* Gimp and described in https://bugs.kde.org/show_bug.cgi?id=281267 and
* http://www24.atwiki.jp/sigetch_2007/pages/17.html. The main
* differences are:
*
* 1) It uses 'distance' instead of 'velocity', since time
* measurements are too unstable in realworld environment
*
* 2) There is no 'Quality' parameter, since the number of samples
* is calculated automatically
*
* 3) 'Tail Aggressiveness' is used for controling the end of the
* stroke
*
* 4) The formila is a little bit different: 'Distance' parameter
* stands for $3 \Sigma$
*/
if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::WEIGHTED_SMOOTHING
&& m_d->smoothingOptions->smoothnessDistance() > 0.0) {
{ // initialize current distance
QPointF prevPos;
if (!m_d->history.isEmpty()) {
const KisPaintInformation &prevPi = m_d->history.last();
prevPos = prevPi.pos();
} else {
prevPos = m_d->previousPaintInformation.pos();
}
qreal currentDistance = QVector2D(info.pos() - prevPos).length();
m_d->distanceHistory.append(currentDistance);
}
m_d->history.append(info);
qreal x = 0.0;
qreal y = 0.0;
if (m_d->history.size() > 3) {
const qreal sigma = m_d->effectiveSmoothnessDistance() / 3.0; // '3.0' for (3 * sigma) range
qreal gaussianWeight = 1 / (sqrt(2 * M_PI) * sigma);
qreal gaussianWeight2 = sigma * sigma;
qreal distanceSum = 0.0;
qreal scaleSum = 0.0;
qreal pressure = 0.0;
qreal baseRate = 0.0;
Q_ASSERT(m_d->history.size() == m_d->distanceHistory.size());
for (int i = m_d->history.size() - 1; i >= 0; i--) {
qreal rate = 0.0;
const KisPaintInformation nextInfo = m_d->history.at(i);
double distance = m_d->distanceHistory.at(i);
Q_ASSERT(distance >= 0.0);
qreal pressureGrad = 0.0;
if (i < m_d->history.size() - 1) {
pressureGrad = nextInfo.pressure() - m_d->history.at(i + 1).pressure();
const qreal tailAgressiveness = 40.0 * m_d->smoothingOptions->tailAggressiveness();
if (pressureGrad > 0.0 ) {
pressureGrad *= tailAgressiveness * (1.0 - nextInfo.pressure());
distance += pressureGrad * 3.0 * sigma; // (3 * sigma) --- holds > 90% of the region
}
}
if (gaussianWeight2 != 0.0) {
distanceSum += distance;
rate = gaussianWeight * exp(-distanceSum * distanceSum / (2 * gaussianWeight2));
}
if (m_d->history.size() - i == 1) {
baseRate = rate;
} else if (baseRate / rate > 100) {
break;
}
scaleSum += rate;
x += rate * nextInfo.pos().x();
y += rate * nextInfo.pos().y();
if (m_d->smoothingOptions->smoothPressure()) {
pressure += rate * nextInfo.pressure();
}
}
if (scaleSum != 0.0) {
x /= scaleSum;
y /= scaleSum;
if (m_d->smoothingOptions->smoothPressure()) {
pressure /= scaleSum;
}
}
if ((x != 0.0 && y != 0.0) || (x == info.pos().x() && y == info.pos().y())) {
info.setPos(QPointF(x, y));
if (m_d->smoothingOptions->smoothPressure()) {
info.setPressure(pressure);
}
m_d->history.last() = info;
}
}
}
if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::SIMPLE_SMOOTHING
|| m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::WEIGHTED_SMOOTHING)
{
// Now paint between the coordinates, using the bezier curve interpolation
if (!m_d->haveTangent) {
m_d->haveTangent = true;
m_d->previousTangent =
(info.pos() - m_d->previousPaintInformation.pos()) /
qMax(qreal(1.0), info.currentTime() - m_d->previousPaintInformation.currentTime());
} else {
QPointF newTangent = (info.pos() - m_d->olderPaintInformation.pos()) /
qMax(qreal(1.0), info.currentTime() - m_d->olderPaintInformation.currentTime());
paintBezierSegment(m_d->olderPaintInformation, m_d->previousPaintInformation,
m_d->previousTangent, newTangent);
m_d->previousTangent = newTangent;
}
m_d->olderPaintInformation = m_d->previousPaintInformation;
m_d->strokeTimeoutTimer.start(100);
}
else if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::NO_SMOOTHING){
paintLine(m_d->previousPaintInformation, info);
}
if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
m_d->stabilizedSampler.addEvent(info);
} else {
m_d->previousPaintInformation = info;
}
if(m_d->airbrushingTimer.isActive()) {
m_d->airbrushingTimer.start();
}
}
void KisToolFreehandHelper::endPaint()
{
if (!m_d->hasPaintAtLeastOnce) {
paintAt(m_d->previousPaintInformation);
} else if (m_d->smoothingOptions->smoothingType() != KisSmoothingOptions::NO_SMOOTHING) {
finishStroke();
}
m_d->strokeTimeoutTimer.stop();
if(m_d->airbrushingTimer.isActive()) {
m_d->airbrushingTimer.stop();
}
if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
stabilizerEnd();
}
/**
* There might be some timer events still pending, so
* we should cancel them. Use this flag for the purpose.
* Please note that we are not in MT here, so no mutex
* is needed
*/
m_d->painterInfos.clear();
m_d->strokesFacade->endStroke(m_d->strokeId);
m_d->strokeId.clear();
if(m_d->recordingAdapter) {
m_d->recordingAdapter->endStroke();
}
}
void KisToolFreehandHelper::cancelPaint()
{
if (!m_d->strokeId) return;
m_d->strokeTimeoutTimer.stop();
if (m_d->airbrushingTimer.isActive()) {
m_d->airbrushingTimer.stop();
}
if (m_d->stabilizerPollTimer.isActive()) {
m_d->stabilizerPollTimer.stop();
}
// see a comment in endPaint()
m_d->painterInfos.clear();
m_d->strokesFacade->cancelStroke(m_d->strokeId);
m_d->strokeId.clear();
if(m_d->recordingAdapter) {
//FIXME: not implemented
//m_d->recordingAdapter->cancelStroke();
}
}
int KisToolFreehandHelper::elapsedStrokeTime() const
{
return m_d->strokeTime.elapsed();
}
void KisToolFreehandHelper::stabilizerStart(KisPaintInformation firstPaintInfo)
{
// FIXME: Ugly hack, this is no a "distance" in any way
int sampleSize = qRound(m_d->effectiveSmoothnessDistance());
sampleSize = qMax(3, sampleSize);
// Fill the deque with the current value repeated until filling the sample
m_d->stabilizerDeque.clear();
for (int i = sampleSize; i > 0; i--) {
m_d->stabilizerDeque.enqueue(firstPaintInfo);
}
// Poll and draw regularly
KisConfig cfg;
m_d->stabilizerPollTimer.setInterval(cfg.stabilizerSampleSize());
m_d->stabilizerPollTimer.start();
m_d->stabilizedSampler.clear();
}
KisPaintInformation
KisToolFreehandHelper::Private::getStabilizedPaintInfo(const QQueue &queue,
const KisPaintInformation &lastPaintInfo)
{
KisPaintInformation result(lastPaintInfo);
if (queue.size() > 1) {
QQueue::const_iterator it = queue.constBegin();
QQueue::const_iterator end = queue.constEnd();
/**
* The first point is going to be overridden by lastPaintInfo, skip it.
*/
it++;
int i = 2;
if (smoothingOptions->stabilizeSensors()) {
while (it != end) {
qreal k = qreal(i - 1) / i; // coeff for uniform averaging
result = KisPaintInformation::mix(k, *it, result);
it++;
i++;
}
} else{
while (it != end) {
qreal k = qreal(i - 1) / i; // coeff for uniform averaging
result = KisPaintInformation::mixOnlyPosition(k, *it, result);
it++;
i++;
}
}
}
return result;
}
void KisToolFreehandHelper::stabilizerPollAndPaint()
{
KisStabilizedEventsSampler::iterator it;
KisStabilizedEventsSampler::iterator end;
std::tie(it, end) = m_d->stabilizedSampler.range();
for (; it != end; ++it) {
KisPaintInformation sampledInfo = *it;
bool canPaint = true;
if (m_d->smoothingOptions->useDelayDistance()) {
const qreal R = m_d->smoothingOptions->delayDistance() /
m_d->resources->effectiveZoom();
QPointF diff = sampledInfo.pos() - m_d->previousPaintInformation.pos();
qreal dx = sqrt(pow2(diff.x()) + pow2(diff.y()));
canPaint = dx > R;
}
if (canPaint) {
KisPaintInformation newInfo =
m_d->getStabilizedPaintInfo(m_d->stabilizerDeque, sampledInfo);
paintLine(m_d->previousPaintInformation, newInfo);
m_d->previousPaintInformation = newInfo;
// Push the new entry through the queue
m_d->stabilizerDeque.dequeue();
m_d->stabilizerDeque.enqueue(sampledInfo);
emit requestExplicitUpdateOutline();
} else if (m_d->stabilizerDeque.head().pos() != m_d->previousPaintInformation.pos()) {
QQueue::iterator it = m_d->stabilizerDeque.begin();
QQueue::iterator end = m_d->stabilizerDeque.end();
while (it != end) {
*it = m_d->previousPaintInformation;
++it;
}
}
}
m_d->stabilizedSampler.clear();
}
void KisToolFreehandHelper::stabilizerEnd()
{
// FIXME: Ugly hack, this is no a "distance" in any way
int sampleSize = m_d->smoothingOptions->smoothnessDistance();
assert(sampleSize > 0);
// Stop the timer
m_d->stabilizerPollTimer.stop();
// Finish the line
for (int i = sampleSize; i > 0; i--) {
// In each iteration we add the latest paint info and delete the oldest
// After `sampleSize` iterations the deque will be filled with the latest
// value and we will have reached the end point.
if (m_d->smoothingOptions->finishStabilizedCurve()) {
stabilizerPollAndPaint();
}
}
}
const KisPaintOp* KisToolFreehandHelper::currentPaintOp() const
{
return !m_d->painterInfos.isEmpty() ? m_d->painterInfos.first()->painter->paintOp() : 0;
}
void KisToolFreehandHelper::finishStroke()
{
if (m_d->haveTangent) {
m_d->haveTangent = false;
QPointF newTangent = (m_d->previousPaintInformation.pos() - m_d->olderPaintInformation.pos()) /
(m_d->previousPaintInformation.currentTime() - m_d->olderPaintInformation.currentTime());
paintBezierSegment(m_d->olderPaintInformation,
m_d->previousPaintInformation,
m_d->previousTangent,
newTangent);
}
}
void KisToolFreehandHelper::doAirbrushing()
{
if(!m_d->painterInfos.isEmpty()) {
paintAt(m_d->previousPaintInformation);
}
}
void KisToolFreehandHelper::paintAt(int painterInfoId,
const KisPaintInformation &pi)
{
m_d->hasPaintAtLeastOnce = true;
m_d->strokesFacade->addJob(m_d->strokeId,
new FreehandStrokeStrategy::Data(m_d->resources->currentNode(),
painterInfoId, pi));
if(m_d->recordingAdapter) {
m_d->recordingAdapter->addPoint(pi);
}
}
void KisToolFreehandHelper::paintLine(int painterInfoId,
const KisPaintInformation &pi1,
const KisPaintInformation &pi2)
{
m_d->hasPaintAtLeastOnce = true;
m_d->strokesFacade->addJob(m_d->strokeId,
new FreehandStrokeStrategy::Data(m_d->resources->currentNode(),
painterInfoId, pi1, pi2));
if(m_d->recordingAdapter) {
m_d->recordingAdapter->addLine(pi1, pi2);
}
}
void KisToolFreehandHelper::paintBezierCurve(int painterInfoId,
const KisPaintInformation &pi1,
const QPointF &control1,
const QPointF &control2,
const KisPaintInformation &pi2)
{
#ifdef DEBUG_BEZIER_CURVES
KisPaintInformation tpi1;
KisPaintInformation tpi2;
tpi1 = pi1;
tpi2 = pi2;
tpi1.setPressure(0.3);
tpi2.setPressure(0.3);
paintLine(tpi1, tpi2);
tpi1.setPressure(0.6);
tpi2.setPressure(0.3);
tpi1.setPos(pi1.pos());
tpi2.setPos(control1);
paintLine(tpi1, tpi2);
tpi1.setPos(pi2.pos());
tpi2.setPos(control2);
paintLine(tpi1, tpi2);
#endif
m_d->hasPaintAtLeastOnce = true;
m_d->strokesFacade->addJob(m_d->strokeId,
new FreehandStrokeStrategy::Data(m_d->resources->currentNode(),
painterInfoId,
pi1, control1, control2, pi2));
if(m_d->recordingAdapter) {
m_d->recordingAdapter->addCurve(pi1, control1, control2, pi2);
}
}
void KisToolFreehandHelper::createPainters(QVector &painterInfos,
const QPointF &lastPosition,
int lastTime)
{
painterInfos << new PainterInfo(lastPosition, lastTime);
}
void KisToolFreehandHelper::paintAt(const KisPaintInformation &pi)
{
paintAt(0, pi);
}
void KisToolFreehandHelper::paintLine(const KisPaintInformation &pi1,
const KisPaintInformation &pi2)
{
paintLine(0, pi1, pi2);
}
void KisToolFreehandHelper::paintBezierCurve(const KisPaintInformation &pi1,
const QPointF &control1,
const QPointF &control2,
const KisPaintInformation &pi2)
{
paintBezierCurve(0, pi1, control1, control2, pi2);
}
int KisToolFreehandHelper::canvasRotation()
{
return m_d->canvasRotation;
}
void KisToolFreehandHelper::setCanvasRotation(int rotation)
{
m_d->canvasRotation = rotation;
}
bool KisToolFreehandHelper::canvasMirroredH()
{
return m_d->canvasMirroredH;
}
void KisToolFreehandHelper::setCanvasHorizontalMirrorState(bool mirrored)
{
m_d->canvasMirroredH = mirrored;
}
diff --git a/plugins/dockers/defaultdockers/kis_layer_box.cpp b/plugins/dockers/defaultdockers/kis_layer_box.cpp
index 5743535776..8d15a08132 100644
--- a/plugins/dockers/defaultdockers/kis_layer_box.cpp
+++ b/plugins/dockers/defaultdockers/kis_layer_box.cpp
@@ -1,853 +1,852 @@
/*
* kis_layer_box.cc - part of Krita aka Krayon aka KimageShop
*
* Copyright (c) 2002 Patrick Julien
* Copyright (C) 2006 Gábor Lehel
* Copyright (C) 2007 Thomas Zander
* Copyright (C) 2007 Boudewijn Rempt
* Copyright (c) 2011 José Luis Vergara
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_layer_box.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kis_action.h"
#include "kis_action_manager.h"
#include "widgets/kis_cmb_composite.h"
#include "widgets/kis_slider_spin_box.h"
#include "KisViewManager.h"
#include "kis_node_manager.h"
#include "kis_node_model.h"
#include "canvas/kis_canvas2.h"
#include "KisDocument.h"
#include "kis_dummies_facade_base.h"
#include "kis_shape_controller.h"
#include "kis_selection_mask.h"
#include "kis_config.h"
#include "KisView.h"
#include "krita_utils.h"
#include "sync_button_and_action.h"
#include "kis_color_label_selector_widget.h"
#include "kis_signals_blocker.h"
#include "kis_color_filter_combo.h"
#include "kis_node_filter_proxy_model.h"
#include "kis_layer_utils.h"
#include "ui_wdglayerbox.h"
inline void KisLayerBox::connectActionToButton(KisViewManager* view, QAbstractButton *button, const QString &id)
{
Q_ASSERT(view);
KisAction *action = view->actionManager()->actionByName(id);
connect(button, SIGNAL(clicked()), action, SLOT(trigger()));
connect(action, SIGNAL(sigEnableSlaves(bool)), button, SLOT(setEnabled(bool)));
}
inline void KisLayerBox::addActionToMenu(QMenu *menu, const QString &id)
{
if (m_canvas) {
menu->addAction(m_canvas->viewManager()->actionManager()->actionByName(id));
}
}
KisLayerBox::KisLayerBox()
: QDockWidget(i18n("Layers"))
, m_canvas(0)
, m_wdgLayerBox(new Ui_WdgLayerBox)
, m_thumbnailCompressor(500, KisSignalCompressor::FIRST_INACTIVE)
, m_colorLabelCompressor(900, KisSignalCompressor::FIRST_INACTIVE)
{
KisConfig cfg;
QWidget* mainWidget = new QWidget(this);
setWidget(mainWidget);
m_opacityDelayTimer.setSingleShot(true);
m_wdgLayerBox->setupUi(mainWidget);
connect(m_wdgLayerBox->listLayers,
SIGNAL(contextMenuRequested(const QPoint&, const QModelIndex&)),
this, SLOT(slotContextMenuRequested(const QPoint&, const QModelIndex&)));
connect(m_wdgLayerBox->listLayers,
SIGNAL(collapsed(const QModelIndex&)), SLOT(slotCollapsed(const QModelIndex &)));
connect(m_wdgLayerBox->listLayers,
SIGNAL(expanded(const QModelIndex&)), SLOT(slotExpanded(const QModelIndex &)));
connect(m_wdgLayerBox->listLayers,
SIGNAL(selectionChanged(const QModelIndexList&)), SLOT(selectionChanged(const QModelIndexList&)));
m_wdgLayerBox->bnAdd->setIcon(KisIconUtils::loadIcon("addlayer"));
m_wdgLayerBox->bnDelete->setIcon(KisIconUtils::loadIcon("deletelayer"));
m_wdgLayerBox->bnDelete->setIconSize(QSize(22, 22));
m_wdgLayerBox->bnRaise->setEnabled(false);
m_wdgLayerBox->bnRaise->setIcon(KisIconUtils::loadIcon("arrowupblr"));
m_wdgLayerBox->bnRaise->setIconSize(QSize(22, 22));
m_wdgLayerBox->bnLower->setEnabled(false);
m_wdgLayerBox->bnLower->setIcon(KisIconUtils::loadIcon("arrowdown"));
m_wdgLayerBox->bnLower->setIconSize(QSize(22, 22));
m_wdgLayerBox->bnProperties->setIcon(KisIconUtils::loadIcon("properties"));
m_wdgLayerBox->bnProperties->setIconSize(QSize(22, 22));
m_wdgLayerBox->bnDuplicate->setIcon(KisIconUtils::loadIcon("duplicatelayer"));
m_wdgLayerBox->bnDuplicate->setIconSize(QSize(22, 22));
if (cfg.sliderLabels()) {
m_wdgLayerBox->opacityLabel->hide();
m_wdgLayerBox->doubleOpacity->setPrefix(QString("%1: ").arg(i18n("Opacity")));
}
m_wdgLayerBox->doubleOpacity->setRange(0, 100, 0);
m_wdgLayerBox->doubleOpacity->setSuffix("%");
connect(m_wdgLayerBox->doubleOpacity, SIGNAL(valueChanged(qreal)), SLOT(slotOpacitySliderMoved(qreal)));
connect(&m_opacityDelayTimer, SIGNAL(timeout()), SLOT(slotOpacityChanged()));
connect(m_wdgLayerBox->cmbComposite, SIGNAL(activated(int)), SLOT(slotCompositeOpChanged(int)));
m_selectOpaque = new KisAction(i18n("&Select Opaque"), this);
m_selectOpaque->setActivationFlags(KisAction::ACTIVE_LAYER);
m_selectOpaque->setActivationConditions(KisAction::SELECTION_EDITABLE);
m_selectOpaque->setObjectName("select_opaque");
connect(m_selectOpaque, SIGNAL(triggered(bool)), this, SLOT(slotSelectOpaque()));
m_actions.append(m_selectOpaque);
m_newLayerMenu = new QMenu(this);
m_wdgLayerBox->bnAdd->setMenu(m_newLayerMenu);
m_wdgLayerBox->bnAdd->setPopupMode(QToolButton::MenuButtonPopup);
m_nodeModel = new KisNodeModel(this);
m_filteringModel = new KisNodeFilterProxyModel(this);
m_filteringModel->setNodeModel(m_nodeModel);
/**
* Connect model updateUI() to enable/disable controls.
* Note: nodeActivated() is connected separately in setImage(), because
* it needs particular order of calls: first the connection to the
* node manager should be called, then updateUI()
*/
connect(m_nodeModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)), SLOT(updateUI()));
connect(m_nodeModel, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), SLOT(updateUI()));
connect(m_nodeModel, SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)), SLOT(updateUI()));
connect(m_nodeModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), SLOT(updateUI()));
connect(m_nodeModel, SIGNAL(modelReset()), SLOT(slotModelReset()));
KisAction *showGlobalSelectionMask = new KisAction(i18n("&Show Global Selection Mask"), this);
showGlobalSelectionMask->setObjectName("show-global-selection-mask");
showGlobalSelectionMask->setToolTip(i18nc("@info:tooltip", "Shows global selection as a usual selection mask in Layers docker"));
showGlobalSelectionMask->setCheckable(true);
connect(showGlobalSelectionMask, SIGNAL(triggered(bool)), SLOT(slotEditGlobalSelection(bool)));
m_actions.append(showGlobalSelectionMask);
showGlobalSelectionMask->setChecked(cfg.showGlobalSelection());
m_colorSelector = new KisColorLabelSelectorWidget(this);
connect(m_colorSelector, SIGNAL(currentIndexChanged(int)), SLOT(slotColorLabelChanged(int)));
m_colorSelectorAction = new QWidgetAction(this);
m_colorSelectorAction->setDefaultWidget(m_colorSelector);
connect(m_nodeModel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
&m_colorLabelCompressor, SLOT(start()));
m_wdgLayerBox->listLayers->setModel(m_filteringModel);
// this connection should be done *after* the setModel() call to
// happen later than the internal selection model
connect(m_filteringModel.data(), &KisNodeFilterProxyModel::rowsAboutToBeRemoved,
this, &KisLayerBox::slotAboutToRemoveRows);
connect(m_wdgLayerBox->cmbFilter, SIGNAL(selectedColorsChanged()), SLOT(updateLayerFiltering()));
setEnabled(false);
connect(&m_thumbnailCompressor, SIGNAL(timeout()), SLOT(updateThumbnail()));
connect(&m_colorLabelCompressor, SIGNAL(timeout()), SLOT(updateAvailableLabels()));
}
KisLayerBox::~KisLayerBox()
{
delete m_wdgLayerBox;
}
void expandNodesRecursively(KisNodeSP root, QPointer filteringModel, KisNodeView *nodeView)
{
if (!root) return;
if (filteringModel.isNull()) return;
if (!nodeView) return;
nodeView->blockSignals(true);
KisNodeSP node = root->firstChild();
while (node) {
QModelIndex idx = filteringModel->indexFromNode(node);
if (idx.isValid()) {
nodeView->setExpanded(idx, !node->collapsed());
}
if (node->childCount() > 0) {
expandNodesRecursively(node, filteringModel, nodeView);
}
node = node->nextSibling();
}
nodeView->blockSignals(false);
}
void KisLayerBox::setMainWindow(KisViewManager* kisview)
{
m_nodeManager = kisview->nodeManager();
Q_FOREACH (KisAction *action, m_actions) {
kisview->actionManager()->
addAction(action->objectName(),
action);
}
connectActionToButton(kisview, m_wdgLayerBox->bnAdd, "add_new_paint_layer");
connectActionToButton(kisview, m_wdgLayerBox->bnDuplicate, "duplicatelayer");
KisActionManager *actionManager = kisview->actionManager();
KisAction *action = actionManager->createAction("RenameCurrentLayer");
connect(action, SIGNAL(triggered()), this, SLOT(slotRenameCurrentNode()));
m_propertiesAction = actionManager->createAction("layer_properties");
new SyncButtonAndAction(m_propertiesAction, m_wdgLayerBox->bnProperties, this);
connect(m_propertiesAction, SIGNAL(triggered()), this, SLOT(slotPropertiesClicked()));
m_removeAction = actionManager->createAction("remove_layer");
new SyncButtonAndAction(m_removeAction, m_wdgLayerBox->bnDelete, this);
connect(m_removeAction, SIGNAL(triggered()), this, SLOT(slotRmClicked()));
action = actionManager->createAction("move_layer_up");
new SyncButtonAndAction(action, m_wdgLayerBox->bnRaise, this);
connect(action, SIGNAL(triggered()), this, SLOT(slotRaiseClicked()));
action = actionManager->createAction("move_layer_down");
new SyncButtonAndAction(action, m_wdgLayerBox->bnLower, this);
connect(action, SIGNAL(triggered()), this, SLOT(slotLowerClicked()));
}
void KisLayerBox::setCanvas(KoCanvasBase *canvas)
{
if(m_canvas == canvas)
return;
setEnabled(canvas != 0);
if (m_canvas) {
m_canvas->disconnectCanvasObserver(this);
m_nodeModel->setDummiesFacade(0, 0, 0, 0, 0);
disconnect(m_image, 0, this, 0);
disconnect(m_nodeManager, 0, this, 0);
disconnect(m_nodeModel, 0, m_nodeManager, 0);
m_nodeManager->slotSetSelectedNodes(KisNodeList());
}
m_canvas = dynamic_cast(canvas);
if (m_canvas) {
m_image = m_canvas->image();
connect(m_image, SIGNAL(sigImageUpdated(QRect)), &m_thumbnailCompressor, SLOT(start()));
KisDocument* doc = static_cast(m_canvas->imageView()->document());
KisShapeController *kritaShapeController =
dynamic_cast(doc->shapeController());
KisDummiesFacadeBase *kritaDummiesFacade =
static_cast(kritaShapeController);
m_nodeModel->setDummiesFacade(kritaDummiesFacade, m_image, kritaShapeController, m_nodeManager->nodeSelectionAdapter(), m_nodeManager->nodeInsertionAdapter());
connect(m_image, SIGNAL(sigAboutToBeDeleted()), SLOT(notifyImageDeleted()));
connect(m_image, SIGNAL(sigNodeCollapsedChanged()), SLOT(slotNodeCollapsedChanged()));
// cold start
if (m_nodeManager) {
setCurrentNode(m_nodeManager->activeNode());
}
else {
setCurrentNode(m_canvas->imageView()->currentNode());
}
// Connection KisNodeManager -> KisLayerBox
connect(m_nodeManager, SIGNAL(sigUiNeedChangeActiveNode(KisNodeSP)),
this, SLOT(setCurrentNode(KisNodeSP)));
connect(m_nodeManager,
SIGNAL(sigUiNeedChangeSelectedNodes(const QList &)),
SLOT(slotNodeManagerChangedSelection(const QList &)));
// Connection KisLayerBox -> KisNodeManager (isolate layer)
connect(m_nodeModel, SIGNAL(toggleIsolateActiveNode()),
m_nodeManager, SLOT(toggleIsolateActiveNode()));
expandNodesRecursively(m_image->rootLayer(), m_filteringModel, m_wdgLayerBox->listLayers);
m_wdgLayerBox->listLayers->scrollTo(m_wdgLayerBox->listLayers->currentIndex());
updateAvailableLabels();
addActionToMenu(m_newLayerMenu, "add_new_paint_layer");
addActionToMenu(m_newLayerMenu, "add_new_group_layer");
addActionToMenu(m_newLayerMenu, "add_new_clone_layer");
addActionToMenu(m_newLayerMenu, "add_new_shape_layer");
addActionToMenu(m_newLayerMenu, "add_new_adjustment_layer");
addActionToMenu(m_newLayerMenu, "add_new_fill_layer");
addActionToMenu(m_newLayerMenu, "add_new_file_layer");
m_newLayerMenu->addSeparator();
addActionToMenu(m_newLayerMenu, "add_new_transparency_mask");
addActionToMenu(m_newLayerMenu, "add_new_filter_mask");
addActionToMenu(m_newLayerMenu, "add_new_transform_mask");
addActionToMenu(m_newLayerMenu, "add_new_selection_mask");
}
}
void KisLayerBox::unsetCanvas()
{
setEnabled(false);
if (m_canvas) {
m_newLayerMenu->clear();
}
m_filteringModel->unsetDummiesFacade();
disconnect(m_image, 0, this, 0);
disconnect(m_nodeManager, 0, this, 0);
disconnect(m_nodeModel, 0, m_nodeManager, 0);
m_nodeManager->slotSetSelectedNodes(KisNodeList());
m_canvas = 0;
}
void KisLayerBox::notifyImageDeleted()
{
setCanvas(0);
}
void KisLayerBox::updateUI()
{
if (!m_canvas) return;
if (!m_nodeManager) return;
KisNodeSP activeNode = m_nodeManager->activeNode();
m_wdgLayerBox->bnRaise->setEnabled(activeNode && activeNode->isEditable(false) && (activeNode->nextSibling()
|| (activeNode->parent() && activeNode->parent() != m_image->root())));
m_wdgLayerBox->bnLower->setEnabled(activeNode && activeNode->isEditable(false) && (activeNode->prevSibling()
|| (activeNode->parent() && activeNode->parent() != m_image->root())));
m_wdgLayerBox->doubleOpacity->setEnabled(activeNode && activeNode->isEditable(false));
m_wdgLayerBox->cmbComposite->setEnabled(activeNode && activeNode->isEditable(false));
if (activeNode) {
if (m_nodeManager->activePaintDevice()) {
slotFillCompositeOps(m_nodeManager->activeColorSpace());
} else {
slotFillCompositeOps(m_image->colorSpace());
}
if (activeNode->inherits("KisMask")) {
m_wdgLayerBox->cmbComposite->setEnabled(false);
m_wdgLayerBox->doubleOpacity->setEnabled(false);
} else if (activeNode->inherits("KisLayer")) {
m_wdgLayerBox->doubleOpacity->setEnabled(true);
KisLayerSP l = qobject_cast(activeNode.data());
slotSetOpacity(l->opacity() * 100.0 / 255);
const KoCompositeOp* compositeOp = l->compositeOp();
if (compositeOp) {
slotSetCompositeOp(compositeOp);
} else {
m_wdgLayerBox->cmbComposite->setEnabled(false);
}
const KisGroupLayer *group = qobject_cast(activeNode.data());
bool compositeSelectionActive = !(group && group->passThroughMode());
m_wdgLayerBox->cmbComposite->setEnabled(compositeSelectionActive);
}
}
}
/**
* This method is callen *only* when non-GUI code requested the
* change of the current node
*/
void KisLayerBox::setCurrentNode(KisNodeSP node)
{
m_filteringModel->setActiveNode(node);
QModelIndex index = node ? m_filteringModel->indexFromNode(node) : QModelIndex();
m_filteringModel->setData(index, true, KisNodeModel::ActiveRole);
updateUI();
}
void KisLayerBox::slotModelReset()
{
if(m_nodeModel->hasDummiesFacade()) {
QItemSelection selection;
Q_FOREACH (const KisNodeSP node, m_nodeManager->selectedNodes()) {
const QModelIndex &idx = m_filteringModel->indexFromNode(node);
if(idx.isValid()){
QItemSelectionRange selectionRange(idx);
selection << selectionRange;
}
}
m_wdgLayerBox->listLayers->selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect);
}
updateUI();
}
void KisLayerBox::slotSetCompositeOp(const KoCompositeOp* compositeOp)
{
KoID opId = KoCompositeOpRegistry::instance().getKoID(compositeOp->id());
m_wdgLayerBox->cmbComposite->blockSignals(true);
m_wdgLayerBox->cmbComposite->selectCompositeOp(opId);
m_wdgLayerBox->cmbComposite->blockSignals(false);
}
void KisLayerBox::slotFillCompositeOps(const KoColorSpace* colorSpace)
{
m_wdgLayerBox->cmbComposite->validate(colorSpace);
}
// range: 0-100
void KisLayerBox::slotSetOpacity(double opacity)
{
Q_ASSERT(opacity >= 0 && opacity <= 100);
m_wdgLayerBox->doubleOpacity->blockSignals(true);
m_wdgLayerBox->doubleOpacity->setValue(opacity);
m_wdgLayerBox->doubleOpacity->blockSignals(false);
}
void KisLayerBox::slotContextMenuRequested(const QPoint &pos, const QModelIndex &index)
{
KisNodeList nodes = m_nodeManager->selectedNodes();
KisNodeSP activeNode = m_nodeManager->activeNode();
if (nodes.isEmpty() || !activeNode) return;
if (m_canvas) {
QMenu menu;
const bool singleLayer = nodes.size() == 1;
if (index.isValid()) {
menu.addAction(m_propertiesAction);
if (singleLayer) {
addActionToMenu(&menu, "layer_style");
}
{
KisSignalsBlocker b(m_colorSelector);
m_colorSelector->setCurrentIndex(singleLayer ? activeNode->colorLabelIndex() : -1);
}
menu.addAction(m_colorSelectorAction);
- if (singleLayer) {
- menu.addSeparator();
- addActionToMenu(&menu, "show_in_timeline");
- }
-
menu.addSeparator();
addActionToMenu(&menu, "cut_layer_clipboard");
addActionToMenu(&menu, "copy_layer_clipboard");
- addActionToMenu(&menu, "paste_layer_from_clipboard");
+ addActionToMenu(&menu, "paste_layer_from_clipboard");
+ menu.addAction(m_removeAction);
+ addActionToMenu(&menu, "duplicatelayer");
+ addActionToMenu(&menu, "merge_layer");
+
+ if (singleLayer) {
+ addActionToMenu(&menu, "flatten_image");
+ addActionToMenu(&menu, "flatten_layer");
+ }
menu.addSeparator();
QMenu *selectMenu = menu.addMenu(i18n("&Select"));
addActionToMenu(selectMenu, "select_all_layers");
addActionToMenu(selectMenu, "select_visible_layers");
addActionToMenu(selectMenu, "select_invisible_layers");
addActionToMenu(selectMenu, "select_locked_layers");
addActionToMenu(selectMenu, "select_unlocked_layers");
- menu.addSeparator();
- addActionToMenu(&menu, "create_quick_group");
- addActionToMenu(&menu, "create_quick_clipping_group");
- addActionToMenu(&menu, "quick_ungroup");
- menu.addSeparator();
+ QMenu *groupMenu = menu.addMenu(i18n("&Group"));
+ addActionToMenu(groupMenu, "create_quick_group");
+ addActionToMenu(groupMenu, "create_quick_clipping_group");
+ addActionToMenu(groupMenu, "quick_ungroup");
- menu.addAction(m_removeAction);
- addActionToMenu(&menu, "duplicatelayer");
- addActionToMenu(&menu, "merge_layer");
if (singleLayer) {
- addActionToMenu(&menu, "flatten_image");
- addActionToMenu(&menu, "flatten_layer");
- menu.addSeparator();
+ QMenu *addLayerMenu = menu.addMenu(i18n("&Add"));
+ addActionToMenu(addLayerMenu, "add_new_transparency_mask");
+ addActionToMenu(addLayerMenu, "add_new_filter_mask");
+ addActionToMenu(addLayerMenu, "add_new_transform_mask");
+ addActionToMenu(addLayerMenu, "add_new_selection_mask");
QMenu *convertToMenu = menu.addMenu(i18n("&Convert"));
addActionToMenu(convertToMenu, "convert_to_paint_layer");
addActionToMenu(convertToMenu, "convert_to_transparency_mask");
addActionToMenu(convertToMenu, "convert_to_filter_mask");
addActionToMenu(convertToMenu, "convert_to_selection_mask");
QMenu *splitAlphaMenu = menu.addMenu(i18n("S&plit Alpha"));
addActionToMenu(splitAlphaMenu, "split_alpha_into_mask");
addActionToMenu(splitAlphaMenu, "split_alpha_write");
addActionToMenu(splitAlphaMenu, "split_alpha_save_merged");
+ }
+
+ menu.addSeparator();
+
+ if (singleLayer) {
+ addActionToMenu(&menu, "show_in_timeline");
KisNodeSP node = m_filteringModel->nodeFromIndex(index);
if (node && !node->inherits("KisTransformMask")) {
addActionToMenu(&menu, "isolate_layer");
}
- }
- }
- if (singleLayer) {
- menu.addSeparator();
- addActionToMenu(&menu, "add_new_transparency_mask");
- addActionToMenu(&menu, "add_new_filter_mask");
- addActionToMenu(&menu, "add_new_transform_mask");
- addActionToMenu(&menu, "add_new_selection_mask");
- menu.addSeparator();
- menu.addAction(m_selectOpaque);
+ menu.addAction(m_selectOpaque);
+ }
}
menu.exec(pos);
}
}
void KisLayerBox::slotMergeLayer()
{
if (!m_canvas) return;
m_nodeManager->mergeLayer();
}
void KisLayerBox::slotMinimalView()
{
m_wdgLayerBox->listLayers->setDisplayMode(KisNodeView::MinimalMode);
}
void KisLayerBox::slotDetailedView()
{
m_wdgLayerBox->listLayers->setDisplayMode(KisNodeView::DetailedMode);
}
void KisLayerBox::slotThumbnailView()
{
m_wdgLayerBox->listLayers->setDisplayMode(KisNodeView::ThumbnailMode);
}
void KisLayerBox::slotRmClicked()
{
if (!m_canvas) return;
m_nodeManager->removeNode();
}
void KisLayerBox::slotRaiseClicked()
{
if (!m_canvas) return;
m_nodeManager->raiseNode();
}
void KisLayerBox::slotLowerClicked()
{
if (!m_canvas) return;
m_nodeManager->lowerNode();
}
void KisLayerBox::slotPropertiesClicked()
{
if (!m_canvas) return;
if (KisNodeSP active = m_nodeManager->activeNode()) {
m_nodeManager->nodeProperties(active);
}
}
void KisLayerBox::slotCompositeOpChanged(int index)
{
Q_UNUSED(index);
if (!m_canvas) return;
QString compositeOp = m_wdgLayerBox->cmbComposite->selectedCompositeOp().id();
m_nodeManager->nodeCompositeOpChanged(m_nodeManager->activeColorSpace()->compositeOp(compositeOp));
}
void KisLayerBox::slotOpacityChanged()
{
if (!m_canvas) return;
m_nodeManager->nodeOpacityChanged(m_newOpacity, true);
}
void KisLayerBox::slotOpacitySliderMoved(qreal opacity)
{
m_newOpacity = opacity;
m_opacityDelayTimer.start(200);
}
void KisLayerBox::slotCollapsed(const QModelIndex &index)
{
KisNodeSP node = m_filteringModel->nodeFromIndex(index);
if (node) {
node->setCollapsed(true);
}
}
void KisLayerBox::slotExpanded(const QModelIndex &index)
{
KisNodeSP node = m_filteringModel->nodeFromIndex(index);
if (node) {
node->setCollapsed(false);
}
}
void KisLayerBox::slotSelectOpaque()
{
if (!m_canvas) return;
QAction *action = m_canvas->viewManager()->actionManager()->actionByName("selectopaque");
if (action) {
action->trigger();
}
}
void KisLayerBox::slotNodeCollapsedChanged()
{
expandNodesRecursively(m_image->rootLayer(), m_filteringModel, m_wdgLayerBox->listLayers);
}
inline bool isSelectionMask(KisNodeSP node)
{
return dynamic_cast(node.data());
}
KisNodeSP KisLayerBox::findNonHidableNode(KisNodeSP startNode)
{
if (isSelectionMask(startNode) &&
startNode->parent() &&
!startNode->parent()->parent()) {
KisNodeSP node = startNode->prevSibling();
while (node && isSelectionMask(node)) {
node = node->prevSibling();
}
if (!node) {
node = startNode->nextSibling();
while (node && isSelectionMask(node)) {
node = node->nextSibling();
}
}
if (!node) {
node = m_image->root()->lastChild();
while (node && isSelectionMask(node)) {
node = node->prevSibling();
}
}
KIS_ASSERT_RECOVER_NOOP(node && "cannot activate any node!");
startNode = node;
}
return startNode;
}
void KisLayerBox::slotEditGlobalSelection(bool showSelections)
{
KisNodeSP lastActiveNode = m_nodeManager->activeNode();
KisNodeSP activateNode = lastActiveNode;
if (!showSelections) {
activateNode = findNonHidableNode(activateNode);
}
m_nodeModel->setShowGlobalSelection(showSelections);
if (showSelections) {
KisNodeSP newMask = m_image->rootLayer()->selectionMask();
if (newMask) {
activateNode = newMask;
}
}
if (activateNode) {
if (lastActiveNode != activateNode) {
m_nodeManager->slotNonUiActivatedNode(activateNode);
} else {
setCurrentNode(lastActiveNode);
}
}
}
void KisLayerBox::selectionChanged(const QModelIndexList selection)
{
if (!m_nodeManager) return;
/**
* When the user clears the extended selection by clicking on the
* empty area of the docker, the selection should be reset on to
* the active layer, which might be even unselected(!).
*/
if (selection.isEmpty() && m_nodeManager->activeNode()) {
QModelIndex selectedIndex =
m_filteringModel->indexFromNode(m_nodeManager->activeNode());
m_wdgLayerBox->listLayers->selectionModel()->
setCurrentIndex(selectedIndex, QItemSelectionModel::ClearAndSelect);
return;
}
QList selectedNodes;
Q_FOREACH (const QModelIndex &idx, selection) {
selectedNodes << m_filteringModel->nodeFromIndex(idx);
}
m_nodeManager->slotSetSelectedNodes(selectedNodes);
updateUI();
}
void KisLayerBox::slotAboutToRemoveRows(const QModelIndex &parent, int start, int end)
{
/**
* Qt has changed its behavior when deleting an item. Previously
* the selection priority was on the next item in the list, and
* now it has shanged to the previous item. Here we just adjust
* the selected item after the node removal. Please take care that
* this method overrides what was done by the corresponding method
* of QItemSelectionModel, which *has already done* its work. That
* is why we use (start - 1) and (end + 1) in the activation
* condition.
*
* See bug: https://bugs.kde.org/show_bug.cgi?id=345601
*/
QModelIndex currentIndex = m_wdgLayerBox->listLayers->currentIndex();
QAbstractItemModel *model = m_filteringModel;
if (currentIndex.isValid() && parent == currentIndex.parent()
&& currentIndex.row() >= start - 1 && currentIndex.row() <= end + 1) {
QModelIndex old = currentIndex;
if (model && end < model->rowCount(parent) - 1) // there are rows left below the change
currentIndex = model->index(end + 1, old.column(), parent);
else if (start > 0) // there are rows left above the change
currentIndex = model->index(start - 1, old.column(), parent);
else // there are no rows left in the table
currentIndex = QModelIndex();
if (currentIndex.isValid() && currentIndex != old) {
m_wdgLayerBox->listLayers->setCurrentIndex(currentIndex);
}
}
}
void KisLayerBox::slotNodeManagerChangedSelection(const KisNodeList &nodes)
{
if (!m_nodeManager) return;
QModelIndexList newSelection;
Q_FOREACH(KisNodeSP node, nodes) {
newSelection << m_filteringModel->indexFromNode(node);
}
QItemSelectionModel *model = m_wdgLayerBox->listLayers->selectionModel();
if (KritaUtils::compareListsUnordered(newSelection, model->selectedIndexes())) {
return;
}
QItemSelection selection;
Q_FOREACH(const QModelIndex &idx, newSelection) {
selection.select(idx, idx);
}
model->select(selection, QItemSelectionModel::ClearAndSelect);
}
void KisLayerBox::updateThumbnail()
{
m_wdgLayerBox->listLayers->updateNode(m_wdgLayerBox->listLayers->currentIndex());
}
void KisLayerBox::slotRenameCurrentNode()
{
m_wdgLayerBox->listLayers->edit(m_wdgLayerBox->listLayers->currentIndex());
}
void KisLayerBox::slotColorLabelChanged(int label)
{
KisNodeList nodes = m_nodeManager->selectedNodes();
Q_FOREACH(KisNodeSP node, nodes) {
auto applyLabelFunc =
[label](KisNodeSP node) {
node->setColorLabelIndex(label);
};
KisLayerUtils::recursiveApplyNodes(node, applyLabelFunc);
}
}
void KisLayerBox::updateAvailableLabels()
{
if (!m_image) return;
m_wdgLayerBox->cmbFilter->updateAvailableLabels(m_image->root());
}
void KisLayerBox::updateLayerFiltering()
{
m_filteringModel->setAcceptedLabels(m_wdgLayerBox->cmbFilter->selectedColors());
}
#include "moc_kis_layer_box.cpp"
diff --git a/plugins/extensions/CMakeLists.txt b/plugins/extensions/CMakeLists.txt
index 9279f1f064..e09bd6e52f 100644
--- a/plugins/extensions/CMakeLists.txt
+++ b/plugins/extensions/CMakeLists.txt
@@ -1,24 +1,25 @@
add_subdirectory( bigbrother )
add_subdirectory( imagesplit )
add_subdirectory( clonesarray )
add_subdirectory( colorrange )
add_subdirectory( colorspaceconversion )
add_subdirectory( histogram )
add_subdirectory( imagesize )
add_subdirectory( metadataeditor )
add_subdirectory( modify_selection )
add_subdirectory( offsetimage )
add_subdirectory( rotateimage )
add_subdirectory( separate_channels )
add_subdirectory( shearimage )
add_subdirectory( layergroupswitcher )
add_subdirectory( resourcemanager )
add_subdirectory( layersplit )
+add_subdirectory( waveletdecompose )
# Allow to skip building GMIC plugin
option(WITH_GMIC "Build the G'Mic plugin" ON)
if(WITH_GMIC)
if (CMAKE_COMPILER_IS_GNUCC)
add_subdirectory( gmic )
endif()
endif()
diff --git a/plugins/extensions/waveletdecompose/CMakeLists.txt b/plugins/extensions/waveletdecompose/CMakeLists.txt
new file mode 100644
index 0000000000..00bfa032a9
--- /dev/null
+++ b/plugins/extensions/waveletdecompose/CMakeLists.txt
@@ -0,0 +1,14 @@
+set(kritawaveletdecompose_SOURCES
+ waveletdecompose.cpp
+ dlg_waveletdecompose.cpp
+ kis_wavelet_kernel.cpp
+ )
+
+ki18n_wrap_ui(kritawaveletdecompose_SOURCES
+ wdg_waveletdecompose.ui
+ )
+
+add_library(kritawaveletdecompose MODULE ${kritawaveletdecompose_SOURCES})
+target_link_libraries(kritawaveletdecompose kritaui)
+install(TARGETS kritawaveletdecompose DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
+install( FILES waveletdecompose.xmlgui DESTINATION ${DATA_INSTALL_DIR}/kritaplugins)
diff --git a/plugins/extensions/waveletdecompose/dlg_waveletdecompose.cpp b/plugins/extensions/waveletdecompose/dlg_waveletdecompose.cpp
new file mode 100644
index 0000000000..63c72ec69f
--- /dev/null
+++ b/plugins/extensions/waveletdecompose/dlg_waveletdecompose.cpp
@@ -0,0 +1,71 @@
+/*
+ *
+ * Copyright (c) 2016 Miroslav Talasek
+ *
+ * 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 "dlg_waveletdecompose.h"
+
+#include
+#include
+
+DlgWaveletDecompose::DlgWaveletDecompose(QWidget * parent,
+ const char * name)
+ : KoDialog(parent)
+{
+ setCaption(i18n("WaveletDecompose"));
+ setButtons(Ok | Cancel);
+ setDefaultButton(Ok);
+ setObjectName(name);
+
+ m_page = new WdgWaveletDecompose(this);
+ Q_CHECK_PTR(m_page);
+ m_page->layout()->setMargin(0);
+ m_page->setObjectName("wavelet_decompose");
+
+ setMainWidget(m_page);
+ resize(m_page->sizeHint());
+
+ connect(this, SIGNAL(okClicked()),
+ this, SLOT(okClicked()));
+
+}
+
+DlgWaveletDecompose::~DlgWaveletDecompose()
+{
+ delete m_page;
+}
+
+void DlgWaveletDecompose::setScales(quint32 scales)
+{
+ m_page->scales->setValue(scales);
+
+}
+
+
+qint32 DlgWaveletDecompose::scales()
+{
+ return m_page->scales->value();
+}
+
+
+// SLOTS
+
+void DlgWaveletDecompose::okClicked()
+{
+ accept();
+}
+
diff --git a/plugins/extensions/waveletdecompose/dlg_waveletdecompose.h b/plugins/extensions/waveletdecompose/dlg_waveletdecompose.h
new file mode 100644
index 0000000000..b24a08c309
--- /dev/null
+++ b/plugins/extensions/waveletdecompose/dlg_waveletdecompose.h
@@ -0,0 +1,60 @@
+/*
+ *
+ * Copyright (c) 2016 Miroslav Talasek
+ *
+ * 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 DLG_WAVELETDECOMPOSE
+#define DLG_WAVELETDECOMPOSE
+
+#include
+
+#include "ui_wdg_waveletdecompose.h"
+
+class WdgWaveletDecompose : public QWidget, public Ui::WdgWaveletDecompose
+{
+ Q_OBJECT
+
+public:
+ WdgWaveletDecompose(QWidget *parent) : QWidget(parent) {
+ setupUi(this);
+ }
+};
+
+class DlgWaveletDecompose: public KoDialog
+{
+
+ Q_OBJECT
+
+public:
+
+ DlgWaveletDecompose(QWidget * parent = 0,
+ const char* name = 0);
+ ~DlgWaveletDecompose();
+
+ void setScales(quint32 scales);
+ qint32 scales();
+
+private Q_SLOTS:
+
+ void okClicked();
+
+private:
+
+ WdgWaveletDecompose * m_page;
+
+};
+
+#endif // DLG_WAVELETDECOMPOSE
diff --git a/plugins/extensions/waveletdecompose/kis_wavelet_kernel.cpp b/plugins/extensions/waveletdecompose/kis_wavelet_kernel.cpp
new file mode 100644
index 0000000000..b6ff63a326
--- /dev/null
+++ b/plugins/extensions/waveletdecompose/kis_wavelet_kernel.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2016 Miroslav Talasek
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "kis_wavelet_kernel.h"
+
+#include "kis_convolution_kernel.h"
+#include
+#include
+
+
+
+int KisWaveletKernel::kernelSizeFromRadius(qreal radius)
+{
+ return 2 * ceil(radius) + 1;
+}
+
+Matrix
+KisWaveletKernel::createHorizontalMatrix(qreal radius)
+{
+ int kernelSize = kernelSizeFromRadius(radius);
+ Matrix matrix(1, kernelSize);
+
+ /**
+ * The kernel size should always be odd, then the position of the
+ * central pixel can be easily calculated
+ */
+ KIS_ASSERT_RECOVER_NOOP(kernelSize & 0x1);
+ const int center = kernelSize / 2;
+
+ for (int x = 0; x < kernelSize; x++) {
+ if (x == 0 || x == kernelSize - 1)
+ matrix(0, x) = 0.25;
+ else if (x == center)
+ matrix(0, x) = 0.5;
+ else
+ matrix(0, x) = 0;
+ }
+
+ return matrix;
+}
+
+Matrix
+KisWaveletKernel::createVerticalMatrix(qreal radius)
+{
+ int kernelSize = kernelSizeFromRadius(radius);
+ Matrix matrix(kernelSize, 1);
+
+
+ /**
+ * The kernel size should always be odd, then the position of the
+ * central pixel can be easily calculated
+ */
+ KIS_ASSERT_RECOVER_NOOP(kernelSize & 0x1);
+ const int center = kernelSize / 2;
+
+ for (int y = 0; y < kernelSize; y++) {
+ if (y == 0 || y == kernelSize - 1)
+ matrix(y, 0) = 0.25;
+ else if (y == center)
+ matrix(y, 0) = 0.5;
+ else
+ matrix(y, 0) = 0;
+ }
+
+ return matrix;
+}
+
+KisConvolutionKernelSP
+KisWaveletKernel::createHorizontalKernel(qreal radius)
+{
+ Matrix matrix = createHorizontalMatrix(radius);
+ return KisConvolutionKernel::fromMatrix(matrix, 0, matrix.sum());
+}
+
+KisConvolutionKernelSP
+KisWaveletKernel::createVerticalKernel(qreal radius)
+{
+ Matrix matrix = createVerticalMatrix(radius);
+ return KisConvolutionKernel::fromMatrix(matrix, 0, matrix.sum());
+}
+
+void KisWaveletKernel::applyWavelet(KisPaintDeviceSP device,
+ const QRect& rect,
+ qreal xRadius, qreal yRadius,
+ const QBitArray &channelFlags,
+ KoUpdater *progressUpdater)
+{
+ QPoint srcTopLeft = rect.topLeft();
+
+ if (xRadius > 0.0 && yRadius > 0.0) {
+ KisPaintDeviceSP interm = new KisPaintDevice(device->colorSpace());
+
+ KisConvolutionKernelSP kernelHoriz = KisWaveletKernel::createHorizontalKernel(xRadius);
+ KisConvolutionKernelSP kernelVertical = KisWaveletKernel::createVerticalKernel(yRadius);
+
+ qreal verticalCenter = qreal(kernelVertical->height()) / 2.0;
+
+ KisConvolutionPainter horizPainter(interm);
+ horizPainter.setChannelFlags(channelFlags);
+ horizPainter.setProgress(progressUpdater);
+ horizPainter.applyMatrix(kernelHoriz, device,
+ srcTopLeft - QPoint(0, ceil(verticalCenter)),
+ srcTopLeft - QPoint(0, ceil(verticalCenter)),
+ rect.size() + QSize(0, 2 * ceil(verticalCenter)), BORDER_REPEAT);
+
+ KisConvolutionPainter verticalPainter(device);
+ verticalPainter.setChannelFlags(channelFlags);
+ verticalPainter.setProgress(progressUpdater);
+ verticalPainter.applyMatrix(kernelVertical, interm, srcTopLeft, srcTopLeft, rect.size(), BORDER_REPEAT);
+
+ } else if (xRadius > 0.0) {
+ KisConvolutionPainter painter(device);
+ painter.setChannelFlags(channelFlags);
+ painter.setProgress(progressUpdater);
+
+ KisConvolutionKernelSP kernelHoriz = KisWaveletKernel::createHorizontalKernel(xRadius);
+ painter.applyMatrix(kernelHoriz, device, srcTopLeft, srcTopLeft, rect.size(), BORDER_REPEAT);
+
+ } else if (yRadius > 0.0) {
+ KisConvolutionPainter painter(device);
+ painter.setChannelFlags(channelFlags);
+ painter.setProgress(progressUpdater);
+
+ KisConvolutionKernelSP kernelVertical = KisWaveletKernel::createVerticalKernel(yRadius);
+ painter.applyMatrix(kernelVertical, device, srcTopLeft, srcTopLeft, rect.size(), BORDER_REPEAT);
+ }
+}
diff --git a/plugins/extensions/waveletdecompose/kis_wavelet_kernel.h b/plugins/extensions/waveletdecompose/kis_wavelet_kernel.h
new file mode 100644
index 0000000000..6cd385ae8a
--- /dev/null
+++ b/plugins/extensions/waveletdecompose/kis_wavelet_kernel.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2016 Miroslav Talasek
+ *
+ * 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_WAVELET_KERNEL_H
+#define __KIS_WAVELET_KERNEL_H
+
+#include "kritaimage_export.h"
+#include "kis_types.h"
+
+#include
+using namespace Eigen;
+
+class QRect;
+
+class KRITAIMAGE_EXPORT KisWaveletKernel
+{
+public:
+ static Matrix
+ createHorizontalMatrix(qreal radius);
+
+ static Matrix
+ createVerticalMatrix(qreal radius);
+
+ static KisConvolutionKernelSP
+ createHorizontalKernel(qreal radius);
+
+ static KisConvolutionKernelSP
+ createVerticalKernel(qreal radius);
+
+ static int kernelSizeFromRadius(qreal radius);
+
+ static void applyWavelet(KisPaintDeviceSP device,
+ const QRect& rect,
+ qreal xRadius, qreal yRadius,
+ const QBitArray &channelFlags,
+ KoUpdater *updater);
+};
+
+#endif /* __KIS_WAVELET_KERNEL_H */
diff --git a/plugins/extensions/waveletdecompose/kritawaveletdecompose.json b/plugins/extensions/waveletdecompose/kritawaveletdecompose.json
new file mode 100644
index 0000000000..4b7d67a0a5
--- /dev/null
+++ b/plugins/extensions/waveletdecompose/kritawaveletdecompose.json
@@ -0,0 +1,9 @@
+{
+ "Id": "Wavelet decomposer",
+ "Type": "Service",
+ "X-KDE-Library": "kritawaveletdecompose",
+ "X-KDE-ServiceTypes": [
+ "Krita/ViewPlugin"
+ ],
+ "X-Krita-Version": "28"
+}
diff --git a/plugins/extensions/waveletdecompose/waveletdecompose.cpp b/plugins/extensions/waveletdecompose/waveletdecompose.cpp
new file mode 100644
index 0000000000..0d3bdb3a69
--- /dev/null
+++ b/plugins/extensions/waveletdecompose/waveletdecompose.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2016 Miroslav Talasek
+ *
+ * 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 "waveletdecompose.h"
+
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+
+#include