diff --git a/krita/krita.action b/krita/krita.action
index c6b06cc039..d7ae56934d 100644
--- a/krita/krita.action
+++ b/krita/krita.action
@@ -1,3592 +1,3604 @@
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
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
Show system information for bug reports.
Show system information for bug reports.
Show system information for bug reports.
false
Rename Composition...
Rename Composition
Rename Composition
0
0
false
Update Composition
Update Composition
Update Composition
0
0
false
Use multiple of 2 for pixel scale
Use multiple of 2 for pixel scale
Use multiple of 2 for pixel scale
Use multiple of 2 for pixel scale
1
0
true
&Invert Selection
Invert current selection
Invert Selection
10000000000
100
Ctrl+Shift+I
false
Painting
lightness-increase
Make brush color lighter
Make brush color lighter
Make brush color lighter
0
0
L
false
lightness-decrease
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
opacity-increase
Increase opacity
Increase opacity
Increase opacity
0
0
O
false
opacity-decrease
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
0
true
symmetry-vertical
Vertical Mirror Tool
Vertical Mirror Tool
Vertical Mirror Tool
0
true
Hide Mirror X Line
Hide Mirror X Line
Hide Mirror X Line
10000
true
Hide Mirror Y Line
Hide Mirror Y Line
Hide Mirror Y Line
10000
true
Lock
Lock X Line
Lock X Line
10000
true
Lock Y Line
Lock Y Line
Lock Y Line
10000
true
Move to Canvas Center
Move to Canvas Center X
Move to Canvas Center X
10000
false
Move to Canvas Center Y
Move to Canvas Center Y
Move to Canvas Center Y
10000
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
preset-switcher
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
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
Selection Mode: Add
Selection Mode: Add
Selection Mode: Add
A
false
Selection Mode: Subtract
Selection Mode: Subtract
Selection Mode: Subtract
S
false
Selection Mode: Intersect
Selection Mode: Intersect
Selection Mode: Intersect
false
Selection Mode: Replace
Selection Mode: Replace
Selection Mode: Replace
R
false
smoothing-weighted
Brush Smoothing: Weighted
Brush Smoothing: Weighted
Brush Smoothing: Weighted
false
smoothing-no
Brush Smoothing: Disabled
Brush Smoothing: Disabled
Brush Smoothing: Disabled
false
smoothing-stabilizer
Brush Smoothing: Stabilizer
Brush Smoothing: Stabilizer
Brush Smoothing: Stabilizer
false
brushsize-decrease
Decrease Brush Size
Decrease Brush Size
Decrease Brush Size
0
0
[
false
smoothing-basic
Brush Smoothing: Basic
Brush Smoothing: Basic
Brush Smoothing: Basic
false
brushsize-increase
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
&Show Global Selection Mask
Shows global selection as a usual selection mask in <interface>Layers</interface> docker
Show Global Selection Mask
100000
100
true
Filters
color-to-alpha
&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
&Invert
Invert
Invert
10000
0
Ctrl+I
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
&Phong Bumpmap...
Phong Bumpmap
Phong Bumpmap
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
Rectangle Tool
Rectangle Tool
Rectangle Tool
false
Multibrush Tool
Multibrush Tool
Multibrush Tool
Q
false
Colorize Mask Tool
Colorize Mask Tool
Colorize Mask Tool
Smart Patch Tool
Smart Patch Tool
Smart Patch Tool
Pan Tool
Pan Tool
Pan Tool
Select Shapes Tool
Select Shapes Tool
Select Shapes Tool
false
Color Picker
Select a color from the image or current layer
Select a color from the image or current layer
P
false
Outline Selection Tool
Outline Selection Tool
Outline Selection Tool
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 or double-click ends the curve.
Bezier Curve Tool. Shift-mouseclick or double-click 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
Zoom Tool
Zoom Tool
Zoom Tool
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
Assistant Tool
Assistant Tool
Assistant Tool
false
Gradient Editing Tool
Gradient editing
Gradient editing
false
Reference Images Tool
Reference Images Tool
Reference Images Tool
false
Blending Modes
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 Hard Overlay Blending Mode
Select Hard Overlay Blending Mode
Select Hard Overlay Blending Mode
0
0
Alt+Shift+P
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
addblankframe
Create Blank Frame
Add blank frame
Add blank frame
100000
0
false
addduplicateframe
Create Duplicate 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
Show in Timeline
true
Insert Keyframe Left
Insert keyframes to the left of selection, moving the tail of animation to the right.
100000
0
false
Insert Keyframe Right
Insert keyframes to the right of selection, moving the tail of animation to the right.
100000
0
false
Insert Multiple Keyframes
Insert several keyframes based on user parameters.
100000
0
false
Remove Frame and Pull
Remove keyframes moving the tail of animation to the left
100000
0
false
deletekeyframe
Remove Keyframe
Remove keyframes without moving anything around
100000
0
false
Insert Column Left
Insert column to the left of selection, moving the tail of animation to the right
100000
0
false
Insert Column Right
Insert column to the right of selection, moving the tail of animation to the right
100000
0
false
Insert Multiple Columns
Insert several columns based on user parameters.
100000
0
false
Remove Column and Pull
Remove columns moving the tail of animation to the left
100000
0
false
Remove Column
Remove columns without moving anything around
100000
0
false
Insert Hold Frame
Insert a hold frame after every keyframe
100000
0
false
Insert Multiple Hold Frames
Insert N hold frames after every keyframe
100000
0
false
Remove Hold Frame
Remove a hold frame after every keyframe
100000
0
false
Remove Multiple Hold Frames
Remove N hold frames after every keyframe
100000
0
false
Insert Hold Column
Insert a hold column into the frame at the current position
100000
0
false
Insert Multiple Hold Columns
Insert N hold columns into the frame at the current position
100000
0
false
Remove Hold Column
Remove a hold column from the frame at the current position
100000
0
false
Remove Multiple Hold Columns
Remove N hold columns from the frame at the current position
100000
0
false
Mirror Frames
Mirror frames' position
100000
0
false
Mirror Columns
Mirror columns' position
100000
0
false
Copy to Clipboard
Copy frames to clipboard
100000
0
false
Cut to Clipboard
Cut frames to clipboard
100000
0
false
Paste from Clipboard
Paste frames from clipboard
100000
0
false
Copy Columns to Clipboard
Copy columns to clipboard
100000
0
false
Cut Columns to Clipboard
Cut columns to clipboard
100000
0
false
Paste Columns from Clipboard
Paste columns from clipboard
100000
0
false
Set Start Time
100000
0
false
Set End Time
100000
0
false
Update Playback Range
100000
0
false
Layers
Activate next layer
Activate next layer
Activate next layer
1000
0
PgUp
false
Activate previous layer
Activate previous layer
Activate previous layer
1000
0
PgDown
false
Activate previously selected layer
Activate previously selected layer
Activate previously selected layer
1000
0
;
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
filterMask
&Colorize Mask
Colorize Mask
Colorize 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
layer-locked
&Toggle layer lock
Toggle layer lock
Toggle layer lock
1000
0
false
visible
Toggle layer &visibility
Toggle layer visibility
Toggle layer visibility
1000
0
false
transparency-locked
Toggle layer &alpha
Toggle layer alpha
Toggle layer alpha
1000
0
false
transparency-enabled
Toggle layer alpha &inheritance
Toggle layer alpha inheritance
Toggle layer alpha inheritance
1000
0
false
paintLayer
&Paint Layer
Paint Layer
Paint Layer
1000
0
Insert
false
&New Layer From Visible
New layer from visible
New layer from visible
1000
0
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
1000
0
false
Cut Layer
Cut layer to clipboard
Cut layer to clipboard
1000
0
false
Paste Layer
Paste layer from clipboard
Paste layer from clipboard
1000
0
false
Quick Group
Create a group layer containing selected layers
Quick Group
1000
0
Ctrl+G
false
Quick Ungroup
Remove grouping of the layers or remove one layer out of the group
Quick Ungroup
100000
0
Ctrl+Alt+G
false
Quick Clipping Group
Group selected layers and add a layer with clipped alpha channel
Quick Clipping Group
100000
0
Ctrl+Shift+G
false
All Layers
Select all layers
Select all layers
10000
0
false
Visible Layers
Select all visible layers
Select all visible layers
10000
0
false
Locked Layers
Select all locked layers
Select all locked layers
10000
0
false
Invisible Layers
Select all invisible layers
Select all invisible layers
10000
0
false
Unlocked Layers
Select all unlocked layers
Select all unlocked layers
10000
0
false
document-save
&Save Layer/Mask...
Save Layer/Mask
Save Layer/Mask
1000
0
false
document-save
Save Vector Layer as SVG...
Save Vector Layer as SVG
Save Vector Layer as SVG
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
fileLayer
to &File Layer
Saves out the layers into a new image and then references that image.
Convert to File Layer
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
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
1000
1
false
object-rotate-right
Rotate &Layer 90° to the Right
Rotate Layer 90° to the Right
Rotate Layer 90° to the Right
1000
1
false
object-rotate-left
Rotate Layer &90° to the Left
Rotate Layer 90° to the Left
Rotate Layer 90° to the Left
1000
1
false
Rotate Layer &180°
Rotate Layer 180°
Rotate Layer 180°
1000
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
1000
1
false
symmetry-horizontal
Mirror All Layers Hori&zontally
Mirror All Layers Horizontally
Mirror All Layers Horizontally
1000
1
false
symmetry-vertical
Mirror All Layers &Vertically
Mirror All Layers Vertically
Mirror All Layers Vertically
1000
1
false
&Rotate All Layers...
Rotate All Layers
Rotate All Layers
1000
1
false
object-rotate-right
Rotate All &Layers 90° to the Right
Rotate All Layers 90° to the Right
Rotate All Layers 90° to the Right
1000
1
false
object-rotate-left
Rotate All Layers &90° to the Left
Rotate All Layers 90° to the Left
Rotate All Layers 90° to the Left
1000
1
false
Rotate All Layers &180°
Rotate All Layers 180°
Rotate All Layers 180°
1000
1
false
Scale All &Layers to new Size...
Scale All Layers to new Size
Scale All Layers to new Size
100000
1
false
&Shear All Layers...
Shear All Layers
Shear All Layers
1000
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-layer-below
&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
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
+
+
+ Set Copy F&rom...
+
+ Set the source for the selected clone layer(s).
+ Set Copy From
+ 1000
+ 1
+
+ false
+
+
diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt
index 8c1f23f7d1..3202763d35 100644
--- a/libs/ui/CMakeLists.txt
+++ b/libs/ui/CMakeLists.txt
@@ -1,608 +1,611 @@
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/qtlockedfile
)
include_directories(SYSTEM
${EIGEN3_INCLUDE_DIR}
${OCIO_INCLUDE_DIR}
)
add_subdirectory( tests )
if (APPLE)
find_library(FOUNDATION_LIBRARY Foundation)
find_library(APPKIT_LIBRARY AppKit)
endif ()
set(kritaui_LIB_SRCS
canvas/kis_canvas_widget_base.cpp
canvas/kis_canvas2.cpp
canvas/kis_canvas_updates_compressor.cpp
canvas/kis_canvas_controller.cpp
canvas/kis_paintop_transformation_connector.cpp
canvas/kis_display_color_converter.cpp
canvas/kis_display_filter.cpp
canvas/kis_exposure_gamma_correction_interface.cpp
canvas/kis_tool_proxy.cpp
canvas/kis_canvas_decoration.cc
canvas/kis_coordinates_converter.cpp
canvas/kis_grid_manager.cpp
canvas/kis_grid_decoration.cpp
canvas/kis_grid_config.cpp
canvas/kis_prescaled_projection.cpp
canvas/kis_qpainter_canvas.cpp
canvas/kis_projection_backend.cpp
canvas/kis_update_info.cpp
canvas/kis_image_patch.cpp
canvas/kis_image_pyramid.cpp
canvas/kis_infinity_manager.cpp
canvas/kis_change_guides_command.cpp
canvas/kis_guides_decoration.cpp
canvas/kis_guides_manager.cpp
canvas/kis_guides_config.cpp
canvas/kis_snap_config.cpp
canvas/kis_snap_line_strategy.cpp
canvas/KisSnapPointStrategy.cpp
canvas/KisSnapPixelStrategy.cpp
canvas/KisMirrorAxisConfig.cpp
dialogs/kis_about_application.cpp
dialogs/kis_dlg_adj_layer_props.cc
dialogs/kis_dlg_adjustment_layer.cc
dialogs/kis_dlg_filter.cpp
dialogs/kis_dlg_generator_layer.cpp
dialogs/kis_dlg_file_layer.cpp
dialogs/kis_dlg_filter.cpp
dialogs/kis_dlg_stroke_selection_properties.cpp
dialogs/kis_dlg_image_properties.cc
dialogs/kis_dlg_layer_properties.cc
dialogs/kis_dlg_preferences.cc
dialogs/slider_and_spin_box_sync.cpp
dialogs/kis_dlg_blacklist_cleanup.cpp
dialogs/kis_dlg_layer_style.cpp
dialogs/kis_dlg_png_import.cpp
dialogs/kis_dlg_import_image_sequence.cpp
dialogs/kis_delayed_save_dialog.cpp
dialogs/KisSessionManagerDialog.cpp
dialogs/KisNewWindowLayoutDialog.cpp
+ dialogs/KisDlgChangeCloneSource.cpp
flake/kis_node_dummies_graph.cpp
flake/kis_dummies_facade_base.cpp
flake/kis_dummies_facade.cpp
flake/kis_node_shapes_graph.cpp
flake/kis_node_shape.cpp
flake/kis_shape_controller.cpp
flake/kis_shape_layer.cc
flake/kis_shape_layer_canvas.cpp
flake/kis_shape_selection.cpp
flake/kis_shape_selection_canvas.cpp
flake/kis_shape_selection_model.cpp
flake/kis_take_all_shapes_command.cpp
brushhud/kis_uniform_paintop_property_widget.cpp
brushhud/kis_brush_hud.cpp
brushhud/kis_round_hud_button.cpp
brushhud/kis_dlg_brush_hud_config.cpp
brushhud/kis_brush_hud_properties_list.cpp
brushhud/kis_brush_hud_properties_config.cpp
kis_aspect_ratio_locker.cpp
kis_autogradient.cc
kis_bookmarked_configurations_editor.cc
kis_bookmarked_configurations_model.cc
kis_bookmarked_filter_configurations_model.cc
KisPaintopPropertiesBase.cpp
kis_canvas_resource_provider.cpp
kis_derived_resources.cpp
kis_categories_mapper.cpp
kis_categorized_list_model.cpp
kis_categorized_item_delegate.cpp
kis_clipboard.cc
kis_config.cc
KisOcioConfiguration.cpp
kis_control_frame.cpp
kis_composite_ops_model.cc
kis_paint_ops_model.cpp
kis_cursor.cc
kis_cursor_cache.cpp
kis_custom_pattern.cc
kis_file_layer.cpp
kis_change_file_layer_command.h
kis_safe_document_loader.cpp
kis_splash_screen.cpp
kis_filter_manager.cc
kis_filters_model.cc
KisImageBarrierLockerWithFeedback.cpp
kis_image_manager.cc
kis_image_view_converter.cpp
kis_import_catcher.cc
kis_layer_manager.cc
kis_mask_manager.cc
kis_mimedata.cpp
kis_node_commands_adapter.cpp
kis_node_manager.cpp
kis_node_juggler_compressed.cpp
kis_node_selection_adapter.cpp
kis_node_insertion_adapter.cpp
KisNodeDisplayModeAdapter.cpp
kis_node_model.cpp
kis_node_filter_proxy_model.cpp
kis_model_index_converter_base.cpp
kis_model_index_converter.cpp
kis_model_index_converter_show_all.cpp
kis_painting_assistant.cc
kis_painting_assistants_decoration.cpp
KisDecorationsManager.cpp
kis_paintop_box.cc
kis_paintop_option.cpp
kis_paintop_options_model.cpp
kis_paintop_settings_widget.cpp
kis_popup_palette.cpp
kis_png_converter.cpp
kis_preference_set_registry.cpp
KisResourceServerProvider.cpp
KisResourceBundleServerProvider.cpp
KisSelectedShapesProxy.cpp
kis_selection_decoration.cc
kis_selection_manager.cc
KisSelectionActionsAdapter.cpp
kis_statusbar.cc
kis_zoom_manager.cc
kis_favorite_resource_manager.cpp
kis_workspace_resource.cpp
kis_action.cpp
kis_action_manager.cpp
KisActionPlugin.cpp
kis_canvas_controls_manager.cpp
kis_tooltip_manager.cpp
kis_multinode_property.cpp
kis_stopgradient_editor.cpp
KisWelcomePageWidget.cpp
+ KisChangeCloneLayersCommand.cpp
kisexiv2/kis_exif_io.cpp
kisexiv2/kis_exiv2.cpp
kisexiv2/kis_iptc_io.cpp
kisexiv2/kis_xmp_io.cpp
opengl/kis_opengl.cpp
opengl/kis_opengl_canvas2.cpp
opengl/kis_opengl_canvas_debugger.cpp
opengl/kis_opengl_image_textures.cpp
opengl/kis_texture_tile.cpp
opengl/kis_opengl_shader_loader.cpp
opengl/kis_texture_tile_info_pool.cpp
opengl/KisOpenGLUpdateInfoBuilder.cpp
opengl/KisOpenGLModeProber.cpp
opengl/KisScreenInformationAdapter.cpp
kis_fps_decoration.cpp
tool/KisToolChangesTracker.cpp
tool/KisToolChangesTrackerData.cpp
tool/kis_selection_tool_helper.cpp
tool/kis_selection_tool_config_widget_helper.cpp
tool/kis_rectangle_constraint_widget.cpp
tool/kis_shape_tool_helper.cpp
tool/kis_tool.cc
tool/kis_delegated_tool_policies.cpp
tool/kis_tool_freehand.cc
tool/kis_speed_smoother.cpp
tool/kis_painting_information_builder.cpp
tool/kis_stabilized_events_sampler.cpp
tool/kis_tool_freehand_helper.cpp
tool/kis_tool_multihand_helper.cpp
tool/kis_figure_painting_tool_helper.cpp
tool/kis_tool_paint.cc
tool/kis_tool_shape.cc
tool/kis_tool_ellipse_base.cpp
tool/kis_tool_rectangle_base.cpp
tool/kis_tool_polyline_base.cpp
tool/kis_tool_utils.cpp
tool/kis_resources_snapshot.cpp
tool/kis_smoothing_options.cpp
tool/KisStabilizerDelayedPaintHelper.cpp
tool/KisStrokeSpeedMonitor.cpp
tool/strokes/freehand_stroke.cpp
tool/strokes/KisStrokeEfficiencyMeasurer.cpp
tool/strokes/kis_painter_based_stroke_strategy.cpp
tool/strokes/kis_filter_stroke_strategy.cpp
tool/strokes/kis_color_picker_stroke_strategy.cpp
tool/strokes/KisFreehandStrokeInfo.cpp
tool/strokes/KisMaskedFreehandStrokePainter.cpp
tool/strokes/KisMaskingBrushRenderer.cpp
tool/strokes/KisMaskingBrushCompositeOpFactory.cpp
tool/strokes/move_stroke_strategy.cpp
tool/KisSelectionToolFactoryBase.cpp
tool/KisToolPaintFactoryBase.cpp
widgets/kis_cmb_composite.cc
widgets/kis_cmb_contour.cpp
widgets/kis_cmb_gradient.cpp
widgets/kis_paintop_list_widget.cpp
widgets/kis_cmb_idlist.cc
widgets/kis_color_space_selector.cc
widgets/kis_advanced_color_space_selector.cc
widgets/kis_cie_tongue_widget.cpp
widgets/kis_tone_curve_widget.cpp
widgets/kis_curve_widget.cpp
widgets/kis_custom_image_widget.cc
widgets/kis_image_from_clipboard_widget.cpp
widgets/kis_double_widget.cc
widgets/kis_filter_selector_widget.cc
widgets/kis_gradient_chooser.cc
widgets/kis_iconwidget.cc
widgets/kis_mask_widgets.cpp
widgets/kis_meta_data_merge_strategy_chooser_widget.cc
widgets/kis_multi_bool_filter_widget.cc
widgets/kis_multi_double_filter_widget.cc
widgets/kis_multi_integer_filter_widget.cc
widgets/kis_multipliers_double_slider_spinbox.cpp
widgets/kis_paintop_presets_popup.cpp
widgets/kis_tool_options_popup.cpp
widgets/kis_paintop_presets_chooser_popup.cpp
widgets/kis_paintop_presets_save.cpp
widgets/kis_paintop_preset_icon_library.cpp
widgets/kis_pattern_chooser.cc
widgets/kis_preset_chooser.cpp
widgets/kis_progress_widget.cpp
widgets/kis_selection_options.cc
widgets/kis_scratch_pad.cpp
widgets/kis_scratch_pad_event_filter.cpp
widgets/kis_preset_selector_strip.cpp
widgets/kis_slider_spin_box.cpp
widgets/KisSelectionPropertySlider.cpp
widgets/kis_size_group.cpp
widgets/kis_size_group_p.cpp
widgets/kis_wdg_generator.cpp
widgets/kis_workspace_chooser.cpp
widgets/kis_categorized_list_view.cpp
widgets/kis_widget_chooser.cpp
widgets/kis_tool_button.cpp
widgets/kis_floating_message.cpp
widgets/kis_lod_availability_widget.cpp
widgets/kis_color_label_selector_widget.cpp
widgets/kis_color_filter_combo.cpp
widgets/kis_elided_label.cpp
widgets/kis_stopgradient_slider_widget.cpp
widgets/kis_preset_live_preview_view.cpp
widgets/KisScreenColorPicker.cpp
widgets/KoDualColorButton.cpp
widgets/KoStrokeConfigWidget.cpp
widgets/KoFillConfigWidget.cpp
widgets/KisLayerStyleAngleSelector.cpp
widgets/KisMemoryReportButton.cpp
KisPaletteEditor.cpp
dialogs/KisDlgPaletteEditor.cpp
widgets/KisNewsWidget.cpp
widgets/KisGamutMaskToolbar.cpp
utils/kis_document_aware_spin_box_unit_manager.cpp
utils/KisSpinBoxSplineUnitConverter.cpp
input/kis_input_manager.cpp
input/kis_input_manager_p.cpp
input/kis_extended_modifiers_mapper.cpp
input/kis_abstract_input_action.cpp
input/kis_tool_invocation_action.cpp
input/kis_pan_action.cpp
input/kis_alternate_invocation_action.cpp
input/kis_rotate_canvas_action.cpp
input/kis_zoom_action.cpp
input/kis_change_frame_action.cpp
input/kis_gamma_exposure_action.cpp
input/kis_show_palette_action.cpp
input/kis_change_primary_setting_action.cpp
input/kis_abstract_shortcut.cpp
input/kis_native_gesture_shortcut.cpp
input/kis_single_action_shortcut.cpp
input/kis_stroke_shortcut.cpp
input/kis_shortcut_matcher.cpp
input/kis_select_layer_action.cpp
input/KisQtWidgetsTweaker.cpp
input/KisInputActionGroup.cpp
operations/kis_operation.cpp
operations/kis_operation_configuration.cpp
operations/kis_operation_registry.cpp
operations/kis_operation_ui_factory.cpp
operations/kis_operation_ui_widget.cpp
operations/kis_filter_selection_operation.cpp
actions/kis_selection_action_factories.cpp
actions/KisPasteActionFactory.cpp
actions/KisTransformToolActivationCommand.cpp
input/kis_touch_shortcut.cpp
kis_document_undo_store.cpp
kis_gui_context_command.cpp
kis_gui_context_command_p.cpp
input/kis_tablet_debugger.cpp
input/kis_input_profile_manager.cpp
input/kis_input_profile.cpp
input/kis_shortcut_configuration.cpp
input/config/kis_input_configuration_page.cpp
input/config/kis_edit_profiles_dialog.cpp
input/config/kis_input_profile_model.cpp
input/config/kis_input_configuration_page_item.cpp
input/config/kis_action_shortcuts_model.cpp
input/config/kis_input_type_delegate.cpp
input/config/kis_input_mode_delegate.cpp
input/config/kis_input_button.cpp
input/config/kis_input_editor_delegate.cpp
input/config/kis_mouse_input_editor.cpp
input/config/kis_wheel_input_editor.cpp
input/config/kis_key_input_editor.cpp
processing/fill_processing_visitor.cpp
kis_asl_layer_style_serializer.cpp
kis_psd_layer_style_resource.cpp
canvas/kis_mirror_axis.cpp
kis_abstract_perspective_grid.cpp
KisApplication.cpp
KisAutoSaveRecoveryDialog.cpp
KisDetailsPane.cpp
KisDocument.cpp
KisCloneDocumentStroke.cpp
kis_node_view_color_scheme.cpp
KisImportExportFilter.cpp
KisFilterEntry.cpp
KisImportExportManager.cpp
KisImportExportUtils.cpp
kis_async_action_feedback.cpp
KisMainWindow.cpp
KisOpenPane.cpp
KisPart.cpp
KisPrintJob.cpp
KisTemplate.cpp
KisTemplateCreateDia.cpp
KisTemplateGroup.cpp
KisTemplates.cpp
KisTemplatesPane.cpp
KisTemplateTree.cpp
KisUndoActionsUpdateManager.cpp
KisView.cpp
KisImportExportErrorCode.cpp
thememanager.cpp
kis_mainwindow_observer.cpp
KisViewManager.cpp
kis_mirror_manager.cpp
qtlockedfile/qtlockedfile.cpp
qtsingleapplication/qtlocalpeer.cpp
qtsingleapplication/qtsingleapplication.cpp
KisResourceBundle.cpp
KisResourceBundleManifest.cpp
kis_md5_generator.cpp
KisApplicationArguments.cpp
KisNetworkAccessManager.cpp
KisMultiFeedRSSModel.cpp
KisRemoteFileFetcher.cpp
KisSaveGroupVisitor.cpp
KisWindowLayoutResource.cpp
KisWindowLayoutManager.cpp
KisSessionResource.cpp
KisReferenceImagesDecoration.cpp
KisReferenceImage.cpp
flake/KisReferenceImagesLayer.cpp
flake/KisReferenceImagesLayer.h
)
if(WIN32)
# Private headers are needed for:
# * KisDlgCustomTabletResolution
# * KisScreenInformationAdapter
include_directories(SYSTEM ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
qtlockedfile/qtlockedfile_win.cpp
)
if (NOT USE_QT_TABLET_WINDOWS)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
input/wintab/kis_tablet_support_win.cpp
input/wintab/kis_screen_size_choice_dialog.cpp
input/wintab/kis_tablet_support_win8.cpp
)
else()
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
dialogs/KisDlgCustomTabletResolution.cpp
)
endif()
endif()
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
kis_animation_frame_cache.cpp
kis_animation_cache_populator.cpp
KisAsyncAnimationRendererBase.cpp
KisAsyncAnimationCacheRenderer.cpp
KisAsyncAnimationFramesSavingRenderer.cpp
dialogs/KisAsyncAnimationRenderDialogBase.cpp
dialogs/KisAsyncAnimationCacheRenderDialog.cpp
dialogs/KisAsyncAnimationFramesSaveDialog.cpp
canvas/kis_animation_player.cpp
kis_animation_importer.cpp
KisSyncedAudioPlayback.cpp
KisFrameDataSerializer.cpp
KisFrameCacheStore.cpp
KisFrameCacheSwapper.cpp
KisAbstractFrameCacheSwapper.cpp
KisInMemoryFrameCacheSwapper.cpp
input/wintab/drawpile_tablettester/tablettester.cpp
input/wintab/drawpile_tablettester/tablettest.cpp
)
if (UNIX)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
qtlockedfile/qtlockedfile_unix.cpp
)
endif()
if(APPLE)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
osx.mm
)
endif()
ki18n_wrap_ui(kritaui_LIB_SRCS
widgets/KoFillConfigWidget.ui
widgets/KoStrokeConfigWidget.ui
forms/wdgdlgpngimport.ui
forms/wdgfullscreensettings.ui
forms/wdgautogradient.ui
forms/wdggeneralsettings.ui
forms/wdgperformancesettings.ui
forms/wdggenerators.ui
forms/wdgbookmarkedconfigurationseditor.ui
forms/wdgapplyprofile.ui
forms/wdgcustompattern.ui
forms/wdglayerproperties.ui
forms/wdgcolorsettings.ui
forms/wdgtabletsettings.ui
forms/wdgcolorspaceselector.ui
forms/wdgcolorspaceselectoradvanced.ui
forms/wdgdisplaysettings.ui
forms/kis_previewwidgetbase.ui
forms/kis_matrix_widget.ui
forms/wdgselectionoptions.ui
forms/wdggeometryoptions.ui
forms/wdgnewimage.ui
forms/wdgimageproperties.ui
forms/wdgmaskfromselection.ui
forms/wdgmasksource.ui
forms/wdgfilterdialog.ui
forms/wdgmetadatamergestrategychooser.ui
forms/wdgpaintoppresets.ui
forms/wdgpaintopsettings.ui
forms/wdgdlggeneratorlayer.ui
forms/wdgdlgfilelayer.ui
forms/wdgfilterselector.ui
forms/wdgfilternodecreation.ui
forms/wdgmultipliersdoublesliderspinbox.ui
forms/wdgnodequerypatheditor.ui
forms/wdgpresetselectorstrip.ui
forms/wdgsavebrushpreset.ui
forms/wdgpreseticonlibrary.ui
forms/wdgdlgblacklistcleanup.ui
forms/wdgrectangleconstraints.ui
forms/wdgimportimagesequence.ui
forms/wdgstrokeselectionproperties.ui
forms/KisDetailsPaneBase.ui
forms/KisOpenPaneBase.ui
forms/wdgstopgradienteditor.ui
forms/wdgsessionmanager.ui
forms/wdgnewwindowlayout.ui
forms/KisWelcomePage.ui
forms/WdgDlgPaletteEditor.ui
forms/KisNewsPage.ui
forms/wdgGamutMaskToolbar.ui
+ forms/wdgchangeclonesource.ui
brushhud/kis_dlg_brush_hud_config.ui
dialogs/kis_delayed_save_dialog.ui
input/config/kis_input_configuration_page.ui
input/config/kis_edit_profiles_dialog.ui
input/config/kis_input_configuration_page_item.ui
input/config/kis_mouse_input_editor.ui
input/config/kis_wheel_input_editor.ui
input/config/kis_key_input_editor.ui
layerstyles/wdgBevelAndEmboss.ui
layerstyles/wdgblendingoptions.ui
layerstyles/WdgColorOverlay.ui
layerstyles/wdgContour.ui
layerstyles/wdgdropshadow.ui
layerstyles/WdgGradientOverlay.ui
layerstyles/wdgInnerGlow.ui
layerstyles/wdglayerstyles.ui
layerstyles/WdgPatternOverlay.ui
layerstyles/WdgSatin.ui
layerstyles/WdgStroke.ui
layerstyles/wdgstylesselector.ui
layerstyles/wdgTexture.ui
layerstyles/wdgKisLayerStyleAngleSelector.ui
wdgsplash.ui
input/wintab/kis_screen_size_choice_dialog.ui
input/wintab/drawpile_tablettester/tablettest.ui
)
if(WIN32)
if(USE_QT_TABLET_WINDOWS)
ki18n_wrap_ui(kritaui_LIB_SRCS
dialogs/KisDlgCustomTabletResolution.ui
)
else()
ki18n_wrap_ui(kritaui_LIB_SRCS
input/wintab/kis_screen_size_choice_dialog.ui
)
endif()
endif()
add_library(kritaui SHARED ${kritaui_HEADERS_MOC} ${kritaui_LIB_SRCS} )
generate_export_header(kritaui BASE_NAME kritaui)
target_link_libraries(kritaui KF5::CoreAddons KF5::Completion KF5::I18n KF5::ItemViews Qt5::Network
kritaimpex kritacolor kritaimage kritalibbrush kritawidgets kritawidgetutils ${PNG_LIBRARIES} LibExiv2::LibExiv2
)
if (HAVE_QT_MULTIMEDIA)
target_link_libraries(kritaui Qt5::Multimedia)
endif()
if (NOT WIN32 AND NOT APPLE)
target_link_libraries(kritaui ${X11_X11_LIB}
${X11_Xinput_LIB}
${XCB_LIBRARIES})
endif()
if(APPLE)
target_link_libraries(kritaui ${FOUNDATION_LIBRARY})
target_link_libraries(kritaui ${APPKIT_LIBRARY})
endif ()
target_link_libraries(kritaui ${OPENEXR_LIBRARIES})
# Add VSync disable workaround
if(NOT WIN32 AND NOT APPLE)
target_link_libraries(kritaui ${CMAKE_DL_LIBS} Qt5::X11Extras)
endif()
if(X11_FOUND)
target_link_libraries(kritaui Qt5::X11Extras ${X11_LIBRARIES})
endif()
target_include_directories(kritaui
PUBLIC
$
$
$
$
$
$
$
)
set_target_properties(kritaui
PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION}
)
install(TARGETS kritaui ${INSTALL_TARGETS_DEFAULT_ARGS})
if (APPLE)
install(FILES osx.stylesheet DESTINATION ${DATA_INSTALL_DIR}/krita)
endif ()
diff --git a/libs/ui/KisChangeCloneLayersCommand.cpp b/libs/ui/KisChangeCloneLayersCommand.cpp
new file mode 100644
index 0000000000..2d8c29a75d
--- /dev/null
+++ b/libs/ui/KisChangeCloneLayersCommand.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2019 Tusooa Zhu
+ *
+ * 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 "KisChangeCloneLayersCommand.h"
+
+#include
+
+struct KisChangeCloneLayersCommand::Private
+{
+ QList cloneLayers;
+ QList originalSource;
+ KisLayerSP newSource;
+};
+
+KisChangeCloneLayersCommand::KisChangeCloneLayersCommand(QList cloneLayers, KisLayerSP newSource, KUndo2Command *parent)
+ : KUndo2Command(kundo2_i18n("Change Clone Layers"), parent)
+ , d(new Private())
+{
+ KIS_SAFE_ASSERT_RECOVER_RETURN(!cloneLayers.isEmpty());
+ d->cloneLayers = cloneLayers;
+ Q_FOREACH (KisCloneLayerSP layer, d->cloneLayers) {
+ d->originalSource << layer->copyFrom();
+ }
+ d->newSource = newSource;
+}
+
+void KisChangeCloneLayersCommand::redo()
+{
+ Q_FOREACH (KisCloneLayerSP layer, d->cloneLayers) {
+ layer->setCopyFrom(d->newSource);
+ layer->setDirty();
+ }
+}
+
+void KisChangeCloneLayersCommand::undo()
+{
+ KIS_SAFE_ASSERT_RECOVER_RETURN(d->cloneLayers.size() == d->originalSource.size());
+ for (int i = 0; i < d->cloneLayers.size(); ++i) {
+ KisCloneLayerSP layer = d->cloneLayers.at(i);
+ layer->setCopyFrom(d->originalSource.at(i));
+ layer->setDirty();
+ }
+}
+
+bool KisChangeCloneLayersCommand::mergeWith(const KUndo2Command *command)
+{
+ const KisChangeCloneLayersCommand *other = dynamic_cast(command);
+
+ if (other && d->cloneLayers == other->d->cloneLayers) {
+ d->newSource = other->d->newSource;
+ return true;
+ }
+
+ return false;
+}
diff --git a/libs/ui/KisChangeCloneLayersCommand.h b/libs/ui/KisChangeCloneLayersCommand.h
new file mode 100644
index 0000000000..3e0308b95b
--- /dev/null
+++ b/libs/ui/KisChangeCloneLayersCommand.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2019 Tusooa Zhu
+ *
+ * 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_CHANGE_CLONE_LAYERS_COMMAND_H_
+#define KIS_CHANGE_CLONE_LAYERS_COMMAND_H_
+
+#include
+#include "kis_types.h"
+
+class KisChangeCloneLayersCommand : public KUndo2Command
+{
+
+public:
+ KisChangeCloneLayersCommand(QList cloneLayers, KisLayerSP newSource, KUndo2Command *parent = 0);
+
+ void undo() override;
+ void redo() override;
+ bool mergeWith(const KUndo2Command *) override;
+
+private:
+ struct Private;
+ QScopedPointer d;
+};
+
+#endif // KIS_CHANGE_CLONE_LAYERS_COMMAND_H_
diff --git a/libs/ui/dialogs/KisDlgChangeCloneSource.cpp b/libs/ui/dialogs/KisDlgChangeCloneSource.cpp
new file mode 100644
index 0000000000..d3c67421f7
--- /dev/null
+++ b/libs/ui/dialogs/KisDlgChangeCloneSource.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2019 Tusooa Zhu
+ *
+ * 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 "KisDlgChangeCloneSource.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "KisViewManager.h"
+#include "KisChangeCloneLayersCommand.h"
+
+struct KisDlgChangeCloneSource::Private
+{
+ Private(QList layers, KisViewManager *view)
+ : cloneLayers(layers)
+ , view(view)
+ , image(view->image())
+ , applicator(new KisProcessingApplicator(image, 0,
+ KisProcessingApplicator::NONE,
+ /* emitSignals = */ KisImageSignalVector() << ModifiedSignal,
+ kundo2_i18n("Change Clone Layers")))
+ , modified(false) {}
+
+ QList cloneLayers;
+ KisViewManager *view;
+ KisImageSP image;
+ QList validTargets;
+ Ui::WdgChangeCloneSource ui;
+ QScopedPointer applicator;
+ bool modified;
+
+ void addToTargetListRecursively(KisNodeSP node, bool addSelf = true);
+ void filterOutAncestorsAndClonesRecursively(KisLayerSP layer);
+ KisLayerSP getSelectedTargetLayer();
+ KUndo2Command *createCommand(KisLayerSP targetLayer);
+};
+
+void KisDlgChangeCloneSource::Private::addToTargetListRecursively(KisNodeSP node, bool addSelf)
+{
+ if (!node) {
+ return;
+ }
+ if (addSelf) {
+ KisLayerSP layer(qobject_cast(node.data()));
+ if (layer) {
+ validTargets << layer;
+ }
+ }
+ for (KisNodeSP childNode = node->lastChild(); childNode; childNode = childNode->prevSibling()) {
+ KisLayerSP childLayer(qobject_cast(childNode.data()));
+ if (childLayer) {
+ addToTargetListRecursively(childLayer);
+ }
+ }
+
+}
+
+void KisDlgChangeCloneSource::Private::filterOutAncestorsAndClonesRecursively(KisLayerSP layer)
+{
+ validTargets.removeOne(layer);
+ // remove `layer` and its ancestors
+ KisLayerSP parent = qobject_cast(layer->parent().data());
+ if (parent) {
+ filterOutAncestorsAndClonesRecursively(parent);
+ }
+
+ // remove all clones of `layer`, and their ancestors
+ Q_FOREACH (KisCloneLayerSP clone, layer->registeredClones()) {
+ filterOutAncestorsAndClonesRecursively(clone);
+ }
+}
+
+KisLayerSP KisDlgChangeCloneSource::Private::getSelectedTargetLayer()
+{
+ int index = ui.cmbSourceLayer->currentIndex();
+ if (index != -1) {
+ return validTargets.at(index);
+ } else {
+ return 0;
+ }
+}
+
+KUndo2Command *KisDlgChangeCloneSource::Private::createCommand(KisLayerSP targetLayer)
+{
+ return new KisChangeCloneLayersCommand(cloneLayers, targetLayer);
+}
+
+KisDlgChangeCloneSource::KisDlgChangeCloneSource(QList layers, KisViewManager *view, QWidget *parent)
+ : KoDialog(parent)
+ , d(new Private(layers, view))
+{
+ KIS_SAFE_ASSERT_RECOVER_RETURN(!layers.isEmpty());
+
+ connect(d->image.data(), &KisImage::sigStrokeCancellationRequested,
+ this, &KisDlgChangeCloneSource::reject);
+ connect(d->image.data(), &KisImage::sigUndoDuringStrokeRequested,
+ this, &KisDlgChangeCloneSource::reject);
+ connect(d->image.data(), &KisImage::sigStrokeEndRequested,
+ this, &KisDlgChangeCloneSource::accept);
+
+ setButtons(Ok | Cancel);
+ setDefaultButton(Ok);
+
+ QWidget *widget = new QWidget(this);
+ d->ui.setupUi(widget);
+ setMainWidget(widget);
+
+ connect(d->ui.cmbSourceLayer, QOverload::of(&QComboBox::currentIndexChanged),
+ this, &KisDlgChangeCloneSource::slotCancelChangesAndSetNewTarget);
+
+ updateTargetLayerList();
+}
+
+KisDlgChangeCloneSource::~KisDlgChangeCloneSource()
+{
+ dbgUI << "dialog destroyed";
+ if (d->applicator) {
+ if (result() == QDialog::Accepted && d->modified) {
+ dbgUI << "Accepted";
+ d->applicator->end();
+ } else {
+ dbgUI << "Rejected";
+ d->applicator->cancel();
+ }
+ }
+}
+
+void KisDlgChangeCloneSource::updateTargetLayerList()
+{
+ KisSignalsBlocker b(d->ui.cmbSourceLayer);
+
+ if (!d->image) {
+ return;
+ }
+ KisNodeSP root = d->image->root();
+ d->validTargets.clear();
+ d->addToTargetListRecursively(root, /* addSelf = */ false);
+
+ KisLayerSP commonCopyFrom(d->cloneLayers.first()->copyFrom());
+
+ Q_FOREACH (KisCloneLayerSP clone, d->cloneLayers) {
+ // filter out invalid targets:
+ // selected clone layers, their ancestors;
+ // the clone layers' registered clone, the clones' ancestors.
+ d->filterOutAncestorsAndClonesRecursively(clone);
+
+ // assume that clone->copyFrom() != 0
+ if (clone->copyFrom() != commonCopyFrom) {
+ commonCopyFrom = 0;
+ }
+ }
+
+ d->ui.cmbSourceLayer->clear();
+ Q_FOREACH (KisNodeSP node, d->validTargets) {
+ d->ui.cmbSourceLayer->addItem(node->name());
+ }
+
+ if (commonCopyFrom) {
+ d->ui.cmbSourceLayer->setCurrentIndex(d->validTargets.indexOf(commonCopyFrom));
+ } else {
+ d->ui.cmbSourceLayer->setCurrentIndex(-1);
+ }
+}
+
+void KisDlgChangeCloneSource::slotCancelChangesAndSetNewTarget()
+{
+ KisLayerSP targetLayer = d->getSelectedTargetLayer();
+ if (targetLayer) {
+ d->applicator->applyCommand(d->createCommand(targetLayer));
+ d->modified = true;
+ }
+}
+
diff --git a/libs/ui/dialogs/KisDlgChangeCloneSource.h b/libs/ui/dialogs/KisDlgChangeCloneSource.h
new file mode 100644
index 0000000000..3957a40d6e
--- /dev/null
+++ b/libs/ui/dialogs/KisDlgChangeCloneSource.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2019 Tusooa Zhu
+ *
+ * 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_DLG_CHANGE_CLONE_SOURCE_H_
+#define KIS_DLG_CHANGE_CLONE_SOURCE_H_
+
+#include "kis_types.h"
+#include
+
+#include "ui_wdgchangeclonesource.h"
+
+class QWidget;
+class KisViewManager;
+
+class KisDlgChangeCloneSource : public KoDialog
+{
+ Q_OBJECT
+
+public:
+ KisDlgChangeCloneSource(QList layers, KisViewManager *view, QWidget *parent = 0);
+
+ ~KisDlgChangeCloneSource() override;
+
+private:
+ void updateTargetLayerList();
+
+public Q_SLOTS:
+ void slotCancelChangesAndSetNewTarget();
+
+private:
+ struct Private;
+ const QScopedPointer d;
+};
+
+#endif // KIS_DLG_CHANGE_CLONE_SOURCE_H_
diff --git a/libs/ui/forms/wdgchangeclonesource.ui b/libs/ui/forms/wdgchangeclonesource.ui
new file mode 100644
index 0000000000..17fae627af
--- /dev/null
+++ b/libs/ui/forms/wdgchangeclonesource.ui
@@ -0,0 +1,65 @@
+
+
+ WdgChangeCloneSource
+
+
+
+ 0
+ 0
+ 358
+ 166
+
+
+
+
+
+
+ -
+
+
-
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Copy from:
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
diff --git a/libs/ui/kis_layer_manager.cc b/libs/ui/kis_layer_manager.cc
index 0b1b4fe53f..af9fa81edb 100644
--- a/libs/ui/kis_layer_manager.cc
+++ b/libs/ui/kis_layer_manager.cc
@@ -1,969 +1,999 @@
/*
* 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_layer_manager.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kis_config.h"
#include "kis_cursor.h"
#include "dialogs/kis_dlg_adj_layer_props.h"
#include "dialogs/kis_dlg_adjustment_layer.h"
#include "dialogs/kis_dlg_layer_properties.h"
#include "dialogs/kis_dlg_generator_layer.h"
#include "dialogs/kis_dlg_file_layer.h"
#include "dialogs/kis_dlg_layer_style.h"
+#include "dialogs/KisDlgChangeCloneSource.h"
#include "kis_filter_manager.h"
#include "kis_node_visitor.h"
#include "kis_paint_layer.h"
#include "commands/kis_image_commands.h"
#include "commands/kis_node_commands.h"
#include "kis_change_file_layer_command.h"
#include "kis_canvas_resource_provider.h"
#include "kis_selection_manager.h"
#include "kis_statusbar.h"
#include "KisViewManager.h"
#include "kis_zoom_manager.h"
#include "canvas/kis_canvas2.h"
#include "widgets/kis_meta_data_merge_strategy_chooser_widget.h"
#include "widgets/kis_wdg_generator.h"
#include "kis_progress_widget.h"
#include "kis_node_commands_adapter.h"
#include "kis_node_manager.h"
#include "kis_action.h"
#include "kis_action_manager.h"
#include "kis_raster_keyframe_channel.h"
#include "kis_signal_compressor_with_param.h"
#include "kis_abstract_projection_plane.h"
#include "commands_new/kis_set_layer_style_command.h"
#include "kis_post_execution_undo_adapter.h"
#include "kis_selection_mask.h"
#include "kis_layer_utils.h"
#include "lazybrush/kis_colorize_mask.h"
#include "kis_processing_applicator.h"
#include "KisSaveGroupVisitor.h"
KisLayerManager::KisLayerManager(KisViewManager * view)
: m_view(view)
, m_imageView(0)
, m_imageFlatten(0)
, m_imageMergeLayer(0)
, m_groupLayersSave(0)
, m_imageResizeToLayer(0)
, m_flattenLayer(0)
, m_rasterizeLayer(0)
, m_commandsAdapter(new KisNodeCommandsAdapter(m_view))
, m_layerStyle(0)
{
}
KisLayerManager::~KisLayerManager()
{
delete m_commandsAdapter;
}
void KisLayerManager::setView(QPointerview)
{
m_imageView = view;
}
KisLayerSP KisLayerManager::activeLayer()
{
if (m_imageView) {
return m_imageView->currentLayer();
}
return 0;
}
KisPaintDeviceSP KisLayerManager::activeDevice()
{
if (activeLayer()) {
return activeLayer()->paintDevice();
}
return 0;
}
void KisLayerManager::activateLayer(KisLayerSP layer)
{
if (m_imageView) {
emit sigLayerActivated(layer);
layersUpdated();
if (layer) {
m_view->canvasResourceProvider()->slotNodeActivated(layer.data());
}
}
}
void KisLayerManager::setup(KisActionManager* actionManager)
{
m_imageFlatten = actionManager->createAction("flatten_image");
connect(m_imageFlatten, SIGNAL(triggered()), this, SLOT(flattenImage()));
m_imageMergeLayer = actionManager->createAction("merge_layer");
connect(m_imageMergeLayer, SIGNAL(triggered()), this, SLOT(mergeLayer()));
m_flattenLayer = actionManager->createAction("flatten_layer");
connect(m_flattenLayer, SIGNAL(triggered()), this, SLOT(flattenLayer()));
m_rasterizeLayer = actionManager->createAction("rasterize_layer");
connect(m_rasterizeLayer, SIGNAL(triggered()), this, SLOT(rasterizeLayer()));
m_groupLayersSave = actionManager->createAction("save_groups_as_images");
connect(m_groupLayersSave, SIGNAL(triggered()), this, SLOT(saveGroupLayers()));
m_convertGroupAnimated = actionManager->createAction("convert_group_to_animated");
connect(m_convertGroupAnimated, SIGNAL(triggered()), this, SLOT(convertGroupToAnimated()));
m_imageResizeToLayer = actionManager->createAction("resizeimagetolayer");
connect(m_imageResizeToLayer, SIGNAL(triggered()), this, SLOT(imageResizeToActiveLayer()));
KisAction *action = actionManager->createAction("trim_to_image");
connect(action, SIGNAL(triggered()), this, SLOT(trimToImage()));
m_layerStyle = actionManager->createAction("layer_style");
connect(m_layerStyle, SIGNAL(triggered()), this, SLOT(layerStyle()));
}
void KisLayerManager::updateGUI()
{
KisImageSP image = m_view->image();
KisLayerSP layer = activeLayer();
const bool isGroupLayer = layer && layer->inherits("KisGroupLayer");
m_imageMergeLayer->setText(
isGroupLayer ?
i18nc("@action:inmenu", "Merge Group") :
i18nc("@action:inmenu", "Merge with Layer Below"));
m_flattenLayer->setVisible(!isGroupLayer);
if (m_view->statusBar())
m_view->statusBar()->setProfile(image);
}
void KisLayerManager::imageResizeToActiveLayer()
{
KisLayerSP layer;
KisImageWSP image = m_view->image();
if (image && (layer = activeLayer())) {
QRect cropRect = layer->projection()->nonDefaultPixelArea();
if (!cropRect.isEmpty()) {
image->cropImage(cropRect);
} else {
m_view->showFloatingMessage(
i18nc("floating message in layer manager",
"Layer is empty "),
QIcon(), 2000, KisFloatingMessage::Low);
}
}
}
void KisLayerManager::trimToImage()
{
KisImageWSP image = m_view->image();
if (image) {
image->cropImage(image->bounds());
}
}
void KisLayerManager::layerProperties()
{
if (!m_view) return;
if (!m_view->document()) return;
KisLayerSP layer = activeLayer();
if (!layer) return;
QList selectedNodes = m_view->nodeManager()->selectedNodes();
const bool multipleLayersSelected = selectedNodes.size() > 1;
KisAdjustmentLayerSP adjustmentLayer = KisAdjustmentLayerSP(dynamic_cast(layer.data()));
KisGeneratorLayerSP groupLayer = KisGeneratorLayerSP(dynamic_cast(layer.data()));
KisFileLayerSP filterLayer = KisFileLayerSP(dynamic_cast(layer.data()));
if (adjustmentLayer && !multipleLayersSelected) {
KisPaintDeviceSP dev = adjustmentLayer->projection();
KisDlgAdjLayerProps dlg(adjustmentLayer, adjustmentLayer.data(), dev, m_view, adjustmentLayer->filter().data(), adjustmentLayer->name(), i18n("Filter Layer Properties"), m_view->mainWindow(), "dlgadjlayerprops");
dlg.resize(dlg.minimumSizeHint());
KisFilterConfigurationSP configBefore(adjustmentLayer->filter());
KIS_ASSERT_RECOVER_RETURN(configBefore);
QString xmlBefore = configBefore->toXML();
if (dlg.exec() == QDialog::Accepted) {
adjustmentLayer->setName(dlg.layerName());
KisFilterConfigurationSP configAfter(dlg.filterConfiguration());
Q_ASSERT(configAfter);
QString xmlAfter = configAfter->toXML();
if(xmlBefore != xmlAfter) {
KisChangeFilterCmd *cmd
= new KisChangeFilterCmd(adjustmentLayer,
configBefore->name(),
xmlBefore,
configAfter->name(),
xmlAfter,
false);
// FIXME: check whether is needed
cmd->redo();
m_view->undoAdapter()->addCommand(cmd);
m_view->document()->setModified(true);
}
}
else {
KisFilterConfigurationSP configAfter(dlg.filterConfiguration());
Q_ASSERT(configAfter);
QString xmlAfter = configAfter->toXML();
if(xmlBefore != xmlAfter) {
adjustmentLayer->setFilter(KisFilterRegistry::instance()->cloneConfiguration(configBefore.data()));
adjustmentLayer->setDirty();
}
}
}
else if (groupLayer && !multipleLayersSelected) {
KisFilterConfigurationSP configBefore(groupLayer->filter());
Q_ASSERT(configBefore);
QString xmlBefore = configBefore->toXML();
KisDlgGeneratorLayer *dlg = new KisDlgGeneratorLayer(groupLayer->name(), m_view, m_view->mainWindow(), groupLayer, configBefore);
dlg->setCaption(i18n("Fill Layer Properties"));
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->setConfiguration(configBefore.data());
dlg->resize(dlg->minimumSizeHint());
Qt::WindowFlags flags = dlg->windowFlags();
dlg->setWindowFlags(flags | Qt::WindowStaysOnTopHint | Qt::Dialog);
dlg->show();
}
else if (filterLayer && !multipleLayersSelected){
QString basePath = QFileInfo(m_view->document()->url().toLocalFile()).absolutePath();
QString fileNameOld = filterLayer->fileName();
KisFileLayer::ScalingMethod scalingMethodOld = filterLayer->scalingMethod();
KisDlgFileLayer dlg(basePath, filterLayer->name(), m_view->mainWindow());
dlg.setCaption(i18n("File Layer Properties"));
dlg.setFileName(fileNameOld);
dlg.setScalingMethod(scalingMethodOld);
if (dlg.exec() == QDialog::Accepted) {
const QString fileNameNew = dlg.fileName();
KisFileLayer::ScalingMethod scalingMethodNew = dlg.scaleToImageResolution();
if(fileNameNew.isEmpty()){
QMessageBox::critical(m_view->mainWindow(), i18nc("@title:window", "Krita"), i18n("No file name specified"));
return;
}
filterLayer->setName(dlg.layerName());
if (fileNameOld!= fileNameNew || scalingMethodOld != scalingMethodNew) {
KisChangeFileLayerCmd *cmd
= new KisChangeFileLayerCmd(filterLayer,
basePath,
fileNameOld,
scalingMethodOld,
basePath,
fileNameNew,
scalingMethodNew);
m_view->undoAdapter()->addCommand(cmd);
}
}
} else { // If layer == normal painting layer, vector layer, or group layer
QList selectedNodes = m_view->nodeManager()->selectedNodes();
KisDlgLayerProperties *dialog = new KisDlgLayerProperties(selectedNodes, m_view);
dialog->resize(dialog->minimumSizeHint());
dialog->setAttribute(Qt::WA_DeleteOnClose);
Qt::WindowFlags flags = dialog->windowFlags();
dialog->setWindowFlags(flags | Qt::WindowStaysOnTopHint | Qt::Dialog);
dialog->show();
}
}
+void KisLayerManager::changeCloneSource()
+{
+ QList selectedNodes = m_view->nodeManager()->selectedNodes();
+ if (selectedNodes.isEmpty()) {
+ return;
+ }
+
+ QList cloneLayers;
+ KisNodeSP node;
+ Q_FOREACH (node, selectedNodes) {
+ KisCloneLayerSP cloneLayer(qobject_cast(node.data()));
+ if (cloneLayer) {
+ cloneLayers << cloneLayer;
+ }
+ }
+
+ if (cloneLayers.isEmpty()) {
+ return;
+ }
+
+ KisDlgChangeCloneSource *dialog = new KisDlgChangeCloneSource(cloneLayers, m_view);
+ dialog->setCaption(i18n("Change Clone Layer"));
+ dialog->resize(dialog->minimumSizeHint());
+ dialog->setAttribute(Qt::WA_DeleteOnClose);
+ Qt::WindowFlags flags = dialog->windowFlags();
+ dialog->setWindowFlags(flags | Qt::WindowStaysOnTopHint | Qt::Dialog);
+ dialog->show();
+}
+
void KisLayerManager::convertNodeToPaintLayer(KisNodeSP source)
{
KisImageWSP image = m_view->image();
if (!image) return;
KisLayer *srcLayer = qobject_cast(source.data());
if (srcLayer && (srcLayer->inherits("KisGroupLayer") || srcLayer->layerStyle() || srcLayer->childCount() > 0)) {
image->flattenLayer(srcLayer);
return;
}
KisPaintDeviceSP srcDevice =
source->paintDevice() ? source->projection() : source->original();
bool putBehind = false;
QString newCompositeOp = source->compositeOpId();
KisColorizeMask *colorizeMask = dynamic_cast(source.data());
if (colorizeMask) {
srcDevice = colorizeMask->coloringProjection();
putBehind = colorizeMask->compositeOpId() == COMPOSITE_BEHIND;
if (putBehind) {
newCompositeOp = COMPOSITE_OVER;
}
}
if (!srcDevice) return;
KisPaintDeviceSP clone;
if (*srcDevice->colorSpace() !=
*srcDevice->compositionSourceColorSpace()) {
clone = new KisPaintDevice(srcDevice->compositionSourceColorSpace());
QRect rc(srcDevice->extent());
KisPainter::copyAreaOptimized(rc.topLeft(), srcDevice, clone, rc);
} else {
clone = new KisPaintDevice(*srcDevice);
}
KisLayerSP layer = new KisPaintLayer(image,
source->name(),
source->opacity(),
clone);
layer->setCompositeOpId(newCompositeOp);
KisNodeSP parent = source->parent();
KisNodeSP above = source->prevSibling();
while (parent && !parent->allowAsChild(layer)) {
above = above ? above->parent() : source->parent();
parent = above ? above->parent() : 0;
}
if (putBehind && above == source->parent()) {
above = above->prevSibling();
}
m_commandsAdapter->beginMacro(kundo2_i18n("Convert to a Paint Layer"));
m_commandsAdapter->removeNode(source);
m_commandsAdapter->addNode(layer, parent, above);
m_commandsAdapter->endMacro();
}
void KisLayerManager::convertGroupToAnimated()
{
KisGroupLayerSP group = dynamic_cast(activeLayer().data());
if (group.isNull()) return;
KisPaintLayerSP animatedLayer = new KisPaintLayer(m_view->image(), group->name(), OPACITY_OPAQUE_U8);
animatedLayer->enableAnimation();
KisRasterKeyframeChannel *contentChannel = dynamic_cast(
animatedLayer->getKeyframeChannel(KisKeyframeChannel::Content.id(), true));
KIS_ASSERT_RECOVER_RETURN(contentChannel);
KisNodeSP child = group->firstChild();
int time = 0;
while (child) {
contentChannel->importFrame(time, child->projection(), NULL);
time++;
child = child->nextSibling();
}
m_commandsAdapter->beginMacro(kundo2_i18n("Convert to an animated layer"));
m_commandsAdapter->addNode(animatedLayer, group->parent(), group);
m_commandsAdapter->removeNode(group);
m_commandsAdapter->endMacro();
}
void KisLayerManager::convertLayerToFileLayer(KisNodeSP source)
{
KisImageSP image = m_view->image();
if (!image) return;
QStringList listMimeFilter = KisImportExportManager::supportedMimeTypes(KisImportExportManager::Export);
KoDialog dlg;
QWidget *page = new QWidget(&dlg);
dlg.setMainWidget(page);
QBoxLayout *layout = new QVBoxLayout(page);
dlg.setWindowTitle(i18n("Save layers to..."));
QLabel *lbl = new QLabel(i18n("Choose the location where the layer will be saved to. The new file layer will then reference this location."));
lbl->setWordWrap(true);
layout->addWidget(lbl);
KisFileNameRequester *urlRequester = new KisFileNameRequester(page);
urlRequester->setMode(KoFileDialog::SaveFile);
urlRequester->setMimeTypeFilters(listMimeFilter);
urlRequester->setFileName(m_view->document()->url().toLocalFile());
if (m_view->document()->url().isLocalFile()) {
QFileInfo location = QFileInfo(m_view->document()->url().toLocalFile()).baseName();
location.setFile(location.dir(), location.baseName() + "_" + source->name() + ".png");
urlRequester->setFileName(location.absoluteFilePath());
}
else {
const QFileInfo location = QFileInfo(QStandardPaths::writableLocation(QStandardPaths::HomeLocation));
const QString proposedFileName = QDir(location.absoluteFilePath()).absoluteFilePath(source->name() + ".png");
urlRequester->setFileName(proposedFileName);
}
// We don't want .kra files as file layers, Krita cannot handle the load.
QStringList mimes = KisImportExportManager::supportedMimeTypes(KisImportExportManager::Export);
int i = mimes.indexOf(KIS_MIME_TYPE);
if (i >= 0 && i < mimes.size()) {
mimes.removeAt(i);
}
urlRequester->setMimeTypeFilters(mimes);
layout->addWidget(urlRequester);
if (!dlg.exec()) return;
QString path = urlRequester->fileName();
if (path.isEmpty()) return;
QFileInfo f(path);
QString mimeType= KisMimeDatabase::mimeTypeForFile(f.fileName());
if (mimeType.isEmpty()) {
mimeType = "image/png";
}
QScopedPointer doc(KisPart::instance()->createDocument());
QRect bounds = source->exactBounds();
KisImageSP dst = new KisImage(doc->createUndoStore(),
image->width(),
image->height(),
image->projection()->compositionSourceColorSpace(),
source->name());
dst->setResolution(image->xRes(), image->yRes());
doc->setFileBatchMode(false);
doc->setCurrentImage(dst);
KisNodeSP node = source->clone();
dst->addNode(node);
dst->initialRefreshGraph();
dst->cropImage(bounds);
dst->waitForDone();
bool r = doc->exportDocumentSync(QUrl::fromLocalFile(path), mimeType.toLatin1());
if (!r) {
qWarning() << "Converting layer to file layer. path:"<< path << "gave errors" << doc->errorMessage();
} else {
QString basePath = QFileInfo(m_view->document()->url().toLocalFile()).absolutePath();
QString relativePath = QDir(basePath).relativeFilePath(path);
KisFileLayer *fileLayer = new KisFileLayer(image, basePath, relativePath, KisFileLayer::None, source->name(), OPACITY_OPAQUE_U8);
fileLayer->setX(bounds.x());
fileLayer->setY(bounds.y());
KisNodeSP dstParent = source->parent();
KisNodeSP dstAboveThis = source->prevSibling();
m_commandsAdapter->beginMacro(kundo2_i18n("Convert to a file layer"));
m_commandsAdapter->removeNode(source);
m_commandsAdapter->addNode(fileLayer, dstParent, dstAboveThis);
m_commandsAdapter->endMacro();
}
doc->closeUrl(false);
}
void KisLayerManager::adjustLayerPosition(KisNodeSP node, KisNodeSP activeNode, KisNodeSP &parent, KisNodeSP &above)
{
Q_ASSERT(activeNode);
parent = activeNode;
above = parent->lastChild();
if (parent->inherits("KisGroupLayer") && parent->collapsed()) {
above = parent;
parent = parent->parent();
return;
}
while (parent &&
(!parent->allowAsChild(node) || parent->userLocked())) {
above = parent;
parent = parent->parent();
}
if (!parent) {
warnKrita << "KisLayerManager::adjustLayerPosition:"
<< "No node accepted newly created node";
parent = m_view->image()->root();
above = parent->lastChild();
}
}
void KisLayerManager::addLayerCommon(KisNodeSP activeNode, KisNodeSP layer, bool updateImage, KisProcessingApplicator *applicator)
{
KisNodeSP parent;
KisNodeSP above;
adjustLayerPosition(layer, activeNode, parent, above);
KisGroupLayer *group = dynamic_cast(parent.data());
const bool parentForceUpdate = group && !group->projectionIsValid();
updateImage |= parentForceUpdate;
m_commandsAdapter->addNodeAsync(layer, parent, above, updateImage, updateImage, applicator);
}
KisLayerSP KisLayerManager::addPaintLayer(KisNodeSP activeNode)
{
KisImageWSP image = m_view->image();
KisLayerSP layer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, image->colorSpace());
addLayerCommon(activeNode, layer, false, 0);
return layer;
}
KisNodeSP KisLayerManager::addGroupLayer(KisNodeSP activeNode)
{
KisImageWSP image = m_view->image();
KisGroupLayerSP group = new KisGroupLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8);
addLayerCommon(activeNode, group, false, 0);
return group;
}
KisNodeSP KisLayerManager::addCloneLayer(KisNodeSP activeNode)
{
KisImageWSP image = m_view->image();
KisNodeSP node = new KisCloneLayer(activeLayer(), image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8);
addLayerCommon(activeNode, node, true, 0);
return node;
}
KisNodeSP KisLayerManager::addShapeLayer(KisNodeSP activeNode)
{
if (!m_view) return 0;
if (!m_view->document()) return 0;
KisImageWSP image = m_view->image();
KisShapeLayerSP layer = new KisShapeLayer(m_view->document()->shapeController(), image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8);
addLayerCommon(activeNode, layer, false, 0);
return layer;
}
KisNodeSP KisLayerManager::addAdjustmentLayer(KisNodeSP activeNode)
{
KisImageWSP image = m_view->image();
KisSelectionSP selection = m_view->selection();
KisProcessingApplicator applicator(image, 0, KisProcessingApplicator::NONE,
KisImageSignalVector() << ModifiedSignal,
kundo2_i18n("Add Layer"));
KisAdjustmentLayerSP adjl = addAdjustmentLayer(activeNode, QString(), 0, selection, &applicator);
KisPaintDeviceSP previewDevice = new KisPaintDevice(*adjl->original());
KisDlgAdjustmentLayer dlg(adjl, adjl.data(), previewDevice, image->nextLayerName(), i18n("New Filter Layer"), m_view);
dlg.resize(dlg.minimumSizeHint());
// ensure that the device may be free'd by the dialog
// when it is not needed anymore
previewDevice = 0;
if (dlg.exec() != QDialog::Accepted || adjl->filter().isNull()) {
// XXX: add messagebox warning if there's no filter set!
applicator.cancel();
} else {
adjl->setName(dlg.layerName());
applicator.end();
}
return adjl;
}
KisAdjustmentLayerSP KisLayerManager::addAdjustmentLayer(KisNodeSP activeNode, const QString & name,
KisFilterConfigurationSP filter,
KisSelectionSP selection,
KisProcessingApplicator *applicator)
{
KisImageWSP image = m_view->image();
KisAdjustmentLayerSP layer = new KisAdjustmentLayer(image, name, filter, selection);
addLayerCommon(activeNode, layer, true, applicator);
return layer;
}
KisNodeSP KisLayerManager::addGeneratorLayer(KisNodeSP activeNode)
{
KisImageWSP image = m_view->image();
QColor currentForeground = m_view->canvasResourceProvider()->fgColor().toQColor();
KisDlgGeneratorLayer dlg(image->nextLayerName(), m_view, m_view->mainWindow(), 0, 0);
KisFilterConfigurationSP defaultConfig = dlg.configuration();
defaultConfig->setProperty("color", currentForeground);
dlg.setConfiguration(defaultConfig);
dlg.resize(dlg.minimumSizeHint());
if (dlg.exec() == QDialog::Accepted) {
KisSelectionSP selection = m_view->selection();
KisFilterConfigurationSP generator = dlg.configuration();
QString name = dlg.layerName();
KisNodeSP node = new KisGeneratorLayer(image, name, generator, selection);
addLayerCommon(activeNode, node, true, 0);
return node;
}
return 0;
}
void KisLayerManager::flattenImage()
{
KisImageSP image = m_view->image();
if (!m_view->blockUntilOperationsFinished(image)) return;
if (image) {
bool doIt = true;
if (image->nHiddenLayers() > 0) {
int answer = QMessageBox::warning(m_view->mainWindow(),
i18nc("@title:window", "Flatten Image"),
i18n("The image contains hidden layers that will be lost. Do you want to flatten the image?"),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::No);
if (answer != QMessageBox::Yes) {
doIt = false;
}
}
if (doIt) {
image->flatten(m_view->activeNode());
}
}
}
inline bool isSelectionMask(KisNodeSP node) {
return dynamic_cast(node.data());
}
bool tryMergeSelectionMasks(KisNodeSP currentNode, KisImageSP image)
{
bool result = false;
KisNodeSP prevNode = currentNode->prevSibling();
if (isSelectionMask(currentNode) &&
prevNode && isSelectionMask(prevNode)) {
QList mergedNodes;
mergedNodes.append(currentNode);
mergedNodes.append(prevNode);
image->mergeMultipleLayers(mergedNodes, currentNode);
result = true;
}
return result;
}
bool tryFlattenGroupLayer(KisNodeSP currentNode, KisImageSP image)
{
bool result = false;
if (currentNode->inherits("KisGroupLayer")) {
KisGroupLayer *layer = qobject_cast(currentNode.data());
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(layer, false);
image->flattenLayer(layer);
result = true;
}
return result;
}
void KisLayerManager::mergeLayer()
{
KisImageSP image = m_view->image();
if (!image) return;
KisLayerSP layer = activeLayer();
if (!layer) return;
if (!m_view->blockUntilOperationsFinished(image)) return;
QList selectedNodes = m_view->nodeManager()->selectedNodes();
if (selectedNodes.size() > 1) {
image->mergeMultipleLayers(selectedNodes, m_view->activeNode());
}
else if (tryMergeSelectionMasks(m_view->activeNode(), image)) {
// already done!
} else if (tryFlattenGroupLayer(m_view->activeNode(), image)) {
// already done!
} else {
if (!layer->prevSibling()) return;
KisLayer *prevLayer = qobject_cast(layer->prevSibling().data());
if (!prevLayer) return;
if (prevLayer->userLocked()) {
m_view->showFloatingMessage(
i18nc("floating message in layer manager",
"Layer is locked "),
QIcon(), 2000, KisFloatingMessage::Low);
}
else if (layer->metaData()->isEmpty() && prevLayer->metaData()->isEmpty()) {
image->mergeDown(layer, KisMetaData::MergeStrategyRegistry::instance()->get("Drop"));
}
else {
const KisMetaData::MergeStrategy* strategy = KisMetaDataMergeStrategyChooserWidget::showDialog(m_view->mainWindow());
if (!strategy) return;
image->mergeDown(layer, strategy);
}
}
m_view->updateGUI();
}
void KisLayerManager::flattenLayer()
{
KisImageSP image = m_view->image();
if (!image) return;
KisLayerSP layer = activeLayer();
if (!layer) return;
if (!m_view->blockUntilOperationsFinished(image)) return;
convertNodeToPaintLayer(layer);
m_view->updateGUI();
}
void KisLayerManager::rasterizeLayer()
{
KisImageSP image = m_view->image();
if (!image) return;
KisLayerSP layer = activeLayer();
if (!layer) return;
if (!m_view->blockUntilOperationsFinished(image)) return;
KisPaintLayerSP paintLayer = new KisPaintLayer(image, layer->name(), layer->opacity());
KisPainter gc(paintLayer->paintDevice());
QRect rc = layer->projection()->exactBounds();
gc.bitBlt(rc.topLeft(), layer->projection(), rc);
m_commandsAdapter->beginMacro(kundo2_i18n("Rasterize Layer"));
m_commandsAdapter->addNode(paintLayer.data(), layer->parent().data(), layer.data());
int childCount = layer->childCount();
for (int i = 0; i < childCount; i++) {
m_commandsAdapter->moveNode(layer->firstChild(), paintLayer, paintLayer->lastChild());
}
m_commandsAdapter->removeNode(layer);
m_commandsAdapter->endMacro();
updateGUI();
}
void KisLayerManager::layersUpdated()
{
KisLayerSP layer = activeLayer();
if (!layer) return;
m_view->updateGUI();
}
void KisLayerManager::saveGroupLayers()
{
QStringList listMimeFilter = KisImportExportManager::supportedMimeTypes(KisImportExportManager::Export);
KoDialog dlg;
QWidget *page = new QWidget(&dlg);
dlg.setMainWidget(page);
QBoxLayout *layout = new QVBoxLayout(page);
KisFileNameRequester *urlRequester = new KisFileNameRequester(page);
urlRequester->setMode(KoFileDialog::SaveFile);
if (m_view->document()->url().isLocalFile()) {
urlRequester->setStartDir(QFileInfo(m_view->document()->url().toLocalFile()).absolutePath());
}
urlRequester->setMimeTypeFilters(listMimeFilter);
urlRequester->setFileName(m_view->document()->url().toLocalFile());
layout->addWidget(urlRequester);
QCheckBox *chkInvisible = new QCheckBox(i18n("Convert Invisible Groups"), page);
chkInvisible->setChecked(false);
layout->addWidget(chkInvisible);
QCheckBox *chkDepth = new QCheckBox(i18n("Export Only Toplevel Groups"), page);
chkDepth->setChecked(true);
layout->addWidget(chkDepth);
if (!dlg.exec()) return;
QString path = urlRequester->fileName();
if (path.isEmpty()) return;
QFileInfo f(path);
QString mimeType= KisMimeDatabase::mimeTypeForFile(f.fileName(), false);
if (mimeType.isEmpty()) {
mimeType = "image/png";
}
QString extension = KisMimeDatabase::suffixesForMimeType(mimeType).first();
QString basename = f.baseName();
KisImageSP image = m_view->image();
if (!image) return;
KisSaveGroupVisitor v(image, chkInvisible->isChecked(), chkDepth->isChecked(), f.absolutePath(), basename, extension, mimeType);
image->rootLayer()->accept(v);
}
bool KisLayerManager::activeLayerHasSelection()
{
return (activeLayer()->selection() != 0);
}
KisNodeSP KisLayerManager::addFileLayer(KisNodeSP activeNode)
{
QString basePath;
QUrl url = m_view->document()->url();
if (url.isLocalFile()) {
basePath = QFileInfo(url.toLocalFile()).absolutePath();
}
KisImageWSP image = m_view->image();
KisDlgFileLayer dlg(basePath, image->nextLayerName(), m_view->mainWindow());
dlg.resize(dlg.minimumSizeHint());
if (dlg.exec() == QDialog::Accepted) {
QString name = dlg.layerName();
QString fileName = dlg.fileName();
if(fileName.isEmpty()){
QMessageBox::critical(m_view->mainWindow(), i18nc("@title:window", "Krita"), i18n("No file name specified"));
return 0;
}
KisFileLayer::ScalingMethod scalingMethod = dlg.scaleToImageResolution();
KisNodeSP node = new KisFileLayer(image, basePath, fileName, scalingMethod, name, OPACITY_OPAQUE_U8);
addLayerCommon(activeNode, node, true, 0);
return node;
}
return 0;
}
void updateLayerStyles(KisLayerSP layer, KisDlgLayerStyle *dlg)
{
KisSetLayerStyleCommand::updateLayerStyle(layer, dlg->style()->clone());
}
void KisLayerManager::layerStyle()
{
KisImageWSP image = m_view->image();
if (!image) return;
KisLayerSP layer = activeLayer();
if (!layer) return;
if (!m_view->blockUntilOperationsFinished(image)) return;
KisPSDLayerStyleSP oldStyle;
if (layer->layerStyle()) {
oldStyle = layer->layerStyle()->clone();
}
else {
oldStyle = toQShared(new KisPSDLayerStyle());
}
KisDlgLayerStyle dlg(oldStyle->clone(), m_view->canvasResourceProvider());
std::function updateCall(std::bind(updateLayerStyles, layer, &dlg));
SignalToFunctionProxy proxy(updateCall);
connect(&dlg, SIGNAL(configChanged()), &proxy, SLOT(start()));
if (dlg.exec() == QDialog::Accepted) {
KisPSDLayerStyleSP newStyle = dlg.style();
KUndo2CommandSP command = toQShared(
new KisSetLayerStyleCommand(layer, oldStyle, newStyle));
image->postExecutionUndoAdapter()->addCommand(command);
}
}
diff --git a/libs/ui/kis_layer_manager.h b/libs/ui/kis_layer_manager.h
index cbf813fc88..6bc545405a 100644
--- a/libs/ui/kis_layer_manager.h
+++ b/libs/ui/kis_layer_manager.h
@@ -1,134 +1,136 @@
/*
* 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_LAYER_MANAGER
#define KIS_LAYER_MANAGER
#include
#include
#include
#include "kis_adjustment_layer.h"
#include "kis_types.h"
#include "KisView.h"
#include
class KisViewManager;
class KisNodeCommandsAdapter;
class KisAction;
class KisActionManager;
class KisProcessingApplicator;
/**
* KisLayerManager takes care of the gui around working with layers:
* adding, removing, editing. It also keeps track of the active layer
* for this view.
*/
class KisLayerManager : public QObject
{
Q_OBJECT
public:
KisLayerManager(KisViewManager * view);
~KisLayerManager() override;
void setView(QPointerview);
Q_SIGNALS:
void sigLayerActivated(KisLayerSP layer);
private:
friend class KisNodeManager;
/**
* Activate the specified layer. The layer may be 0.
*/
void activateLayer(KisLayerSP layer);
KisLayerSP activeLayer();
KisPaintDeviceSP activeDevice();
void setup(KisActionManager *actionManager);
void updateGUI();
private Q_SLOTS:
void mergeLayer();
void imageResizeToActiveLayer();
void trimToImage();
void layerProperties();
void flattenImage();
void flattenLayer();
void rasterizeLayer();
void layersUpdated();
void saveGroupLayers();
bool activeLayerHasSelection();
void convertNodeToPaintLayer(KisNodeSP source);
void convertGroupToAnimated();
void convertLayerToFileLayer(KisNodeSP source);
KisLayerSP addPaintLayer(KisNodeSP activeNode);
KisNodeSP addGroupLayer(KisNodeSP activeNode);
KisNodeSP addCloneLayer(KisNodeSP activeNode);
KisNodeSP addShapeLayer(KisNodeSP activeNode);
KisNodeSP addAdjustmentLayer(KisNodeSP activeNode);
KisAdjustmentLayerSP addAdjustmentLayer(KisNodeSP activeNode, const QString & name, KisFilterConfigurationSP filter, KisSelectionSP selection, KisProcessingApplicator *applicator);
KisNodeSP addGeneratorLayer(KisNodeSP activeNode);
KisNodeSP addFileLayer(KisNodeSP activeNode);
void layerStyle();
+ void changeCloneSource();
+
private:
void adjustLayerPosition(KisNodeSP node, KisNodeSP activeNode, KisNodeSP &parent, KisNodeSP &above);
void addLayerCommon(KisNodeSP activeNode, KisNodeSP layer, bool updateImage = true, KisProcessingApplicator *applicator = 0);
private:
KisViewManager * m_view;
QPointerm_imageView;
KisAction *m_imageFlatten;
KisAction *m_imageMergeLayer;
KisAction *m_groupLayersSave;
KisAction *m_convertGroupAnimated;
KisAction *m_imageResizeToLayer;
KisAction *m_flattenLayer;
KisAction *m_rasterizeLayer;
KisNodeCommandsAdapter* m_commandsAdapter;
KisAction *m_layerStyle;
};
#endif
diff --git a/libs/ui/kis_node_manager.cpp b/libs/ui/kis_node_manager.cpp
index d72f73319b..be153f8a18 100644
--- a/libs/ui/kis_node_manager.cpp
+++ b/libs/ui/kis_node_manager.cpp
@@ -1,1567 +1,1572 @@
/*
* Copyright (C) 2007 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_node_manager.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 "KisPart.h"
#include "canvas/kis_canvas2.h"
#include "kis_shape_controller.h"
#include "kis_canvas_resource_provider.h"
#include "KisViewManager.h"
#include "KisDocument.h"
#include "kis_mask_manager.h"
#include "kis_group_layer.h"
#include "kis_layer_manager.h"
#include "kis_selection_manager.h"
#include "kis_node_commands_adapter.h"
#include "kis_action.h"
#include "kis_action_manager.h"
#include "kis_processing_applicator.h"
#include "kis_sequential_iterator.h"
#include "kis_transaction.h"
#include "kis_node_selection_adapter.h"
#include "kis_node_insertion_adapter.h"
#include "kis_node_juggler_compressed.h"
#include "KisNodeDisplayModeAdapter.h"
#include "kis_clipboard.h"
#include "kis_node_dummies_graph.h"
#include "kis_mimedata.h"
#include "kis_layer_utils.h"
#include "krita_utils.h"
#include "kis_shape_layer.h"
#include "processing/kis_mirror_processing_visitor.h"
#include "KisView.h"
#include
#include
#include
struct KisNodeManager::Private {
Private(KisNodeManager *_q, KisViewManager *v)
: q(_q)
, view(v)
, imageView(0)
, layerManager(v)
, maskManager(v)
, commandsAdapter(v)
, nodeSelectionAdapter(new KisNodeSelectionAdapter(q))
, nodeInsertionAdapter(new KisNodeInsertionAdapter(q))
, nodeDisplayModeAdapter(new KisNodeDisplayModeAdapter())
, lastRequestedIsolatedModeStatus(false)
{
}
KisNodeManager * q;
KisViewManager * view;
QPointerimageView;
KisLayerManager layerManager;
KisMaskManager maskManager;
KisNodeCommandsAdapter commandsAdapter;
QScopedPointer nodeSelectionAdapter;
QScopedPointer nodeInsertionAdapter;
QScopedPointer nodeDisplayModeAdapter;
KisAction *showInTimeline;
KisNodeList selectedNodes;
QPointer nodeJuggler;
KisNodeWSP previouslyActiveNode;
bool activateNodeImpl(KisNodeSP node);
QSignalMapper nodeCreationSignalMapper;
QSignalMapper nodeConversionSignalMapper;
bool lastRequestedIsolatedModeStatus;
void saveDeviceAsImage(KisPaintDeviceSP device,
const QString &defaultName,
const QRect &bounds,
qreal xRes,
qreal yRes,
quint8 opacity);
void mergeTransparencyMaskAsAlpha(bool writeToLayers);
KisNodeJugglerCompressed* lazyGetJuggler(const KUndo2MagicString &actionName);
};
bool KisNodeManager::Private::activateNodeImpl(KisNodeSP node)
{
Q_ASSERT(view);
Q_ASSERT(view->canvasBase());
Q_ASSERT(view->canvasBase()->globalShapeManager());
Q_ASSERT(imageView);
if (node && node == q->activeNode()) {
return false;
}
// Set the selection on the shape manager to the active layer
// and set call KoSelection::setActiveLayer( KoShapeLayer* layer )
// with the parent of the active layer.
KoSelection *selection = view->canvasBase()->globalShapeManager()->selection();
Q_ASSERT(selection);
selection->deselectAll();
if (!node) {
selection->setActiveLayer(0);
imageView->setCurrentNode(0);
maskManager.activateMask(0);
layerManager.activateLayer(0);
previouslyActiveNode = q->activeNode();
} else {
previouslyActiveNode = q->activeNode();
KoShape * shape = view->document()->shapeForNode(node);
//if (!shape) return false;
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(shape, false);
selection->select(shape);
KoShapeLayer * shapeLayer = dynamic_cast(shape);
//if (!shapeLayer) return false;
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(shapeLayer, false);
// shapeLayer->setGeometryProtected(node->userLocked());
// shapeLayer->setVisible(node->visible());
selection->setActiveLayer(shapeLayer);
imageView->setCurrentNode(node);
if (KisLayerSP layer = qobject_cast(node.data())) {
maskManager.activateMask(0);
layerManager.activateLayer(layer);
} else if (KisMaskSP mask = dynamic_cast(node.data())) {
maskManager.activateMask(mask);
// XXX_NODE: for now, masks cannot be nested.
layerManager.activateLayer(static_cast(node->parent().data()));
}
}
return true;
}
KisNodeManager::KisNodeManager(KisViewManager *view)
: m_d(new Private(this, view))
{
connect(&m_d->layerManager, SIGNAL(sigLayerActivated(KisLayerSP)), SIGNAL(sigLayerActivated(KisLayerSP)));
}
KisNodeManager::~KisNodeManager()
{
delete m_d;
}
void KisNodeManager::setView(QPointerimageView)
{
m_d->maskManager.setView(imageView);
m_d->layerManager.setView(imageView);
if (m_d->imageView) {
KisShapeController *shapeController = dynamic_cast(m_d->imageView->document()->shapeController());
Q_ASSERT(shapeController);
shapeController->disconnect(SIGNAL(sigActivateNode(KisNodeSP)), this);
m_d->imageView->image()->disconnect(this);
}
m_d->imageView = imageView;
if (m_d->imageView) {
KisShapeController *shapeController = dynamic_cast(m_d->imageView->document()->shapeController());
Q_ASSERT(shapeController);
connect(shapeController, SIGNAL(sigActivateNode(KisNodeSP)), SLOT(slotNonUiActivatedNode(KisNodeSP)));
connect(m_d->imageView->image(), SIGNAL(sigIsolatedModeChanged()),this, SLOT(slotUpdateIsolateModeActionImageStatusChange()));
connect(m_d->imageView->image(), SIGNAL(sigRequestNodeReselection(KisNodeSP,KisNodeList)),this, SLOT(slotImageRequestNodeReselection(KisNodeSP,KisNodeList)));
m_d->imageView->resourceProvider()->slotNodeActivated(m_d->imageView->currentNode());
}
}
#define NEW_LAYER_ACTION(id, layerType) \
{ \
action = actionManager->createAction(id); \
m_d->nodeCreationSignalMapper.setMapping(action, layerType); \
connect(action, SIGNAL(triggered()), \
&m_d->nodeCreationSignalMapper, SLOT(map())); \
}
#define CONVERT_NODE_ACTION_2(id, layerType, exclude) \
{ \
action = actionManager->createAction(id); \
action->setExcludedNodeTypes(QStringList(exclude)); \
actionManager->addAction(id, action); \
m_d->nodeConversionSignalMapper.setMapping(action, layerType); \
connect(action, SIGNAL(triggered()), \
&m_d->nodeConversionSignalMapper, SLOT(map())); \
}
#define CONVERT_NODE_ACTION(id, layerType) \
CONVERT_NODE_ACTION_2(id, layerType, layerType)
void KisNodeManager::setup(KActionCollection * actionCollection, KisActionManager* actionManager)
{
m_d->layerManager.setup(actionManager);
m_d->maskManager.setup(actionCollection, actionManager);
KisAction * action = 0;
action = actionManager->createAction("mirrorNodeX");
connect(action, SIGNAL(triggered()), this, SLOT(mirrorNodeX()));
action = actionManager->createAction("mirrorNodeY");
connect(action, SIGNAL(triggered()), this, SLOT(mirrorNodeY()));
action = actionManager->createAction("mirrorAllNodesX");
connect(action, SIGNAL(triggered()), this, SLOT(mirrorAllNodesX()));
action = actionManager->createAction("mirrorAllNodesY");
connect(action, SIGNAL(triggered()), this, SLOT(mirrorAllNodesY()));
action = actionManager->createAction("activateNextLayer");
connect(action, SIGNAL(triggered()), this, SLOT(activateNextNode()));
action = actionManager->createAction("activatePreviousLayer");
connect(action, SIGNAL(triggered()), this, SLOT(activatePreviousNode()));
action = actionManager->createAction("switchToPreviouslyActiveNode");
connect(action, SIGNAL(triggered()), this, SLOT(switchToPreviouslyActiveNode()));
action = actionManager->createAction("save_node_as_image");
connect(action, SIGNAL(triggered()), this, SLOT(saveNodeAsImage()));
action = actionManager->createAction("save_vector_node_to_svg");
connect(action, SIGNAL(triggered()), this, SLOT(saveVectorLayerAsImage()));
action->setActivationFlags(KisAction::ACTIVE_SHAPE_LAYER);
action = actionManager->createAction("duplicatelayer");
connect(action, SIGNAL(triggered()), this, SLOT(duplicateActiveNode()));
action = actionManager->createAction("copy_layer_clipboard");
connect(action, SIGNAL(triggered()), this, SLOT(copyLayersToClipboard()));
action = actionManager->createAction("cut_layer_clipboard");
connect(action, SIGNAL(triggered()), this, SLOT(cutLayersToClipboard()));
action = actionManager->createAction("paste_layer_from_clipboard");
connect(action, SIGNAL(triggered()), this, SLOT(pasteLayersFromClipboard()));
action = actionManager->createAction("create_quick_group");
connect(action, SIGNAL(triggered()), this, SLOT(createQuickGroup()));
action = actionManager->createAction("create_quick_clipping_group");
connect(action, SIGNAL(triggered()), this, SLOT(createQuickClippingGroup()));
action = actionManager->createAction("quick_ungroup");
connect(action, SIGNAL(triggered()), this, SLOT(quickUngroup()));
action = actionManager->createAction("select_all_layers");
connect(action, SIGNAL(triggered()), this, SLOT(selectAllNodes()));
action = actionManager->createAction("select_visible_layers");
connect(action, SIGNAL(triggered()), this, SLOT(selectVisibleNodes()));
action = actionManager->createAction("select_locked_layers");
connect(action, SIGNAL(triggered()), this, SLOT(selectLockedNodes()));
action = actionManager->createAction("select_invisible_layers");
connect(action, SIGNAL(triggered()), this, SLOT(selectInvisibleNodes()));
action = actionManager->createAction("select_unlocked_layers");
connect(action, SIGNAL(triggered()), this, SLOT(selectUnlockedNodes()));
action = actionManager->createAction("new_from_visible");
connect(action, SIGNAL(triggered()), this, SLOT(createFromVisible()));
action = actionManager->createAction("show_in_timeline");
action->setCheckable(true);
connect(action, SIGNAL(toggled(bool)), this, SLOT(slotShowHideTimeline(bool)));
m_d->showInTimeline = action;
NEW_LAYER_ACTION("add_new_paint_layer", "KisPaintLayer");
NEW_LAYER_ACTION("add_new_group_layer", "KisGroupLayer");
NEW_LAYER_ACTION("add_new_clone_layer", "KisCloneLayer");
NEW_LAYER_ACTION("add_new_shape_layer", "KisShapeLayer");
NEW_LAYER_ACTION("add_new_adjustment_layer", "KisAdjustmentLayer");
NEW_LAYER_ACTION("add_new_fill_layer", "KisGeneratorLayer");
NEW_LAYER_ACTION("add_new_file_layer", "KisFileLayer");
NEW_LAYER_ACTION("add_new_transparency_mask", "KisTransparencyMask");
NEW_LAYER_ACTION("add_new_filter_mask", "KisFilterMask");
NEW_LAYER_ACTION("add_new_colorize_mask", "KisColorizeMask");
NEW_LAYER_ACTION("add_new_transform_mask", "KisTransformMask");
NEW_LAYER_ACTION("add_new_selection_mask", "KisSelectionMask");
connect(&m_d->nodeCreationSignalMapper, SIGNAL(mapped(QString)),
this, SLOT(createNode(QString)));
CONVERT_NODE_ACTION("convert_to_paint_layer", "KisPaintLayer");
CONVERT_NODE_ACTION_2("convert_to_selection_mask", "KisSelectionMask", QStringList() << "KisSelectionMask" << "KisColorizeMask");
CONVERT_NODE_ACTION_2("convert_to_filter_mask", "KisFilterMask", QStringList() << "KisFilterMask" << "KisColorizeMask");
CONVERT_NODE_ACTION_2("convert_to_transparency_mask", "KisTransparencyMask", QStringList() << "KisTransparencyMask" << "KisColorizeMask");
CONVERT_NODE_ACTION("convert_to_animated", "animated");
CONVERT_NODE_ACTION_2("convert_to_file_layer", "KisFileLayer", QStringList() << "KisGroupLayer" << "KisFileLayer" << "KisCloneLayer");
connect(&m_d->nodeConversionSignalMapper, SIGNAL(mapped(QString)),
this, SLOT(convertNode(QString)));
action = actionManager->createAction("isolate_layer");
connect(action, SIGNAL(triggered(bool)), this, SLOT(toggleIsolateMode(bool)));
action = actionManager->createAction("toggle_layer_visibility");
connect(action, SIGNAL(triggered()), this, SLOT(toggleVisibility()));
action = actionManager->createAction("toggle_layer_lock");
connect(action, SIGNAL(triggered()), this, SLOT(toggleLock()));
action = actionManager->createAction("toggle_layer_inherit_alpha");
connect(action, SIGNAL(triggered()), this, SLOT(toggleInheritAlpha()));
action = actionManager->createAction("toggle_layer_alpha_lock");
connect(action, SIGNAL(triggered()), this, SLOT(toggleAlphaLock()));
action = actionManager->createAction("split_alpha_into_mask");
connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaIntoMask()));
action = actionManager->createAction("split_alpha_write");
connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaWrite()));
// HINT: we can save even when the nodes are not editable
action = actionManager->createAction("split_alpha_save_merged");
connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaSaveMerged()));
connect(this, SIGNAL(sigNodeActivated(KisNodeSP)), SLOT(slotUpdateIsolateModeAction()));
connect(this, SIGNAL(sigNodeActivated(KisNodeSP)), SLOT(slotTryRestartIsolatedMode()));
}
void KisNodeManager::updateGUI()
{
// enable/disable all relevant actions
m_d->layerManager.updateGUI();
m_d->maskManager.updateGUI();
}
KisNodeSP KisNodeManager::activeNode()
{
if (m_d->imageView) {
return m_d->imageView->currentNode();
}
return 0;
}
KisLayerSP KisNodeManager::activeLayer()
{
return m_d->layerManager.activeLayer();
}
const KoColorSpace* KisNodeManager::activeColorSpace()
{
if (m_d->maskManager.activeDevice()) {
return m_d->maskManager.activeDevice()->colorSpace();
} else {
Q_ASSERT(m_d->layerManager.activeLayer());
if (m_d->layerManager.activeLayer()->parentLayer())
return m_d->layerManager.activeLayer()->parentLayer()->colorSpace();
else
return m_d->view->image()->colorSpace();
}
}
void KisNodeManager::moveNodeAt(KisNodeSP node, KisNodeSP parent, int index)
{
if (parent->allowAsChild(node)) {
if (node->inherits("KisSelectionMask") && parent->inherits("KisLayer")) {
KisSelectionMask *m = dynamic_cast(node.data());
KisLayer *l = qobject_cast(parent.data());
if (m && m->active() && l && l->selectionMask()) {
l->selectionMask()->setActive(false);
}
}
m_d->commandsAdapter.moveNode(node, parent, index);
}
}
void KisNodeManager::moveNodesDirect(KisNodeList nodes, KisNodeSP parent, KisNodeSP aboveThis)
{
KUndo2MagicString actionName = kundo2_i18n("Move Nodes");
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
juggler->moveNode(nodes, parent, aboveThis);
}
void KisNodeManager::copyNodesDirect(KisNodeList nodes, KisNodeSP parent, KisNodeSP aboveThis)
{
KUndo2MagicString actionName = kundo2_i18n("Copy Nodes");
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
juggler->copyNode(nodes, parent, aboveThis);
}
void KisNodeManager::addNodesDirect(KisNodeList nodes, KisNodeSP parent, KisNodeSP aboveThis)
{
KUndo2MagicString actionName = kundo2_i18n("Add Nodes");
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
juggler->addNode(nodes, parent, aboveThis);
}
void KisNodeManager::toggleIsolateActiveNode()
{
KisImageWSP image = m_d->view->image();
KisNodeSP activeNode = this->activeNode();
KIS_ASSERT_RECOVER_RETURN(activeNode);
if (activeNode == image->isolatedModeRoot()) {
toggleIsolateMode(false);
} else {
toggleIsolateMode(true);
}
}
void KisNodeManager::toggleIsolateMode(bool checked)
{
KisImageWSP image = m_d->view->image();
KisNodeSP activeNode = this->activeNode();
if (checked && activeNode) {
// Transform and colorize masks don't have pixel data...
if (activeNode->inherits("KisTransformMask") ||
activeNode->inherits("KisColorizeMask")) return;
if (!image->startIsolatedMode(activeNode)) {
KisAction *action = m_d->view->actionManager()->actionByName("isolate_layer");
action->setChecked(false);
}
} else {
image->stopIsolatedMode();
}
m_d->lastRequestedIsolatedModeStatus = checked;
}
void KisNodeManager::slotUpdateIsolateModeActionImageStatusChange()
{
slotUpdateIsolateModeAction();
KisNodeSP isolatedRootNode = m_d->view->image()->isolatedModeRoot();
if (this->activeNode() &&
bool(isolatedRootNode) != m_d->lastRequestedIsolatedModeStatus) {
slotTryRestartIsolatedMode();
}
}
void KisNodeManager::slotUpdateIsolateModeAction()
{
KisAction *action = m_d->view->actionManager()->actionByName("isolate_layer");
Q_ASSERT(action);
KisNodeSP activeNode = this->activeNode();
KisNodeSP isolatedRootNode = m_d->view->image()->isolatedModeRoot();
action->setChecked(isolatedRootNode && isolatedRootNode == activeNode);
}
void KisNodeManager::slotTryRestartIsolatedMode()
{
KisNodeSP isolatedRootNode = m_d->view->image()->isolatedModeRoot();
if (!isolatedRootNode && !m_d->lastRequestedIsolatedModeStatus) return;
this->toggleIsolateMode(true);
}
KisNodeSP KisNodeManager::createNode(const QString & nodeType, bool quiet, KisPaintDeviceSP copyFrom)
{
if (!m_d->view->blockUntilOperationsFinished(m_d->view->image())) {
return 0;
}
KisNodeSP activeNode = this->activeNode();
if (!activeNode) {
activeNode = m_d->view->image()->root();
}
KIS_ASSERT_RECOVER_RETURN_VALUE(activeNode, 0);
// XXX: make factories for this kind of stuff,
// with a registry
if (nodeType == "KisPaintLayer") {
return m_d->layerManager.addPaintLayer(activeNode);
} else if (nodeType == "KisGroupLayer") {
return m_d->layerManager.addGroupLayer(activeNode);
} else if (nodeType == "KisAdjustmentLayer") {
return m_d->layerManager.addAdjustmentLayer(activeNode);
} else if (nodeType == "KisGeneratorLayer") {
return m_d->layerManager.addGeneratorLayer(activeNode);
} else if (nodeType == "KisShapeLayer") {
return m_d->layerManager.addShapeLayer(activeNode);
} else if (nodeType == "KisCloneLayer") {
return m_d->layerManager.addCloneLayer(activeNode);
} else if (nodeType == "KisTransparencyMask") {
return m_d->maskManager.createTransparencyMask(activeNode, copyFrom, false);
} else if (nodeType == "KisFilterMask") {
return m_d->maskManager.createFilterMask(activeNode, copyFrom, quiet, false);
} else if (nodeType == "KisColorizeMask") {
return m_d->maskManager.createColorizeMask(activeNode);
} else if (nodeType == "KisTransformMask") {
return m_d->maskManager.createTransformMask(activeNode);
} else if (nodeType == "KisSelectionMask") {
return m_d->maskManager.createSelectionMask(activeNode, copyFrom, false);
} else if (nodeType == "KisFileLayer") {
return m_d->layerManager.addFileLayer(activeNode);
}
return 0;
}
void KisNodeManager::createFromVisible()
{
KisLayerUtils::newLayerFromVisible(m_d->view->image(), m_d->view->image()->root()->lastChild());
}
void KisNodeManager::slotShowHideTimeline(bool value)
{
Q_FOREACH (KisNodeSP node, selectedNodes()) {
node->setUseInTimeline(value);
}
}
KisLayerSP KisNodeManager::createPaintLayer()
{
KisNodeSP node = createNode("KisPaintLayer");
return dynamic_cast(node.data());
}
void KisNodeManager::convertNode(const QString &nodeType)
{
if (!m_d->view->blockUntilOperationsFinished(m_d->view->image())) {
return;
}
KisNodeSP activeNode = this->activeNode();
if (!activeNode) return;
if (nodeType == "KisPaintLayer") {
m_d->layerManager.convertNodeToPaintLayer(activeNode);
} else if (nodeType == "KisSelectionMask" ||
nodeType == "KisFilterMask" ||
nodeType == "KisTransparencyMask") {
KisPaintDeviceSP copyFrom = activeNode->paintDevice() ?
activeNode->paintDevice() : activeNode->projection();
m_d->commandsAdapter.beginMacro(kundo2_i18n("Convert to a Selection Mask"));
bool result = false;
if (nodeType == "KisSelectionMask") {
result = !m_d->maskManager.createSelectionMask(activeNode, copyFrom, true).isNull();
} else if (nodeType == "KisFilterMask") {
result = !m_d->maskManager.createFilterMask(activeNode, copyFrom, false, true).isNull();
} else if (nodeType == "KisTransparencyMask") {
result = !m_d->maskManager.createTransparencyMask(activeNode, copyFrom, true).isNull();
}
m_d->commandsAdapter.endMacro();
if (!result) {
m_d->view->blockUntilOperationsFinishedForced(m_d->imageView->image());
m_d->commandsAdapter.undoLastCommand();
}
} else if (nodeType == "KisFileLayer") {
m_d->layerManager.convertLayerToFileLayer(activeNode);
} else {
warnKrita << "Unsupported node conversion type:" << nodeType;
}
}
void KisNodeManager::slotSomethingActivatedNodeImpl(KisNodeSP node)
{
KisDummiesFacadeBase *dummiesFacade = dynamic_cast(m_d->imageView->document()->shapeController());
KIS_SAFE_ASSERT_RECOVER_RETURN(dummiesFacade);
const bool nodeVisible = !isNodeHidden(node, !m_d->nodeDisplayModeAdapter->showGlobalSelectionMask());
if (!nodeVisible) {
return;
}
KIS_ASSERT_RECOVER_RETURN(node != activeNode());
if (m_d->activateNodeImpl(node)) {
emit sigUiNeedChangeActiveNode(node);
emit sigNodeActivated(node);
nodesUpdated();
if (node) {
bool toggled = m_d->view->actionCollection()->action("view_show_canvas_only")->isChecked();
if (toggled) {
m_d->view->showFloatingMessage( activeLayer()->name(), QIcon(), 1600, KisFloatingMessage::Medium, Qt::TextSingleLine);
}
}
}
}
void KisNodeManager::slotNonUiActivatedNode(KisNodeSP node)
{
// the node must still be in the graph, some asynchronous
// signals may easily break this requirement
if (node && !node->graphListener()) {
node = 0;
}
if (node == activeNode()) return;
slotSomethingActivatedNodeImpl(node);
if (node) {
bool toggled = m_d->view->actionCollection()->action("view_show_canvas_only")->isChecked();
if (toggled) {
m_d->view->showFloatingMessage( activeLayer()->name(), QIcon(), 1600, KisFloatingMessage::Medium, Qt::TextSingleLine);
}
}
}
void KisNodeManager::slotUiActivatedNode(KisNodeSP node)
{
// the node must still be in the graph, some asynchronous
// signals may easily break this requirement
if (node && !node->graphListener()) {
node = 0;
}
if (node) {
QStringList vectorTools = QStringList()
<< "InteractionTool"
<< "KarbonPatternTool"
<< "KarbonGradientTool"
<< "KarbonCalligraphyTool"
<< "CreateShapesTool"
<< "PathTool";
QStringList pixelTools = QStringList()
<< "KritaShape/KisToolBrush"
<< "KritaShape/KisToolDyna"
<< "KritaShape/KisToolMultiBrush"
<< "KritaFill/KisToolFill"
<< "KritaFill/KisToolGradient";
KisSelectionMask *selectionMask = dynamic_cast(node.data());
const bool nodeHasVectorAbilities = node->inherits("KisShapeLayer") ||
(selectionMask && selectionMask->selection()->hasShapeSelection());
if (nodeHasVectorAbilities) {
if (pixelTools.contains(KoToolManager::instance()->activeToolId())) {
KoToolManager::instance()->switchToolRequested("InteractionTool");
}
}
else {
if (vectorTools.contains(KoToolManager::instance()->activeToolId())) {
KoToolManager::instance()->switchToolRequested("KritaShape/KisToolBrush");
}
}
}
if (node == activeNode()) return;
slotSomethingActivatedNodeImpl(node);
}
void KisNodeManager::nodesUpdated()
{
KisNodeSP node = activeNode();
if (!node) return;
m_d->layerManager.layersUpdated();
m_d->maskManager.masksUpdated();
m_d->view->updateGUI();
m_d->view->selectionManager()->selectionChanged();
{
KisSignalsBlocker b(m_d->showInTimeline);
m_d->showInTimeline->setChecked(node->useInTimeline());
}
}
KisPaintDeviceSP KisNodeManager::activePaintDevice()
{
return m_d->maskManager.activeMask() ?
m_d->maskManager.activeDevice() :
m_d->layerManager.activeDevice();
}
void KisNodeManager::nodeProperties(KisNodeSP node)
{
if ((selectedNodes().size() > 1 && node->inherits("KisLayer")) || node->inherits("KisLayer")) {
m_d->layerManager.layerProperties();
}
else if (node->inherits("KisMask")) {
m_d->maskManager.maskProperties();
}
}
+void KisNodeManager::changeCloneSource()
+{
+ m_d->layerManager.changeCloneSource();
+}
+
qint32 KisNodeManager::convertOpacityToInt(qreal opacity)
{
/**
* Scales opacity from the range 0...100
* to the integer range 0...255
*/
return qMin(255, int(opacity * 2.55 + 0.5));
}
void KisNodeManager::setNodeName(KisNodeSP node, const QString &name)
{
if (!node) return;
if (node->name() == name) return;
m_d->commandsAdapter.setNodeName(node, name);
}
void KisNodeManager::setNodeOpacity(KisNodeSP node, qint32 opacity)
{
if (!node) return;
if (node->opacity() == opacity) return;
m_d->commandsAdapter.setOpacity(node, opacity);
}
void KisNodeManager::setNodeCompositeOp(KisNodeSP node,
const KoCompositeOp* compositeOp)
{
if (!node) return;
if (node->compositeOp() == compositeOp) return;
m_d->commandsAdapter.setCompositeOp(node, compositeOp);
}
void KisNodeManager::slotImageRequestNodeReselection(KisNodeSP activeNode, const KisNodeList &selectedNodes)
{
if (activeNode) {
slotNonUiActivatedNode(activeNode);
}
if (!selectedNodes.isEmpty()) {
slotSetSelectedNodes(selectedNodes);
}
}
void KisNodeManager::slotSetSelectedNodes(const KisNodeList &nodes)
{
m_d->selectedNodes = nodes;
emit sigUiNeedChangeSelectedNodes(nodes);
}
KisNodeList KisNodeManager::selectedNodes()
{
return m_d->selectedNodes;
}
KisNodeSelectionAdapter* KisNodeManager::nodeSelectionAdapter() const
{
return m_d->nodeSelectionAdapter.data();
}
KisNodeInsertionAdapter* KisNodeManager::nodeInsertionAdapter() const
{
return m_d->nodeInsertionAdapter.data();
}
KisNodeDisplayModeAdapter *KisNodeManager::nodeDisplayModeAdapter() const
{
return m_d->nodeDisplayModeAdapter.data();
}
bool KisNodeManager::isNodeHidden(KisNodeSP node, bool isGlobalSelectionHidden)
{
if (dynamic_cast(node.data())) {
return true;
}
if (isGlobalSelectionHidden && dynamic_cast(node.data()) &&
(!node->parent() || !node->parent()->parent())) {
return true;
}
return false;
}
bool KisNodeManager::trySetNodeProperties(KisNodeSP node, KisImageSP image, KisBaseNode::PropertyList properties) const
{
const KisPaintLayer *paintLayer = dynamic_cast(node.data());
if (paintLayer) {
const auto onionSkinOn = KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::onionSkins, true);
if (properties.contains(onionSkinOn)) {
const KisPaintDeviceSP &paintDevice = paintLayer->paintDevice();
if (paintDevice && paintDevice->defaultPixel().opacityU8() == 255) {
m_d->view->showFloatingMessage(i18n("Onion skins require a layer with transparent background."), QIcon());
return false;
}
}
}
KisNodePropertyListCommand::setNodePropertiesNoUndo(node, image, properties);
return true;
}
void KisNodeManager::nodeOpacityChanged(qreal opacity)
{
KisNodeSP node = activeNode();
setNodeOpacity(node, convertOpacityToInt(opacity));
}
void KisNodeManager::nodeCompositeOpChanged(const KoCompositeOp* op)
{
KisNodeSP node = activeNode();
setNodeCompositeOp(node, op);
}
void KisNodeManager::duplicateActiveNode()
{
KUndo2MagicString actionName = kundo2_i18n("Duplicate Nodes");
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
juggler->duplicateNode(selectedNodes());
}
KisNodeJugglerCompressed* KisNodeManager::Private::lazyGetJuggler(const KUndo2MagicString &actionName)
{
KisImageWSP image = view->image();
if (!nodeJuggler ||
(nodeJuggler &&
(nodeJuggler->isEnded() ||
!nodeJuggler->canMergeAction(actionName)))) {
nodeJuggler = new KisNodeJugglerCompressed(actionName, image, q, 750);
nodeJuggler->setAutoDelete(true);
}
return nodeJuggler;
}
void KisNodeManager::raiseNode()
{
KUndo2MagicString actionName = kundo2_i18n("Raise Nodes");
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
juggler->raiseNode(selectedNodes());
}
void KisNodeManager::lowerNode()
{
KUndo2MagicString actionName = kundo2_i18n("Lower Nodes");
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
juggler->lowerNode(selectedNodes());
}
void KisNodeManager::removeSingleNode(KisNodeSP node)
{
if (!node || !node->parent()) {
return;
}
KisNodeList nodes;
nodes << node;
removeSelectedNodes(nodes);
}
void KisNodeManager::removeSelectedNodes(KisNodeList nodes)
{
KUndo2MagicString actionName = kundo2_i18n("Remove Nodes");
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
juggler->removeNode(nodes);
}
void KisNodeManager::removeNode()
{
removeSelectedNodes(selectedNodes());
}
void KisNodeManager::mirrorNodeX()
{
KisNodeSP node = activeNode();
KUndo2MagicString commandName;
if (node->inherits("KisLayer")) {
commandName = kundo2_i18n("Mirror Layer X");
} else if (node->inherits("KisMask")) {
commandName = kundo2_i18n("Mirror Mask X");
}
mirrorNode(node, commandName, Qt::Horizontal, m_d->view->selection());
}
void KisNodeManager::mirrorNodeY()
{
KisNodeSP node = activeNode();
KUndo2MagicString commandName;
if (node->inherits("KisLayer")) {
commandName = kundo2_i18n("Mirror Layer Y");
} else if (node->inherits("KisMask")) {
commandName = kundo2_i18n("Mirror Mask Y");
}
mirrorNode(node, commandName, Qt::Vertical, m_d->view->selection());
}
void KisNodeManager::mirrorAllNodesX()
{
KisNodeSP node = m_d->view->image()->root();
mirrorNode(node, kundo2_i18n("Mirror All Layers X"),
Qt::Vertical, m_d->view->selection());
}
void KisNodeManager::mirrorAllNodesY()
{
KisNodeSP node = m_d->view->image()->root();
mirrorNode(node, kundo2_i18n("Mirror All Layers Y"),
Qt::Vertical, m_d->view->selection());
}
void KisNodeManager::activateNextNode()
{
KisNodeSP activeNode = this->activeNode();
if (!activeNode) return;
KisNodeSP node = activeNode->nextSibling();
while (node && node->childCount() > 0) {
node = node->firstChild();
}
if (!node && activeNode->parent() && activeNode->parent()->parent()) {
node = activeNode->parent();
}
while(node && isNodeHidden(node, true)) {
node = node->nextSibling();
}
if (node) {
slotNonUiActivatedNode(node);
}
}
void KisNodeManager::activatePreviousNode()
{
KisNodeSP activeNode = this->activeNode();
if (!activeNode) return;
KisNodeSP node;
if (activeNode->childCount() > 0) {
node = activeNode->lastChild();
}
else {
node = activeNode->prevSibling();
}
while (!node && activeNode->parent()) {
node = activeNode->parent()->prevSibling();
activeNode = activeNode->parent();
}
while(node && isNodeHidden(node, true)) {
node = node->prevSibling();
}
if (node) {
slotNonUiActivatedNode(node);
}
}
void KisNodeManager::switchToPreviouslyActiveNode()
{
if (m_d->previouslyActiveNode && m_d->previouslyActiveNode->parent()) {
slotNonUiActivatedNode(m_d->previouslyActiveNode);
}
}
void KisNodeManager::mirrorNode(KisNodeSP node,
const KUndo2MagicString& actionName,
Qt::Orientation orientation,
KisSelectionSP selection)
{
KisImageSignalVector emitSignals;
emitSignals << ModifiedSignal;
KisProcessingApplicator applicator(m_d->view->image(), node,
KisProcessingApplicator::RECURSIVE,
emitSignals, actionName);
KisProcessingVisitorSP visitor;
if (selection) {
visitor = new KisMirrorProcessingVisitor(selection, orientation);
} else {
visitor = new KisMirrorProcessingVisitor(m_d->view->image()->bounds(), orientation);
}
if (!selection) {
applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT);
} else {
applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
}
applicator.end();
nodesUpdated();
}
void KisNodeManager::Private::saveDeviceAsImage(KisPaintDeviceSP device,
const QString &defaultName,
const QRect &bounds,
qreal xRes,
qreal yRes,
quint8 opacity)
{
KoFileDialog dialog(view->mainWindow(), KoFileDialog::SaveFile, "savenodeasimage");
dialog.setCaption(i18n("Export \"%1\"", defaultName));
dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
dialog.setMimeTypeFilters(KisImportExportManager::supportedMimeTypes(KisImportExportManager::Export));
QString filename = dialog.filename();
if (filename.isEmpty()) return;
QUrl url = QUrl::fromLocalFile(filename);
if (url.isEmpty()) return;
QString mimefilter = KisMimeDatabase::mimeTypeForFile(filename, false);
QScopedPointer doc(KisPart::instance()->createDocument());
KisImageSP dst = new KisImage(doc->createUndoStore(),
bounds.width(),
bounds.height(),
device->compositionSourceColorSpace(),
defaultName);
dst->setResolution(xRes, yRes);
doc->setCurrentImage(dst);
KisPaintLayer* paintLayer = new KisPaintLayer(dst, "paint device", opacity);
paintLayer->paintDevice()->makeCloneFrom(device, bounds);
dst->addNode(paintLayer, dst->rootLayer(), KisLayerSP(0));
dst->initialRefreshGraph();
if (!doc->exportDocumentSync(url, mimefilter.toLatin1())) {
QMessageBox::warning(0,
i18nc("@title:window", "Krita"),
i18n("Could not save the layer. %1", doc->errorMessage().toUtf8().data()),
QMessageBox::Ok);
}
}
void KisNodeManager::saveNodeAsImage()
{
KisNodeSP node = activeNode();
if (!node) {
warnKrita << "BUG: Save Node As Image was called without any node selected";
return;
}
KisImageWSP image = m_d->view->image();
QRect saveRect = image->bounds() | node->exactBounds();
m_d->saveDeviceAsImage(node->projection(),
node->name(),
saveRect,
image->xRes(), image->yRes(),
node->opacity());
}
#include "SvgWriter.h"
void KisNodeManager::saveVectorLayerAsImage()
{
KisShapeLayerSP shapeLayer = qobject_cast(activeNode().data());
if (!shapeLayer) {
return;
}
KoFileDialog dialog(m_d->view->mainWindow(), KoFileDialog::SaveFile, "savenodeasimage");
dialog.setCaption(i18nc("@title:window", "Export to SVG"));
dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
dialog.setMimeTypeFilters(QStringList() << "image/svg+xml", "image/svg+xml");
QString filename = dialog.filename();
if (filename.isEmpty()) return;
QUrl url = QUrl::fromLocalFile(filename);
if (url.isEmpty()) return;
const QSizeF sizeInPx = m_d->view->image()->bounds().size();
const QSizeF sizeInPt(sizeInPx.width() / m_d->view->image()->xRes(),
sizeInPx.height() / m_d->view->image()->yRes());
QList shapes = shapeLayer->shapes();
std::sort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex);
SvgWriter writer(shapes);
if (!writer.save(filename, sizeInPt, true)) {
QMessageBox::warning(qApp->activeWindow(), i18nc("@title:window", "Krita"), i18n("Could not save to svg: %1", filename));
}
}
void KisNodeManager::slotSplitAlphaIntoMask()
{
KisNodeSP node = activeNode();
// guaranteed by KisActionManager
KIS_ASSERT_RECOVER_RETURN(node->hasEditablePaintDevice());
KisPaintDeviceSP srcDevice = node->paintDevice();
const KoColorSpace *srcCS = srcDevice->colorSpace();
const QRect processRect =
srcDevice->exactBounds() |
srcDevice->defaultBounds()->bounds();
KisPaintDeviceSP selectionDevice =
new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
m_d->commandsAdapter.beginMacro(kundo2_i18n("Split Alpha into a Mask"));
KisTransaction transaction(kundo2_noi18n("__split_alpha_channel__"), srcDevice);
KisSequentialIterator srcIt(srcDevice, processRect);
KisSequentialIterator dstIt(selectionDevice, processRect);
while (srcIt.nextPixel() && dstIt.nextPixel()) {
quint8 *srcPtr = srcIt.rawData();
quint8 *alpha8Ptr = dstIt.rawData();
*alpha8Ptr = srcCS->opacityU8(srcPtr);
srcCS->setOpacity(srcPtr, OPACITY_OPAQUE_U8, 1);
}
m_d->commandsAdapter.addExtraCommand(transaction.endAndTake());
createNode("KisTransparencyMask", false, selectionDevice);
m_d->commandsAdapter.endMacro();
}
void KisNodeManager::Private::mergeTransparencyMaskAsAlpha(bool writeToLayers)
{
KisNodeSP node = q->activeNode();
KisNodeSP parentNode = node->parent();
// guaranteed by KisActionManager
KIS_ASSERT_RECOVER_RETURN(node->inherits("KisTransparencyMask"));
if (writeToLayers && !parentNode->hasEditablePaintDevice()) {
QMessageBox::information(view->mainWindow(),
i18nc("@title:window", "Layer %1 is not editable", parentNode->name()),
i18n("Cannot write alpha channel of "
"the parent layer \"%1\".\n"
"The operation will be cancelled.", parentNode->name()));
return;
}
KisPaintDeviceSP dstDevice;
if (writeToLayers) {
KIS_ASSERT_RECOVER_RETURN(parentNode->paintDevice());
dstDevice = parentNode->paintDevice();
} else {
KisPaintDeviceSP copyDevice = parentNode->paintDevice();
if (!copyDevice) {
copyDevice = parentNode->original();
}
dstDevice = new KisPaintDevice(*copyDevice);
}
const KoColorSpace *dstCS = dstDevice->colorSpace();
KisPaintDeviceSP selectionDevice = node->paintDevice();
KIS_ASSERT_RECOVER_RETURN(selectionDevice->colorSpace()->pixelSize() == 1);
const QRect processRect =
selectionDevice->exactBounds() |
dstDevice->exactBounds() |
selectionDevice->defaultBounds()->bounds();
QScopedPointer transaction;
if (writeToLayers) {
commandsAdapter.beginMacro(kundo2_i18n("Write Alpha into a Layer"));
transaction.reset(new KisTransaction(kundo2_noi18n("__write_alpha_channel__"), dstDevice));
}
KisSequentialIterator srcIt(selectionDevice, processRect);
KisSequentialIterator dstIt(dstDevice, processRect);
while (srcIt.nextPixel() && dstIt.nextPixel()) {
quint8 *alpha8Ptr = srcIt.rawData();
quint8 *dstPtr = dstIt.rawData();
dstCS->setOpacity(dstPtr, *alpha8Ptr, 1);
}
if (writeToLayers) {
commandsAdapter.addExtraCommand(transaction->endAndTake());
commandsAdapter.removeNode(node);
commandsAdapter.endMacro();
} else {
KisImageWSP image = view->image();
QRect saveRect = image->bounds();
saveDeviceAsImage(dstDevice, parentNode->name(),
saveRect,
image->xRes(), image->yRes(),
OPACITY_OPAQUE_U8);
}
}
void KisNodeManager::slotSplitAlphaWrite()
{
m_d->mergeTransparencyMaskAsAlpha(true);
}
void KisNodeManager::slotSplitAlphaSaveMerged()
{
m_d->mergeTransparencyMaskAsAlpha(false);
}
void KisNodeManager::toggleLock()
{
KisNodeList nodes = this->selectedNodes();
KisNodeSP active = activeNode();
if (nodes.isEmpty() || !active) return;
bool isLocked = active->userLocked();
for (auto &node : nodes) {
node->setUserLocked(!isLocked);
}
}
void KisNodeManager::toggleVisibility()
{
KisNodeList nodes = this->selectedNodes();
KisNodeSP active = activeNode();
if (nodes.isEmpty() || !active) return;
bool isVisible = active->visible();
for (auto &node : nodes) {
node->setVisible(!isVisible);
node->setDirty();
}
}
void KisNodeManager::toggleAlphaLock()
{
KisNodeList nodes = this->selectedNodes();
KisNodeSP active = activeNode();
if (nodes.isEmpty() || !active) return;
auto layer = qobject_cast(active.data());
if (!layer) {
return;
}
bool isAlphaLocked = layer->alphaLocked();
for (auto &node : nodes) {
auto layer = qobject_cast(node.data());
if (layer) {
layer->setAlphaLocked(!isAlphaLocked);
}
}
}
void KisNodeManager::toggleInheritAlpha()
{
KisNodeList nodes = this->selectedNodes();
KisNodeSP active = activeNode();
if (nodes.isEmpty() || !active) return;
auto layer = qobject_cast(active.data());
if (!layer) {
return;
}
bool isAlphaDisabled = layer->alphaChannelDisabled();
for (auto &node : nodes) {
auto layer = qobject_cast(node.data());
if (layer) {
layer->disableAlphaChannel(!isAlphaDisabled);
node->setDirty();
}
}
}
void KisNodeManager::cutLayersToClipboard()
{
KisNodeList nodes = this->selectedNodes();
if (nodes.isEmpty()) return;
KisClipboard::instance()->setLayers(nodes, m_d->view->image(), false);
KUndo2MagicString actionName = kundo2_i18n("Cut Nodes");
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
juggler->removeNode(nodes);
}
void KisNodeManager::copyLayersToClipboard()
{
KisNodeList nodes = this->selectedNodes();
KisClipboard::instance()->setLayers(nodes, m_d->view->image(), true);
}
void KisNodeManager::pasteLayersFromClipboard()
{
const QMimeData *data = KisClipboard::instance()->layersMimeData();
if (!data) return;
KisNodeSP activeNode = this->activeNode();
KisShapeController *shapeController = dynamic_cast(m_d->imageView->document()->shapeController());
Q_ASSERT(shapeController);
KisDummiesFacadeBase *dummiesFacade = dynamic_cast(m_d->imageView->document()->shapeController());
Q_ASSERT(dummiesFacade);
const bool copyNode = false;
KisImageSP image = m_d->view->image();
KisNodeDummy *parentDummy = dummiesFacade->dummyForNode(activeNode);
KisNodeDummy *aboveThisDummy = parentDummy ? parentDummy->lastChild() : 0;
KisMimeData::insertMimeLayers(data,
image,
shapeController,
parentDummy,
aboveThisDummy,
copyNode,
nodeInsertionAdapter());
}
void KisNodeManager::createQuickGroupImpl(KisNodeJugglerCompressed *juggler,
const QString &overrideGroupName,
KisNodeSP *newGroup,
KisNodeSP *newLastChild)
{
KisNodeSP active = activeNode();
if (!active) return;
KisImageSP image = m_d->view->image();
QString groupName = !overrideGroupName.isEmpty() ? overrideGroupName : image->nextLayerName();
KisGroupLayerSP group = new KisGroupLayer(image.data(), groupName, OPACITY_OPAQUE_U8);
KisNodeList nodes1;
nodes1 << group;
KisNodeList nodes2;
nodes2 = KisLayerUtils::sortMergableNodes(image->root(), selectedNodes());
KisLayerUtils::filterMergableNodes(nodes2);
if (nodes2.size() == 0) return;
if (KisLayerUtils::checkIsChildOf(active, nodes2)) {
active = nodes2.first();
}
KisNodeSP parent = active->parent();
KisNodeSP aboveThis = active;
juggler->addNode(nodes1, parent, aboveThis);
juggler->moveNode(nodes2, group, 0);
*newGroup = group;
*newLastChild = nodes2.last();
}
void KisNodeManager::createQuickGroup()
{
KUndo2MagicString actionName = kundo2_i18n("Quick Group");
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
KisNodeSP parent;
KisNodeSP above;
createQuickGroupImpl(juggler, "", &parent, &above);
}
void KisNodeManager::createQuickClippingGroup()
{
KUndo2MagicString actionName = kundo2_i18n("Quick Clipping Group");
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
KisNodeSP parent;
KisNodeSP above;
KisImageSP image = m_d->view->image();
createQuickGroupImpl(juggler, image->nextLayerName(i18nc("default name for a clipping group layer", "Clipping Group")), &parent, &above);
KisPaintLayerSP maskLayer = new KisPaintLayer(image.data(), i18nc("default name for quick clip group mask layer", "Mask Layer"), OPACITY_OPAQUE_U8, image->colorSpace());
maskLayer->disableAlphaChannel(true);
juggler->addNode(KisNodeList() << maskLayer, parent, above);
}
void KisNodeManager::quickUngroup()
{
KisNodeSP active = activeNode();
if (!active) return;
KisNodeSP parent = active->parent();
KisNodeSP aboveThis = active;
KUndo2MagicString actionName = kundo2_i18n("Quick Ungroup");
if (parent && dynamic_cast(active.data())) {
KisNodeList nodes = active->childNodes(QStringList(), KoProperties());
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
juggler->moveNode(nodes, parent, active);
juggler->removeNode(KisNodeList() << active);
} else if (parent && parent->parent()) {
KisNodeSP grandParent = parent->parent();
KisNodeList allChildNodes = parent->childNodes(QStringList(), KoProperties());
KisNodeList allSelectedNodes = selectedNodes();
const bool removeParent = KritaUtils::compareListsUnordered(allChildNodes, allSelectedNodes);
KisNodeJugglerCompressed *juggler = m_d->lazyGetJuggler(actionName);
juggler->moveNode(allSelectedNodes, grandParent, parent);
if (removeParent) {
juggler->removeNode(KisNodeList() << parent);
}
}
}
void KisNodeManager::selectLayersImpl(const KoProperties &props, const KoProperties &invertedProps)
{
KisImageSP image = m_d->view->image();
KisNodeList nodes = KisLayerUtils::findNodesWithProps(image->root(), props, true);
KisNodeList selectedNodes = this->selectedNodes();
if (KritaUtils::compareListsUnordered(nodes, selectedNodes)) {
nodes = KisLayerUtils::findNodesWithProps(image->root(), invertedProps, true);
}
if (!nodes.isEmpty()) {
slotImageRequestNodeReselection(nodes.last(), nodes);
}
}
void KisNodeManager::selectAllNodes()
{
KoProperties props;
selectLayersImpl(props, props);
}
void KisNodeManager::selectVisibleNodes()
{
KoProperties props;
props.setProperty("visible", true);
KoProperties invertedProps;
invertedProps.setProperty("visible", false);
selectLayersImpl(props, invertedProps);
}
void KisNodeManager::selectLockedNodes()
{
KoProperties props;
props.setProperty("locked", true);
KoProperties invertedProps;
invertedProps.setProperty("locked", false);
selectLayersImpl(props, invertedProps);
}
void KisNodeManager::selectInvisibleNodes()
{
KoProperties props;
props.setProperty("visible", false);
KoProperties invertedProps;
invertedProps.setProperty("visible", true);
selectLayersImpl(props, invertedProps);
}
void KisNodeManager::selectUnlockedNodes()
{
KoProperties props;
props.setProperty("locked", false);
KoProperties invertedProps;
invertedProps.setProperty("locked", true);
selectLayersImpl(props, invertedProps);
}
diff --git a/libs/ui/kis_node_manager.h b/libs/ui/kis_node_manager.h
index 9848bbdf4f..2686269ce7 100644
--- a/libs/ui/kis_node_manager.h
+++ b/libs/ui/kis_node_manager.h
@@ -1,269 +1,271 @@
/*
* Copyright (C) 2007 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_NODE_MANAGER
#define KIS_NODE_MANAGER
#include
#include
#include "kis_types.h"
#include "kis_base_node.h"
#include
class KActionCollection;
class KoCompositeOp;
class KoColorSpace;
class KUndo2MagicString;
class KisFilterStrategy;
class KisViewManager;
class KisActionManager;
class KisView;
class KisNodeSelectionAdapter;
class KisNodeInsertionAdapter;
class KisNodeDisplayModeAdapter;
class KisNodeJugglerCompressed;
class KoProperties;
/**
* The node manager passes requests for new layers or masks on to the mask and layer
* managers.
*/
class KRITAUI_EXPORT KisNodeManager : public QObject
{
Q_OBJECT
public:
KisNodeManager(KisViewManager * view);
~KisNodeManager() override;
void setView(QPointerimageView);
Q_SIGNALS:
/// emitted whenever a node is selected.
void sigNodeActivated(KisNodeSP node);
/// emitted whenever a different layer is selected.
void sigLayerActivated(KisLayerSP layer);
/// for the layer box: this sets the current node in the layerbox
/// without telling the node manager that the node is activated,
/// preventing loops (I think...)
void sigUiNeedChangeActiveNode(KisNodeSP node);
void sigUiNeedChangeSelectedNodes(const KisNodeList &nodes);
public:
void setup(KActionCollection * collection, KisActionManager* actionManager);
void updateGUI();
/// Convenience function to get the active layer or mask
KisNodeSP activeNode();
/// convenience function to get the active layer. If a mask is
/// active, it's parent layer is the active layer.
KisLayerSP activeLayer();
/// Get the paint device the user wants to paint on now
KisPaintDeviceSP activePaintDevice();
/**
* @return the active color space used for composition, meaning the color space
* of the active mask, or the color space of the parent of the active layer
*/
const KoColorSpace* activeColorSpace();
/**
* Sets the name for the node in a universal way (masks/layers)
*/
void setNodeName(KisNodeSP node, const QString &name);
/**
* Sets opacity for the node in a universal way (masks/layers)
*/
void setNodeOpacity(KisNodeSP node, qint32 opacity);
/**
* Sets compositeOp for the node in a universal way (masks/layers)
*/
void setNodeCompositeOp(KisNodeSP node, const KoCompositeOp* compositeOp);
KisNodeList selectedNodes();
KisNodeSelectionAdapter* nodeSelectionAdapter() const;
KisNodeInsertionAdapter* nodeInsertionAdapter() const;
KisNodeDisplayModeAdapter* nodeDisplayModeAdapter() const;
static bool isNodeHidden(KisNodeSP node, bool isGlobalSelectionHidden);
bool trySetNodeProperties(KisNodeSP node, KisImageSP image, KisBaseNode::PropertyList properties) const;
public Q_SLOTS:
/**
* Explicitly activates \p node
* The UI will be noticed that active node has been changed.
* Both sigNodeActivated and sigUiNeedChangeActiveNode are emitted.
*
* WARNING: normally you needn't call this method manually. It is
* automatically called when a node is added to the graph. If you
* have some special cases when you need to activate a node, consider
* adding them to KisDummiesFacadeBase instead. Calling this method
* directly should be the last resort.
*
* \see slotUiActivatedNode for comparison
*/
void slotNonUiActivatedNode(KisNodeSP node);
/**
* Activates \p node.
* All non-ui listeners are notified with sigNodeActivated,
* sigUiNeedChangeActiveNode is *not* emitted.
*
* \see activateNode
*/
void slotUiActivatedNode(KisNodeSP node);
/**
* Adds a list of nodes without searching appropriate position for
* it. You *must* ensure that the nodes are allowed to be added
* to the parent, otherwise you'll get an assert.
*/
void addNodesDirect(KisNodeList nodes, KisNodeSP parent, KisNodeSP aboveThis);
/**
* Moves a list of nodes without searching appropriate position
* for it. You *must* ensure that the nodes are allowed to be
* added to the parent, otherwise you'll get an assert.
*/
void moveNodesDirect(KisNodeList nodes, KisNodeSP parent, KisNodeSP aboveThis);
/**
* Copies a list of nodes without searching appropriate position
* for it. You *must* ensure that the nodes are allowed to be
* added to the parent, otherwise you'll get an assert.
*/
void copyNodesDirect(KisNodeList nodes, KisNodeSP parent, KisNodeSP aboveThis);
/**
* Create new layer from actually visible
*/
void createFromVisible();
void slotShowHideTimeline(bool value);
void toggleIsolateActiveNode();
void toggleIsolateMode(bool checked);
void slotUpdateIsolateModeActionImageStatusChange();
void slotUpdateIsolateModeAction();
void slotTryRestartIsolatedMode();
void moveNodeAt(KisNodeSP node, KisNodeSP parent, int index);
KisNodeSP createNode(const QString& nodeType, bool quiet = false, KisPaintDeviceSP copyFrom = 0);
void convertNode(const QString &nodeType);
void nodesUpdated();
void nodeProperties(KisNodeSP node);
+ /// pop up a window for changing the source of the selected Clone Layers
+ void changeCloneSource();
void nodeOpacityChanged(qreal opacity);
void nodeCompositeOpChanged(const KoCompositeOp* op);
void duplicateActiveNode();
void removeNode();
void mirrorNodeX();
void mirrorNodeY();
void mirrorAllNodesX();
void mirrorAllNodesY();
void mirrorNode(KisNodeSP node, const KUndo2MagicString& commandName, Qt::Orientation orientation, KisSelectionSP selection);
void activateNextNode();
void activatePreviousNode();
void switchToPreviouslyActiveNode();
/**
* move the active node up the nodestack.
*/
void raiseNode();
/**
* move the active node down the nodestack
*/
void lowerNode();
void saveNodeAsImage();
void saveVectorLayerAsImage();
void slotSplitAlphaIntoMask();
void slotSplitAlphaWrite();
void slotSplitAlphaSaveMerged();
void toggleLock();
void toggleVisibility();
void toggleAlphaLock();
void toggleInheritAlpha();
/**
* @brief slotSetSelectedNodes set the list of nodes selected in the layerbox. Selected nodes are not necessarily active nodes.
* @param nodes the selected nodes
*/
void slotSetSelectedNodes(const KisNodeList &nodes);
void slotImageRequestNodeReselection(KisNodeSP activeNode, const KisNodeList &selectedNodes);
void cutLayersToClipboard();
void copyLayersToClipboard();
void pasteLayersFromClipboard();
void createQuickGroup();
void createQuickClippingGroup();
void quickUngroup();
void selectAllNodes();
void selectVisibleNodes();
void selectLockedNodes();
void selectInvisibleNodes();
void selectUnlockedNodes();
public:
void removeSingleNode(KisNodeSP node);
KisLayerSP createPaintLayer();
private:
/**
* Scales opacity from the range 0...1
* to the integer range 0...255
*/
qint32 convertOpacityToInt(qreal opacity);
void removeSelectedNodes(KisNodeList selectedNodes);
void slotSomethingActivatedNodeImpl(KisNodeSP node);
void createQuickGroupImpl(KisNodeJugglerCompressed *juggler,
const QString &overrideGroupName,
KisNodeSP *newGroup,
KisNodeSP *newLastChild);
void selectLayersImpl(const KoProperties &props, const KoProperties &invertedProps);
struct Private;
Private * const m_d;
};
#endif
diff --git a/plugins/dockers/layerdocker/LayerBox.cpp b/plugins/dockers/layerdocker/LayerBox.cpp
index f425e3c3dc..aebf3caca7 100644
--- a/plugins/dockers/layerdocker/LayerBox.cpp
+++ b/plugins/dockers/layerdocker/LayerBox.cpp
@@ -1,1081 +1,1101 @@
/*
* LayerBox.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 "LayerBox.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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 "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 "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_selection.h"
#include "kis_processing_applicator.h"
#include "commands/kis_set_global_selection_command.h"
#include "KisSelectionActionsAdapter.h"
#include "kis_layer_utils.h"
#include "ui_WdgLayerBox.h"
#include "NodeView.h"
#include "SyncButtonAndAction.h"
class LayerBoxStyle : public QProxyStyle
{
public:
LayerBoxStyle(QStyle *baseStyle = 0) : QProxyStyle(baseStyle) {}
void drawPrimitive(PrimitiveElement element, const QStyleOption *option,
QPainter *painter, const QWidget *widget) const
{
if (element == QStyle::PE_IndicatorItemViewItemDrop)
{
QColor color(widget->palette().color(QPalette::Highlight).lighter());
if (option->rect.height() == 0) {
QBrush brush(color);
QRect r(option->rect);
r.setTop(r.top() - 2);
r.setBottom(r.bottom() + 2);
painter->fillRect(r, brush);
} else {
color.setAlpha(200);
QBrush brush(color);
painter->fillRect(option->rect, brush);
}
}
else
{
QProxyStyle::drawPrimitive(element, option, painter, widget);
}
}
};
inline void LayerBox::connectActionToButton(KisViewManager* viewManager, QAbstractButton *button, const QString &id)
{
if (!viewManager || !button) return;
KisAction *action = viewManager->actionManager()->actionByName(id);
if (!action) return;
connect(button, SIGNAL(clicked()), action, SLOT(trigger()));
connect(action, SIGNAL(sigEnableSlaves(bool)), button, SLOT(setEnabled(bool)));
connect(viewManager->mainWindow(), SIGNAL(themeChanged()), this, SLOT(slotUpdateIcons()));
}
inline void LayerBox::addActionToMenu(QMenu *menu, const QString &id)
{
if (m_canvas) {
menu->addAction(m_canvas->viewManager()->actionManager()->actionByName(id));
}
}
LayerBox::LayerBox()
: QDockWidget(i18n("Layers"))
, m_canvas(0)
, m_wdgLayerBox(new Ui_WdgLayerBox)
, m_thumbnailCompressor(500, KisSignalCompressor::FIRST_INACTIVE)
, m_colorLabelCompressor(900, KisSignalCompressor::FIRST_INACTIVE)
, m_thumbnailSizeCompressor(100, KisSignalCompressor::FIRST_INACTIVE)
{
KisConfig cfg(false);
QWidget* mainWidget = new QWidget(this);
setWidget(mainWidget);
m_opacityDelayTimer.setSingleShot(true);
m_wdgLayerBox->setupUi(mainWidget);
m_wdgLayerBox->listLayers->setStyle(new LayerBoxStyle(m_wdgLayerBox->listLayers->style()));
connect(m_wdgLayerBox->listLayers,
SIGNAL(contextMenuRequested(QPoint,QModelIndex)),
this, SLOT(slotContextMenuRequested(QPoint,QModelIndex)));
connect(m_wdgLayerBox->listLayers,
SIGNAL(collapsed(QModelIndex)), SLOT(slotCollapsed(QModelIndex)));
connect(m_wdgLayerBox->listLayers,
SIGNAL(expanded(QModelIndex)), SLOT(slotExpanded(QModelIndex)));
connect(m_wdgLayerBox->listLayers,
SIGNAL(selectionChanged(QModelIndexList)), SLOT(selectionChanged(QModelIndexList)));
slotUpdateIcons();
m_wdgLayerBox->bnDelete->setIconSize(QSize(22, 22));
m_wdgLayerBox->bnRaise->setIconSize(QSize(22, 22));
m_wdgLayerBox->bnLower->setIconSize(QSize(22, 22));
m_wdgLayerBox->bnProperties->setIconSize(QSize(22, 22));
m_wdgLayerBox->bnDuplicate->setIconSize(QSize(22, 22));
m_wdgLayerBox->bnLower->setEnabled(false);
m_wdgLayerBox->bnRaise->setEnabled(false);
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_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(QModelIndex,int,int)), SLOT(updateUI()));
connect(m_nodeModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT(updateUI()));
connect(m_nodeModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), SLOT(updateUI()));
connect(m_nodeModel, SIGNAL(dataChanged(QModelIndex,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->setActivationFlags(KisAction::ACTIVE_IMAGE);
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(QModelIndex,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, &LayerBox::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()));
// set up the configure menu for changing thumbnail size
QMenu* configureMenu = new QMenu(this);
configureMenu->setStyleSheet("margin: 6px");
configureMenu->addSection(i18n("Thumbnail Size"));
m_wdgLayerBox->configureLayerDockerToolbar->setMenu(configureMenu);
m_wdgLayerBox->configureLayerDockerToolbar->setIcon(KisIconUtils::loadIcon("configure"));
m_wdgLayerBox->configureLayerDockerToolbar->setPopupMode(QToolButton::InstantPopup);
// add horizontal slider
thumbnailSizeSlider = new QSlider(this);
thumbnailSizeSlider->setOrientation(Qt::Horizontal);
thumbnailSizeSlider->setRange(20, 80);
thumbnailSizeSlider->setValue(cfg.layerThumbnailSize(false)); // grab this from the kritarc
thumbnailSizeSlider->setMinimumHeight(20);
thumbnailSizeSlider->setMinimumWidth(40);
thumbnailSizeSlider->setTickInterval(5);
QWidgetAction *sliderAction= new QWidgetAction(this);
sliderAction->setDefaultWidget(thumbnailSizeSlider);
configureMenu->addAction(sliderAction);
connect(thumbnailSizeSlider, SIGNAL(sliderMoved(int)), &m_thumbnailSizeCompressor, SLOT(start()));
connect(&m_thumbnailSizeCompressor, SIGNAL(timeout()), SLOT(slotUpdateThumbnailIconSize()));
+
}
LayerBox::~LayerBox()
{
delete m_wdgLayerBox;
}
void expandNodesRecursively(KisNodeSP root, QPointer filteringModel, NodeView *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->collapsed() && node->childCount() > 0) {
expandNodesRecursively(node, filteringModel, nodeView);
}
node = node->nextSibling();
}
nodeView->blockSignals(false);
}
void LayerBox::slotAddLayerBnClicked()
{
if (m_canvas) {
KisNodeList nodes = m_nodeManager->selectedNodes();
if (nodes.size() == 1) {
KisAction *action = m_canvas->viewManager()->actionManager()->actionByName("add_new_paint_layer");
action->trigger();
} else {
KisAction *action = m_canvas->viewManager()->actionManager()->actionByName("create_quick_group");
action->trigger();
}
}
}
void LayerBox::setViewManager(KisViewManager* kisview)
{
m_nodeManager = kisview->nodeManager();
Q_FOREACH (KisAction *action, m_actions) {
kisview->actionManager()->
addAction(action->objectName(),
action);
}
connect(m_wdgLayerBox->bnAdd, SIGNAL(clicked()), this, SLOT(slotAddLayerBnClicked()));
connectActionToButton(kisview, m_wdgLayerBox->bnDuplicate, "duplicatelayer");
KisActionManager *actionManager = kisview->actionManager();
KisAction *action = actionManager->createAction("RenameCurrentLayer");
Q_ASSERT(action);
connect(action, SIGNAL(triggered()), this, SLOT(slotRenameCurrentNode()));
m_propertiesAction = actionManager->createAction("layer_properties");
Q_ASSERT(m_propertiesAction);
new SyncButtonAndAction(m_propertiesAction, m_wdgLayerBox->bnProperties, this);
connect(m_propertiesAction, SIGNAL(triggered()), this, SLOT(slotPropertiesClicked()));
m_removeAction = actionManager->createAction("remove_layer");
Q_ASSERT(m_removeAction);
new SyncButtonAndAction(m_removeAction, m_wdgLayerBox->bnDelete, this);
connect(m_removeAction, SIGNAL(triggered()), this, SLOT(slotRmClicked()));
action = actionManager->createAction("move_layer_up");
Q_ASSERT(action);
new SyncButtonAndAction(action, m_wdgLayerBox->bnRaise, this);
connect(action, SIGNAL(triggered()), this, SLOT(slotRaiseClicked()));
action = actionManager->createAction("move_layer_down");
Q_ASSERT(action);
new SyncButtonAndAction(action, m_wdgLayerBox->bnLower, this);
connect(action, SIGNAL(triggered()), this, SLOT(slotLowerClicked()));
+
+ m_changeCloneSourceAction = actionManager->createAction("set-copy-from");
+ Q_ASSERT(m_changeCloneSourceAction);
+ connect(m_changeCloneSourceAction, &KisAction::triggered,
+ this, &LayerBox::slotChangeCloneSourceClicked);
}
void LayerBox::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);
m_selectionActionsAdapter.reset();
if (m_image) {
KisImageAnimationInterface *animation = m_image->animationInterface();
animation->disconnect(this);
}
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_selectionActionsAdapter.reset(new KisSelectionActionsAdapter(m_canvas->viewManager()->selectionManager()));
m_nodeModel->setDummiesFacade(kritaDummiesFacade,
m_image,
kritaShapeController,
m_selectionActionsAdapter.data(),
m_nodeManager);
connect(m_image, SIGNAL(sigAboutToBeDeleted()), SLOT(notifyImageDeleted()));
connect(m_image, SIGNAL(sigNodeCollapsedChanged()), SLOT(slotNodeCollapsedChanged()));
// cold start
if (m_nodeManager) {
setCurrentNode(m_nodeManager->activeNode());
// Connection KisNodeManager -> LayerBox
connect(m_nodeManager, SIGNAL(sigUiNeedChangeActiveNode(KisNodeSP)),
this, SLOT(setCurrentNode(KisNodeSP)));
connect(m_nodeManager,
SIGNAL(sigUiNeedChangeSelectedNodes(QList)),
SLOT(slotNodeManagerChangedSelection(QList)));
}
else {
setCurrentNode(m_canvas->imageView()->currentNode());
}
// Connection LayerBox -> KisNodeManager (isolate layer)
connect(m_nodeModel, SIGNAL(toggleIsolateActiveNode()),
m_nodeManager, SLOT(toggleIsolateActiveNode()));
KisImageAnimationInterface *animation = m_image->animationInterface();
connect(animation, &KisImageAnimationInterface::sigUiTimeChanged, this, &LayerBox::slotImageTimeChanged);
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_colorize_mask");
addActionToMenu(m_newLayerMenu, "add_new_transform_mask");
addActionToMenu(m_newLayerMenu, "add_new_selection_mask");
}
}
void LayerBox::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 LayerBox::notifyImageDeleted()
{
setCanvas(0);
}
void LayerBox::updateUI()
{
if (!m_canvas) return;
if (!m_nodeManager) return;
KisNodeSP activeNode = m_nodeManager->activeNode();
if (activeNode != m_activeNode) {
if( !m_activeNode.isNull() )
m_activeNode->disconnect(this);
m_activeNode = activeNode;
if (activeNode) {
KisKeyframeChannel *opacityChannel = activeNode->getKeyframeChannel(KisKeyframeChannel::Opacity.id(), false);
if (opacityChannel) {
watchOpacityChannel(opacityChannel);
} else {
watchOpacityChannel(0);
connect(activeNode.data(), &KisNode::keyframeChannelAdded, this, &LayerBox::slotKeyframeChannelAdded);
}
}
}
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));
m_wdgLayerBox->cmbComposite->validate(m_image->colorSpace());
if (activeNode) {
if (activeNode->inherits("KisColorizeMask") ||
activeNode->inherits("KisLayer")) {
m_wdgLayerBox->doubleOpacity->setEnabled(true);
if (!m_wdgLayerBox->doubleOpacity->isDragging()) {
slotSetOpacity(activeNode->opacity() * 100.0 / 255);
}
const KoCompositeOp* compositeOp = activeNode->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);
} else if (activeNode->inherits("KisMask")) {
m_wdgLayerBox->cmbComposite->setEnabled(false);
m_wdgLayerBox->doubleOpacity->setEnabled(false);
}
}
}
/**
* This method is called *only* when non-GUI code requested the
* change of the current node
*/
void LayerBox::setCurrentNode(KisNodeSP node)
{
m_filteringModel->setActiveNode(node);
QModelIndex index = node ? m_filteringModel->indexFromNode(node) : QModelIndex();
m_filteringModel->setData(index, true, KisNodeModel::ActiveRole);
updateUI();
}
void LayerBox::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 LayerBox::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);
}
// range: 0-100
void LayerBox::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 LayerBox::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");
}
+ Q_FOREACH(KisNodeSP node, nodes) {
+ if (node && node->inherits("KisCloneLayer")) {
+ menu.addAction(m_changeCloneSourceAction);
+ break;
+ }
+ }
+
{
KisSignalsBlocker b(m_colorSelector);
m_colorSelector->setCurrentIndex(singleLayer ? activeNode->colorLabelIndex() : -1);
}
menu.addAction(m_colorSelectorAction);
menu.addSeparator();
addActionToMenu(&menu, "cut_layer_clipboard");
addActionToMenu(&menu, "copy_layer_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");
QMenu *groupMenu = menu.addMenu(i18n("&Group"));
addActionToMenu(groupMenu, "create_quick_group");
addActionToMenu(groupMenu, "create_quick_clipping_group");
addActionToMenu(groupMenu, "quick_ungroup");
QMenu *locksMenu = menu.addMenu(i18n("&Toggle Locks && Visibility"));
addActionToMenu(locksMenu, "toggle_layer_visibility");
addActionToMenu(locksMenu, "toggle_layer_lock");
addActionToMenu(locksMenu, "toggle_layer_inherit_alpha");
addActionToMenu(locksMenu, "toggle_layer_alpha_lock");
if (singleLayer) {
QMenu *addLayerMenu = menu.addMenu(i18n("&Add"));
addActionToMenu(addLayerMenu, "add_new_transparency_mask");
addActionToMenu(addLayerMenu, "add_new_filter_mask");
addActionToMenu(addLayerMenu, "add_new_colorize_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");
addActionToMenu(convertToMenu, "convert_to_file_layer");
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();
addActionToMenu(&menu, "show_in_timeline");
if (singleLayer) {
KisNodeSP node = m_filteringModel->nodeFromIndex(index);
if (node && !node->inherits("KisTransformMask")) {
addActionToMenu(&menu, "isolate_layer");
}
addActionToMenu(&menu, "selectopaque");
+
}
}
menu.exec(pos);
}
}
void LayerBox::slotMinimalView()
{
m_wdgLayerBox->listLayers->setDisplayMode(NodeView::MinimalMode);
}
void LayerBox::slotDetailedView()
{
m_wdgLayerBox->listLayers->setDisplayMode(NodeView::DetailedMode);
}
void LayerBox::slotThumbnailView()
{
m_wdgLayerBox->listLayers->setDisplayMode(NodeView::ThumbnailMode);
}
void LayerBox::slotRmClicked()
{
if (!m_canvas) return;
m_nodeManager->removeNode();
}
void LayerBox::slotRaiseClicked()
{
if (!m_canvas) return;
m_nodeManager->raiseNode();
}
void LayerBox::slotLowerClicked()
{
if (!m_canvas) return;
m_nodeManager->lowerNode();
}
void LayerBox::slotPropertiesClicked()
{
if (!m_canvas) return;
if (KisNodeSP active = m_nodeManager->activeNode()) {
m_nodeManager->nodeProperties(active);
}
}
+void LayerBox::slotChangeCloneSourceClicked()
+{
+ if (!m_canvas) return;
+ m_nodeManager->changeCloneSource();
+}
+
void LayerBox::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 LayerBox::slotOpacityChanged()
{
if (!m_canvas) return;
m_blockOpacityUpdate = true;
m_nodeManager->nodeOpacityChanged(m_newOpacity);
m_blockOpacityUpdate = false;
}
void LayerBox::slotOpacitySliderMoved(qreal opacity)
{
m_newOpacity = opacity;
m_opacityDelayTimer.start(200);
}
void LayerBox::slotCollapsed(const QModelIndex &index)
{
KisNodeSP node = m_filteringModel->nodeFromIndex(index);
if (node) {
node->setCollapsed(true);
}
}
void LayerBox::slotExpanded(const QModelIndex &index)
{
KisNodeSP node = m_filteringModel->nodeFromIndex(index);
if (node) {
node->setCollapsed(false);
}
}
void LayerBox::slotSelectOpaque()
{
if (!m_canvas) return;
QAction *action = m_canvas->viewManager()->actionManager()->actionByName("selectopaque");
if (action) {
action->trigger();
}
}
void LayerBox::slotNodeCollapsedChanged()
{
expandNodesRecursively(m_image->rootLayer(), m_filteringModel, m_wdgLayerBox->listLayers);
}
inline bool isSelectionMask(KisNodeSP node)
{
return dynamic_cast(node.data());
}
KisNodeSP LayerBox::findNonHidableNode(KisNodeSP startNode)
{
if (KisNodeManager::isNodeHidden(startNode, true) &&
startNode->parent() &&
!startNode->parent()->parent()) {
KisNodeSP node = startNode->prevSibling();
while (node && KisNodeManager::isNodeHidden(node, true)) {
node = node->prevSibling();
}
if (!node) {
node = startNode->nextSibling();
while (node && KisNodeManager::isNodeHidden(node, true)) {
node = node->nextSibling();
}
}
if (!node) {
node = m_image->root()->lastChild();
while (node && KisNodeManager::isNodeHidden(node, true)) {
node = node->prevSibling();
}
}
KIS_ASSERT_RECOVER_NOOP(node && "cannot activate any node!");
startNode = node;
}
return startNode;
}
void LayerBox::slotEditGlobalSelection(bool showSelections)
{
KisNodeSP lastActiveNode = m_nodeManager->activeNode();
KisNodeSP activateNode = lastActiveNode;
KisSelectionMaskSP globalSelectionMask;
if (!showSelections) {
activateNode = findNonHidableNode(activateNode);
}
m_nodeModel->setShowGlobalSelection(showSelections);
globalSelectionMask = m_image->rootLayer()->selectionMask();
// try to find deactivated, but visible masks
if (!globalSelectionMask) {
KoProperties properties;
properties.setProperty("visible", true);
QList masks = m_image->rootLayer()->childNodes(QStringList("KisSelectionMask"), properties);
if (!masks.isEmpty()) {
globalSelectionMask = dynamic_cast(masks.first().data());
}
}
// try to find at least any selection mask
if (!globalSelectionMask) {
KoProperties properties;
QList masks = m_image->rootLayer()->childNodes(QStringList("KisSelectionMask"), properties);
if (!masks.isEmpty()) {
globalSelectionMask = dynamic_cast(masks.first().data());
}
}
if (globalSelectionMask) {
if (showSelections) {
activateNode = globalSelectionMask;
}
}
if (activateNode != lastActiveNode) {
m_nodeManager->slotNonUiActivatedNode(activateNode);
} else if (lastActiveNode) {
setCurrentNode(lastActiveNode);
}
if (showSelections && !globalSelectionMask) {
KisProcessingApplicator applicator(m_image, 0,
KisProcessingApplicator::NONE,
KisImageSignalVector() << ModifiedSignal,
kundo2_i18n("Quick Selection Mask"));
applicator.applyCommand(
new KisLayerUtils::KeepNodesSelectedCommand(
m_nodeManager->selectedNodes(), KisNodeList(),
lastActiveNode, 0, m_image, false),
KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE);
applicator.applyCommand(new KisSetEmptyGlobalSelectionCommand(m_image),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
applicator.applyCommand(new KisLayerUtils::SelectGlobalSelectionMask(m_image),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
applicator.end();
} else if (!showSelections &&
globalSelectionMask &&
globalSelectionMask->selection()->selectedRect().isEmpty()) {
KisProcessingApplicator applicator(m_image, 0,
KisProcessingApplicator::NONE,
KisImageSignalVector() << ModifiedSignal,
kundo2_i18n("Cancel Quick Selection Mask"));
applicator.applyCommand(new KisSetGlobalSelectionCommand(m_image, 0), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE);
applicator.end();
}
}
void LayerBox::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 LayerBox::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 LayerBox::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 LayerBox::updateThumbnail()
{
m_wdgLayerBox->listLayers->updateNode(m_wdgLayerBox->listLayers->currentIndex());
}
void LayerBox::slotRenameCurrentNode()
{
m_wdgLayerBox->listLayers->edit(m_wdgLayerBox->listLayers->currentIndex());
}
void LayerBox::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 LayerBox::updateAvailableLabels()
{
if (!m_image) return;
m_wdgLayerBox->cmbFilter->updateAvailableLabels(m_image->root());
}
void LayerBox::updateLayerFiltering()
{
m_filteringModel->setAcceptedLabels(m_wdgLayerBox->cmbFilter->selectedColors());
}
void LayerBox::slotKeyframeChannelAdded(KisKeyframeChannel *channel)
{
if (channel->id() == KisKeyframeChannel::Opacity.id()) {
watchOpacityChannel(channel);
}
}
void LayerBox::watchOpacityChannel(KisKeyframeChannel *channel)
{
if (m_opacityChannel) {
m_opacityChannel->disconnect(this);
}
m_opacityChannel = channel;
if (m_opacityChannel) {
connect(m_opacityChannel, SIGNAL(sigKeyframeAdded(KisKeyframeSP)), this, SLOT(slotOpacityKeyframeChanged(KisKeyframeSP)));
connect(m_opacityChannel, SIGNAL(sigKeyframeRemoved(KisKeyframeSP)), this, SLOT(slotOpacityKeyframeChanged(KisKeyframeSP)));
connect(m_opacityChannel, SIGNAL(sigKeyframeMoved(KisKeyframeSP)), this, SLOT(slotOpacityKeyframeMoved(KisKeyframeSP)));
connect(m_opacityChannel, SIGNAL(sigKeyframeChanged(KisKeyframeSP)), this, SLOT(slotOpacityKeyframeChanged(KisKeyframeSP)));
}
}
void LayerBox::slotOpacityKeyframeChanged(KisKeyframeSP keyframe)
{
Q_UNUSED(keyframe);
if (m_blockOpacityUpdate) return;
updateUI();
}
void LayerBox::slotOpacityKeyframeMoved(KisKeyframeSP keyframe, int fromTime)
{
Q_UNUSED(fromTime);
slotOpacityKeyframeChanged(keyframe);
}
void LayerBox::slotImageTimeChanged(int time)
{
Q_UNUSED(time);
updateUI();
}
void LayerBox::slotUpdateIcons() {
m_wdgLayerBox->bnAdd->setIcon(KisIconUtils::loadIcon("addlayer"));
m_wdgLayerBox->bnRaise->setIcon(KisIconUtils::loadIcon("arrowupblr"));
m_wdgLayerBox->bnDelete->setIcon(KisIconUtils::loadIcon("deletelayer"));
m_wdgLayerBox->bnLower->setIcon(KisIconUtils::loadIcon("arrowdown"));
m_wdgLayerBox->bnProperties->setIcon(KisIconUtils::loadIcon("properties"));
m_wdgLayerBox->bnDuplicate->setIcon(KisIconUtils::loadIcon("duplicatelayer"));
// call child function about needing to update icons
m_wdgLayerBox->listLayers->slotUpdateIcons();
}
void LayerBox::slotUpdateThumbnailIconSize()
{
KisConfig cfg(false);
cfg.setLayerThumbnailSize(thumbnailSizeSlider->value());
// this is a hack to force the layers list to update its display and
// re-layout all the layers with the new thumbnail size
resize(this->width()+1, this->height()+1);
resize(this->width()-1, this->height()-1);
}
#include "moc_LayerBox.cpp"
diff --git a/plugins/dockers/layerdocker/LayerBox.h b/plugins/dockers/layerdocker/LayerBox.h
index 8bef83e1c2..61e6d6ea75 100644
--- a/plugins/dockers/layerdocker/LayerBox.h
+++ b/plugins/dockers/layerdocker/LayerBox.h
@@ -1,200 +1,202 @@
/*
* LayerBox.h - 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-2009 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_LAYERBOX_H
#define KIS_LAYERBOX_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kis_action.h"
#include "KisViewManager.h"
#include "kis_mainwindow_observer.h"
#include "kis_signal_compressor.h"
#include
class QModelIndex;
typedef QList QModelIndexList;
class QMenu;
class QAbstractButton;
class KoCompositeOp;
class KisCanvas2;
class KisNodeModel;
class KisNodeFilterProxyModel;
class Ui_WdgLayerBox;
class KisNodeJugglerCompressed;
class KisColorLabelSelectorWidget;
class QWidgetAction;
class KisKeyframeChannel;
class KisSelectionActionsAdapter;
/**
* A widget that shows a visualization of the layer structure.
*
* The center of the layer box is KisNodeModel, which shows the actual layers.
* This widget adds docking functionality and command buttons.
*
*/
class LayerBox : public QDockWidget, public KisMainwindowObserver
{
Q_OBJECT
public:
LayerBox();
~LayerBox() override;
QString observerName() override { return "LayerBox"; }
/// reimplemented from KisMainwindowObserver
void setViewManager(KisViewManager* kisview) override;
void setCanvas(KoCanvasBase *canvas) override;
void unsetCanvas() override;
private Q_SLOTS:
void notifyImageDeleted();
void slotContextMenuRequested(const QPoint &pos, const QModelIndex &index);
void slotMinimalView();
void slotDetailedView();
void slotThumbnailView();
// From the node manager to the layerbox
void slotSetCompositeOp(const KoCompositeOp* compositeOp);
void slotSetOpacity(double opacity);
void updateUI();
void setCurrentNode(KisNodeSP node);
void slotModelReset();
// from the layerbox to the node manager
void slotRmClicked();
void slotRaiseClicked();
void slotLowerClicked();
void slotPropertiesClicked();
+ void slotChangeCloneSourceClicked();
void slotCompositeOpChanged(int index);
void slotOpacityChanged();
void slotOpacitySliderMoved(qreal opacity);
void slotCollapsed(const QModelIndex &index);
void slotExpanded(const QModelIndex &index);
void slotSelectOpaque();
void slotNodeCollapsedChanged();
void slotEditGlobalSelection(bool showSelections);
void slotRenameCurrentNode();
void slotAboutToRemoveRows(const QModelIndex &parent, int first, int last);
void selectionChanged(const QModelIndexList selection);
void slotNodeManagerChangedSelection(const QList &nodes);
void slotColorLabelChanged(int index);
void slotUpdateIcons();
void slotAddLayerBnClicked();
void updateThumbnail();
void updateAvailableLabels();
void updateLayerFiltering();
void slotUpdateThumbnailIconSize();
// Opacity keyframing
void slotKeyframeChannelAdded(KisKeyframeChannel *channel);
void slotOpacityKeyframeChanged(KisKeyframeSP keyframe);
void slotOpacityKeyframeMoved(KisKeyframeSP keyframe, int fromTime);
void slotImageTimeChanged(int time);
private:
inline void connectActionToButton(KisViewManager* view, QAbstractButton *button, const QString &id);
inline void addActionToMenu(QMenu *menu, const QString &id);
void watchOpacityChannel(KisKeyframeChannel *channel);
KisNodeSP findNonHidableNode(KisNodeSP startNode);
private:
QPointer m_canvas;
QScopedPointer m_selectionActionsAdapter;
QMenu *m_newLayerMenu;
KisImageWSP m_image;
QPointer