No OneTemporary

File Metadata

Created
Mon, Jun 3, 8:30 AM
This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/.gitignore b/.gitignore
index f64623ff7f..4e87106445 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,29 +1,30 @@
*.orig
__pycache__
+*.egg-info
*.trace
build
qtcreator-build
*.kdev4
*~
.kateconfig
CMakeLists.txt.user*
.directory
*.autosave
*.swp
.gdb_history
.kdev_include_paths
*.config
*.creator
*.creator.user
*.files
*.includes
.DS_Store
*.kate-swap
.idea
GTAGS
GPATH
GRTAGS
GSYMS
BROWSE
*.kate-swp
/po/
diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt
index c992e5fade..81127ae7e4 100644
--- a/3rdparty/CMakeLists.txt
+++ b/3rdparty/CMakeLists.txt
@@ -1,210 +1,211 @@
project (krita-and-all-its-deps)
#
# Build all dependencies for Krita and finally Krita itself.
# Parameters: EXTERNALS_DOWNLOAD_DIR place to download all packages
# INSTALL_ROOT place to install everything to
# MXE_TOOLCHAIN: the toolchain file to cross-compile using MXE
#
# Example usage: cmake ..\kritadeposx -DEXTERNALS_DOWNLOAD_DIR=/dev2/d -DINSTALL_ROOT=/dev2/i -DWIN64_BUILD=TRUE -DBOOST_LIBRARYDIR=/dev2/i/lib -G "Visual Studio 11 Win64"
cmake_minimum_required(VERSION 2.8.6)
if(NOT SUBMAKE_JOBS)
set(SUBMAKE_JOBS 1)
endif()
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
message(FATAL_ERROR "Compiling in the source directory is not supported. Use for example 'mkdir build; cd build; cmake ..'.")
endif (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
# Tools must be obtained to work with:
include (ExternalProject)
# allow specification of a directory with pre-downloaded
# requirements
if(NOT IS_DIRECTORY ${EXTERNALS_DOWNLOAD_DIR})
message(FATAL_ERROR "No externals download dir set. Use -DEXTERNALS_DOWNLOAD_DIR")
endif()
if(NOT IS_DIRECTORY ${INSTALL_ROOT})
message(FATAL_ERROR "No install dir set. Use -DINSTALL_ROOT")
endif()
set(TOP_INST_DIR ${INSTALL_ROOT})
set(EXTPREFIX "${TOP_INST_DIR}")
set(CMAKE_PREFIX_PATH "${EXTPREFIX}")
if (${CMAKE_GENERATOR} STREQUAL "Visual Studio 14 2015 Win64")
SET(GLOBAL_PROFILE
-DCMAKE_MODULE_LINKER_FLAGS=/machine:x64
-DCMAKE_EXE_LINKER_FLAGS=/machine:x64
-DCMAKE_SHARED_LINKER_FLAGS=/machine:x64
-DCMAKE_STATIC_LINKER_FLAGS=/machine:x64
)
endif ()
message( STATUS "CMAKE_GENERATOR: ${CMAKE_GENERATOR}")
message( STATUS "CMAKE_CL_64: ${CMAKE_CL_64}")
set(GLOBAL_BUILD_TYPE RelWithDebInfo)
set(GLOBAL_PROFILE ${GLOBAL_PROFILE} -DBUILD_TESTING=false)
if (MINGW)
option(QT_ENABLE_DEBUG_INFO "Build Qt with debug info included" OFF)
option(QT_ENABLE_DYNAMIC_OPENGL "Build Qt with dynamic ANGLE support '-opengl dynamic -angle' (needs env var 'WindowsSdkDir' set to path of Windows 10 SDK)" ON)
if (QT_ENABLE_DYNAMIC_OPENGL)
if (DEFINED ENV{WindowsSdkDir})
message(STATUS "WindowsSdkDir is set to '$ENV{WindowsSdkDir}'")
else (DEFINED ENV{WindowsSdkDir})
message(FATAL_ERROR "Environment variable 'WindowsSdkDir' not set! Please set it to path of Windows 10 SDK or disable QT_ENABLE_DYNAMIC_OPENGL")
endif ()
endif ()
endif (MINGW)
set(SECURITY_EXE_LINKER_FLAGS "")
set(SECURITY_SHARED_LINKER_FLAGS "")
set(SECURITY_MODULE_LINKER_FLAGS "")
if (MINGW)
option(USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags (mingw-w64)" ON)
if (USE_MINGW_HARDENING_LINKER)
set(SECURITY_EXE_LINKER_FLAGS "-Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base")
set(SECURITY_SHARED_LINKER_FLAGS "-Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base")
set(SECURITY_MODULE_LINKER_FLAGS "-Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base")
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
# Enable high-entropy ASLR for 64-bit
# The image base has to be >4GB for HEASLR to be enabled.
# The values used here are kind of arbitrary.
set(SECURITY_EXE_LINKER_FLAGS "${SECURITY_EXE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x140000000")
set(SECURITY_SHARED_LINKER_FLAGS "${SECURITY_SHARED_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000")
set(SECURITY_MODULE_LINKER_FLAGS "${SECURITY_MODULE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000")
set(GLOBAL_PROFILE ${GLOBAL_PROFILE}
-DCMAKE_EXE_LINKER_FLAGS=${SECURITY_EXE_LINKER_FLAGS}
-DCMAKE_SHARED_LINKER_FLAGS=${SECURITY_SHARED_LINKER_FLAGS}
-DCMAKE_MODULE_LINKER_FLAGS=${SECURITY_MODULE_LINKER_FLAGS}
)
endif ()
else ()
message(WARNING "Linker Security Flags not enabled!")
endif ()
endif ()
if (DEFINED EP_PREFIX)
set_directory_properties(PROPERTIES EP_PREFIX ${EP_PREFIX})
endif ()
if (MSVC)
message(FATAL_ERROR "Krita cannot be built with MSVC. See the README.md file!")
endif()
if (MINGW)
set(PATCH_COMMAND myptch)
endif()
if (MSYS)
set(PATCH_COMMAND patch)
set(GLOBAL_PROFILE ${GLOBAL_PROFILE}
-DCMAKE_TOOLCHAIN_FILE=${MXE_TOOLCHAIN}
-DCMAKE_FIND_PREFIX_PATH=${CMAKE_PREFIX_PATH}
-DCMAKE_SYSTEM_INCLUDE_PATH=${CMAKE_PREFIX_PATH}/include
-DCMAKE_INCLUDE_PATH=${CMAKE_PREFIX_PATH}/include
-DCMAKE_LIBRARY_PATH=${CMAKE_PREFIX_PATH}/lib
-DZLIB_ROOT=${CMAKE_PREFIX_PATH}
)
set(GLOBAL_AUTOMAKE_PROFILE --host=i686-pc-mingw32 )
endif()
if (APPLE)
set(GLOBAL_PROFILE ${GLOBAL_PROFILE} -DCMAKE_MACOSX_RPATH=ON -DKDE_SKIP_RPATH_SETTINGS=ON -DBUILD_WITH_INSTALL_RPATH=ON -DAPPLE_SUPPRESS_X11_WARNING=ON)
set(PATCH_COMMAND patch)
endif ()
if (UNIX AND NOT APPLE)
set(LINUX true)
set(PATCH_COMMAND patch)
endif ()
function(TestCompileLinkPythonLibs OUTPUT_VARNAME)
include(CheckCXXSourceCompiles)
set(CMAKE_REQUIRED_INCLUDES ${PYTHON_INCLUDE_PATH})
set(CMAKE_REQUIRED_LIBRARIES ${PYTHON_LIBRARIES})
if (MINGW)
set(CMAKE_REQUIRED_DEFINITIONS -D_hypot=hypot)
endif ()
unset(${OUTPUT_VARNAME} CACHE)
CHECK_CXX_SOURCE_COMPILES("
#include <Python.h>
int main(int argc, char *argv[]) {
Py_InitializeEx(0);
}" ${OUTPUT_VARNAME})
endfunction()
if (MINGW)
option(ENABLE_PYTHON_DEPS "Enable Python deps (sip, pyqt)" ON)
if (ENABLE_PYTHON_DEPS)
if (ENABLE_PYTHON_2)
message(FATAL_ERROR "Python 2.7 is not supported on Windows at the moment.")
else(ENABLE_PYTHON_2)
find_package(PythonInterp 3.6 EXACT)
find_package(PythonLibs 3.6 EXACT)
endif()
if (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND)
message(STATUS "Python requirements met.")
TestCompileLinkPythonLibs(CAN_USE_PYTHON_LIBS)
if (NOT CAN_USE_PYTHON_LIBS)
message(FATAL_ERROR "Compiling with Python library failed, please check whether the architecture is correct!")
endif ()
else (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND)
message(FATAL_ERROR "Python requirements not met. To disable Python deps, set ENABLE_PYTHON_DEPS to OFF.")
endif ()
endif ()
endif ()
# this list must be dependency-ordered
if (ENABLE_PYTHON_DEPS OR NOT MINGW)
add_subdirectory( ext_python )
endif ()
if (MINGW)
add_subdirectory( ext_patch )
add_subdirectory( ext_png2ico )
endif ()
add_subdirectory( ext_lzma )
add_subdirectory( ext_iconv )
add_subdirectory( ext_gettext )
add_subdirectory( ext_zlib )
add_subdirectory( ext_boost )
add_subdirectory( ext_jpeg )
add_subdirectory( ext_tiff )
add_subdirectory( ext_png )
add_subdirectory( ext_eigen3 )
add_subdirectory( ext_expat ) # for exiv2
add_subdirectory( ext_exiv2 )
add_subdirectory( ext_ilmbase )
add_subdirectory( ext_lcms2 )
add_subdirectory( ext_openexr )
add_subdirectory( ext_vc )
add_subdirectory( ext_gsl )
add_subdirectory( ext_fftw3 )
add_subdirectory( ext_ocio )
add_subdirectory( ext_fontconfig)
add_subdirectory( ext_freetype)
add_subdirectory( ext_qt )
add_subdirectory( ext_poppler )
add_subdirectory( ext_libraw )
add_subdirectory( ext_frameworks )
if (ENABLE_PYTHON_DEPS OR NOT MINGW)
add_subdirectory( ext_sip )
add_subdirectory( ext_pyqt )
endif ()
if (MINGW)
add_subdirectory( ext_drmingw )
# add_subdirectory( ext_ffmpeg )
endif ()
if (NOT APPLE)
add_subdirectory( ext_gmic )
endif ()
add_subdirectory(ext_giflib)
+add_subdirectory(ext_quazip)
diff --git a/3rdparty/ext_exiv2/CMakeLists.txt b/3rdparty/ext_exiv2/CMakeLists.txt
index 29c51c7ed6..8c971f7221 100644
--- a/3rdparty/ext_exiv2/CMakeLists.txt
+++ b/3rdparty/ext_exiv2/CMakeLists.txt
@@ -1,16 +1,17 @@
SET(PREFIX_ext_exiv2 "${EXTPREFIX}" )
ExternalProject_Add( ext_exiv2
DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR}
URL http://files.kde.org/krita/build/dependencies/exiv2-0.26-trunk.tar.gz
URL_MD5 5399e3b570d7f9205f0e76d47582da4c
PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/tzname.patch
COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/patch_mingw.patch
-
+ COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/disable_exiv_apps.diff
+
INSTALL_DIR ${PREFIX_ext_exiv2}
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${PREFIX_ext_exiv2} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DEXIV2_ENABLE_BUILD_SAMPLES=OFF -DEXIV2_ENABLE_BUILD_PO=OFF -DEXIV2_ENABLE_NLS=OFF -DICONV_INCLUDE_DIR=${PREFIX_ext_exiv2}/include
UPDATE_COMMAND ""
DEPENDS ext_iconv ext_expat
)
diff --git a/3rdparty/ext_exiv2/disable_exiv_apps.diff b/3rdparty/ext_exiv2/disable_exiv_apps.diff
new file mode 100644
index 0000000000..0a582b21c8
--- /dev/null
+++ b/3rdparty/ext_exiv2/disable_exiv_apps.diff
@@ -0,0 +1,35 @@
+diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
+index aecd621..d879cf8 100644
+--- a/src/CMakeLists.txt
++++ b/src/CMakeLists.txt
+@@ -315,14 +315,14 @@ msvc_runtime_configure(${EXIV2_ENABLE_SHARED} ${EXIV2_ENABLE_DYNAMIC_RUNTIME})
+
+ # ******************************************************************************
+ # exiv2 application
+-ADD_EXECUTABLE( exiv2 ${EXIV2_SRC} ${EXIV2_HDR} )
+-TARGET_LINK_LIBRARIES( exiv2 exiv2lib )
+-INSTALL( TARGETS exiv2 ${INSTALL_TARGET_STANDARD_ARGS} )
++#ADD_EXECUTABLE( exiv2 ${EXIV2_SRC} ${EXIV2_HDR} )
++#TARGET_LINK_LIBRARIES( exiv2 exiv2lib )
++#INSTALL( TARGETS exiv2 ${INSTALL_TARGET_STANDARD_ARGS} )
+
+ # ******************************************************************************
+ # connection test application
+-ADD_EXECUTABLE( conntest ${CONNTEST} )
+-TARGET_LINK_LIBRARIES( conntest ${PRIVATE_VAR} exiv2lib ${CURL_LIBRARIES} ${SSH_LIBRARIES})
++#ADD_EXECUTABLE( conntest ${CONNTEST} )
++#TARGET_LINK_LIBRARIES( conntest ${PRIVATE_VAR} exiv2lib ${CURL_LIBRARIES} ${SSH_LIBRARIES})
+
+ # ******************************************************************************
+ # exifprint application
+@@ -331,8 +331,8 @@ TARGET_LINK_LIBRARIES( conntest ${PRIVATE_VAR} exiv2lib ${CURL_LIBRARIES} ${SSH
+
+ # ******************************************************************************
+ # remotetest application
+-ADD_EXECUTABLE( remotetest ${REMOTETEST} )
+-TARGET_LINK_LIBRARIES( remotetest exiv2lib )
++#ADD_EXECUTABLE( remotetest ${REMOTETEST} )
++#TARGET_LINK_LIBRARIES( remotetest exiv2lib )
+
+ # ******************************************************************************
+ # Headers
diff --git a/3rdparty/ext_ocio/CMakeLists.txt b/3rdparty/ext_ocio/CMakeLists.txt
index 3805b69b03..8cc74eadf0 100644
--- a/3rdparty/ext_ocio/CMakeLists.txt
+++ b/3rdparty/ext_ocio/CMakeLists.txt
@@ -1,35 +1,37 @@
#
# The latest opencolorio doesn't build on Windows without using boost::ptr, but if you build
# it with boost::ptr, you cannot link to it because of missing dll exports, so build an older
# ocio on Windows.
#
SET(EXTPREFIX_ocio "${EXTPREFIX}" )
if (MINGW)
ExternalProject_Add(
ext_ocio
DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR}
- URL https://github.com/imageworks/OpenColorIO/archive/v1.1.0.zip
+ URL http://files.kde.org/krita/build/dependencies/OpenColorIO-1.1.0.zip
URL_MD5 aa52d77b6a4e03017beefaefa613b49f
INSTALL_DIR ${EXTPREFIX_ocio}
PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/ocio-mingw.patch
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_ocio} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DOCIO_BUILD_APPS=OFF -DOCIO_BUILD_TRUELIGHT=OFF -DOCIO_BUILD_NUKE=OFF -DOCIO_BUILD_DOCS=OFF -DOCIO_BUILD_TESTS=OFF -DOCIO_BUILD_PYGLUE=OFF -DOCIO_BUILD_STATIC=OFF -DOCIO_BUILD_JNIGLUE=OFF
UPDATE_COMMAND ""
DEPENDS ext_boost
)
else()
ExternalProject_Add(
ext_ocio
DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR}
- URL https://github.com/imageworks/OpenColorIO/archive/v1.1.0.zip
- URL_MD5 aa52d77b6a4e03017beefaefa613b49f
+ URL http://files.kde.org/krita/build/dependencies/OpenColorIO-1.1.0.tar.gz
+ URL_MD5 802d8f5b1d1fe316ec5f76511aa611b8
INSTALL_DIR ${EXTPREFIX_ocio}
+ PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/warnings.diff
+
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_ocio} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DOCIO_BUILD_APPS=OFF -DOCIO_BUILD_TRUELIGHT=OFF -DOCIO_BUILD_NUKE=OFF -DOCIO_BUILD_DOCS=OFF -DOCIO_BUILD_TESTS=OFF -DOCIO_BUILD_PYGLUE=OFF -DOCIO_BUILD_STATIC_JNIGLUE=OFF
UPDATE_COMMAND ""
DEPENDS ext_boost
)
endif()
diff --git a/3rdparty/ext_ocio/warnings.diff b/3rdparty/ext_ocio/warnings.diff
new file mode 100644
index 0000000000..25d7577324
--- /dev/null
+++ b/3rdparty/ext_ocio/warnings.diff
@@ -0,0 +1,13 @@
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index b05c7e4..8577e54 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -135,7 +135,7 @@ endif()
+ if(CMAKE_COMPILER_IS_GNUCXX)
+ # Enable a bunch of compiler warnings...
+ # http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
+- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wshadow -Wconversion -Wcast-qual -Wformat=2")
++ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wshadow -Wconversion -Wcast-qual -Wformat=2 -Wno-unused-function -Wno-deprecated-declarations")
+ # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic")
+ endif(CMAKE_COMPILER_IS_GNUCXX)
+
diff --git a/3rdparty/ext_poppler/CMakeLists.txt b/3rdparty/ext_poppler/CMakeLists.txt
index 76acb1f1a5..fe0fcbbb90 100644
--- a/3rdparty/ext_poppler/CMakeLists.txt
+++ b/3rdparty/ext_poppler/CMakeLists.txt
@@ -1,13 +1,13 @@
SET(PREFIX_ext_poppler "${EXTPREFIX}" )
ExternalProject_Add( ext_poppler
DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR}
URL https://poppler.freedesktop.org/poppler-0.62.0.tar.xz
URL_MD5 42b801f2defaccb6b6cf1bf783ee1552
PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/no_tests.diff
INSTALL_DIR ${PREFIX_ext_poppler}
- CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${PREFIX_ext_poppler} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DBUILD_GTK_TESTS=OFF -DBUILD_QT5_TESTS=FALSE -BUILD_CPP_TESTS=FALSE -DENABLE_UTILS=FALSE -DENABLE_GLIB=FALSE -DENABLE_LIBOPENJPEG=none
+ CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${PREFIX_ext_poppler} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DBUILD_GTK_TESTS=OFF -DBUILD_QT5_TESTS=FALSE -DBUILD_CPP_TESTS=FALSE -DENABLE_UTILS=FALSE -DENABLE_GLIB=FALSE -DENABLE_LIBOPENJPEG=none
UPDATE_COMMAND ""
)
diff --git a/3rdparty/ext_quazip/CMakeLists.txt b/3rdparty/ext_quazip/CMakeLists.txt
new file mode 100644
index 0000000000..832353aa75
--- /dev/null
+++ b/3rdparty/ext_quazip/CMakeLists.txt
@@ -0,0 +1,13 @@
+SET(PREFIX_ext_quazip "${EXTPREFIX}" )
+ExternalProject_Add( ext_quazip
+ DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR}
+ URL https://github.com/stachenov/quazip/archive/0.7.6.zip
+ URL_MD5 a3335649c34053385d8390dd1a6f1ca4
+ PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/find_quazip.diff
+ COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/liblocation.diff
+ INSTALL_DIR ${PREFIX_ext_quazip}
+ CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${PREFIX_ext_quazip} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE}
+
+ UPDATE_COMMAND ""
+ DEPENDS ext_zlib
+)
diff --git a/3rdparty/ext_quazip/find_quazip.diff b/3rdparty/ext_quazip/find_quazip.diff
new file mode 100644
index 0000000000..a5fb1a5291
--- /dev/null
+++ b/3rdparty/ext_quazip/find_quazip.diff
@@ -0,0 +1,10 @@
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 3d2fb55..5d8a5cc 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -60,4 +60,4 @@ set(QUAZIP_LIB_TARGET_NAME quazip${QUAZIP_LIB_VERSION_SUFFIX} CACHE
+
+ add_subdirectory(quazip)
+
+-install(FILES FindQuaZip.cmake RENAME FindQuaZip${QUAZIP_LIB_VERSION_SUFFIX}.cmake DESTINATION ${CMAKE_ROOT}/Modules)
++#install(FILES FindQuaZip.cmake RENAME FindQuaZip${QUAZIP_LIB_VERSION_SUFFIX}.cmake DESTINATION ${CMAKE_ROOT}/Modules)
diff --git a/3rdparty/ext_quazip/liblocation.diff b/3rdparty/ext_quazip/liblocation.diff
new file mode 100644
index 0000000000..1bdc03fc11
--- /dev/null
+++ b/3rdparty/ext_quazip/liblocation.diff
@@ -0,0 +1,17 @@
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 5d8a5cc..d53548a 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -54,7 +54,11 @@ endif(UNIX OR MINGW)
+ set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR})
+
+ set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)")
+-set(LIB_DESTINATION "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE STRING "Library directory name" FORCE)
++if (WIN32)
++ set(LIB_DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" CACHE STRING "Library directory name" FORCE)
++else()
++ set(LIB_DESTINATION "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE STRING "Library directory name" FORCE)
++endif()
+ set(QUAZIP_LIB_TARGET_NAME quazip${QUAZIP_LIB_VERSION_SUFFIX} CACHE
+ INTERNAL "Target name of libquazip" FORCE)
+
diff --git a/CMakeLists.txt b/CMakeLists.txt
index aa473b68e4..d2491f5f2f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,757 +1,775 @@
project(krita)
message(STATUS "Using CMake version: ${CMAKE_VERSION}")
cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR)
set(MIN_QT_VERSION 5.6.0)
set(MIN_FRAMEWORKS_VERSION 5.18.0)
if (POLICY CMP0002)
cmake_policy(SET CMP0002 OLD)
endif()
if (POLICY CMP0017)
cmake_policy(SET CMP0017 NEW)
endif ()
if (POLICY CMP0022)
cmake_policy(SET CMP0022 OLD)
endif ()
if (POLICY CMP0026)
cmake_policy(SET CMP0026 OLD)
endif()
if (POLICY CMP0042)
cmake_policy(SET CMP0042 NEW)
endif()
if (POLICY CMP0046)
cmake_policy(SET CMP0046 OLD)
endif ()
if (POLICY CMP0059)
cmake_policy(SET CMP0059 OLD)
endif()
if (POLICY CMP0063)
cmake_policy(SET CMP0063 OLD)
endif()
if (POLICY CMP0054)
cmake_policy(SET CMP0054 OLD)
endif()
if (POLICY CMP0064)
cmake_policy(SET CMP0064 OLD)
endif()
if (POLICY CMP0071)
cmake_policy(SET CMP0071 OLD)
endif()
if (APPLE)
set(APPLE_SUPPRESS_X11_WARNING TRUE)
set(KDE_SKIP_RPATH_SETTINGS TRUE)
set(CMAKE_MACOSX_RPATH 1)
set(BUILD_WITH_INSTALL_RPATH 1)
add_definitions(-mmacosx-version-min=10.11 -Wno-macro-redefined -Wno-deprecated-register)
endif()
if (CMAKE_COMPILER_IS_GNUCXX AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9 AND NOT WIN32)
endif()
######################
#######################
## Constants defines ##
#######################
######################
# define common versions of Krita applications, used to generate kritaversion.h
# update these version for every release:
set(KRITA_VERSION_STRING "4.2.0-pre-alpha")
# Major version: 3 for 3.x, 4 for 4.x, etc.
set(KRITA_STABLE_VERSION_MAJOR 4)
# Minor version: 0 for 4.0, 1 for 4.1, etc.
set(KRITA_STABLE_VERSION_MINOR 2)
# Bugfix release version, or 0 for before the first stable release
set(KRITA_VERSION_RELEASE 0)
# the 4th digit, really only used for the Windows installer:
# - [Pre-]Alpha: Starts from 0, increment 1 per release
# - Beta: Starts from 50, increment 1 per release
# - Stable: Set to 100, bump to 101 if emergency update is needed
set(KRITA_VERSION_REVISION 0)
set(KRITA_ALPHA 1) # uncomment only for Alpha
#set(KRITA_BETA 1) # uncomment only for Beta
#set(KRITA_RC 1) # uncomment only for RC
set(KRITA_YEAR 2018) # update every year
if(NOT DEFINED KRITA_ALPHA AND NOT DEFINED KRITA_BETA AND NOT DEFINED KRITA_RC)
set(KRITA_STABLE 1) # do not edit
endif()
message(STATUS "Krita version: ${KRITA_VERSION_STRING}")
# Define the generic version of the Krita libraries here
# This makes it easy to advance it when the next Krita release comes.
# 14 was the last GENERIC_KRITA_LIB_VERSION_MAJOR of the previous Krita series
# (2.x) so we're starting with 15 in 3.x series, 16 in 4.x series
if(KRITA_STABLE_VERSION_MAJOR EQUAL 4)
math(EXPR GENERIC_KRITA_LIB_VERSION_MAJOR "${KRITA_STABLE_VERSION_MINOR} + 16")
else()
# let's make sure we won't forget to update the "16"
message(FATAL_ERROR "Reminder: please update offset == 16 used to compute GENERIC_KRITA_LIB_VERSION_MAJOR to something bigger")
endif()
set(GENERIC_KRITA_LIB_VERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}.0.0")
set(GENERIC_KRITA_LIB_SOVERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}")
LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules")
LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/kde_macro")
# fetch git revision for the current build
set(KRITA_GIT_SHA1_STRING "")
set(KRITA_GIT_BRANCH_STRING "")
include(GetGitRevisionDescription)
get_git_head_hash(GIT_SHA1)
get_git_branch(GIT_BRANCH)
if(GIT_SHA1)
string(SUBSTRING ${GIT_SHA1} 0 7 GIT_SHA1)
set(KRITA_GIT_SHA1_STRING ${GIT_SHA1})
if(GIT_BRANCH)
set(KRITA_GIT_BRANCH_STRING ${GIT_BRANCH})
else()
set(KRITA_GIT_BRANCH_STRING "(detached HEAD)")
endif()
endif()
# create test make targets
enable_testing()
# collect list of broken tests, empty here to start fresh with each cmake run
set(KRITA_BROKEN_TESTS "" CACHE INTERNAL "KRITA_BROKEN_TESTS")
############
#############
## Options ##
#############
############
include(FeatureSummary)
if (WIN32)
option(USE_DRMINGW "Support the Dr. Mingw crash handler (only on windows)" ON)
add_feature_info("Dr. Mingw" USE_DRMINGW "Enable the Dr. Mingw crash handler")
if (MINGW)
option(USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags (mingw-w64)" ON)
add_feature_info("Linker Security Flags" USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags")
if (USE_MINGW_HARDENING_LINKER)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base")
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
# Enable high-entropy ASLR for 64-bit
# The image base has to be >4GB for HEASLR to be enabled.
# The values used here are kind of arbitrary.
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x140000000")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000")
endif ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
else (USE_MINGW_HARDENING_LINKER)
message(WARNING "Linker Security Flags not enabled!")
endif (USE_MINGW_HARDENING_LINKER)
endif (MINGW)
endif ()
-if (UNIX AND NOT APPLE)
-option(USE_QT_XCB "Do not use Krita's forked XCB connection and tablet support on X11, but leave everything to Qt." OFF)
-configure_file(config_use_qt_xcb.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config_use_qt_xcb.h)
-add_feature_info("Use Qt's XCB and Tablet support on X11" USE_QT_XCB "Do not use Krita's forked XCB connection and tablet support on X11, but leave everything to Qt.")
-endif()
-
option(HIDE_SAFE_ASSERTS "Don't show message box for \"safe\" asserts, just ignore them automatically and dump a message to the terminal." ON)
configure_file(config-hide-safe-asserts.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hide-safe-asserts.h)
add_feature_info("Hide Safe Asserts" HIDE_SAFE_ASSERTS "Don't show message box for \"safe\" asserts, just ignore them automatically and dump a message to the terminal.")
option(USE_LOCK_FREE_HASH_TABLE "Use lock free hash table instead of blocking." ON)
configure_file(config-hash-table-implementaion.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hash-table-implementaion.h)
add_feature_info("Lock free hash table" USE_LOCK_FREE_HASH_TABLE "Use lock free hash table instead of blocking.")
option(FOUNDATION_BUILD "A Foundation build is a binary release build that can package some extra things like color themes. Linux distributions that build and install Krita into a default system location should not define this option to true." OFF)
add_feature_info("Foundation Build" FOUNDATION_BUILD "A Foundation build is a binary release build that can package some extra things like color themes. Linux distributions that build and install Krita into a default system location should not define this option to true.")
option(KRITA_ENABLE_BROKEN_TESTS "Enable tests that are marked as broken" OFF)
add_feature_info("Enable Broken Tests" KRITA_ENABLE_BROKEN_TESTS "Runs broken test when \"make test\" is invoked (use -DKRITA_ENABLE_BROKEN_TESTS=ON to enable).")
option(HAVE_HDR "Enable HDR surface format selection. Available only on certain patched versions of Qt" OFF)
configure_file(config-hdr.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hdr.h)
add_feature_info("Enable HDR" HAVE_HDR "Enable selection of HDR surface fort Krita window. Needs a particular patched version of Qt")
option(LIMIT_LONG_TESTS "Run long running unittests in a limited quick mode" ON)
configure_file(config-limit-long-tests.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-limit-long-tests.h)
add_feature_info("Limit long tests" LIMIT_LONG_TESTS "Run long running unittests in a limited quick mode")
option(ENABLE_PYTHON_2 "Enables the compiler to look for Python 2.7 instead of Python 3. Some packaged scripts are not compatible with Python 2 and this should only be used if you really have to use 2.7." OFF)
option(BUILD_KRITA_QT_DESIGNER_PLUGINS "Build Qt Designer plugins for Krita widgets" OFF)
add_feature_info("Build Qt Designer plugins" BUILD_KRITA_QT_DESIGNER_PLUGINS "Builds Qt Designer plugins for Krita widgets (use -DBUILD_KRITA_QT_DESIGNER_PLUGINS=ON to enable).")
include(MacroJPEG)
#########################################################
## Look for Python3 It is also searched by KF5, ##
## so we should request the correct version in advance ##
#########################################################
function(TestCompileLinkPythonLibs OUTPUT_VARNAME)
include(CheckCXXSourceCompiles)
set(CMAKE_REQUIRED_INCLUDES ${PYTHON_INCLUDE_PATH})
set(CMAKE_REQUIRED_LIBRARIES ${PYTHON_LIBRARIES})
if (MINGW)
set(CMAKE_REQUIRED_DEFINITIONS -D_hypot=hypot)
endif (MINGW)
unset(${OUTPUT_VARNAME} CACHE)
CHECK_CXX_SOURCE_COMPILES("
#include <Python.h>
int main(int argc, char *argv[]) {
Py_InitializeEx(0);
}" ${OUTPUT_VARNAME})
endfunction()
if(MINGW)
if(ENABLE_PYTHON_2)
message(FATAL_ERROR "Python 2.7 is not supported on Windows at the moment.")
else(ENABLE_PYTHON_2)
find_package(PythonInterp 3.6 EXACT)
find_package(PythonLibs 3.6 EXACT)
endif(ENABLE_PYTHON_2)
if (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND)
if(ENABLE_PYTHON_2)
find_package(PythonLibrary 2.7)
else(ENABLE_PYTHON_2)
find_package(PythonLibrary 3.6)
endif(ENABLE_PYTHON_2)
TestCompileLinkPythonLibs(CAN_USE_PYTHON_LIBS)
if (NOT CAN_USE_PYTHON_LIBS)
message(FATAL_ERROR "Compiling with Python library failed, please check whether the architecture is correct. Python will be disabled.")
endif (NOT CAN_USE_PYTHON_LIBS)
endif (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND)
else(MINGW)
if(ENABLE_PYTHON_2)
find_package(PythonInterp 2.7)
find_package(PythonLibrary 2.7)
else(ENABLE_PYTHON_2)
find_package(PythonInterp 3.0)
find_package(PythonLibrary 3.0)
endif(ENABLE_PYTHON_2)
endif(MINGW)
########################
#########################
## Look for KDE and Qt ##
#########################
########################
find_package(ECM 5.22 REQUIRED NOMODULE)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR})
include(ECMOptionalAddSubdirectory)
include(ECMAddAppIcon)
include(ECMSetupVersion)
include(ECMMarkNonGuiExecutable)
include(ECMGenerateHeaders)
include(GenerateExportHeader)
include(ECMMarkAsTest)
include(ECMInstallIcons)
include(CMakePackageConfigHelpers)
include(WriteBasicConfigVersionFile)
include(CheckFunctionExists)
include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDECompilerSettings)
# do not reorder to be alphabetical: this is the order in which the frameworks
# depend on each other.
find_package(KF5 ${MIN_FRAMEWORKS_VERSION} REQUIRED COMPONENTS
- Archive
Config
WidgetsAddons
Completion
CoreAddons
GuiAddons
I18n
ItemModels
ItemViews
WindowSystem
)
# KConfig deprecated authorizeKAction. In order to be warning free,
# compile with the updated function when the dependency is new enough.
# Remove this (and the uses of the define) when the minimum KF5
# version is >= 5.24.0.
if (${KF5Config_VERSION} VERSION_LESS "5.24.0" )
message("Old KConfig (< 5.24.0) found.")
add_definitions(-DKCONFIG_BEFORE_5_24)
endif()
find_package(Qt5 ${MIN_QT_VERSION}
REQUIRED COMPONENTS
Core
Gui
Widgets
Xml
Network
PrintSupport
Svg
Test
Concurrent
)
+if (UNIX AND NOT APPLE)
+ if (${Qt5_VERSION} VERSION_GREATER "5.11")
+ set (USE_QT_XCB ON)
+ else()
+ option(USE_QT_XCB "Do not use Krita's forked XCB connection and tablet support on X11, but leave everything to Qt." OFF)
+ add_feature_info("Use Qt's XCB and Tablet support on X11" USE_QT_XCB "Do not use Krita's forked XCB connection and tablet support on X11, but leave everything to Qt.")
+ endif()
+ configure_file(config_use_qt_xcb.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config_use_qt_xcb.h)
+endif()
+
include (MacroAddFileDependencies)
include (MacroBoolTo01)
include (MacroEnsureOutOfSourceBuild)
macro_ensure_out_of_source_build("Compiling Krita inside the source directory is not possible. Please refer to the build instruction https://community.kde.org/Krita#Build_Instructions")
# Note: OPTIONAL_COMPONENTS does not seem to be reliable
# (as of ECM 5.15.0, CMake 3.2)
find_package(Qt5Multimedia ${MIN_QT_VERSION})
set_package_properties(Qt5Multimedia PROPERTIES
DESCRIPTION "Qt multimedia integration"
URL "http://www.qt.io/"
TYPE OPTIONAL
PURPOSE "Optionally used to provide sound support for animations")
macro_bool_to_01(Qt5Multimedia_FOUND HAVE_QT_MULTIMEDIA)
configure_file(config-qtmultimedia.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-qtmultimedia.h )
if (NOT APPLE)
find_package(Qt5Quick ${MIN_QT_VERSION})
set_package_properties(Qt5Quick PROPERTIES
DESCRIPTION "QtQuick"
URL "http://www.qt.io/"
TYPE OPTIONAL
PURPOSE "Optionally used for the touch gui for Krita")
macro_bool_to_01(Qt5Quick_FOUND HAVE_QT_QUICK)
find_package(Qt5QuickWidgets ${MIN_QT_VERSION})
set_package_properties(Qt5QuickWidgets PROPERTIES
DESCRIPTION "QtQuickWidgets"
URL "http://www.qt.io/"
TYPE OPTIONAL
PURPOSE "Optionally used for the touch gui for Krita")
endif()
if (NOT WIN32 AND NOT APPLE)
find_package(Qt5 ${MIN_QT_VERSION} REQUIRED X11Extras)
find_package(Qt5DBus ${MIN_QT_VERSION})
set(HAVE_DBUS ${Qt5DBus_FOUND})
set_package_properties(Qt5DBus PROPERTIES
DESCRIPTION "Qt DBUS integration"
URL "http://www.qt.io/"
TYPE OPTIONAL
PURPOSE "Optionally used to provide a dbus api on Linux")
find_package(KF5Crash ${MIN_FRAMEWORKS_VERSION})
macro_bool_to_01(KF5Crash_FOUND HAVE_KCRASH)
set_package_properties(KF5Crash PROPERTIES
DESCRIPTION "KDE's Crash Handler"
URL "http://api.kde.org/frameworks-api/frameworks5-apidocs/kcrash/html/index.html"
TYPE OPTIONAL
PURPOSE "Optionally used to provide crash reporting on Linux")
find_package(X11 REQUIRED COMPONENTS Xinput)
set(HAVE_X11 TRUE)
add_definitions(-DHAVE_X11)
find_package(XCB COMPONENTS XCB ATOM)
set(HAVE_XCB ${XCB_FOUND})
else()
set(HAVE_DBUS FALSE)
set(HAVE_X11 FALSE)
set(HAVE_XCB FALSE)
endif()
add_definitions(
-DQT_USE_QSTRINGBUILDER
-DQT_STRICT_ITERATORS
-DQT_NO_SIGNALS_SLOTS_KEYWORDS
-DQT_NO_URL_CAST_FROM_STRING
)
if (${Qt5_VERSION} VERSION_GREATER "5.8.0" )
add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50900)
elseif(${Qt5_VERSION} VERSION_GREATER "5.7.0" )
add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50800)
elseif(${Qt5_VERSION} VERSION_GREATER "5.6.0" )
add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50700)
else()
add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50600)
endif()
add_definitions(-DTRANSLATION_DOMAIN=\"krita\")
#
# The reason for this mode is that the Debug mode disable inlining
#
if(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fext-numeric-literals")
endif()
option(KRITA_DEVS "For Krita developers. This modifies the DEBUG build type to use -O3 -g, while still enabling Q_ASSERT. This is necessary because the Qt5 cmake modules normally append QT_NO_DEBUG to any build type that is not labeled Debug")
if (KRITA_DEVS)
set(CMAKE_CXX_FLAGS_DEBUG "-O3 -g" CACHE STRING "" FORCE)
endif()
if(UNIX)
set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES};m")
endif()
if(WIN32)
if(MSVC)
# C4522: 'class' : multiple assignment operators specified
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4522")
endif()
endif()
# KDECompilerSettings adds the `--export-all-symbols` linker flag.
# We don't really need it.
if(MINGW)
string(REPLACE "-Wl,--export-all-symbols" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}")
string(REPLACE "-Wl,--export-all-symbols" "" CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}")
endif(MINGW)
# enable exceptions globally
kde_enable_exceptions()
set(KRITA_DEFAULT_TEST_DATA_DIR ${CMAKE_SOURCE_DIR}/sdk/tests/data/)
macro(macro_add_unittest_definitions)
add_definitions(-DFILES_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data/")
add_definitions(-DFILES_OUTPUT_DIR="${CMAKE_CURRENT_BINARY_DIR}")
add_definitions(-DFILES_DEFAULT_DATA_DIR="${KRITA_DEFAULT_TEST_DATA_DIR}")
add_definitions(-DSYSTEM_RESOURCES_DATA_DIR="${CMAKE_SOURCE_DIR}/krita/data/")
endmacro()
# overcome some platform incompatibilities
if(WIN32)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/winquirks)
add_definitions(-D_USE_MATH_DEFINES)
add_definitions(-DNOMINMAX)
set(WIN32_PLATFORM_NET_LIBS ws2_32.lib netapi32.lib)
endif()
# set custom krita plugin installdir
set(KRITA_PLUGIN_INSTALL_DIR ${LIB_INSTALL_DIR}/kritaplugins)
###########################
############################
## Required dependencies ##
############################
###########################
find_package(PNG REQUIRED)
if (APPLE)
# this is not added correctly on OSX -- see http://forum.kde.org/viewtopic.php?f=139&t=101867&p=221242#p221242
include_directories(SYSTEM ${PNG_INCLUDE_DIR})
endif()
add_definitions(-DBOOST_ALL_NO_LIB)
find_package(Boost 1.55 REQUIRED COMPONENTS system)
include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
##
## Test for GNU Scientific Library
##
find_package(GSL)
set_package_properties(GSL PROPERTIES
URL "http://www.gnu.org/software/gsl"
TYPE RECOMMENDED
PURPOSE "Required by Krita's Transform tool.")
macro_bool_to_01(GSL_FOUND HAVE_GSL)
configure_file(config-gsl.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-gsl.h )
###########################
############################
## Optional dependencies ##
############################
###########################
find_package(ZLIB)
set_package_properties(ZLIB PROPERTIES
DESCRIPTION "Compression library"
URL "http://www.zlib.net/"
TYPE OPTIONAL
PURPOSE "Optionally used by the G'Mic and the PSD plugins")
macro_bool_to_01(ZLIB_FOUND HAVE_ZLIB)
find_package(OpenEXR)
set_package_properties(OpenEXR PROPERTIES
DESCRIPTION "High dynamic-range (HDR) image file format"
URL "http://www.openexr.com"
TYPE OPTIONAL
PURPOSE "Required by the Krita OpenEXR filter")
macro_bool_to_01(OPENEXR_FOUND HAVE_OPENEXR)
set(LINK_OPENEXR_LIB)
if(OPENEXR_FOUND)
include_directories(SYSTEM ${OPENEXR_INCLUDE_DIR})
set(LINK_OPENEXR_LIB ${OPENEXR_LIBRARIES})
add_definitions(${OPENEXR_DEFINITIONS})
endif()
find_package(TIFF)
set_package_properties(TIFF PROPERTIES
DESCRIPTION "TIFF Library and Utilities"
URL "http://www.remotesensing.org/libtiff"
TYPE OPTIONAL
PURPOSE "Required by the Krita TIFF filter")
find_package(JPEG)
set_package_properties(JPEG PROPERTIES
DESCRIPTION "Free library for JPEG image compression. Note: libjpeg8 is NOT supported."
URL "http://www.libjpeg-turbo.org"
TYPE OPTIONAL
PURPOSE "Required by the Krita JPEG filter")
find_package(GIF)
set_package_properties(GIF PROPERTIES
DESCRIPTION "Library for loading and saving gif files."
URL "http://giflib.sourceforge.net/"
TYPE OPTIONAL
PURPOSE "Required by the Krita GIF filter")
find_package(HEIF "1.3.0")
set_package_properties(HEIF PROPERTIES
DESCRIPTION "Library for loading and saving heif files."
URL "https://github.com/strukturag/libheif"
TYPE OPTIONAL
PURPOSE "Required by the Krita HEIF filter")
set(LIBRAW_MIN_VERSION "0.16")
find_package(LibRaw ${LIBRAW_MIN_VERSION})
set_package_properties(LibRaw PROPERTIES
DESCRIPTION "Library to decode RAW images"
URL "http://www.libraw.org"
TYPE OPTIONAL
PURPOSE "Required to build the raw import plugin")
find_package(FFTW3)
set_package_properties(FFTW3 PROPERTIES
DESCRIPTION "A fast, free C FFT library"
URL "http://www.fftw.org/"
TYPE OPTIONAL
PURPOSE "Required by the Krita for fast convolution operators and some G'Mic features")
macro_bool_to_01(FFTW3_FOUND HAVE_FFTW3)
find_package(OCIO)
set_package_properties(OCIO PROPERTIES
DESCRIPTION "The OpenColorIO Library"
URL "http://www.opencolorio.org"
TYPE OPTIONAL
PURPOSE "Required by the Krita LUT docker")
macro_bool_to_01(OCIO_FOUND HAVE_OCIO)
set_package_properties(PythonLibrary PROPERTIES
DESCRIPTION "Python Library"
URL "http://www.python.org"
TYPE OPTIONAL
PURPOSE "Required by the Krita PyQt plugin")
macro_bool_to_01(PYTHONLIBS_FOUND HAVE_PYTHONLIBS)
-find_package(SIP "4.18.0")
+find_package(SIP "4.19.13")
set_package_properties(SIP PROPERTIES
DESCRIPTION "Support for generating SIP Python bindings"
URL "https://www.riverbankcomputing.com/software/sip/download"
TYPE OPTIONAL
PURPOSE "Required by the Krita PyQt plugin")
macro_bool_to_01(SIP_FOUND HAVE_SIP)
find_package(PyQt5 "5.6.0")
set_package_properties(PyQt5 PROPERTIES
DESCRIPTION "Python bindings for Qt5."
URL "https://www.riverbankcomputing.com/software/pyqt/download5"
TYPE OPTIONAL
PURPOSE "Required by the Krita PyQt plugin")
macro_bool_to_01(PYQT5_FOUND HAVE_PYQT5)
##
## Look for OpenGL
##
# TODO: see if there is a better check for QtGui being built with opengl support (and thus the QOpenGL* classes)
if(Qt5Gui_OPENGL_IMPLEMENTATION)
message(STATUS "Found QtGui OpenGL support")
else()
message(FATAL_ERROR "Did NOT find QtGui OpenGL support. Check your Qt configuration. You cannot build Krita without Qt OpenGL support.")
endif()
##
## Test for eigen3
##
find_package(Eigen3 3.0 REQUIRED)
set_package_properties(Eigen3 PROPERTIES
DESCRIPTION "C++ template library for linear algebra"
URL "http://eigen.tuxfamily.org"
TYPE REQUIRED)
##
## Test for exiv2
##
-find_package(Exiv2 0.16 REQUIRED)
-set_package_properties(Exiv2 PROPERTIES
- DESCRIPTION "Image metadata library and tools"
- URL "http://www.exiv2.org"
- PURPOSE "Required by Krita")
+find_package(LibExiv2 0.16 REQUIRED)
##
## Test for lcms
##
find_package(LCMS2 2.4 REQUIRED)
set_package_properties(LCMS2 PROPERTIES
DESCRIPTION "LittleCMS Color management engine"
URL "http://www.littlecms.com"
TYPE REQUIRED
PURPOSE "Will be used for color management and is necessary for Krita")
if(LCMS2_FOUND)
if(NOT ${LCMS2_VERSION} VERSION_LESS 2040 )
set(HAVE_LCMS24 TRUE)
endif()
set(HAVE_REQUIRED_LCMS_VERSION TRUE)
set(HAVE_LCMS2 TRUE)
endif()
##
## Test for Vc
##
set(OLD_CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} )
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules )
set(HAVE_VC FALSE)
if (NOT ${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm")
if(NOT MSVC)
find_package(Vc 1.1.0)
set_package_properties(Vc PROPERTIES
DESCRIPTION "Portable, zero-overhead SIMD library for C++"
URL "https://github.com/VcDevel/Vc"
TYPE OPTIONAL
PURPOSE "Required by the Krita for vectorization")
macro_bool_to_01(Vc_FOUND HAVE_VC)
endif()
endif()
configure_file(config-vc.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-vc.h )
if(HAVE_VC)
message(STATUS "Vc found!")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
"${CMAKE_SOURCE_DIR}/cmake/vc")
include (VcMacros)
if(Vc_COMPILER_IS_CLANG)
set(ADDITIONAL_VC_FLAGS "-Wabi -ffp-contract=fast")
if(NOT WIN32)
set(ADDITIONAL_VC_FLAGS "${ADDITIONAL_VC_FLAGS} -fPIC")
endif()
elseif (NOT MSVC)
set(ADDITIONAL_VC_FLAGS "-Wabi -fabi-version=0 -ffp-contract=fast")
if(NOT WIN32)
set(ADDITIONAL_VC_FLAGS "${ADDITIONAL_VC_FLAGS} -fPIC")
endif()
endif()
#Handle Vc master
if(Vc_COMPILER_IS_GCC OR Vc_COMPILER_IS_CLANG)
AddCompilerFlag("-std=c++11" _ok)
if(NOT _ok)
AddCompilerFlag("-std=c++0x" _ok)
endif()
endif()
macro(ko_compile_for_all_implementations_no_scalar _objs _src)
vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2)
endmacro()
macro(ko_compile_for_all_implementations _objs _src)
vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY Scalar SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2)
endmacro()
endif()
set(CMAKE_MODULE_PATH ${OLD_CMAKE_MODULE_PATH} )
add_definitions(${QT_DEFINITIONS} ${QT_QTDBUS_DEFINITIONS})
##
## Test endianness
##
include (TestBigEndian)
test_big_endian(CMAKE_WORDS_BIGENDIAN)
##
## Test for qt-poppler
##
find_package(Poppler COMPONENTS Qt5)
set_package_properties(Poppler PROPERTIES
DESCRIPTION "A PDF rendering library"
URL "http://poppler.freedesktop.org"
TYPE OPTIONAL
PURPOSE "Required by the Krita PDF filter.")
+
+##
+## Test for quazip
+##
+find_package(QuaZip 0.6)
+set_package_properties(QuaZip PROPERTIES
+ DESCRIPTION "A library for reading and writing zip files"
+ URL "https://stachenov.github.io/quazip/"
+ TYPE REQUIRED
+ PURPOSE "Needed for reading and writing KRA and ORA files"
+)
+
+
+
+##
+## Test for Atomics
+##
+include(CheckAtomic)
+
############################
#############################
## Add Krita helper macros ##
#############################
############################
include(MacroKritaAddBenchmark)
####################
#####################
## Define includes ##
#####################
####################
# for config.h and <toplevel/foo.h> includes (if any?)
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_SOURCE_DIR}/interfaces
)
add_subdirectory(libs)
add_subdirectory(plugins)
add_subdirectory(benchmarks)
add_subdirectory(krita)
configure_file(KoConfig.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/KoConfig.h )
configure_file(config_convolution.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config_convolution.h)
configure_file(config-ocio.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-ocio.h )
check_function_exists(powf HAVE_POWF)
configure_file(config-powf.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-powf.h)
if(WIN32)
include(${CMAKE_CURRENT_LIST_DIR}/packaging/windows/installer/ConfigureInstallerNsis.cmake)
endif()
message("\nBroken tests:")
foreach(tst ${KRITA_BROKEN_TESTS})
message(" * ${tst}")
endforeach()
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/po OR EXISTS ${CMAKE_CURRENT_BINARY_DIR}/po )
find_package(KF5I18n CONFIG REQUIRED)
ki18n_install(po)
endif()
diff --git a/benchmarks/kis_composition_benchmark.cpp b/benchmarks/kis_composition_benchmark.cpp
index 3925ee1d25..81b28defb0 100644
--- a/benchmarks/kis_composition_benchmark.cpp
+++ b/benchmarks/kis_composition_benchmark.cpp
@@ -1,950 +1,950 @@
/*
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.com>
* Copyright (c) 2015 Thorsten Zachmann <zachmann@kde.org>
*
* 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.
*/
// for calculation of the needed alignment
#include <config-vc.h>
#ifdef HAVE_VC
#if defined _MSC_VER
// Lets shut up the "possible loss of data" and "forcing value to bool 'true' or 'false'
#pragma warning ( push )
#pragma warning ( disable : 4244 )
#pragma warning ( disable : 4800 )
#endif
#include <Vc/Vc>
#include <Vc/IO>
#if defined _MSC_VER
#pragma warning ( pop )
#endif
#include <KoOptimizedCompositeOpOver32.h>
#include <KoOptimizedCompositeOpOver128.h>
#include <KoOptimizedCompositeOpAlphaDarken32.h>
#endif
#include "kis_composition_benchmark.h"
#include <QTest>
#include <KoColorSpace.h>
#include <KoCompositeOp.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorSpaceTraits.h>
#include <KoCompositeOpAlphaDarken.h>
#include <KoCompositeOpOver.h>
#include "KoOptimizedCompositeOpFactory.h"
// for posix_memalign()
#include <stdlib.h>
#include <kis_debug.h>
#if defined _MSC_VER
#define MEMALIGN_ALLOC(p, a, s) ((*(p)) = _aligned_malloc((s), (a)), *(p) ? 0 : errno)
#define MEMALIGN_FREE(p) _aligned_free((p))
#else
#define MEMALIGN_ALLOC(p, a, s) posix_memalign((p), (a), (s))
#define MEMALIGN_FREE(p) free((p))
#endif
const int alpha_pos = 3;
enum AlphaRange {
ALPHA_ZERO,
ALPHA_UNIT,
ALPHA_RANDOM
};
template <typename channel_type, class RandomGenerator>
inline channel_type generateAlphaValue(AlphaRange range, RandomGenerator &rnd) {
channel_type value = 0;
switch (range) {
case ALPHA_ZERO:
break;
case ALPHA_UNIT:
value = rnd.unit();
break;
case ALPHA_RANDOM:
value = rnd();
break;
}
return value;
}
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_smallint.hpp>
#include <boost/random/uniform_real.hpp>
template <typename channel_type>
struct RandomGenerator {
channel_type operator() () {
qFatal("Wrong template instantiation");
return channel_type(0);
}
channel_type unit() {
qFatal("Wrong template instantiation");
return channel_type(0);
}
};
template <>
struct RandomGenerator<quint8>
{
RandomGenerator(int seed)
: m_smallint(0,255),
m_rnd(seed)
{
}
quint8 operator() () {
return m_smallint(m_rnd);
}
quint8 unit() {
return KoColorSpaceMathsTraits<quint8>::unitValue;
}
boost::uniform_smallint<int> m_smallint;
boost::mt11213b m_rnd;
};
template <>
struct RandomGenerator<float>
{
RandomGenerator(int seed)
: m_rnd(seed)
{
}
float operator() () {
//return float(m_rnd()) / float(m_rnd.max());
return m_smallfloat(m_rnd);
}
float unit() {
return KoColorSpaceMathsTraits<float>::unitValue;
}
boost::uniform_real<float> m_smallfloat;
boost::mt11213b m_rnd;
};
template <>
struct RandomGenerator<double> : RandomGenerator<float>
{
RandomGenerator(int seed)
: RandomGenerator<float>(seed)
{
}
};
template <typename channel_type>
void generateDataLine(uint seed, int numPixels, quint8 *srcPixels, quint8 *dstPixels, quint8 *mask, AlphaRange srcAlphaRange, AlphaRange dstAlphaRange)
{
Q_ASSERT(numPixels >= 4);
RandomGenerator<channel_type> rnd(seed);
RandomGenerator<quint8> maskRnd(seed + 1);
channel_type *srcArray = reinterpret_cast<channel_type*>(srcPixels);
channel_type *dstArray = reinterpret_cast<channel_type*>(dstPixels);
for (int i = 0; i < numPixels; i++) {
for (int j = 0; j < 3; j++) {
channel_type s = rnd();
channel_type d = rnd();
*(srcArray++) = s;
*(dstArray++) = d;
}
channel_type sa = generateAlphaValue<channel_type>(srcAlphaRange, rnd);
channel_type da = generateAlphaValue<channel_type>(dstAlphaRange, rnd);
*(srcArray++) = sa;
*(dstArray++) = da;
*(mask++) = maskRnd();
}
}
void printData(int numPixels, quint8 *srcPixels, quint8 *dstPixels, quint8 *mask)
{
for (int i = 0; i < numPixels; i++) {
dbgKrita << "Src: "
<< srcPixels[i*4] << "\t"
<< srcPixels[i*4+1] << "\t"
<< srcPixels[i*4+2] << "\t"
<< srcPixels[i*4+3] << "\t"
<< "Msk:" << mask[i];
dbgKrita << "Dst: "
<< dstPixels[i*4] << "\t"
<< dstPixels[i*4+1] << "\t"
<< dstPixels[i*4+2] << "\t"
<< dstPixels[i*4+3];
}
}
const int rowStride = 64;
const int totalRows = 64;
const QRect processRect(0,0,64,64);
const int numPixels = rowStride * totalRows;
const int numTiles = 1024;
struct Tile {
quint8 *src;
quint8 *dst;
quint8 *mask;
};
#include <stdint.h>
QVector<Tile> generateTiles(int size,
const int srcAlignmentShift,
const int dstAlignmentShift,
AlphaRange srcAlphaRange,
AlphaRange dstAlphaRange,
const quint32 pixelSize)
{
QVector<Tile> tiles(size);
#ifdef HAVE_VC
const int vecSize = Vc::float_v::size();
#else
const int vecSize = 1;
#endif
// the 256 are used to make sure that we have a good alignment no matter what build options are used.
const size_t pixelAlignment = qMax(size_t(vecSize * sizeof(float)), size_t(256));
const size_t maskAlignment = qMax(size_t(vecSize), size_t(256));
for (int i = 0; i < size; i++) {
void *ptr = 0;
int error = MEMALIGN_ALLOC(&ptr, pixelAlignment, numPixels * pixelSize + srcAlignmentShift);
if (error) {
qFatal("posix_memalign failed: %d", error);
}
tiles[i].src = (quint8*)ptr + srcAlignmentShift;
error = MEMALIGN_ALLOC(&ptr, pixelAlignment, numPixels * pixelSize + dstAlignmentShift);
if (error) {
qFatal("posix_memalign failed: %d", error);
}
tiles[i].dst = (quint8*)ptr + dstAlignmentShift;
error = MEMALIGN_ALLOC(&ptr, maskAlignment, numPixels);
if (error) {
qFatal("posix_memalign failed: %d", error);
}
tiles[i].mask = (quint8*)ptr;
if (pixelSize == 4) {
generateDataLine<quint8>(1, numPixels, tiles[i].src, tiles[i].dst, tiles[i].mask, srcAlphaRange, dstAlphaRange);
} else if (pixelSize == 16) {
generateDataLine<float>(1, numPixels, tiles[i].src, tiles[i].dst, tiles[i].mask, srcAlphaRange, dstAlphaRange);
} else {
qFatal("Pixel size %i is not implemented", pixelSize);
}
}
return tiles;
}
void freeTiles(QVector<Tile> tiles,
const int srcAlignmentShift,
const int dstAlignmentShift)
{
Q_FOREACH (const Tile &tile, tiles) {
MEMALIGN_FREE(tile.src - srcAlignmentShift);
MEMALIGN_FREE(tile.dst - dstAlignmentShift);
MEMALIGN_FREE(tile.mask);
}
}
template <typename channel_type>
inline bool fuzzyCompare(channel_type a, channel_type b, channel_type prec) {
return qAbs(a - b) <= prec;
}
template <typename channel_type>
inline bool comparePixels(channel_type *p1, channel_type *p2, channel_type prec) {
return (p1[3] == p2[3] && p1[3] == 0) ||
(fuzzyCompare(p1[0], p2[0], prec) &&
fuzzyCompare(p1[1], p2[1], prec) &&
fuzzyCompare(p1[2], p2[2], prec) &&
fuzzyCompare(p1[3], p2[3], prec));
}
template <typename channel_type>
bool compareTwoOpsPixels(QVector<Tile> &tiles, channel_type prec) {
channel_type *dst1 = reinterpret_cast<channel_type*>(tiles[0].dst);
channel_type *dst2 = reinterpret_cast<channel_type*>(tiles[1].dst);
channel_type *src1 = reinterpret_cast<channel_type*>(tiles[0].src);
channel_type *src2 = reinterpret_cast<channel_type*>(tiles[1].src);
for (int i = 0; i < numPixels; i++) {
if (!comparePixels<channel_type>(dst1, dst2, prec)) {
dbgKrita << "Wrong result:" << i;
dbgKrita << "Act: " << dst1[0] << dst1[1] << dst1[2] << dst1[3];
dbgKrita << "Exp: " << dst2[0] << dst2[1] << dst2[2] << dst2[3];
dbgKrita << "Dif: " << dst1[0] - dst2[0] << dst1[1] - dst2[1] << dst1[2] - dst2[2] << dst1[3] - dst2[3];
channel_type *s1 = src1 + 4 * i;
channel_type *s2 = src2 + 4 * i;
dbgKrita << "SrcA:" << s1[0] << s1[1] << s1[2] << s1[3];
dbgKrita << "SrcE:" << s2[0] << s2[1] << s2[2] << s2[3];
dbgKrita << "MskA:" << tiles[0].mask[i];
dbgKrita << "MskE:" << tiles[1].mask[i];
return false;
}
dst1 += 4;
dst2 += 4;
}
return true;
}
bool compareTwoOps(bool haveMask, const KoCompositeOp *op1, const KoCompositeOp *op2)
{
Q_ASSERT(op1->colorSpace()->pixelSize() == op2->colorSpace()->pixelSize());
const quint32 pixelSize = op1->colorSpace()->pixelSize();
const int alignment = 16;
QVector<Tile> tiles = generateTiles(2, alignment, alignment, ALPHA_RANDOM, ALPHA_RANDOM, op1->colorSpace()->pixelSize());
KoCompositeOp::ParameterInfo params;
params.dstRowStride = 4 * rowStride;
params.srcRowStride = 4 * rowStride;
params.maskRowStride = rowStride;
params.rows = processRect.height();
params.cols = processRect.width();
// This is a hack as in the old version we get a rounding of opacity to this value
params.opacity = float(Arithmetic::scale<quint8>(0.5*1.0f))/255.0;
params.flow = 0.3*1.0f;
params.channelFlags = QBitArray();
params.dstRowStart = tiles[0].dst;
params.srcRowStart = tiles[0].src;
params.maskRowStart = haveMask ? tiles[0].mask : 0;
op1->composite(params);
params.dstRowStart = tiles[1].dst;
params.srcRowStart = tiles[1].src;
params.maskRowStart = haveMask ? tiles[1].mask : 0;
op2->composite(params);
bool compareResult = true;
if (pixelSize == 4) {
compareResult = compareTwoOpsPixels<quint8>(tiles, 10);
}
else if (pixelSize == 16) {
compareResult = compareTwoOpsPixels<float>(tiles, 2e-7);
}
else {
qFatal("Pixel size %i is not implemented", pixelSize);
}
freeTiles(tiles, alignment, alignment);
return compareResult;
}
QString getTestName(bool haveMask,
const int srcAlignmentShift,
const int dstAlignmentShift,
AlphaRange srcAlphaRange,
AlphaRange dstAlphaRange)
{
QString testName;
testName +=
!srcAlignmentShift && !dstAlignmentShift ? "Aligned " :
!srcAlignmentShift && dstAlignmentShift ? "SrcUnalig " :
srcAlignmentShift && !dstAlignmentShift ? "DstUnalig " :
srcAlignmentShift && dstAlignmentShift ? "Unaligned " : "###";
testName += haveMask ? "Mask " : "NoMask ";
testName +=
srcAlphaRange == ALPHA_RANDOM ? "SrcRand " :
srcAlphaRange == ALPHA_ZERO ? "SrcZero " :
srcAlphaRange == ALPHA_UNIT ? "SrcUnit " : "###";
testName +=
dstAlphaRange == ALPHA_RANDOM ? "DstRand" :
dstAlphaRange == ALPHA_ZERO ? "DstZero" :
dstAlphaRange == ALPHA_UNIT ? "DstUnit" : "###";
return testName;
}
void benchmarkCompositeOp(const KoCompositeOp *op,
bool haveMask,
qreal opacity,
qreal flow,
const int srcAlignmentShift,
const int dstAlignmentShift,
AlphaRange srcAlphaRange,
AlphaRange dstAlphaRange)
{
QString testName = getTestName(haveMask, srcAlignmentShift, dstAlignmentShift, srcAlphaRange, dstAlphaRange);
QVector<Tile> tiles =
generateTiles(numTiles, srcAlignmentShift, dstAlignmentShift, srcAlphaRange, dstAlphaRange, op->colorSpace()->pixelSize());
const int tileOffset = 4 * (processRect.y() * rowStride + processRect.x());
KoCompositeOp::ParameterInfo params;
params.dstRowStride = 4 * rowStride;
params.srcRowStride = 4 * rowStride;
params.maskRowStride = rowStride;
params.rows = processRect.height();
params.cols = processRect.width();
params.opacity = opacity;
params.flow = flow;
params.channelFlags = QBitArray();
QTime timer;
timer.start();
Q_FOREACH (const Tile &tile, tiles) {
params.dstRowStart = tile.dst + tileOffset;
params.srcRowStart = tile.src + tileOffset;
params.maskRowStart = haveMask ? tile.mask : 0;
op->composite(params);
}
dbgKrita << testName << "RESULT:" << timer.elapsed() << "msec";
freeTiles(tiles, srcAlignmentShift, dstAlignmentShift);
}
void benchmarkCompositeOp(const KoCompositeOp *op, const QString &postfix)
{
dbgKrita << "Testing Composite Op:" << op->id() << "(" << postfix << ")";
benchmarkCompositeOp(op, true, 0.5, 0.3, 0, 0, ALPHA_RANDOM, ALPHA_RANDOM);
benchmarkCompositeOp(op, true, 0.5, 0.3, 8, 0, ALPHA_RANDOM, ALPHA_RANDOM);
benchmarkCompositeOp(op, true, 0.5, 0.3, 0, 8, ALPHA_RANDOM, ALPHA_RANDOM);
benchmarkCompositeOp(op, true, 0.5, 0.3, 4, 8, ALPHA_RANDOM, ALPHA_RANDOM);
/// --- Vary the content of the source and destination
benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_RANDOM, ALPHA_RANDOM);
benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_ZERO, ALPHA_RANDOM);
benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_UNIT, ALPHA_RANDOM);
/// ---
benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_RANDOM, ALPHA_ZERO);
benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_ZERO, ALPHA_ZERO);
benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_UNIT, ALPHA_ZERO);
/// ---
benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_RANDOM, ALPHA_UNIT);
benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_ZERO, ALPHA_UNIT);
benchmarkCompositeOp(op, false, 1.0, 1.0, 0, 0, ALPHA_UNIT, ALPHA_UNIT);
}
#ifdef HAVE_VC
template<class Compositor>
void checkRounding(qreal opacity, qreal flow, qreal averageOpacity = -1, quint32 pixelSize = 4)
{
QVector<Tile> tiles =
generateTiles(2, 0, 0, ALPHA_RANDOM, ALPHA_RANDOM, pixelSize);
const int vecSize = Vc::float_v::size();
const int numBlocks = numPixels / vecSize;
quint8 *src1 = tiles[0].src;
quint8 *dst1 = tiles[0].dst;
quint8 *msk1 = tiles[0].mask;
quint8 *src2 = tiles[1].src;
quint8 *dst2 = tiles[1].dst;
quint8 *msk2 = tiles[1].mask;
KoCompositeOp::ParameterInfo params;
params.opacity = opacity;
params.flow = flow;
if (averageOpacity >= 0.0) {
params._lastOpacityData = averageOpacity;
params.lastOpacity = &params._lastOpacityData;
}
params.channelFlags = QBitArray();
- typename Compositor::OptionalParams optionalParams(params);
+ typename Compositor::ParamsWrapper paramsWrapper(params);
// The error count is needed as 38.5 gets rounded to 38 instead of 39 in the vc version.
int errorcount = 0;
for (int i = 0; i < numBlocks; i++) {
- Compositor::template compositeVector<true,true, Vc::CurrentImplementation::current()>(src1, dst1, msk1, params.opacity, optionalParams);
+ Compositor::template compositeVector<true,true, Vc::CurrentImplementation::current()>(src1, dst1, msk1, params.opacity, paramsWrapper);
for (int j = 0; j < vecSize; j++) {
//if (8 * i + j == 7080) {
// dbgKrita << "src: " << src2[0] << src2[1] << src2[2] << src2[3];
// dbgKrita << "dst: " << dst2[0] << dst2[1] << dst2[2] << dst2[3];
// dbgKrita << "msk:" << msk2[0];
//}
- Compositor::template compositeOnePixelScalar<true, Vc::CurrentImplementation::current()>(src2, dst2, msk2, params.opacity, optionalParams);
+ Compositor::template compositeOnePixelScalar<true, Vc::CurrentImplementation::current()>(src2, dst2, msk2, params.opacity, paramsWrapper);
bool compareResult = true;
if (pixelSize == 4) {
compareResult = comparePixels<quint8>(dst1, dst2, 0);
if (!compareResult) {
++errorcount;
compareResult = comparePixels<quint8>(dst1, dst2, 1);
if (!compareResult) {
++errorcount;
}
}
}
else if (pixelSize == 16) {
compareResult = comparePixels<float>(reinterpret_cast<float*>(dst1), reinterpret_cast<float*>(dst2), 0);
}
else {
qFatal("Pixel size %i is not implemented", pixelSize);
}
if(!compareResult || errorcount > 1) {
dbgKrita << "Wrong rounding in pixel:" << 8 * i + j;
dbgKrita << "Vector version: " << dst1[0] << dst1[1] << dst1[2] << dst1[3];
dbgKrita << "Scalar version: " << dst2[0] << dst2[1] << dst2[2] << dst2[3];
dbgKrita << "src:" << src1[0] << src1[1] << src1[2] << src1[3];
dbgKrita << "msk:" << msk1[0];
QFAIL("Wrong rounding");
}
src1 += pixelSize;
dst1 += pixelSize;
src2 += pixelSize;
dst2 += pixelSize;
msk1++;
msk2++;
}
}
freeTiles(tiles, 0, 0);
}
#endif
void KisCompositionBenchmark::checkRoundingAlphaDarken_05_03()
{
#ifdef HAVE_VC
checkRounding<AlphaDarkenCompositor32<quint8, quint32> >(0.5,0.3);
#endif
}
void KisCompositionBenchmark::checkRoundingAlphaDarken_05_05()
{
#ifdef HAVE_VC
checkRounding<AlphaDarkenCompositor32<quint8, quint32> >(0.5,0.5);
#endif
}
void KisCompositionBenchmark::checkRoundingAlphaDarken_05_07()
{
#ifdef HAVE_VC
checkRounding<AlphaDarkenCompositor32<quint8, quint32> >(0.5,0.7);
#endif
}
void KisCompositionBenchmark::checkRoundingAlphaDarken_05_10()
{
#ifdef HAVE_VC
checkRounding<AlphaDarkenCompositor32<quint8, quint32> >(0.5,1.0);
#endif
}
void KisCompositionBenchmark::checkRoundingAlphaDarken_05_10_08()
{
#ifdef HAVE_VC
checkRounding<AlphaDarkenCompositor32<quint8, quint32> >(0.5,1.0,0.8);
#endif
}
void KisCompositionBenchmark::checkRoundingAlphaDarkenF32_05_03()
{
#ifdef HAVE_VC
checkRounding<OverCompositor128<float, float, false, true> >(0.5, 0.3, -1, 16);
#endif
}
void KisCompositionBenchmark::checkRoundingAlphaDarkenF32_05_05()
{
#ifdef HAVE_VC
checkRounding<OverCompositor128<float, float, false, true> >(0.5, 0.5, -1, 16);
#endif
}
void KisCompositionBenchmark::checkRoundingAlphaDarkenF32_05_07()
{
#ifdef HAVE_VC
checkRounding<OverCompositor128<float, float, false, true> >(0.5, 0.7, -1, 16);
#endif
}
void KisCompositionBenchmark::checkRoundingAlphaDarkenF32_05_10()
{
#ifdef HAVE_VC
checkRounding<OverCompositor128<float, float, false, true> >(0.5, 1.0, -1, 16);
#endif
}
void KisCompositionBenchmark::checkRoundingAlphaDarkenF32_05_10_08()
{
#ifdef HAVE_VC
checkRounding<OverCompositor128<float, float, false, true> >(0.5, 1.0, 0.8, 16);
#endif
}
void KisCompositionBenchmark::checkRoundingOver()
{
#ifdef HAVE_VC
checkRounding<OverCompositor32<quint8, quint32, false, true> >(0.5, 0.3);
#endif
}
void KisCompositionBenchmark::checkRoundingOverRgbaF32()
{
#ifdef HAVE_VC
checkRounding<OverCompositor128<float, float, false, true> >(0.5, 0.3, -1, 16);
#endif
}
void KisCompositionBenchmark::compareAlphaDarkenOps()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *opAct = KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs);
- KoCompositeOp *opExp = new KoCompositeOpAlphaDarken<KoBgrU8Traits>(cs);
+ KoCompositeOp *opExp = new KoCompositeOpAlphaDarken<KoBgrU8Traits, KoAlphaDarkenParamsWrapperCreamy>(cs);
QVERIFY(compareTwoOps(true, opAct, opExp));
delete opExp;
delete opAct;
}
void KisCompositionBenchmark::compareRgbF32AlphaDarkenOps()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace("RGBA", "F32", "");
KoCompositeOp *opAct = KoOptimizedCompositeOpFactory::createAlphaDarkenOp128(cs);
- KoCompositeOp *opExp = new KoCompositeOpAlphaDarken<KoRgbF32Traits>(cs);
+ KoCompositeOp *opExp = new KoCompositeOpAlphaDarken<KoRgbF32Traits, KoAlphaDarkenParamsWrapperCreamy>(cs);
QVERIFY(compareTwoOps(true, opAct, opExp));
delete opExp;
delete opAct;
}
void KisCompositionBenchmark::compareAlphaDarkenOpsNoMask()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *opAct = KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs);
- KoCompositeOp *opExp = new KoCompositeOpAlphaDarken<KoBgrU8Traits>(cs);
+ KoCompositeOp *opExp = new KoCompositeOpAlphaDarken<KoBgrU8Traits, KoAlphaDarkenParamsWrapperCreamy>(cs);
QVERIFY(compareTwoOps(false, opAct, opExp));
delete opExp;
delete opAct;
}
void KisCompositionBenchmark::compareOverOps()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *opAct = KoOptimizedCompositeOpFactory::createOverOp32(cs);
KoCompositeOp *opExp = new KoCompositeOpOver<KoBgrU8Traits>(cs);
QVERIFY(compareTwoOps(true, opAct, opExp));
delete opExp;
delete opAct;
}
void KisCompositionBenchmark::compareOverOpsNoMask()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *opAct = KoOptimizedCompositeOpFactory::createOverOp32(cs);
KoCompositeOp *opExp = new KoCompositeOpOver<KoBgrU8Traits>(cs);
QVERIFY(compareTwoOps(false, opAct, opExp));
delete opExp;
delete opAct;
}
void KisCompositionBenchmark::compareRgbF32OverOps()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace("RGBA", "F32", "");
KoCompositeOp *opAct = KoOptimizedCompositeOpFactory::createOverOp128(cs);
KoCompositeOp *opExp = new KoCompositeOpOver<KoRgbF32Traits>(cs);
QVERIFY(compareTwoOps(false, opAct, opExp));
delete opExp;
delete opAct;
}
void KisCompositionBenchmark::testRgb8CompositeAlphaDarkenLegacy()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
- KoCompositeOp *op = new KoCompositeOpAlphaDarken<KoBgrU8Traits>(cs);
+ KoCompositeOp *op = new KoCompositeOpAlphaDarken<KoBgrU8Traits, KoAlphaDarkenParamsWrapperCreamy>(cs);
benchmarkCompositeOp(op, "Legacy");
delete op;
}
void KisCompositionBenchmark::testRgb8CompositeAlphaDarkenOptimized()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *op = KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs);
benchmarkCompositeOp(op, "Optimized");
delete op;
}
void KisCompositionBenchmark::testRgb8CompositeOverLegacy()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *op = new KoCompositeOpOver<KoBgrU8Traits>(cs);
benchmarkCompositeOp(op, "Legacy");
delete op;
}
void KisCompositionBenchmark::testRgb8CompositeOverOptimized()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoCompositeOp *op = KoOptimizedCompositeOpFactory::createOverOp32(cs);
benchmarkCompositeOp(op, "Optimized");
delete op;
}
void KisCompositionBenchmark::testRgbF32CompositeAlphaDarkenLegacy()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace("RGBA", "F32", "");
- KoCompositeOp *op = new KoCompositeOpAlphaDarken<KoRgbF32Traits>(cs);
+ KoCompositeOp *op = new KoCompositeOpAlphaDarken<KoRgbF32Traits, KoAlphaDarkenParamsWrapperCreamy>(cs);
benchmarkCompositeOp(op, "Legacy");
delete op;
}
void KisCompositionBenchmark::testRgbF32CompositeAlphaDarkenOptimized()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace("RGBA", "F32", "");
KoCompositeOp *op = KoOptimizedCompositeOpFactory::createAlphaDarkenOp128(cs);
benchmarkCompositeOp(op, "Optimized");
delete op;
}
void KisCompositionBenchmark::testRgbF32CompositeOverLegacy()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace("RGBA", "F32", "");
KoCompositeOp *op = new KoCompositeOpOver<KoRgbF32Traits>(cs);
benchmarkCompositeOp(op, "RGBF32 Legacy");
delete op;
}
void KisCompositionBenchmark::testRgbF32CompositeOverOptimized()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace("RGBA", "F32", "");
KoCompositeOp *op = KoOptimizedCompositeOpFactory::createOverOp128(cs);
benchmarkCompositeOp(op, "RGBF32 Optimized");
delete op;
}
void KisCompositionBenchmark::testRgb8CompositeAlphaDarkenReal_Aligned()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
const KoCompositeOp *op = cs->compositeOp(COMPOSITE_ALPHA_DARKEN);
benchmarkCompositeOp(op, true, 0.5, 0.3, 0, 0, ALPHA_RANDOM, ALPHA_RANDOM);
}
void KisCompositionBenchmark::testRgb8CompositeOverReal_Aligned()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
const KoCompositeOp *op = cs->compositeOp(COMPOSITE_OVER);
benchmarkCompositeOp(op, true, 0.5, 0.3, 0, 0, ALPHA_RANDOM, ALPHA_RANDOM);
}
void KisCompositionBenchmark::testRgb8CompositeCopyLegacy()
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
const KoCompositeOp *op = cs->compositeOp(COMPOSITE_COPY);
benchmarkCompositeOp(op, "Copy");
}
void KisCompositionBenchmark::benchmarkMemcpy()
{
QVector<Tile> tiles =
generateTiles(numTiles, 0, 0, ALPHA_UNIT, ALPHA_UNIT, 4);
QBENCHMARK_ONCE {
Q_FOREACH (const Tile &tile, tiles) {
memcpy(tile.dst, tile.src, 4 * numPixels);
}
}
freeTiles(tiles, 0, 0);
}
#ifdef HAVE_VC
const int vecSize = Vc::float_v::size();
const size_t uint8VecAlignment = qMax(vecSize * sizeof(quint8), sizeof(void*));
const size_t uint32VecAlignment = qMax(vecSize * sizeof(quint32), sizeof(void*));
const size_t floatVecAlignment = qMax(vecSize * sizeof(float), sizeof(void*));
#endif
void KisCompositionBenchmark::benchmarkUintFloat()
{
#ifdef HAVE_VC
using uint_v = Vc::SimdArray<unsigned int, Vc::float_v::size()>;
const int dataSize = 4096;
void *ptr = 0;
int error = MEMALIGN_ALLOC(&ptr, uint8VecAlignment, dataSize);
if (error) {
qFatal("posix_memalign failed: %d", error);
}
quint8 *iData = (quint8*)ptr;
error = MEMALIGN_ALLOC(&ptr, floatVecAlignment, dataSize * sizeof(float));
if (error) {
qFatal("posix_memalign failed: %d", error);
}
float *fData = (float*)ptr;
QBENCHMARK {
for (int i = 0; i < dataSize; i += Vc::float_v::size()) {
// convert uint -> float directly, this causes
// static_cast helper be called
Vc::float_v b(uint_v(iData + i));
b.store(fData + i);
}
}
MEMALIGN_FREE(iData);
MEMALIGN_FREE(fData);
#endif
}
void KisCompositionBenchmark::benchmarkUintIntFloat()
{
#ifdef HAVE_VC
using int_v = Vc::SimdArray<int, Vc::float_v::size()>;
using uint_v = Vc::SimdArray<unsigned int, Vc::float_v::size()>;
const int dataSize = 4096;
void *ptr = 0;
int error = MEMALIGN_ALLOC(&ptr, uint8VecAlignment, dataSize);
if (error) {
qFatal("posix_memalign failed: %d", error);
}
quint8 *iData = (quint8*)ptr;
error = MEMALIGN_ALLOC(&ptr, floatVecAlignment, dataSize * sizeof(float));
if (error) {
qFatal("posix_memalign failed: %d", error);
}
float *fData = (float*)ptr;
QBENCHMARK {
for (int i = 0; i < dataSize; i += Vc::float_v::size()) {
// convert uint->int->float, that avoids special sign
// treating, and gives 2.6 times speedup
Vc::float_v b(int_v(uint_v(iData + i)));
b.store(fData + i);
}
}
MEMALIGN_FREE(iData);
MEMALIGN_FREE(fData);
#endif
}
void KisCompositionBenchmark::benchmarkFloatUint()
{
#ifdef HAVE_VC
using uint_v = Vc::SimdArray<unsigned int, Vc::float_v::size()>;
const int dataSize = 4096;
void *ptr = 0;
int error = MEMALIGN_ALLOC(&ptr, uint32VecAlignment, dataSize * sizeof(quint32));
if (error) {
qFatal("posix_memalign failed: %d", error);
}
quint32 *iData = (quint32*)ptr;
error = MEMALIGN_ALLOC(&ptr, floatVecAlignment, dataSize * sizeof(float));
if (error) {
qFatal("posix_memalign failed: %d", error);
}
float *fData = (float*)ptr;
QBENCHMARK {
for (int i = 0; i < dataSize; i += Vc::float_v::size()) {
// conversion float -> uint
uint_v b(Vc::float_v(fData + i));
b.store(iData + i);
}
}
MEMALIGN_FREE(iData);
MEMALIGN_FREE(fData);
#endif
}
void KisCompositionBenchmark::benchmarkFloatIntUint()
{
#ifdef HAVE_VC
using int_v = Vc::SimdArray<int, Vc::float_v::size()>;
using uint_v = Vc::SimdArray<unsigned int, Vc::float_v::size()>;
const int dataSize = 4096;
void *ptr = 0;
int error = MEMALIGN_ALLOC(&ptr, uint32VecAlignment, dataSize * sizeof(quint32));
if (error) {
qFatal("posix_memalign failed: %d", error);
}
quint32 *iData = (quint32*)ptr;
error = MEMALIGN_ALLOC(&ptr, floatVecAlignment, dataSize * sizeof(float));
if (error) {
qFatal("posix_memalign failed: %d", error);
}
float *fData = (float*)ptr;
QBENCHMARK {
for (int i = 0; i < dataSize; i += Vc::float_v::size()) {
// conversion float -> int -> uint
uint_v b(int_v(Vc::float_v(fData + i)));
b.store(iData + i);
}
}
MEMALIGN_FREE(iData);
MEMALIGN_FREE(fData);
#endif
}
QTEST_MAIN(KisCompositionBenchmark)
diff --git a/build-tools/windows/build.cmd b/build-tools/windows/build.cmd
index a8cb71fb4c..6713d5f0dc 100644
--- a/build-tools/windows/build.cmd
+++ b/build-tools/windows/build.cmd
@@ -1,770 +1,785 @@
@echo off
setlocal enabledelayedexpansion
goto begin
:: Subroutines
:find_on_path out_variable file_name
set %1=%~f$PATH:2
goto :EOF
:get_dir_path out_variable file_path
set %1=%~dp2
goto :EOF
:get_full_path out_variable file_path
setlocal
set FULL_PATH=%~f2
if not exist "%FULL_PATH%" (
set FULL_PATH=
) else (
if exist "%FULL_PATH%\" (
set FULL_PATH=
)
)
endlocal & set "%1=%FULL_PATH%"
goto :EOF
:get_full_path_dir out_variable file_path
setlocal
set FULL_PATH=%~dp2
if not exist "%FULL_PATH%" (
set FULL_PATH=
)
endlocal & set "%1=%FULL_PATH%"
goto :EOF
:prompt_for_string out_variable prompt
set /p %1=%~2^>
goto :EOF
:prompt_for_positive_integer out_variable prompt
setlocal
call :prompt_for_string USER_INPUT "%~2"
if "%USER_INPUT%" == "" set USER_INPUT=0
set /a RESULT=%USER_INPUT%
if not %RESULT% GTR 0 (
set RESULT=
)
endlocal & set "%1=%RESULT%"
goto :EOF
:prompt_for_file out_variable prompt
setlocal
:prompt_for_file__retry
call :prompt_for_string USER_INPUT "%~2"
if "%USER_INPUT%" == "" (
endlocal
set %1=
goto :EOF
)
call :get_full_path RESULT "%USER_INPUT%"
if "%RESULT%" == "" (
echo Input does not point to valid file!
set USER_INPUT=
goto prompt_for_file__retry
)
endlocal & set "%1=%RESULT%"
goto :EOF
:prompt_for_dir out_variable prompt
setlocal
:prompt_for_dir__retry
call :prompt_for_string USER_INPUT "%~2"
if "%USER_INPUT%" == "" (
endlocal
set %1=
goto :EOF
)
call :get_full_path_dir RESULT "%USER_INPUT%\"
if "%RESULT%" == "" (
echo Input does not point to valid dir!
set USER_INPUT=
goto prompt_for_dir__retry
)
endlocal & set "%1=%RESULT%"
goto :EOF
:usage
echo Usage:
echo %~n0 [--no-interactive] [ OPTIONS ... ]
echo.
echo Basic options:
echo --no-interactive Run without interactive prompts
echo When not specified, the script will prompt
echo for some of the parameters.
echo --jobs ^<count^> Set parallel jobs count when building
echo Defaults to no. of logical cores
echo --skip-deps Skips (re)building of deps
echo --skip-krita Skips (re)building of Krita
echo.
echo Path options:
echo --src-dir ^<dir_path^> Specify Krita source dir
echo If unspecified, this will be determined from
echo the script location.
echo --download-dir ^<dir_path^> Specify deps download dir
echo Can be omitted if --skip-deps is used
echo --deps-build-dir ^<dir_path^> Specify deps build dir
echo Can be omitted if --skip-deps is used
echo --deps-install-dir ^<dir_path^> Specify deps install dir
echo --krita-build-dir ^<dir_path^> Specify Krita build dir
echo Can be omitted if --skip-krita is used
echo --krita-install-dir ^<dir_path^> Specify Krita install dir
echo Can be omitted if --skip-krita is used
echo.
goto :EOF
:usage_and_exit
call :usage
exit /b
:usage_and_fail
call :usage
exit /b 100
:: ----------------------------
:begin
echo Krita build script for Windows
echo.
:: command-line args parsing
set ARG_NO_INTERACTIVE=
set ARG_JOBS=
set ARG_SKIP_DEPS=
set ARG_SKIP_KRITA=
set ARG_SRC_DIR=
set ARG_DOWNLOAD_DIR=
set ARG_DEPS_BUILD_DIR=
set ARG_DEPS_INSTALL_DIR=
set ARG_KRITA_BUILD_DIR=
set ARG_KRITA_INSTALL_DIR=
:args_parsing_loop
set CURRENT_MATCHED=
if not "%1" == "" (
if "%1" == "--no-interactive" (
set ARG_NO_INTERACTIVE=1
set CURRENT_MATCHED=1
)
if "%1" == "--jobs" (
if not "%ARG_JOBS%" == "" (
echo ERROR: Arg --jobs specified more than once 1>&2
echo.
goto usage_and_fail
)
set /a "ARG_JOBS=%2"
if not !ARG_JOBS! GTR 0 (
echo ERROR: Arg --jobs is not a positive integer 1>&2
echo.
goto usage_and_fail
)
shift /2
set CURRENT_MATCHED=1
)
if "%1" == "--skip-deps" (
set ARG_SKIP_DEPS=1
set CURRENT_MATCHED=1
)
if "%1" == "--skip-krita" (
set ARG_SKIP_KRITA=1
set CURRENT_MATCHED=1
)
if "%1" == "--src-dir" (
if not "%ARG_SRC_DIR%" == "" (
echo ERROR: Arg --src-dir specified more than once 1>&2
echo.
goto usage_and_fail
)
if not exist "%~f2\" (
echo ERROR: Arg --src-dir does not point to a directory 1>&2
echo.
goto usage_and_fail
)
call :get_dir_path ARG_SRC_DIR "%~f2\"
shift /2
set CURRENT_MATCHED=1
)
if "%1" == "--download-dir" (
if not "%ARG_DOWNLOAD_DIR%" == "" (
echo ERROR: Arg --download-dir specified more than once 1>&2
echo.
goto usage_and_fail
)
if "%~f2" == "" (
echo ERROR: Arg --download-dir does not point to a valid path 1>&2
echo.
goto usage_and_fail
)
call :get_dir_path ARG_DOWNLOAD_DIR "%~f2\"
shift /2
set CURRENT_MATCHED=1
)
if "%1" == "--deps-build-dir" (
if not "%ARG_DEPS_BUILD_DIR%" == "" (
echo ERROR: Arg --deps-build-dir specified more than once 1>&2
echo.
goto usage_and_fail
)
if "%~f2" == "" (
echo ERROR: Arg --deps-build-dir does not point to a valid path 1>&2
echo.
goto usage_and_fail
)
call :get_dir_path ARG_DEPS_BUILD_DIR "%~f2\"
shift /2
set CURRENT_MATCHED=1
)
if "%1" == "--deps-install-dir" (
if not "%ARG_DEPS_INSTALL_DIR%" == "" (
echo ERROR: Arg --deps-install-dir specified more than once 1>&2
echo.
goto usage_and_fail
)
if "%~f2" == "" (
echo ERROR: Arg --deps-install-dir does not point to a valid path 1>&2
echo.
goto usage_and_fail
)
call :get_dir_path ARG_DEPS_INSTALL_DIR "%~f2\"
shift /2
set CURRENT_MATCHED=1
)
if "%1" == "--krita-build-dir" (
if not "%ARG_KRITA_BUILD_DIR%" == "" (
echo ERROR: Arg --krita-build-dir specified more than once 1>&2
echo.
goto usage_and_fail
)
if "%~f2" == "" (
echo ERROR: Arg --krita-build-dir does not point to a valid path 1>&2
echo.
goto usage_and_fail
)
call :get_dir_path ARG_KRITA_BUILD_DIR "%~f2\"
shift /2
set CURRENT_MATCHED=1
)
if "%1" == "--krita-install-dir" (
if not "%ARG_KRITA_INSTALL_DIR%" == "" (
echo ERROR: Arg --krita-install-dir specified more than once 1>&2
echo.
goto usage_and_fail
)
if "%~f2" == "" (
echo ERROR: Arg --krita-install-dir does not point to a valid path 1>&2
echo.
goto usage_and_fail
)
call :get_dir_path ARG_KRITA_INSTALL_DIR "%~f2\"
shift /2
set CURRENT_MATCHED=1
)
if "%1" == "--help" (
goto usage_and_exit
)
if not "!CURRENT_MATCHED!" == "1" (
echo ERROR: Unknown option %1 1>&2
echo.
goto usage_and_fail
)
shift /1
goto args_parsing_loop
)
if "%ARG_NO_INTERACTIVE%" == "1" (
echo Non-interactive mode
) else (
echo Interactive mode
:: Trick to pause on exit
call :real_begin
pause
exit /b !ERRORLEVEL!
)
:real_begin
echo.
if "%ARG_SKIP_DEPS%" == "1" (
if "%ARG_SKIP_KRITA%" == "1" (
echo ERROR: You cannot skip both deps and Krita 1>&2
echo.
exit /b 102
)
echo Building of deps will be skipped.
) else (
if "%ARG_SKIP_KRITA%" == "1" (
echo Building of Krita will be skipped.
) else (
echo Both deps and Krita will be built.
)
)
:: Check environment config
if "%CMAKE_EXE%" == "" (
call :find_on_path CMAKE_EXE cmake.exe
if "!CMAKE_EXE!" == "" (
if not "%ARG_NO_INTERACTIVE%" == "1" (
call :prompt_for_file CMAKE_EXE "Provide path to cmake.exe"
)
if "!CMAKE_EXE!" == "" (
echo ERROR: CMake not found! 1>&2
exit /b 102
)
) else (
echo Found CMake on PATH: !CMAKE_EXE!
if not "%ARG_NO_INTERACTIVE%" == "1" (
choice /c ny /n /m "Is this correct? [y/n] "
if errorlevel 3 exit 255
if not errorlevel 2 (
call :prompt_for_file CMAKE_EXE "Provide path to cmake.exe"
if "!CMAKE_EXE!" == "" (
echo ERROR: CMake not found! 1>&2
exit /b 102
)
)
)
)
)
echo CMake: %CMAKE_EXE%
if "%SEVENZIP_EXE%" == "" (
call :find_on_path SEVENZIP_EXE 7z.exe
if "!SEVENZIP_EXE!" == "" (
set "SEVENZIP_EXE=%ProgramFiles%\7-Zip\7z.exe"
if "!SEVENZIP_EXE!" == "" (
set "SEVENZIP_EXE=%ProgramFiles(x86)%\7-Zip\7z.exe"
)
if "!SEVENZIP_EXE!" == "" (
echo 7-Zip not found
)
)
)
if "%SEVENZIP_EXE%" == "" (
echo 7-Zip: %SEVENZIP_EXE%
)
if "%MINGW_BIN_DIR%" == "" (
call :find_on_path MINGW_BIN_DIR_MAKE_EXE mingw32-make.exe
if "!MINGW_BIN_DIR_MAKE_EXE!" == "" (
if not "%ARG_NO_INTERACTIVE%" == "1" (
call :prompt_for_file MINGW_BIN_DIR_MAKE_EXE "Provide path to mingw32-make.exe of mingw-w64"
)
if "!MINGW_BIN_DIR_MAKE_EXE!" == "" (
echo ERROR: mingw-w64 not found! 1>&2
exit /b 102
)
call :get_dir_path MINGW_BIN_DIR "!MINGW_BIN_DIR_MAKE_EXE!"
) else (
call :get_dir_path MINGW_BIN_DIR "!MINGW_BIN_DIR_MAKE_EXE!"
echo Found mingw on PATH: !MINGW_BIN_DIR!
if not "%ARG_NO_INTERACTIVE%" == "1" (
choice /c ny /n /m "Is this correct? [y/n] "
if errorlevel 3 exit 255
if not errorlevel 2 (
call :prompt_for_file MINGW_BIN_DIR_MAKE_EXE "Provide path to mingw32-make.exe of mingw-w64"
if "!MINGW_BIN_DIR_MAKE_EXE!" == "" (
echo ERROR: mingw-w64 not found! 1>&2
exit /b 102
)
call :get_dir_path MINGW_BIN_DIR "!MINGW_BIN_DIR_MAKE_EXE!"
)
)
)
)
echo mingw-w64: %MINGW_BIN_DIR%
if "%PYTHON_BIN_DIR%" == "" (
call :find_on_path PYTHON_BIN_DIR_PYTHON_EXE python.exe
if "!PYTHON_BIN_DIR_PYTHON_EXE!" == "" (
if not "%ARG_NO_INTERACTIVE%" == "1" (
call :prompt_for_file PYTHON_BIN_DIR_PYTHON_EXE "Provide path to python.exe of Python 3.6.2"
)
if "!PYTHON_BIN_DIR_PYTHON_EXE!" == "" (
echo ERROR: Python not found! 1>&2
exit /b 102
)
call :get_dir_path PYTHON_BIN_DIR "!PYTHON_BIN_DIR_PYTHON_EXE!"
) else (
call :get_dir_path PYTHON_BIN_DIR "!PYTHON_BIN_DIR_PYTHON_EXE!"
echo Found Python on PATH: !PYTHON_BIN_DIR!
if not "%ARG_NO_INTERACTIVE%" == "1" (
choice /c ny /n /m "Is this correct? [y/n] "
if errorlevel 3 exit 255
if not errorlevel 2 (
call :prompt_for_file PYTHON_BIN_DIR_PYTHON_EXE "Provide path to python.exe of Python 3.6.2"
if "!PYTHON_BIN_DIR_PYTHON_EXE!" == "" (
echo ERROR: Python not found! 1>&2
exit /b 102
)
call :get_dir_path PYTHON_BIN_DIR "!PYTHON_BIN_DIR_PYTHON_EXE!"
)
)
)
)
echo Python: %PYTHON_BIN_DIR%
if "%ARG_SKIP_DEPS%" == "1" goto skip_windows_sdk_dir_check
if "%WindowsSdkDir%" == "" if not "%ProgramFiles(x86)%" == "" set "WindowsSdkDir=%ProgramFiles(x86)%\Windows Kits\10"
if "%WindowsSdkDir%" == "" set "WindowsSdkDir=%ProgramFiles(x86)%\Windows Kits\10"
if exist "%WindowsSdkDir%\" (
pushd "%WindowsSdkDir%"
if exist "bin\x64\fxc.exe" (
set HAVE_FXC_EXE=1
if "%WindowsSdkVerBinPath%" == "" set "WindowsSdkVerBinPath=%WindowsSdkDir%"
) else (
for /f "delims=" %%a in ('dir /a:d /b "bin\10.*"') do (
if exist "bin\%%a\x64\fxc.exe" (
set HAVE_FXC_EXE=1
if "%WindowsSdkVerBinPath%" == "" set "WindowsSdkVerBinPath=%WindowsSdkDir%\bin\%%a\"
)
)
)
popd
)
set QT_ENABLE_DYNAMIC_OPENGL=ON
if not "%HAVE_FXC_EXE%" == "1" (
set WindowsSdkDir=
echo Windows SDK 10 with fxc.exe not found
echo Qt will *not* be built with ANGLE ^(dynamic OpenGL^) support.
if not "%ARG_NO_INTERACTIVE%" == "1" (
choice /c ny /n /m "Is this ok? [y/n] "
if errorlevel 3 exit 255
if not errorlevel 2 (
exit /b 102
)
)
set QT_ENABLE_DYNAMIC_OPENGL=OFF
) else echo Windows SDK 10 with fxc.exe found on %WindowsSdkDir%
:skip_windows_sdk_dir_check
if not "%ARG_JOBS%" == "" (
set "PARALLEL_JOBS=%ARG_JOBS%"
)
if "%PARALLEL_JOBS%" == "" (
echo Number of logical CPU cores detected: %NUMBER_OF_PROCESSORS%
echo Enabling %NUMBER_OF_PROCESSORS% parallel jobs
set PARALLEL_JOBS=%NUMBER_OF_PROCESSORS%
if not "%ARG_NO_INTERACTIVE%" == "1" (
choice /c ny /n /m "Is this correct? [y/n] "
if errorlevel 3 exit 255
if not errorlevel 2 (
call :prompt_for_positive_integer PARALLEL_JOBS "Provide no. of parallel jobs"
if "!PARALLEL_JOBS!" == "" (
echo ERROR: Invalid job count! 1>&2
exit /b 102
)
)
)
)
echo Parallel jobs count: %PARALLEL_JOBS%
if not "%ARG_SRC_DIR%" == "" (
set "KRITA_SRC_DIR=%ARG_SRC_DIR%"
)
if "%KRITA_SRC_DIR%" == "" (
:: Check whether this looks like to be in the source tree
set "_temp=%~dp0"
if "!_temp:~-21!" == "\build-tools\windows\" (
if exist "!_temp:~0,-21!\CMakeLists.txt" (
if exist "!_temp:~0,-21!\3rdparty\CMakeLists.txt" (
set "KRITA_SRC_DIR=!_temp:~0,-21!\"
echo Script is running inside Krita src dir
)
)
)
)
if "%KRITA_SRC_DIR%" == "" (
if not "%ARG_NO_INTERACTIVE%" == "1" (
call :prompt_for_dir KRITA_SRC_DIR "Provide path of Krita src dir"
)
if "!KRITA_SRC_DIR!" == "" (
echo ERROR: Krita src dir not found! 1>&2
exit /b 102
)
)
echo Krita src: %KRITA_SRC_DIR%
if "%ARG_SKIP_DEPS%" == "1" goto skip_deps_args_check
if not "%ARG_DOWNLOAD_DIR%" == "" (
set "DEPS_DOWNLOAD_DIR=%ARG_DOWNLOAD_DIR%"
)
if "%DEPS_DOWNLOAD_DIR%" == "" (
set DEPS_DOWNLOAD_DIR=%CD%\d\
echo Using default deps download dir: !DEPS_DOWNLOAD_DIR!
if not "%ARG_NO_INTERACTIVE%" == "1" (
choice /c ny /n /m "Is this ok? [y/n] "
if errorlevel 3 exit 255
if not errorlevel 2 (
call :prompt_for_dir DEPS_DOWNLOAD_DIR "Provide path of depps download dir"
)
)
if "!DEPS_DOWNLOAD_DIR!" == "" (
echo ERROR: Deps download dir not set! 1>&2
exit /b 102
)
)
echo Deps download dir: %DEPS_DOWNLOAD_DIR%
if not "%ARG_DEPS_BUILD_DIR%" == "" (
set "DEPS_BUILD_DIR=%ARG_DEPS_BUILD_DIR%"
)
if "%DEPS_BUILD_DIR%" == "" (
set DEPS_BUILD_DIR=%CD%\b_deps\
echo Using default deps build dir: !DEPS_BUILD_DIR!
if not "%ARG_NO_INTERACTIVE%" == "1" (
choice /c ny /n /m "Is this ok? [y/n] "
if errorlevel 3 exit 255
if not errorlevel 2 (
call :prompt_for_dir DEPS_BUILD_DIR "Provide path of deps build dir"
)
)
if "!DEPS_BUILD_DIR!" == "" (
echo ERROR: Deps build dir not set! 1>&2
exit /b 102
)
)
echo Deps build dir: %DEPS_BUILD_DIR%
:skip_deps_args_check
if not "%ARG_DEPS_INSTALL_DIR%" == "" (
set "DEPS_INSTALL_DIR=%ARG_DEPS_INSTALL_DIR%"
)
if "%DEPS_INSTALL_DIR%" == "" (
set DEPS_INSTALL_DIR=%CD%\i_deps\
echo Using default deps install dir: !DEPS_INSTALL_DIR!
if not "%ARG_NO_INTERACTIVE%" == "1" (
choice /c ny /n /m "Is this ok? [y/n] "
if errorlevel 3 exit 255
if not errorlevel 2 (
call :prompt_for_dir DEPS_INSTALL_DIR "Provide path of deps install dir"
)
)
if "!DEPS_INSTALL_DIR!" == "" (
echo ERROR: Deps install dir not set! 1>&2
exit /b 102
)
)
echo Deps install dir: %DEPS_INSTALL_DIR%
if "%ARG_SKIP_KRITA%" == "1" goto skip_krita_args_check
if not "%ARG_KRITA_BUILD_DIR%" == "" (
set "KRITA_BUILD_DIR=%ARG_KRITA_BUILD_DIR%"
)
if "%KRITA_BUILD_DIR%" == "" (
set KRITA_BUILD_DIR=%CD%\b\
echo Using default Krita build dir: !KRITA_BUILD_DIR!
if not "%ARG_NO_INTERACTIVE%" == "1" (
choice /c ny /n /m "Is this ok? [y/n] "
if errorlevel 3 exit 255
if not errorlevel 2 (
call :prompt_for_dir KRITA_BUILD_DIR "Provide path of Krita build dir"
)
)
if "!KRITA_BUILD_DIR!" == "" (
echo ERROR: Krita build dir not set! 1>&2
exit /b 102
)
)
echo Krita build dir: %KRITA_BUILD_DIR%
if not "%ARG_KRITA_INSTALL_DIR%" == "" (
set "KRITA_INSTALL_DIR=%ARG_KRITA_INSTALL_DIR%"
)
if "%KRITA_INSTALL_DIR%" == "" (
set KRITA_INSTALL_DIR=%CD%\i\
echo Using default Krita install dir: !KRITA_INSTALL_DIR!
if not "%ARG_NO_INTERACTIVE%" == "1" (
choice /c ny /n /m "Is this ok? [y/n] "
if errorlevel 3 exit 255
if not errorlevel 2 (
call :prompt_for_dir KRITA_INSTALL_DIR "Provide path of Krita install dir"
)
)
if "!KRITA_INSTALL_DIR!" == "" (
echo ERROR: Krita install dir not set! 1>&2
exit /b 102
)
)
echo Krita install dir: %KRITA_INSTALL_DIR%
:skip_krita_args_check
echo.
if not "%ARG_NO_INTERACTIVE%" == "1" (
choice /c ny /n /m "Is the above ok? [y/n] "
if errorlevel 3 exit 255
if not errorlevel 2 (
exit /b 1
)
echo.
)
:: Initialize clean PATH
set PATH=%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\
set PATH=%MINGW_BIN_DIR%;%PYTHON_BIN_DIR%;%PATH%
echo Creating dirs...
if NOT "%ARG_SKIP_DEPS%" == "1" (
mkdir %DEPS_DOWNLOAD_DIR%
if errorlevel 1 (
if not exist "%DEPS_DOWNLOAD_DIR%\" (
echo ERROR: Cannot create deps download dir! 1>&2
exit /b 103
)
)
mkdir %DEPS_BUILD_DIR%
if errorlevel 1 (
if not exist "%DEPS_BUILD_DIR%\" (
echo ERROR: Cannot create deps build dir! 1>&2
exit /b 103
)
)
mkdir %DEPS_INSTALL_DIR%
if errorlevel 1 (
if not exist "%DEPS_INSTALL_DIR%\" (
echo ERROR: Cannot create deps install dir! 1>&2
exit /b 103
)
)
)
if NOT "%ARG_SKIP_KRITA%" == "1" (
mkdir %KRITA_BUILD_DIR%
if errorlevel 1 (
if not exist "%KRITA_BUILD_DIR%\" (
echo ERROR: Cannot create Krita build dir! 1>&2
exit /b 103
)
)
mkdir %KRITA_INSTALL_DIR%
if errorlevel 1 (
if not exist "%KRITA_INSTALL_DIR%\" (
echo ERROR: Cannot create Krita install dir! 1>&2
exit /b 103
)
)
)
echo.
set CMAKE_BUILD_TYPE=RelWithDebInfo
set QT_ENABLE_DEBUG_INFO=OFF
:: Paths for CMake
set "BUILDDIR_DOWNLOAD_CMAKE=%DEPS_DOWNLOAD_DIR:\=/%"
set "BUILDDIR_DOWNLOAD_CMAKE=%BUILDDIR_DOWNLOAD_CMAKE: =\ %"
set "BUILDDIR_DEPS_INSTALL_CMAKE=%DEPS_INSTALL_DIR:\=/%"
set "BUILDDIR_DEPS_INSTALL_CMAKE=%BUILDDIR_DEPS_INSTALL_CMAKE: =\ %"
set "BUILDDIR_KRITA_INSTALL_CMAKE=%KRITA_INSTALL_DIR:\=/%"
set "BUILDDIR_KRITA_INSTALL_CMAKE=%BUILDDIR_KRITA_INSTALL_CMAKE: =\ %"
set PATH=%DEPS_INSTALL_DIR%\bin;%PATH%
if not "%GETTEXT_SEARCH_PATH%" == "" (
set PATH=!PATH!;!GETTEXT_SEARCH_PATH!
)
if "%ARG_SKIP_DEPS%" == "1" goto skip_build_deps
pushd %DEPS_BUILD_DIR%
if errorlevel 1 (
echo ERROR: Cannot enter deps build dir! 1>&2
exit /b 104
)
echo Running CMake for deps...
"%CMAKE_EXE%" "%KRITA_SRC_DIR%\3rdparty" ^
-DSUBMAKE_JOBS=%PARALLEL_JOBS% ^
-DQT_ENABLE_DEBUG_INFO=%QT_ENABLE_DEBUG_INFO% ^
-DQT_ENABLE_DYNAMIC_OPENGL=%QT_ENABLE_DYNAMIC_OPENGL% ^
-DEXTERNALS_DOWNLOAD_DIR=%BUILDDIR_DOWNLOAD_CMAKE% ^
-DINSTALL_ROOT=%BUILDDIR_DEPS_INSTALL_CMAKE% ^
-G "MinGW Makefiles" ^
-DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%
if errorlevel 1 (
echo ERROR: CMake configure failed! 1>&2
exit /b 104
)
echo.
set EXT_TARGETS=patch png2ico zlib lzma gettext qt boost eigen3 exiv2 fftw3 ilmbase
set EXT_TARGETS=%EXT_TARGETS% jpeg lcms2 ocio openexr png tiff gsl vc libraw
set EXT_TARGETS=%EXT_TARGETS% giflib freetype poppler kwindowsystem drmingw gmic
set EXT_TARGETS=%EXT_TARGETS% python sip pyqt
+set EXT_TARGETS=%EXT_TARGETS% quazip
for %%a in (%EXT_TARGETS%) do (
echo Building ext_%%a...
"%CMAKE_EXE%" --build . --config %CMAKE_BUILD_TYPE% --target ext_%%a
if errorlevel 1 (
echo ERROR: Building of ext_%%a failed! 1>&2
exit /b 105
)
)
echo.
echo ******** Built deps ********
popd
:skip_build_deps
if "%ARG_SKIP_KRITA%" == "1" goto skip_build_krita
pushd %KRITA_BUILD_DIR%
if errorlevel 1 (
echo ERROR: Cannot enter Krita build dir! 1>&2
exit /b 104
)
echo Running CMake for Krita...
+echo "%CMAKE_EXE%" "%KRITA_SRC_DIR%\." ^
+ -DBoost_DEBUG=OFF ^
+ -DBOOST_INCLUDEDIR=%BUILDDIR_DEPS_INSTALL_CMAKE%/include ^
+ -DBOOST_ROOT=%BUILDDIR_DEPS_INSTALL_CMAKE% ^
+ -DBOOST_LIBRARYDIR=%BUILDDIR_DEPS_INSTALL_CMAKE%/lib ^
+ -DCMAKE_PREFIX_PATH=%BUILDDIR_DEPS_INSTALL_CMAKE% ^
+ -DCMAKE_INSTALL_PREFIX=%BUILDDIR_KRITA_INSTALL_CMAKE% ^
+ -DBUILD_TESTING=OFF ^
+ -DHAVE_MEMORY_LEAK_TRACKER=OFF ^
+ -DFOUNDATION_BUILD=ON ^
+ -Wno-dev ^
+ -G "MinGW Makefiles" ^
+ -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%
+
"%CMAKE_EXE%" "%KRITA_SRC_DIR%\." ^
-DBoost_DEBUG=OFF ^
-DBOOST_INCLUDEDIR=%BUILDDIR_DEPS_INSTALL_CMAKE%/include ^
-DBOOST_ROOT=%BUILDDIR_DEPS_INSTALL_CMAKE% ^
-DBOOST_LIBRARYDIR=%BUILDDIR_DEPS_INSTALL_CMAKE%/lib ^
-DCMAKE_PREFIX_PATH=%BUILDDIR_DEPS_INSTALL_CMAKE% ^
-DCMAKE_INSTALL_PREFIX=%BUILDDIR_KRITA_INSTALL_CMAKE% ^
-DBUILD_TESTING=OFF ^
-DHAVE_MEMORY_LEAK_TRACKER=OFF ^
-DFOUNDATION_BUILD=ON ^
-Wno-dev ^
-G "MinGW Makefiles" ^
-DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%
if errorlevel 1 (
echo ERROR: CMake configure failed! 1>&2
exit /b 104
)
echo.
echo Building Krita...
"%CMAKE_EXE%" --build . --config %CMAKE_BUILD_TYPE% --target install -- -j%PARALLEL_JOBS%
if errorlevel 1 (
echo ERROR: Building of Krita failed! 1>&2
exit /b 105
)
echo.
echo ******** Built Krita ********
popd
:skip_build_krita
echo Krita build completed!
diff --git a/cmake/modules/CheckAtomic.cmake b/cmake/modules/CheckAtomic.cmake
new file mode 100644
index 0000000000..d72807f5ef
--- /dev/null
+++ b/cmake/modules/CheckAtomic.cmake
@@ -0,0 +1,118 @@
+# ==============================================================================
+# LLVM Release License
+# ==============================================================================
+# University of Illinois/NCSA
+# Open Source License
+#
+# Copyright (c) 2003-2018 University of Illinois at Urbana-Champaign.
+# All rights reserved.
+#
+# Developed by:
+#
+# LLVM Team
+#
+# University of Illinois at Urbana-Champaign
+#
+# http://llvm.org
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy of
+# this software and associated documentation files (the "Software"), to deal with
+# the Software without restriction, including without limitation the rights to
+# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is furnished to do
+# so, subject to the following conditions:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimers.
+#
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimers in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the names of the LLVM Team, University of Illinois at
+# Urbana-Champaign, nor the names of its contributors may be used to
+# endorse or promote products derived from this Software without specific
+# prior written permission.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+# SOFTWARE.
+
+INCLUDE(CheckCXXSourceCompiles)
+INCLUDE(CheckLibraryExists)
+
+# Sometimes linking against libatomic is required for atomic ops, if
+# the platform doesn't support lock-free atomics.
+
+function(check_working_cxx_atomics varname)
+set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
+set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++11")
+CHECK_CXX_SOURCE_COMPILES("
+#include <atomic>
+std::atomic<int> x;
+int main() {
+return std::atomic_is_lock_free(&x);
+}
+" ${varname})
+set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
+endfunction(check_working_cxx_atomics)
+
+function(check_working_cxx_atomics64 varname)
+set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
+set(CMAKE_REQUIRED_FLAGS "-std=c++11 ${CMAKE_REQUIRED_FLAGS}")
+CHECK_CXX_SOURCE_COMPILES("
+#include <atomic>
+#include <cstdint>
+std::atomic<uint64_t> x (0);
+int main() {
+uint64_t i = x.load(std::memory_order_relaxed);
+return std::atomic_is_lock_free(&x);
+}
+" ${varname})
+set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS})
+endfunction(check_working_cxx_atomics64)
+
+
+# This isn't necessary on MSVC, so avoid command-line switch annoyance
+# by only running on GCC-like hosts.
+if (LLVM_COMPILER_IS_GCC_COMPATIBLE)
+ # First check if atomics work without the library.
+ check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITHOUT_LIB)
+ # If not, check if the library exists, and atomics work with it.
+ if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB)
+ check_library_exists(atomic __atomic_fetch_add_4 "" HAVE_LIBATOMIC)
+ if( HAVE_LIBATOMIC )
+ list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic")
+ check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITH_LIB)
+ if (NOT HAVE_CXX_ATOMICS_WITH_LIB)
+ message(FATAL_ERROR "Host compiler must support std::atomic!")
+ endif()
+ else()
+ message(FATAL_ERROR "Host compiler appears to require libatomic, but cannot find it.")
+ endif()
+ endif()
+endif()
+# Check for 64 bit atomic operations.
+if(MSVC)
+ set(HAVE_CXX_ATOMICS64_WITHOUT_LIB True)
+else()
+ check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITHOUT_LIB)
+endif()
+
+# If not, check if the library exists, and atomics work with it.
+if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB)
+ check_library_exists(atomic __atomic_load_8 "" HAVE_CXX_LIBATOMICS64)
+ if(HAVE_CXX_LIBATOMICS64)
+ list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic")
+ check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB)
+ if (NOT HAVE_CXX_ATOMICS64_WITH_LIB)
+ message(FATAL_ERROR "Host compiler must support std::atomic!")
+ endif()
+ else()
+ message(FATAL_ERROR "Host compiler appears to require libatomic, but cannot find it.")
+ endif()
+endif()
diff --git a/cmake/modules/FindExiv2.cmake b/cmake/modules/FindExiv2.cmake
deleted file mode 100644
index 82cc1c7103..0000000000
--- a/cmake/modules/FindExiv2.cmake
+++ /dev/null
@@ -1,80 +0,0 @@
-# - Try to find the Exiv2 library
-#
-# EXIV2_MIN_VERSION - You can set this variable to the minimum version you need
-# before doing FIND_PACKAGE(Exiv2). The default is 0.12.
-#
-# Once done this will define
-#
-# EXIV2_FOUND - system has libexiv2
-# EXIV2_INCLUDE_DIR - the libexiv2 include directory
-# EXIV2_LIBRARIES - Link these to use libexiv2
-# EXIV2_DEFINITIONS - Compiler switches required for using libexiv2
-#
-# The minimum required version of Exiv2 can be specified using the
-# standard syntax, e.g. find_package(Exiv2 0.17)
-#
-# For compatibility, also the variable EXIV2_MIN_VERSION can be set to the minimum version
-# you need before doing FIND_PACKAGE(Exiv2). The default is 0.12.
-
-# Copyright (c) 2010, Alexander Neundorf, <neundorf@kde.org>
-# Copyright (c) 2008, Gilles Caulier, <caulier.gilles@gmail.com>
-#
-# Redistribution and use is allowed according to the terms of the BSD license.
-# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
-
-# Support EXIV2_MIN_VERSION for compatibility:
-if(NOT Exiv2_FIND_VERSION)
- set(Exiv2_FIND_VERSION "${EXIV2_MIN_VERSION}")
-endif(NOT Exiv2_FIND_VERSION)
-
-# the minimum version of exiv2 we require
-if(NOT Exiv2_FIND_VERSION)
- set(Exiv2_FIND_VERSION "0.12")
-endif(NOT Exiv2_FIND_VERSION)
-
-
-if (NOT WIN32)
- # use pkg-config to get the directories and then use these values
- # in the FIND_PATH() and FIND_LIBRARY() calls
- find_package(PkgConfig)
- pkg_check_modules(PC_EXIV2 QUIET exiv2)
- set(EXIV2_DEFINITIONS ${PC_EXIV2_CFLAGS_OTHER})
-endif (NOT WIN32)
-
-
-find_path(EXIV2_INCLUDE_DIR NAMES exiv2/exif.hpp
- HINTS
- ${PC_EXIV2_INCLUDEDIR}
- ${PC_EXIV2_INCLUDE_DIRS}
- )
-
-find_library(EXIV2_LIBRARY NAMES exiv2 libexiv2
- HINTS
- ${PC_EXIV2_LIBDIR}
- ${PC_EXIV2_LIBRARY_DIRS}
- )
-
-
-# Get the version number from exiv2/version.hpp and store it in the cache:
-if(EXIV2_INCLUDE_DIR AND NOT EXIV2_VERSION)
- file(READ ${EXIV2_INCLUDE_DIR}/exiv2/version.hpp EXIV2_VERSION_CONTENT)
- string(REGEX MATCH "#define EXIV2_MAJOR_VERSION +\\( *([0-9]+) *\\)" _dummy "${EXIV2_VERSION_CONTENT}")
- set(EXIV2_VERSION_MAJOR "${CMAKE_MATCH_1}")
-
- string(REGEX MATCH "#define EXIV2_MINOR_VERSION +\\( *([0-9]+) *\\)" _dummy "${EXIV2_VERSION_CONTENT}")
- set(EXIV2_VERSION_MINOR "${CMAKE_MATCH_1}")
-
- string(REGEX MATCH "#define EXIV2_PATCH_VERSION +\\( *([0-9]+) *\\)" _dummy "${EXIV2_VERSION_CONTENT}")
- set(EXIV2_VERSION_PATCH "${CMAKE_MATCH_1}")
-
- set(EXIV2_VERSION "${EXIV2_VERSION_MAJOR}.${EXIV2_VERSION_MINOR}.${EXIV2_VERSION_PATCH}" CACHE STRING "Version number of Exiv2" FORCE)
-endif(EXIV2_INCLUDE_DIR AND NOT EXIV2_VERSION)
-
-set(EXIV2_LIBRARIES "${EXIV2_LIBRARY}")
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(Exiv2 REQUIRED_VARS EXIV2_LIBRARY EXIV2_INCLUDE_DIR
- VERSION_VAR EXIV2_VERSION)
-
-mark_as_advanced(EXIV2_INCLUDE_DIR EXIV2_LIBRARY)
-
diff --git a/cmake/modules/FindLibExiv2.cmake b/cmake/modules/FindLibExiv2.cmake
new file mode 100644
index 0000000000..935cee2c55
--- /dev/null
+++ b/cmake/modules/FindLibExiv2.cmake
@@ -0,0 +1,115 @@
+#.rst:
+# FindLibExiv2
+# ------------
+#
+# Try to find the Exiv2 library.
+#
+# This will define the following variables:
+#
+# ``LibExiv2_FOUND``
+# System has LibExiv2.
+#
+# ``LibExiv2_VERSION``
+# The version of LibExiv2.
+#
+# ``LibExiv2_INCLUDE_DIRS``
+# This should be passed to target_include_directories() if
+# the target is not used for linking.
+#
+# ``LibExiv2_LIBRARIES``
+# The LibExiv2 library.
+# This can be passed to target_link_libraries() instead of
+# the ``LibExiv2::LibExiv2`` target
+#
+# If ``LibExiv2_FOUND`` is TRUE, the following imported target
+# will be available:
+#
+# ``LibExiv2::LibExiv2``
+# The Exiv2 library
+#
+# Since 5.53.0.
+#
+#=============================================================================
+# Copyright (c) 2018, Christophe Giboudeaux, <christophe@krop.fr>
+# Copyright (c) 2010, Alexander Neundorf, <neundorf@kde.org>
+# Copyright (c) 2008, Gilles Caulier, <caulier.gilles@gmail.com>
+#
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. The name of the author may not be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#=============================================================================
+
+find_package(PkgConfig QUIET)
+pkg_check_modules(PC_EXIV2 QUIET exiv2)
+
+find_path(LibExiv2_INCLUDE_DIRS NAMES exiv2/exif.hpp
+ HINTS ${PC_EXIV2_INCLUDEDIR}
+)
+
+find_library(LibExiv2_LIBRARIES NAMES exiv2 libexiv2
+ HINTS ${PC_EXIV2_LIBRARY_DIRS}
+)
+
+set(LibExiv2_VERSION ${PC_EXIV2_VERSION})
+
+if(NOT LibExiv2_VERSION AND DEFINED LibExiv2_INCLUDE_DIRS)
+ # With exiv >= 0.27, the version #defines are in exv_conf.h instead of version.hpp
+ foreach(_exiv2_version_file "version.hpp" "exv_conf.h")
+ if(EXISTS "${LibExiv2_INCLUDE_DIRS}/exiv2/${_exiv2_version_file}")
+ file(READ "${LibExiv2_INCLUDE_DIRS}/exiv2/${_exiv2_version_file}" _exiv_version_file_content)
+ string(REGEX MATCH "#define EXIV2_MAJOR_VERSION[ ]+\\([0-9]+\\)" EXIV2_MAJOR_VERSION_MATCH ${_exiv_version_file_content})
+ string(REGEX MATCH "#define EXIV2_MINOR_VERSION[ ]+\\([0-9]+\\)" EXIV2_MINOR_VERSION_MATCH ${_exiv_version_file_content})
+ string(REGEX MATCH "#define EXIV2_PATCH_VERSION[ ]+\\([0-9]+\\)" EXIV2_PATCH_VERSION_MATCH ${_exiv_version_file_content})
+ if(EXIV2_MAJOR_VERSION_MATCH)
+ string(REGEX REPLACE ".*_MAJOR_VERSION[ ]+\\((.*)\\)" "\\1" EXIV2_MAJOR_VERSION ${EXIV2_MAJOR_VERSION_MATCH})
+ string(REGEX REPLACE ".*_MINOR_VERSION[ ]+\\((.*)\\)" "\\1" EXIV2_MINOR_VERSION ${EXIV2_MINOR_VERSION_MATCH})
+ string(REGEX REPLACE ".*_PATCH_VERSION[ ]+\\((.*)\\)" "\\1" EXIV2_PATCH_VERSION ${EXIV2_PATCH_VERSION_MATCH})
+ endif()
+ endif()
+ endforeach()
+
+ set(LibExiv2_VERSION "${EXIV2_MAJOR_VERSION}.${EXIV2_MINOR_VERSION}.${EXIV2_PATCH_VERSION}")
+endif()
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(LibExiv2
+ FOUND_VAR LibExiv2_FOUND
+ REQUIRED_VARS LibExiv2_LIBRARIES LibExiv2_INCLUDE_DIRS
+ VERSION_VAR LibExiv2_VERSION
+)
+
+mark_as_advanced(LibExiv2_INCLUDE_DIRS LibExiv2_LIBRARIES)
+
+if(LibExiv2_FOUND AND NOT TARGET LibExiv2::LibExiv2)
+ add_library(LibExiv2::LibExiv2 UNKNOWN IMPORTED)
+ set_target_properties(LibExiv2::LibExiv2 PROPERTIES
+ IMPORTED_LOCATION "${LibExiv2_LIBRARIES}"
+ INTERFACE_INCLUDE_DIRECTORIES "${LibExiv2_INCLUDE_DIRS}"
+ )
+endif()
+
+include(FeatureSummary)
+set_package_properties(LibExiv2 PROPERTIES
+ URL "http://www.exiv2.org"
+ DESCRIPTION "Image metadata support"
+)
diff --git a/cmake/modules/FindQuaZip.cmake b/cmake/modules/FindQuaZip.cmake
new file mode 100644
index 0000000000..7e628fcd4b
--- /dev/null
+++ b/cmake/modules/FindQuaZip.cmake
@@ -0,0 +1,43 @@
+# QUAZIP_FOUND - QuaZip library was found
+# QUAZIP_INCLUDE_DIR - Path to QuaZip include dir
+# QUAZIP_INCLUDE_DIRS - Path to QuaZip and zlib include dir (combined from QUAZIP_INCLUDE_DIR + ZLIB_INCLUDE_DIR)
+# QUAZIP_LIBRARIES - List of QuaZip libraries
+# QUAZIP_ZLIB_INCLUDE_DIR - The include dir of zlib headers
+
+
+IF (QUAZIP_INCLUDE_DIRS AND QUAZIP_LIBRARIES)
+ # in cache already
+ SET(QUAZIP_FOUND TRUE)
+ELSE (QUAZIP_INCLUDE_DIRS AND QUAZIP_LIBRARIES)
+ IF (Qt5Core_FOUND)
+ set(QUAZIP_LIB_VERSION_SUFFIX 5)
+ ENDIF()
+ IF (WIN32)
+ FIND_PATH(QUAZIP_LIBRARY_DIR
+ WIN32_DEBUG_POSTFIX d
+ NAMES libquazip${QUAZIP_LIB_VERSION_SUFFIX}.dll
+ HINTS "C:/Programme/" "C:/Program Files"
+ PATH_SUFFIXES QuaZip/lib
+ )
+ FIND_LIBRARY(QUAZIP_LIBRARIES NAMES libquazip${QUAZIP_LIB_VERSION_SUFFIX}.dll HINTS ${QUAZIP_LIBRARY_DIR})
+ FIND_PATH(QUAZIP_INCLUDE_DIR NAMES quazip.h HINTS ${QUAZIP_LIBRARY_DIR}/../ PATH_SUFFIXES include/quazip${QUAZIP_LIB_VERSION_SUFFIX})
+ FIND_PATH(QUAZIP_ZLIB_INCLUDE_DIR NAMES zlib.h)
+ ELSE(WIN32)
+ FIND_PACKAGE(PkgConfig)
+# pkg_check_modules(PC_QCA2 QUIET qca2)
+ pkg_check_modules(PC_QUAZIP quazip)
+ FIND_LIBRARY(QUAZIP_LIBRARIES
+ WIN32_DEBUG_POSTFIX d
+ NAMES quazip${QUAZIP_LIB_VERSION_SUFFIX}
+ HINTS /usr/lib /usr/lib64
+ )
+ FIND_PATH(QUAZIP_INCLUDE_DIR quazip.h
+ HINTS /usr/include /usr/local/include
+ PATH_SUFFIXES quazip${QUAZIP_LIB_VERSION_SUFFIX}
+ )
+ FIND_PATH(QUAZIP_ZLIB_INCLUDE_DIR zlib.h HINTS /usr/include /usr/local/include)
+ ENDIF (WIN32)
+ INCLUDE(FindPackageHandleStandardArgs)
+ SET(QUAZIP_INCLUDE_DIRS ${QUAZIP_INCLUDE_DIR} ${QUAZIP_ZLIB_INCLUDE_DIR})
+ find_package_handle_standard_args(QUAZIP DEFAULT_MSG QUAZIP_LIBRARIES QUAZIP_INCLUDE_DIR QUAZIP_ZLIB_INCLUDE_DIR QUAZIP_INCLUDE_DIRS)
+ENDIF (QUAZIP_INCLUDE_DIRS AND QUAZIP_LIBRARIES)
diff --git a/dev-tools/python/dev-requirements.txt b/dev-tools/python/dev-requirements.txt
new file mode 100644
index 0000000000..08ebcd2d80
--- /dev/null
+++ b/dev-tools/python/dev-requirements.txt
@@ -0,0 +1,6 @@
+flake8==3.7.4
+pytest==4.0.2
+PyQt5==5.11.3
+PyQt5-sip==4.19.13
+
+-e dev-tools/python/krita-mock
diff --git a/dev-tools/python/krita-mock/setup.py b/dev-tools/python/krita-mock/setup.py
new file mode 100644
index 0000000000..93198f1ded
--- /dev/null
+++ b/dev-tools/python/krita-mock/setup.py
@@ -0,0 +1,8 @@
+from setuptools import setup, find_namespace_packages
+
+setup(
+ name="krita",
+ version="0.1",
+ packages=find_namespace_packages('src'),
+ package_dir={'': 'src'},
+)
diff --git a/dev-tools/python/krita-mock/src/krita/__init__.py b/dev-tools/python/krita-mock/src/krita/__init__.py
new file mode 100644
index 0000000000..ac3fd3a76a
--- /dev/null
+++ b/dev-tools/python/krita-mock/src/krita/__init__.py
@@ -0,0 +1,31 @@
+"""This is a mock krita module for Python unit tests.
+
+This module returns a mock object for any attribute name and thus
+prevents any errors surrounding the krita module in unit tests. This
+makes it possible to write unit tests for Krita-independent code
+units.
+
+Caveats:
+
+Will only work with proper imports:
+
+ import krita
+ krita.Krita.instance() # no-op on a mock object
+
+Not with wildcard imports:
+
+ from krita import *
+ Krita.instance() # error
+
+(Wildcard imports should be avoided anyway.)
+
+"""
+
+import builtins
+import sys
+from unittest.mock import MagicMock
+
+
+sys.modules['krita'] = MagicMock()
+
+builtins.i18n = lambda s: s
diff --git a/krita/data/aboutdata/developers.txt b/krita/data/aboutdata/developers.txt
index 988a1c6e5b..2712d4f763 100644
--- a/krita/data/aboutdata/developers.txt
+++ b/krita/data/aboutdata/developers.txt
@@ -1,198 +1,199 @@
Boudewijn Rempt
Aaron J. Seigo
Adam Celarek
Adam Pigg
Adriaan de Groot
Adrian Page
Adrian Schroeter
Albert Astals Cid
Alberto Villa
Alexander Neundorf
Alexander Potashev
Alexis Ménard
Alfredo Beaumont Sainz
Allen Winter
Ana Beatriz Guerrero López
Andras Mantia
Andreas Hartmetz
Andreas Lundin
André Marcelo Alvarenga
Andrew Coles
Andre Woebbeking
Andrius da Costa Ribas
Andy Fawcett
Anne-Marie Mahfouf
Ariya Hidayat
Arjen Hiemstra
Bart Coppens
Ben Cooksley
Benjamin K. Stuhl
Benjamin Meyer
Benjamin Reed
Benoît Jacob
Ben Schleimer
Bernhard Rosenkraenzer
Bo Thorsen
Brad Hards
Bram Schoenmakers
Burkhard Lück
Carlo Segato
C. Boemann
Christer Stenbrenden
Christian Ehrlicher
Christian Mueller
Christoph Feck
Chusslove Illich
Clarence Dang
Cyrille Berger
Daniel M. Duley
Daniel Molkentin
Dan Leinir Turthra Jensen
Dan Meltzer
Danny Allen
David Faure
David Gowers
Dirk Mueller
Dirk Schönberger
Dmitry Kazakov
Edward Apap
Elvis Stansvik
Emanuele Tamponi
Enrique Matías Sánchez
Fabian Kosmale
Frank Osterfeld
Frederik Schwarzer
Fredrik Edemar
Fredy Yanardi
Friedrich W. H. Kossebau
Gábor Lehel
Gary Cramblitt
Geoffry Song
Gioele Barabucci
Giovanni Venturi
Gopalakrishna Bhat A
Hanna Scott
Harald Sitter
Hasso Tepper
Helge Deller
Helio Castro
Hideki Saito
Hoàng Đức Hiếu
Hugo Pereira Da Costa
Inge Wallin
Ingo Klöcker
İsmail Dönmez
Ivan Yossi
Jaime
Jaime Torres
Jaison Lee
Jakob Petsovits
Jakub Stachowski
Jan Hambrecht
Jarosław Staniek
Jens Herden
Jessica Hall
Johannes Simon
John Layt
Jonathan Riddell
Jonathan Singer
José Luis Vergara
Juan Luis Boya García
Juan Palacios
Jure Repinc
Kai-Uwe Behrmann
Kevin Krammer
Kevin Ottens
Kurt Pfeifle
Laurent Montel
Lauri Watts
Leo Savernik
Lukáš Tinkl
Lukáš Tvrdý
Maciej Mrozowski
Malcolm Hunter
Manuel Riecke
manu tortosa
Marc Pegon
Marijn Kruisselbrink
Martin Ellis
Martin Gräßlin
Matthew Woehlke
Matthias Klumpp
Matthias Kretz
Matus Talcik
Maximiliano Curia
Melchior Franz
Michael David Howell
Michael Drueing
Michael Thaler
Michel Hermier
Mohit Goyal
Mojtaba Shahi Senobari
Montel Laurent
Moritz Molch
Nick Shaforostoff
Nicolas Goutte
Olivier Goffart
Patrick Julien
Patrick Spendrin
+Pavel Belskiy
Pavel Heimlich
Peter Simonsson
Pierre Ducroquet
Pierre Stirnweiss
Pino Toscano
Rafael Fernández López
Raphael Langerhorst
Rex Dieter
Rob Buis
Roopesh Chander
Sahil Nagpal
Salil Kapur
Samuel Buttigieg
Sander Koning
Sascha Suelzer
Scott Petrovic
Scott Wheeler
Sebastian Sauer
Shivaraman Aiyer
Siddharth Sharma
Silvio Heinrich
Somsubhra Bairi
Spencer Brown
Srikanth Tiyyagura
Stefan Nikolaus
Stephan Binner
Stephan Kulow
Stuart Dickson
Sune Vuorela
Sven Langkamp
Thiago Macieira
Thomas Capricelli
Thomas Friedrichsmeier
Thomas Klausner
Thomas Nagy
Thomas Zander
Thorsten Staerk
Thorsten Zachmann
Tim Beaulen
Timothée Giet
Tobias Koenig
Tom Burdick
Torio Mlshi
Torsten Rahn
Unai Garro
Urs Wolfer
Vadim Zhukov
Vera Lukman
Victor Lafon
Victor Wåhlström
Volker Krause
Waldo Bastian
Werner Trobin
Wilco Greven
Will Entriken
William Steidtmann
Wolthera van Hovell
Yann Bodson
Yue Liu
Yuri Chornoivan
\ No newline at end of file
diff --git a/krita/data/kritarc b/krita/data/kritarc
index 13cc8372ae..2c20fd084f 100644
--- a/krita/data/kritarc
+++ b/krita/data/kritarc
@@ -1,478 +1,478 @@
favoriteCompositeOps=normal,erase,multiply,burn,darken,add,dodge,screen,overlay,soft_light_svg,luminize,lighten,saturation,color
ArtColorSel.ColorSpace=0
ArtColorSel.InversedSaturation=false
ArtColorSel.Light=0.5
ArtColorSel.LightPieces=11
ArtColorSel.NumRings=7
ArtColorSel.RingAngles=0,0,0,0,0,0,0,0,0,0,0
ArtColorSel.RingPieces=12
ArtColorSel.SelColorA=1
ArtColorSel.SelColorH=0
ArtColorSel.SelColorS=0
ArtColorSel.SelColorX=0.5
BackgroundColorForNewImage=255,255,255
BackgroundOpacityForNewImage=255
BackgroundStyleForNewImage=0
Krita/Ocio/OcioColorManagementMode=0
Krita/Ocio/OcioLockColorVisualRepresentation=false
Krita/Ocio/UseOcio=false
LastBackGroundColor=<!DOCTYPE LastBackGroundColor>\n<LastBackGroundColor>\n <RGB g="1" r="1" space="sRGB-elle-V2-srgbtrc.icc" b="1"/>\n</LastBackGroundColor>\n
LastForeGroundColor=<!DOCTYPE LastForeGroundColor>\n<LastForeGroundColor>\n <RGB g="0" r="0" space="sRGB-elle-V2-srgbtrc.icc" b="0"/>\n</LastForeGroundColor>\n
LastPreset=Basic_circle
LastPreset_-1=Basic_circle
LineSmoothingDelayDistance=50
LineSmoothingDistance=50
LineSmoothingFinishStabilizedCurve=true
LineSmoothingStabilizeSensors=true
LineSmoothingTailAggressiveness=0.14999999999999999
LineSmoothingType=1
LineSmoothingUseDelayDistance=true
NumberOfLayersForNewImage=2
PaintopPopupDetached=false
SpecificColorSelector/ShowColorSpaceSelector=false
baseLength=50
colorDepthDef=U8
colorModelDef=RGBA
colorProfileDef=sRGB-elle-V2-srgbtrc.icc
favoritePresetsTag=★ My Favorites
internal_selector_active_color_set=Default
globalSnapBoundingBox=false
globalSnapExtension=false
globalSnapImageBounds=true
globalSnapImageCenter=true
globalSnapIntersection=false
globalSnapNode=false
globalSnapOrthogonal=false
gridmaincolor=99,99,99
gridmainstyle=0
gridsubdivisioncolor=150,150,150
gridsubdivisionstyle=1
guidesColor=99,99,99
guidesLineStyle=0
imageHeightDef=1200
imageResolutionDef=300
imageWidthDef=1600
levelOfDetailEnabled=true
numberOfOnionSkins=10
oninSkinTintColorForward=0,255,0
onionSkinOpacity_-1=173
onionSkinOpacity_-10=22
onionSkinOpacity_-2=163
onionSkinOpacity_-3=147
onionSkinOpacity_-4=127
onionSkinOpacity_-5=107
onionSkinOpacity_-6=84
onionSkinOpacity_-7=63
onionSkinOpacity_-8=48
onionSkinOpacity_-9=33
onionSkinOpacity_0=175
onionSkinOpacity_1=173
onionSkinOpacity_10=22
onionSkinOpacity_2=163
onionSkinOpacity_3=147
onionSkinOpacity_4=127
onionSkinOpacity_5=107
onionSkinOpacity_6=84
onionSkinOpacity_7=63
onionSkinOpacity_8=48
onionSkinOpacity_9=33
onionSkinState_-1=true
onionSkinState_-10=false
onionSkinState_-2=true
onionSkinState_-3=false
onionSkinState_-4=false
onionSkinState_-5=false
onionSkinState_-6=false
onionSkinState_-7=false
onionSkinState_-8=false
onionSkinState_-9=false
onionSkinState_0=true
onionSkinState_1=true
onionSkinState_10=false
onionSkinState_2=true
onionSkinState_3=false
onionSkinState_4=false
onionSkinState_5=false
onionSkinState_6=false
onionSkinState_7=false
onionSkinState_8=false
onionSkinState_9=false
onionSkinTintColorBackward=255,0,0
onionSkinTintFactor=191
presethistory=Basic_tip_default
showAdditionalOnionSkinsSettings=true
toolbarslider_1=opacity
toolbarslider_2=size
toolbarslider_3=flow
[advancedColorSelector]
allowHorizontalLayout=true
colorSelectorConfiguration=3|0|5|0
commonColorsAlignment=false
commonColorsAutoUpdate=false
commonColorsCount=12
commonColorsHeight=16
commonColorsNumCols=1
commonColorsNumRows=1
commonColorsScrolling=true
commonColorsShow=true
commonColorsWidth=16
customColorSpaceDepthID=U8
customColorSpaceModel=RGBA
customColorSpaceProfile=sRGB built-in
lastUsedColorsAlignment=true
lastUsedColorsCount=20
lastUsedColorsHeight=16
lastUsedColorsNumCols=1
lastUsedColorsNumRows=1
lastUsedColorsScrolling=true
lastUsedColorsShow=true
lastUsedColorsWidth=16
minimalShadeSelectorAsGradient=true
minimalShadeSelectorLineConfig=0|0.2|0|0|0|0|0;1|0|1|1|0|0|0;2|0|-1|1|0|0|0;
minimalShadeSelectorLineHeight=10
minimalShadeSelectorPatchCount=10
popupOnMouseClick=true
popupOnMouseOver=false
shadeSelectorHideable=false
shadeSelectorType=Minimal
shadeSelectorUpdateOnBackground=true
shadeSelectorUpdateOnForeground=true
shadeSelectorUpdateOnLeftClick=false
shadeSelectorUpdateOnRightClick=false
useCustomColorSpace=false
zoomSize=280
[DockWidget sharedtooldocker]
TabbedMode=false
[KisToolTransform]
filterId=Bicubic
[MainWindow]
State=AAAA/wAAAAD9AAAABAAAAAAAAABJAAADzfwCAAAAA/sAAAAOAFQAbwBvAGwAQgBvAHgBAAAAPwAAA80AAAAxAP////sAAAAkAEYAbABvAHcAUwBoAGEAcABlAEIAbwB4AEQAbwBjAGsAZQByAAAAA2oAAADHAAAAAAAAAAD7AAAAKABGAGwAbwB3AFMAdABlAG4AYwBpAGwAQgBvAHgARABvAGMAawBlAHIAAAADfQAAAMcAAAAAAAAAAAAAAAEAAAEGAAADzfwCAAAAQPsAAAAaAEsAaQBzAEIAaQByAGQAZQB5AGUAQgBvAHgAAAAAAP////8AAAAAAAAAAPsAAAAgAEsAaQBzAFAAYQBsAGUAdAB0AGUARABvAGMAawBlAHIAAAAAAP////8AAAAAAAAAAPsAAAAaAEsAbwBDAG8AbABvAHIARABvAGMAawBlAHIAAAAAAP////8AAAAAAAAAAPsAAAAwAEsAaQBzAFQAcgBpAGEAbgBnAGwAZQBDAG8AbABvAHIAUwBlAGwAZQBjAHQAbwByAAAAAAD/////AAAAAAAAAAD7AAAAIgBTAGgAYQBkAG8AdwAgAFAAcgBvAHAAZQByAHQAaQBlAHMAAAAAAP////8AAAAAAAAAAPsAAAAgAFMAaABhAHAAZQAgAFAAcgBvAHAAZQByAHQAaQBlAHMAAAAAAP////8AAAAAAAAAAPsAAAAaAFMAaABhAHAAZQBTAGUAbABlAGMAdABvAHIAAAAASAAAAEQAAAAAAAAAAPsAAAAkAFMAaQBtAHAAbABlACAAVABlAHgAdAAgAEUAZABpAHQAbwByAAAAAAD/////AAAAAAAAAAD8AAAAPwAAAOIAAACEAQAAHfoAAAAAAQAAAAf7AAAAHgBDAG8AbABvAHIAUwBlAGwAZQBjAHQAbwByAE4AZwEAAAAA/////wAAADoA////+wAAACAAcwBoAGEAcgBlAGQAdABvAG8AbABkAG8AYwBrAGUAcgEAAAAA/////wAAAFMA////+wAAABwATwB2AGUAcgB2AGkAZQB3AEQAbwBjAGsAZQByAQAAAAD/////AAAA2gD////7AAAAKgBTAHAAZQBjAGkAZgBpAGMAQwBvAGwAbwByAFMAZQBsAGUAYwB0AG8AcgAAAAAA/////wAAAL4A////+wAAABYAQwBvAGwAbwByAFMAbABpAGQAZQByAAAAAAD/////AAAAkQD////7AAAAFgBJAG0AYQBnAGUARABvAGMAawBlAHIAAAAAAP////8AAAAAAAAAAPsAAAAqAFMAaABhAHAAZQBDAG8AbABsAGUAYwB0AGkAbwBuAEQAbwBjAGsAZQByAAAABkgAAAEoAAAAAAAAAAD7AAAARgBLAHIAaQB0AGEAUwBoAGEAcABlAC8ASwBpAHMAVABvAG8AbABEAHkAbgBhAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQBAAAAUgAAABIAAAAAAAAAAPsAAAAsAEsAcgBpAHQAYQBTAGgAYQBwAGUALwBLAGkAcwBUAG8AbwBsAEwAaQBuAGUBAAAAPAAAAGkAAAAAAAAAAPsAAAAyAEsAcgBpAHQAYQBTAGgAYQBwAGUALwBLAGkAcwBUAG8AbwBsAEUAbABsAGkAcABzAGUBAAAAkQAAABIAAAAAAAAAAPsAAAAcAEsAaQBzAFQAbwBvAGwAUABvAGwAeQBnAG8AbgEAAACmAAAAEgAAAAAAAAAA+wAAAB4ASwBpAHMAVABvAG8AbABQAG8AbAB5AGwAaQBuAGUBAAAAuwAAABIAAAAAAAAAAPsAAAAWAEsAaQBzAFQAbwBvAGwAUwB0AGEAcgEAAADQAAAAEwAAAAAAAAAA+wAAACoAUwBuAGEAcABHAHUAaQBkAGUAQwBvAG4AZgBpAGcAVwBpAGQAZwBlAHQAAAAA7wAAAHEAAAAAAAAAAPsAAAAyAEsAaQBzAFQAbwBvAGwAQwByAG8AcAAgAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQBAAAA+wAAABIAAAAAAAAAAPsAAABQAEsAcgBpAHQAYQBUAHIAYQBuAHMAZgBvAHIAbQAvAEsAaQBzAFQAbwBvAGwATQBvAHYAZQAgAE8AcAB0AGkAbwBuACAAVwBpAGQAZwBlAHQBAAABEAAAABIAAAAAAAAAAPsAAAA8AEsAaQBzAFQAbwBvAGwAVAByAGEAbgBzAGYAbwByAG0AIABvAHAAdABpAG8AbgAgAHcAaQBkAGcAZQB0AQAAADwAAAAvAAAAAAAAAAD7AAAATgBLAHIAaQB0AGEAUwBoAGEAcABlAC8ASwBpAHMAVABvAG8AbABNAGUAYQBzAHUAcgBlACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAA8AAAAQgAAAAAAAAAA+wAAAFwASwByAGkAdABhAFMAZQBsAGUAYwB0AGUAZAAvAEsAaQBzAFQAbwBvAGwAQwBvAGwAbwByAFAAaQBjAGsAZQByACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAA8AAAA/wAAAAAAAAAA+wAAAEYASwBpAHMAUgB1AGwAZQByAEEAcwBzAGkAcwB0AGEAbgB0AFQAbwBvAGwAIABPAHAAdABpAG8AbgAgAFcAaQBkAGcAZQB0AQAAADwAAAASAAAAAAAAAAD7AAAASABLAGkAcwBUAG8AbwBsAFAAZQByAHMAcABlAGMAdABpAHYAZQBHAHIAaQBkACAATwBwAHQAaQBvAG4AIABXAGkAZABnAGUAdAEAAAGjAAAAEgAAAAAAAAAA+wAAADIASwBpAHMAVABvAG8AbABHAHIAaQBkACAATwBwAHQAaQBvAG4AIABXAGkAZABnAGUAdAEAAAG4AAAAEwAAAAAAAAAA+wAAAEwASwBpAHMAVABvAG8AbABTAGUAbABlAGMAdABSAGUAYwB0AGEAbgBnAHUAbABhAHIAIABvAHAAdABpAG8AbgAgAHcAaQBkAGcAZQB0AQAAAc4AAAASAAAAAAAAAAD7AAAASgBLAGkAcwBUAG8AbwBsAFMAZQBsAGUAYwB0AEUAbABsAGkAcAB0AGkAYwBhAGwAIABvAHAAdABpAG8AbgAgAHcAaQBkAGcAZQB0AQAAAeMAAAASAAAAAAAAAAD7AAAASABLAGkAcwBUAG8AbwBsAFMAZQBsAGUAYwB0AFAAbwBsAHkAZwBvAG4AYQBsACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAH4AAAAEgAAAAAAAAAA+wAAAEQASwBpAHMAVABvAG8AbABTAGUAbABlAGMAdABPAHUAdABsAGkAbgBlACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAINAAAAEgAAAAAAAAAA+wAAAEoASwBpAHMAVABvAG8AbABTAGUAbABlAGMAdABDAG8AbgB0AGkAZwB1AG8AdQBzACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAIiAAAAEgAAAAAAAAAA+wAAAEQASwBpAHMAVABvAG8AbABTAGUAbABlAGMAdABTAGkAbQBpAGwAYQByACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAI3AAAAEgAAAAAAAAAA/AAAAbYAAABaAAAAAAD////6AAAAAAEAAAAC+wAAAC4ASwBvAFMAaABhAHAAZQBDAG8AbABsAGUAYwB0AGkAbwBuAEQAbwBjAGsAZQByAQAAAAD/////AAAAAAAAAAD7AAAAJABTAG0AYQBsAGwAQwBvAGwAbwByAFMAZQBsAGUAYwB0AG8AcgAAAANuAAABBAAAADoA/////AAAASgAAAEwAAAA1QEAAB36AAAAAAEAAAAD+wAAABYASwBpAHMATABhAHkAZQByAEIAbwB4AQAAAAD/////AAABAgD////7AAAAGgBDAGgAYQBuAG4AZQBsAEQAbwBjAGsAZQByAQAAAAD/////AAAAVQD////7AAAALgBLAGkAcwBQAGEAaQBuAHQAZQByAGwAeQBNAGkAeABlAHIARABvAGMAawBlAHIAAAAAAP////8AAAAAAAAAAPwAAAJfAAABrQAAAJcBAAAd+gAAAAABAAAAAvsAAAAYAFAAcgBlAHMAZQB0AEQAbwBjAGsAZQByAQAAAAD/////AAAAZgD////7AAAAGgBQAHIAZQBzAGUAdABIAGkAcwB0AG8AcgB5AQAACPoAAAEGAAAAVQD////7AAAASABLAHIAaQB0AGEAUwBoAGEAcABlAC8ASwBpAHMAVABvAG8AbABCAHIAdQBzAGgAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAPcAAAAaAAAAAAAAAAA+wAAACIAUwB0AHIAbwBrAGUAIABQAHIAbwBwAGUAcgB0AGkAZQBzAAAAAAD/////AAAAAAAAAAD7AAAAFgBTAHQAeQBsAGUARABvAGMAawBlAHIAAAAAAP////8AAAAAAAAAAPsAAAAgAEsAaQBzAEgAaQBzAHQAbwBnAHIAYQBtAEQAbwBjAGsAAAAAAP////8AAAAAAAAAAPsAAAASAFMAYwByAGkAcAB0AGkAbgBnAAAAAAD/////AAAAAAAAAAD7AAAAMABEAGUAZgBhAHUAbAB0AFQAbwBvAGwAQQByAHIAYQBuAGcAZQBXAGkAZABnAGUAdAAAAAK8AAAAUgAAAAAAAAAA+wAAACIARABlAGYAYQB1AGwAdABUAG8AbwBsAFcAaQBkAGcAZQB0AAAAAxEAAABbAAAAAAAAAAD7AAAAJABLAGkAcwBIAGkAcwB0AG8AZwByAGEAbQBEAG8AYwBrAGUAcgAAAAJCAAAAewAAAAAAAAAA+wAAABgARABpAGcAaQB0AGEAbABNAGkAeABlAHIAAAAAAP////8AAACTAP////sAAAAOAEgAaQBzAHQAbwByAHkAAAADkAAAALQAAACuAP////sAAABOAEsAcgBpAHQAYQBGAGkAbABsAC8ASwBpAHMAVABvAG8AbABHAHIAYQBkAGkAZQBuAHQAIABvAHAAdABpAG8AbgAgAHcAaQBkAGcAZQB0AAAABCgAAAAcAAAAAAAAAAD7AAAARgBLAHIAaQB0AGEARgBpAGwAbAAvAEsAaQBzAFQAbwBvAGwARgBpAGwAbAAgAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQAAAADUAAAABwAAAAAAAAAAPsAAAA2AEsAcgBpAHQAYQBTAGgAYQBwAGUALwBLAGkAcwBUAG8AbwBsAFIAZQBjAHQAYQBuAGcAbABlAAAAAwUAAABnAAAAAAAAAAD7AAAAIgBDAG8AbQBwAG8AcwBpAHQAaQBvAG4ARABvAGMAawBlAHIAAAAAAP////8AAACMAP////sAAAAqAEEAcgB0AGkAcwB0AGkAYwBDAG8AbABvAHIAUwBlAGwAZQBjAHQAbwByAAAAAAD/////AAAAdwD////7AAAAGgBQAGEAdAB0AGUAcgBuAEQAbwBjAGsAZQByAAAAAtkAAAFJAAAAswD////7AAAAGgBUAGEAcwBrAHMAZQB0AEQAbwBjAGsAZQByAAAAAAD/////AAAAjAD////7AAAAKABTAG4AYQBwAEcAdQBpAGQAZQAgAFAAcgBvAHAAZQByAHQAaQBlAHMAAAAAAP////8AAAAAAAAAAPsAAAA4AFQAZQB4AHQARABvAGMAdQBtAGUAbgB0AEkAbgBzAHAAZQBjAHQAaQBvAG4ARABvAGMAawBlAHICAAAEmgAAAhUAAAEqAAAArvsAAAASAEwAdQB0AEQAbwBjAGsAZQByAAAAAAD/////AAABXQD////7AAAAGgBQAGEAbABlAHQAdABlAEQAbwBjAGsAZQByAAAAAAD/////AAAAQgD////7AAAAFABHAHIAaQBkAEQAbwBjAGsAZQByAAAAAAD/////AAABLQD////7AAAAHgBIAGkAcwB0AG8AZwByAGEAbQBEAG8AYwBrAGUAcgAAAAAA/////wAAAEcA////+wAAACoAQQBuAGkAbQBhAHQAaQBvAG4AQwB1AHIAdgBlAHMARABvAGMAawBlAHIAAAAAAP////8AAACMAP////sAAAAyAFMAdgBnAFMAeQBtAGIAbwBsAEMAbwBsAGwAZQBjAHQAaQBvAG4ARABvAGMAawBlAHIAAAAAAP////8AAACMAP////sAAAAWAFQAbwB1AGMAaABEAG8AYwBrAGUAcgAAAAJMAAABMQAAABMA////+wAAABoAQQByAHIAYQBuAGcAZQBEAG8AYwBrAGUAcgAAAAAA/////wAAAHoA////+wAAADoAYwBvAG0AaQBjAHMAXwBwAHIAbwBqAGUAYwB0AF8AbQBhAG4AYQBnAGUAcgBfAGQAbwBjAGsAZQByAAAAAAD/////AAAAuQD////7AAAAKgBxAHUAaQBjAGsAXwBzAGUAdAB0AGkAbgBnAHMAXwBkAG8AYwBrAGUAcgAAAAAA/////wAAAIwA////+wAAABYAUABhAGcAZQByAEQAbwBjAGsAZQByAAAAAAD/////AAAALQD////7AAAAJgBsAGEAcwB0AGQAbwBjAHUAbQBlAG4AdABzAGQAbwBjAGsAZQByAAAAAAD/////AAAAiQD///8AAAACAAAKAAAAALz8AQAAAAH7AAAAGgBUAG8AbwBsAEIAYQByAEQAbwBjAGsAZQByAAAAAAD/////AAAAAAAAAAAAAAADAAAAAAAAAAD8AQAAAAT7AAAAHABGAGwAaQBwAGIAbwBvAGsARABvAGMAawBlAHIAAAAAAP////8AAAAAAAAAAPsAAAAeAEEAbgBpAG0AYQB0AGkAbwBuAEQAbwBjAGsAZQByAAAAAAD/////AAABBQD////7AAAAIABPAG4AaQBvAG4AUwBrAGkAbgBzAEQAbwBjAGsAZQByAAAAAAD/////AAABNgD////7AAAAHABUAGkAbQBlAGwAaQBuAGUARABvAGMAawBlAHIAAAAAAP////8AAABVAP///wAABU0AAAPNAAAABAAAAAQAAAAIAAAACPwAAAABAAAAAgAAAAIAAAAWAG0AYQBpAG4AVABvAG8AbABCAGEAcgEAAAAA/////wAAAAAAAAAAAAAAHgBCAHIAdQBzAGgAZQBzAEEAbgBkAFMAdAB1AGYAZgEAAAC5/////wAAAAAAAAAA
[advancedColorSelector]
gamma=2.2000000000000002
hidePopupOnClickCheck=false
hsxSettingType=0
lumaB=0.0722
lumaG=0.71519999999999995
lumaR=0.21260000000000001
onDockerResize=0
shadeMyPaintType=HSV
zoomSelectorOptions=0
[calligra]
ColorSpaceExtensionsPlugins=\\0
ColorSpaceExtensionsPluginsDisabled=
ColorSpacePlugins=\\0
ColorSpacePluginsDisabled=
DockerPlugins=\\0
DockerPluginsDisabled=textdocumentinspection
FlakePlugins=,
ShapePlugins=,
ToolsBlacklist=CreatePathTool,KoPencilTool,ConnectionTool,KarbonFilterEffectsTool,KritaShape/KisToolText,ArtisticTextTool,TextTool
ToolPlugins=,,
ToolPluginsDisabled=
[KoShapeCollection]
QuickShapes=ArtisticText,TextShapeID,EllipseShape,RectangleShape
[colorhotkeys]
steps_blueyellow=10
steps_hue=36
steps_lightness=10
steps_redgreen=10
steps_saturation=10
[crashprevention]
CreatingCanvas=false
[hsxColorSlider]
hsiH=false
hsiI=false
hsiS=false
hslH=true
hslL=true
hslS=true
hsvH=false
hsvS=false
hsvV=false
hsyH=false
hsyS=false
hsyY=false
[krita]
State=AAAA/wAAAAD9AAAABAAAAAAAAABEAAAE6PwCAAAAA/sAAAAOAFQAbwBvAGwAQgBvAHgBAAAARAAABOgAAAAdAQAAA/sAAAAkAEYAbABvAHcAUwBoAGEAcABlAEIAbwB4AEQAbwBjAGsAZQByAAAAA2oAAADHAAAAAAAAAAD7AAAAKABGAGwAbwB3AFMAdABlAG4AYwBpAGwAQgBvAHgARABvAGMAawBlAHIAAAADfQAAAMcAAAAAAAAAAAAAAAEAAAEZAAAE6PwCAAAAO/sAAAAaAEsAaQBzAEIAaQByAGQAZQB5AGUAQgBvAHgAAAAAAP////8AAAAAAAAAAPsAAAAgAEsAaQBzAFAAYQBsAGUAdAB0AGUARABvAGMAawBlAHIAAAAAAP////8AAAAAAAAAAPsAAAAaAEsAbwBDAG8AbABvAHIARABvAGMAawBlAHIAAAAAAP////8AAAAAAAAAAPsAAAAwAEsAaQBzAFQAcgBpAGEAbgBnAGwAZQBDAG8AbABvAHIAUwBlAGwAZQBjAHQAbwByAAAAAAD/////AAAAAAAAAAD7AAAAIgBTAGgAYQBkAG8AdwAgAFAAcgBvAHAAZQByAHQAaQBlAHMAAAAAAP////8AAAAAAAAAAPsAAAAgAFMAaABhAHAAZQAgAFAAcgBvAHAAZQByAHQAaQBlAHMAAAAAAP////8AAAAAAAAAAPsAAAAaAFMAaABhAHAAZQBTAGUAbABlAGMAdABvAHIAAAAASAAAAEQAAAAAAAAAAPsAAAAkAFMAaQBtAHAAbABlACAAVABlAHgAdAAgAEUAZABpAHQAbwByAAAAAAD/////AAAAAAAAAAD8AAAARAAAAKUAAAAAAP////r/////AQAAAAL7AAAAFgBDAG8AbABvAHIAUwBsAGkAZABlAHIAAAAAAP////8AAACuAQAAA/sAAAAaAFAAYQBsAGUAdAB0AGUARABvAGMAawBlAHIAAAAAAP////8AAADtAQAAA/wAAABEAAABMgAAAIgBAAAb+gAAAAIBAAAABvsAAAAcAE8AdgBlAHIAdgBpAGUAdwBEAG8AYwBrAGUAcgEAAAAA/////wAAAKMBAAAD+wAAACAAcwBoAGEAcgBlAGQAdABvAG8AbABkAG8AYwBrAGUAcgEAAAAA/////wAAAJgBAAAD+wAAAB4AQwBvAGwAbwByAFMAZQBsAGUAYwB0AG8AcgBOAGcBAAAAAP////8AAAD7AQAAA/sAAAAqAFMAcABlAGMAaQBmAGkAYwBDAG8AbABvAHIAUwBlAGwAZQBjAHQAbwByAAAAAAD/////AAAA5gEAAAP7AAAAFgBJAG0AYQBnAGUARABvAGMAawBlAHIAAAAAAP////8AAADbAQAAA/sAAAAqAFMAaABhAHAAZQBDAG8AbABsAGUAYwB0AGkAbwBuAEQAbwBjAGsAZQByAAAABkgAAAEoAAAAAAAAAAD7AAAARgBLAHIAaQB0AGEAUwBoAGEAcABlAC8ASwBpAHMAVABvAG8AbABEAHkAbgBhAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQBAAAAUgAAABIAAAAAAAAAAPsAAAAsAEsAcgBpAHQAYQBTAGgAYQBwAGUALwBLAGkAcwBUAG8AbwBsAEwAaQBuAGUBAAAAPAAAAGkAAAAAAAAAAPsAAAAyAEsAcgBpAHQAYQBTAGgAYQBwAGUALwBLAGkAcwBUAG8AbwBsAEUAbABsAGkAcABzAGUBAAAAkQAAABIAAAAAAAAAAPsAAAAcAEsAaQBzAFQAbwBvAGwAUABvAGwAeQBnAG8AbgEAAACmAAAAEgAAAAAAAAAA+wAAAB4ASwBpAHMAVABvAG8AbABQAG8AbAB5AGwAaQBuAGUBAAAAuwAAABIAAAAAAAAAAPsAAAAWAEsAaQBzAFQAbwBvAGwAUwB0AGEAcgEAAADQAAAAEwAAAAAAAAAA+wAAACoAUwBuAGEAcABHAHUAaQBkAGUAQwBvAG4AZgBpAGcAVwBpAGQAZwBlAHQAAAAA7wAAAHEAAAAAAAAAAPsAAAAyAEsAaQBzAFQAbwBvAGwAQwByAG8AcAAgAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQBAAAA+wAAABIAAAAAAAAAAPsAAABQAEsAcgBpAHQAYQBUAHIAYQBuAHMAZgBvAHIAbQAvAEsAaQBzAFQAbwBvAGwATQBvAHYAZQAgAE8AcAB0AGkAbwBuACAAVwBpAGQAZwBlAHQBAAABEAAAABIAAAAAAAAAAPsAAAA8AEsAaQBzAFQAbwBvAGwAVAByAGEAbgBzAGYAbwByAG0AIABvAHAAdABpAG8AbgAgAHcAaQBkAGcAZQB0AQAAADwAAAAvAAAAAAAAAAD7AAAATgBLAHIAaQB0AGEAUwBoAGEAcABlAC8ASwBpAHMAVABvAG8AbABNAGUAYQBzAHUAcgBlACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAA8AAAAQgAAAAAAAAAA+wAAAFwASwByAGkAdABhAFMAZQBsAGUAYwB0AGUAZAAvAEsAaQBzAFQAbwBvAGwAQwBvAGwAbwByAFAAaQBjAGsAZQByACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAA8AAAA/wAAAAAAAAAA+wAAAEYASwBpAHMAUgB1AGwAZQByAEEAcwBzAGkAcwB0AGEAbgB0AFQAbwBvAGwAIABPAHAAdABpAG8AbgAgAFcAaQBkAGcAZQB0AQAAADwAAAASAAAAAAAAAAD7AAAASABLAGkAcwBUAG8AbwBsAFAAZQByAHMAcABlAGMAdABpAHYAZQBHAHIAaQBkACAATwBwAHQAaQBvAG4AIABXAGkAZABnAGUAdAEAAAGjAAAAEgAAAAAAAAAA+wAAADIASwBpAHMAVABvAG8AbABHAHIAaQBkACAATwBwAHQAaQBvAG4AIABXAGkAZABnAGUAdAEAAAG4AAAAEwAAAAAAAAAA+wAAAEwASwBpAHMAVABvAG8AbABTAGUAbABlAGMAdABSAGUAYwB0AGEAbgBnAHUAbABhAHIAIABvAHAAdABpAG8AbgAgAHcAaQBkAGcAZQB0AQAAAc4AAAASAAAAAAAAAAD7AAAASgBLAGkAcwBUAG8AbwBsAFMAZQBsAGUAYwB0AEUAbABsAGkAcAB0AGkAYwBhAGwAIABvAHAAdABpAG8AbgAgAHcAaQBkAGcAZQB0AQAAAeMAAAASAAAAAAAAAAD7AAAASABLAGkAcwBUAG8AbwBsAFMAZQBsAGUAYwB0AFAAbwBsAHkAZwBvAG4AYQBsACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAH4AAAAEgAAAAAAAAAA+wAAAEQASwBpAHMAVABvAG8AbABTAGUAbABlAGMAdABPAHUAdABsAGkAbgBlACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAINAAAAEgAAAAAAAAAA+wAAAEoASwBpAHMAVABvAG8AbABTAGUAbABlAGMAdABDAG8AbgB0AGkAZwB1AG8AdQBzACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAIiAAAAEgAAAAAAAAAA+wAAAEQASwBpAHMAVABvAG8AbABTAGUAbABlAGMAdABTAGkAbQBpAGwAYQByACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAEAAAI3AAAAEgAAAAAAAAAA/AAAAbYAAABaAAAAAAD////6AAAAAAEAAAAC+wAAAC4ASwBvAFMAaABhAHAAZQBDAG8AbABsAGUAYwB0AGkAbwBuAEQAbwBjAGsAZQByAQAAAAD/////AAAAAAAAAAD7AAAAJABTAG0AYQBsAGwAQwBvAGwAbwByAFMAZQBsAGUAYwB0AG8AcgAAAANuAAABBAAAANkBAAAD/AAAAXcAAAGjAAAA3gEAABv6AAAAAAEAAAAF+wAAABYASwBpAHMATABhAHkAZQByAEIAbwB4AQAAAAD/////AAABBgEAAAP7AAAAIgBDAG8AbQBwAG8AcwBpAHQAaQBvAG4ARABvAGMAawBlAHIAAAAAAP////8AAAC0AQAAA/sAAAAOAEgAaQBzAHQAbwByAHkAAAAAAP////8AAACxAQAAA/sAAAAaAEMAaABhAG4AbgBlAGwARABvAGMAawBlAHIBAAAAAP////8AAACjAQAAA/sAAAAuAEsAaQBzAFAAYQBpAG4AdABlAHIAbAB5AE0AaQB4AGUAcgBEAG8AYwBrAGUAcgAAAAAA/////wAAAAAAAAAA+wAAABgAUAByAGUAcwBlAHQARABvAGMAawBlAHIBAAADGwAAAhEAAACCAQAAA/sAAABIAEsAcgBpAHQAYQBTAGgAYQBwAGUALwBLAGkAcwBUAG8AbwBsAEIAcgB1AHMAaABvAHAAdABpAG8AbgAgAHcAaQBkAGcAZQB0AQAAA9wAAABoAAAAAAAAAAD7AAAAIgBTAHQAcgBvAGsAZQAgAFAAcgBvAHAAZQByAHQAaQBlAHMAAAAAAP////8AAAAAAAAAAPsAAAAWAFMAdAB5AGwAZQBEAG8AYwBrAGUAcgAAAAAA/////wAAAAAAAAAA+wAAACAASwBpAHMASABpAHMAdABvAGcAcgBhAG0ARABvAGMAawAAAAAA/////wAAAAAAAAAA+wAAABIAUwBjAHIAaQBwAHQAaQBuAGcAAAAAAP////8AAAAAAAAAAPsAAAAwAEQAZQBmAGEAdQBsAHQAVABvAG8AbABBAHIAcgBhAG4AZwBlAFcAaQBkAGcAZQB0AAAAArwAAABSAAAAAAAAAAD7AAAAIgBEAGUAZgBhAHUAbAB0AFQAbwBvAGwAVwBpAGQAZwBlAHQAAAADEQAAAFsAAAAAAAAAAPsAAAAkAEsAaQBzAEgAaQBzAHQAbwBnAHIAYQBtAEQAbwBjAGsAZQByAAAAAkIAAAB7AAAAAAAAAAD7AAAAGABEAGkAZwBpAHQAYQBsAE0AaQB4AGUAcgAAAAAA/////wAAAKEBAAAD+wAAAE4ASwByAGkAdABhAEYAaQBsAGwALwBLAGkAcwBUAG8AbwBsAEcAcgBhAGQAaQBlAG4AdAAgAG8AcAB0AGkAbwBuACAAdwBpAGQAZwBlAHQAAAAEKAAAABwAAAAAAAAAAPsAAABGAEsAcgBpAHQAYQBGAGkAbABsAC8ASwBpAHMAVABvAG8AbABGAGkAbABsACAAbwBwAHQAaQBvAG4AIAB3AGkAZABnAGUAdAAAAANQAAAAHAAAAAAAAAAA+wAAADYASwByAGkAdABhAFMAaABhAHAAZQAvAEsAaQBzAFQAbwBvAGwAUgBlAGMAdABhAG4AZwBsAGUAAAADBQAAAGcAAAAAAAAAAPsAAAAqAEEAcgB0AGkAcwB0AGkAYwBDAG8AbABvAHIAUwBlAGwAZQBjAHQAbwByAAAAAAD/////AAAAgAEAAAP7AAAAGgBQAGEAdAB0AGUAcgBuAEQAbwBjAGsAZQByAAAAAtkAAAFJAAAAvAEAAAP7AAAAGgBUAGEAcwBrAHMAZQB0AEQAbwBjAGsAZQByAAAAAAD/////AAAAmAEAAAP7AAAAKABTAG4AYQBwAEcAdQBpAGQAZQAgAFAAcgBvAHAAZQByAHQAaQBlAHMAAAAAAP////8AAAAAAAAAAPsAAAA4AFQAZQB4AHQARABvAGMAdQBtAGUAbgB0AEkAbgBzAHAAZQBjAHQAaQBvAG4ARABvAGMAawBlAHICAAAEmgAAAhUAAAEqAAAArvsAAAASAEwAdQB0AEQAbwBjAGsAZQByAAAAA3wAAAEuAAABsQEAAAP7AAAAFABHAHIAaQBkAEQAbwBjAGsAZQByAAAAAAD/////AAABNgEAAAP7AAAAHgBIAGkAcwB0AG8AZwByAGEAbQBEAG8AYwBrAGUAcgAAAAAA/////wAAAFABAAAD+wAAABoAUAByAGUAcwBlAHQASABpAHMAdABvAHIAeQAAAAAA/////wAAAHABAAAD+wAAADIAUwB2AGcAUwB5AG0AYgBvAGwAQwBvAGwAbABlAGMAdABpAG8AbgBEAG8AYwBrAGUAcgAAAAAA/////wAAAAAAAAAA+wAAABYAVABvAHUAYwBoAEQAbwBjAGsAZQByAAAAAAD/////AAAAHAEAAAP7AAAAGgBBAHIAcgBhAG4AZwBlAEQAbwBjAGsAZQByAAAAAAD/////AAAAkAEAAAP7AAAAKgBBAG4AaQBtAGEAdABpAG8AbgBDAHUAcgB2AGUAcwBEAG8AYwBrAGUAcgAAAAAA/////wAAAJgBAAADAAAAAgAAB4AAAAC8/AEAAAAB+wAAABoAVABvAG8AbABCAGEAcgBEAG8AYwBrAGUAcgAAAAAA/////wAAAAAAAAAAAAAAAwAAAAAAAAAA/AEAAAAE+wAAABwARgBsAGkAcABiAG8AbwBrAEQAbwBjAGsAZQByAAAAAAD/////AAAAAAAAAAD7AAAAIABPAG4AaQBvAG4AUwBrAGkAbgBzAEQAbwBjAGsAZQByAAAAAAD/////AAABSAEAAAP7AAAAHgBBAG4AaQBtAGEAdABpAG8AbgBEAG8AYwBrAGUAcgAAAAAA/////wAAASUBAAAD+wAAABwAVABpAG0AZQBsAGkAbgBlAEQAbwBjAGsAZQByAAAAAAD/////AAAAlgEAAAMAAAihAAAE6AAAAAQAAAAEAAAACAAAAAj8AAAAAQAAAAIAAAACAAAAFgBtAGEAaQBuAFQAbwBvAGwAQgBhAHIBAAAAAP////8AAAAAAAAAAAAAAB4AQgByAHUAcwBoAGUAcwBBAG4AZABTAHQAdQBmAGYBAAAA1P////8AAAAAAAAAAA==
ToolBarsMovable=Enabled
[krita][DockWidget AnimationCurvesDocker]
Collapsed=false
DockArea=2
Locked=false
height=421
width=448
xPosition=0
yPosition=0
[krita][DockWidget AnimationDocker]
Collapsed=false
DockArea=8
Locked=false
height=160
width=280
xPosition=0
yPosition=0
[krita][DockWidget ArtisticColorSelector]
Collapsed=false
DockArea=2
Locked=false
height=294
width=337
xPosition=0
yPosition=0
[krita][DockWidget ChannelDocker]
Collapsed=false
DockArea=2
Locked=false
height=30
width=100
xPosition=0
yPosition=0
[krita][DockWidget ColorSelectorNg]
Collapsed=false
DockArea=2
Locked=false
height=176
width=281
xPosition=0
yPosition=20
[krita][DockWidget ColorSlider]
Collapsed=false
DockArea=2
Locked=false
height=460
width=640
xPosition=0
yPosition=20
[krita][DockWidget CompositionDocker]
Collapsed=false
DockArea=2
Locked=false
height=300
width=400
xPosition=0
yPosition=0
[krita][DockWidget DigitalMixer]
Collapsed=false
DockArea=2
Locked=false
height=30
width=100
xPosition=0
yPosition=0
[krita][DockWidget GridDocker]
Collapsed=false
DockArea=2
Locked=false
height=342
width=441
xPosition=0
yPosition=0
[krita][DockWidget HistogramDocker]
Collapsed=false
DockArea=2
Locked=false
height=91
width=281
xPosition=0
yPosition=20
[krita][DockWidget History]
Collapsed=false
DockArea=2
Locked=false
height=460
width=640
xPosition=0
yPosition=20
[krita][DockWidget ImageDocker]
Collapsed=false
DockArea=2
Locked=false
height=300
width=399
xPosition=0
yPosition=0
[krita][DockWidget KisLayerBox]
DockArea=2
Locked=false
height=358
width=281
xPosition=0
yPosition=20
[krita][DockWidget LutDocker]
Collapsed=false
DockArea=2
Locked=false
height=286
width=357
xPosition=0
yPosition=0
[krita][DockWidget OnionSkinsDocker]
Collapsed=false
DockArea=8
Locked=false
height=210
width=356
xPosition=0
yPosition=0
[krita][DockWidget OverviewDocker]
Collapsed=false
DockArea=2
Locked=false
height=30
width=100
xPosition=0
yPosition=0
[krita][DockWidget PaletteDocker]
Collapsed=false
DockArea=2
Locked=false
height=219
width=256
xPosition=0
yPosition=0
[krita][DockWidget PatternDocker]
Collapsed=false
DockArea=2
Locked=false
height=30
width=100
xPosition=0
yPosition=0
[krita][DockWidget PresetDocker]
Collapsed=false
DockArea=2
Locked=false
height=460
width=640
xPosition=0
yPosition=20
[krita][DockWidget PresetHistory]
Collapsed=false
DockArea=2
Locked=false
height=30
width=100
xPosition=0
yPosition=0
[krita][DockWidget Shape Properties]
DockArea=2
Locked=false
height=480
width=640
xPosition=0
yPosition=0
[krita][DockWidget ShapeCollectionDocker]
Collapsed=false
DockArea=2
Locked=false
height=0
width=0
xPosition=0
yPosition=20
[krita][DockWidget SmallColorSelector]
DockArea=2
Locked=false
height=460
width=640
xPosition=0
yPosition=20
[krita][DockWidget SpecificColorSelector]
DockArea=2
Locked=false
height=460
width=640
xPosition=0
yPosition=20
[krita][DockWidget TasksetDocker]
Collapsed=false
DockArea=2
Locked=false
height=300
width=400
xPosition=0
yPosition=0
[krita][DockWidget TimelineDocker]
Collapsed=false
DockArea=8
Locked=false
height=30
width=100
xPosition=0
yPosition=0
[krita][DockWidget ToolBox]
DockArea=1
Locked=false
height=610
width=63
xPosition=0
yPosition=20
[krita][DockWidget sharedtooldocker]
Collapsed=false
DockArea=2
Locked=false
height=460
width=640
xPosition=0
yPosition=20
[krita][Toolbar mainToolBar]
ToolButtonStyle=IconOnly
[TemplateChooserDialog]
ShowCustomDocumentWidgetByDefault=true
LastReturnType=Custom Document
[theme]
Theme=Krita dark
[python]
enable_colorspace=true
enable_comics_project_management_tools=true
enable_documenttools=true
enable_exportlayers=true
enable_filtermanager=true
enable_lastdocumentsdocker=true
enable_quick_settings_docker=true
enable_scripter=true
enable_tenbrushes=true
enable_tenscripts=true
-
+enable_plugin_importer=true
diff --git a/krita/data/palettes/pixelart-16.gpl b/krita/data/palettes/pixelart-16.gpl
index ccc005d4fa..8a7f32dbac 100644
--- a/krita/data/palettes/pixelart-16.gpl
+++ b/krita/data/palettes/pixelart-16.gpl
@@ -1,26 +1,26 @@
GIMP Palette
Name: PixelArt 16
-Columns: 1
+Columns: 8
#
# Richard Fhager's carefully balanced 16color pixel art palette.
#
# URL : http://www.pixeljoint.com/forum/forum_posts.asp?TID=12795
#
0 0 0 Black
20 12 28 Haiti
68 36 52 Castro
48 52 109 Torea bay
78 74 78 Salt box
89 125 206 Cornflower
133 149 161 Pigeon post
109 194 202 Fountain blue
52 101 36 San felix
133 76 48 Cinnamon
117 113 97 Flint
208 70 72 Valencia
210 125 44 California
109 170 44 Christi
210 170 153 Quicksand
218 212 94 Witch haze
222 238 214 Peppermint
diff --git a/krita/data/templates/animation/.directory b/krita/data/templates/animation/.directory
index c551bced07..65a1e5ebdc 100644
--- a/krita/data/templates/animation/.directory
+++ b/krita/data/templates/animation/.directory
@@ -1,26 +1,27 @@
[Desktop Entry]
Name=Animation Templates
Name[ar]=قوالب الحركات
Name[ca]=Plantilles d'animació
Name[ca@valencia]=Plantilles d'animació
Name[cs]=Šablony animací:
Name[de]=Animations-Vorlagen
Name[el]=Πρότυπα εφέ κίνησης
Name[en_GB]=Animation Templates
Name[es]=Plantillas de animación
Name[eu]=Animazio-txantiloiak
+Name[fi]=Animaatiopohjat
Name[fr]=Modèles pour animation
Name[gl]=Modelos de animación
Name[is]=Sniðmát fyrir hreyfimyndir
Name[it]=Modelli di animazioni
Name[nl]=Animatiesjablonen
Name[pl]=Szablony animacji
Name[pt]=Modelos de Animações
Name[pt_BR]=Modelos de animação
Name[sv]=Animeringsmallar
Name[tr]=Canlandırma Şablonları
Name[uk]=Шаблони анімацій
Name[x-test]=xxAnimation Templatesxx
Name[zh_CN]=动画模板
Name[zh_TW]=動畫範本
X-KDE-DefaultTab=true
diff --git a/krita/data/templates/animation/Anim-Jp-EN.desktop b/krita/data/templates/animation/Anim-Jp-EN.desktop
index aa69ed4919..eaca1b9ffa 100644
--- a/krita/data/templates/animation/Anim-Jp-EN.desktop
+++ b/krita/data/templates/animation/Anim-Jp-EN.desktop
@@ -1,31 +1,32 @@
[Desktop Entry]
Type=Link
URL=.source/Anim-Jp-EN.kra
Icon=template_animation
Name=Animation-Japanese-En
Name[ar]=حركة يابانية (إنجليزي)
Name[ca]=Animació-Japonès-EN
Name[ca@valencia]=Animació-Japonés-EN
Name[de]=Animation-Japanisch-En
Name[el]=Εφέ-κίνησης-Ιαπωνικό-En
Name[en_GB]=Animation-Japanese-En
Name[es]=Animación-Japonés-En
Name[et]=Animation-Japanese-En
Name[eu]=Animazioa-Japoniarra-En
+Name[fi]=Animaatio-japanilainen-EN
Name[fr]=Animation japonaise (en)
Name[gl]=Animación-xaponesa-en-inglés
Name[is]=Hreyfimynd-Japanska-En
Name[it]=Animazione-Giapponese-EN
Name[ja]=日本式アニメ(英語版)
Name[nl]=Animatie-Japans-En
Name[pl]=Animacja-Japońska-En
Name[pt]=Animação-Japonês-EN
Name[pt_BR]=Animation-Japanese-En
Name[ru]=Анимация-японская-англ
Name[sk]=Animation-Japanese-En
Name[sv]=Animering-japanska-en
Name[tr]=Canlandırma-Japonca-İngilizce
Name[uk]=Японська анімація (англійською)
Name[x-test]=xxAnimation-Japanese-Enxx
Name[zh_CN]=日式动画 (英文图层名)
Name[zh_TW]=動畫-Japanese-En
diff --git a/krita/data/templates/animation/Anim-Jp-JP.desktop b/krita/data/templates/animation/Anim-Jp-JP.desktop
index 50e6e6920a..4a7ff9bac7 100644
--- a/krita/data/templates/animation/Anim-Jp-JP.desktop
+++ b/krita/data/templates/animation/Anim-Jp-JP.desktop
@@ -1,31 +1,32 @@
[Desktop Entry]
Type=Link
URL=.source/Anim-Jp-JP.kra
Icon=template_animation
Name=Animation-Japanese-JP
Name[ar]=حركة يابانية (ياباني)
Name[ca]=Animació-Japonès-JP
Name[ca@valencia]=Animació-Japonés-JP
Name[de]=Animation-Japanisch-JP
Name[el]=Εφέ-κίνησης-Ιαπωνικό-JP
Name[en_GB]=Animation-Japanese-JP
Name[es]=Animación-Japonés-JP
Name[et]=Animation-Japanese-JP
Name[eu]=Animazioa-Japoniarra-JP
+Name[fi]=Animaatio-japanilainen-JP
Name[fr]=Animation japonaise (jp)
Name[gl]=Animación-xaponesa-en-xaponés
Name[is]=Hreyfimynd-Japanska-JP
Name[it]=Animazione-Giapponese-JP
Name[ja]=日本式アニメ(日本語版)
Name[nl]=Animatie-Japans-JP
Name[pl]=Animacja-Japońska-JP
Name[pt]=Animação-Japonês-JP
Name[pt_BR]=Animation-Japanese-JP
Name[ru]=Анимация-японская-японск
Name[sk]=Animation-Japanese-JP
Name[sv]=Animering-japanska-jp
Name[tr]=Canlandırma-Japonca-JP
Name[uk]=Японська анімація (японською)
Name[x-test]=xxAnimation-Japanese-JPxx
Name[zh_CN]=日本动画 (日式)
Name[zh_TW]=動畫-Japanese-JP
diff --git a/krita/data/templates/comics/BD-EuroTemplate.desktop b/krita/data/templates/comics/BD-EuroTemplate.desktop
index 67c5eadb51..fda45918d3 100644
--- a/krita/data/templates/comics/BD-EuroTemplate.desktop
+++ b/krita/data/templates/comics/BD-EuroTemplate.desktop
@@ -1,76 +1,76 @@
[Desktop Entry]
Type=Link
URL=.source/BD-EuroTemplate.kra
Icon=template_comics_empty
Name=European BD template
Name[bs]=Evropski BD predložak
-Name[ca]=Plantilla europea BD
-Name[ca@valencia]=Plantilla europea BD
+Name[ca]=Plantilla BD europeu
+Name[ca@valencia]=Plantilla BD europeu
Name[da]=Europæisk BD-skabelon
Name[de]=Europäische „Bande Dessinée (BD)“-Vorlage
Name[el]=Ευρωπαϊκό BD πρότυπο
Name[en_GB]=European BD template
Name[es]=plantilla de cómic europeo
Name[et]=Euroopa BD mall
Name[eu]=Europako BD-txantiloia
Name[fi]=Eurooppalainen BD-pohja
Name[fr]=Modèle européen de bandes dessinées
Name[gl]=Formato europeo (2×4 viñetas)
Name[hu]=Európai BD sablon
Name[is]=Evrópskt BD teiknimyndasögusniðmát
Name[it]=Modello MD europeo
Name[ja]=バンドデシネテンプレート
Name[kk]=Еуропалық BD үлгісі
Name[lt]=Europos DB šablonas
Name[nb]=Europeisk BD-mal
Name[nds]=Europääsch BD-Vörlaag
Name[nl]=Europees BD-sjabloon
Name[pl]=Europejski szablon BD
Name[pt]=Modelo de BD Europeia
Name[pt_BR]=Modelo Europeu BD
Name[ru]=Шаблон в европейском стиле (BD)
Name[sk]=Európska BD šablóna
Name[sl]=Evropska predloga BD
Name[sv]=Europeisk BD-mall
Name[tr]=Avrupa BD Şablonu
Name[uk]=Європейський шаблон BD
Name[wa]=Modele di binde d' imådje a l' uropeyinne
Name[x-test]=xxEuropean BD templatexx
Name[zh_CN]=欧式 BD (比利时-法国) 风格漫画模板
Name[zh_TW]=歐洲漫畫範本
Comment=template for European BD-style comics
Comment[bs]=predložak za evropske BD stripove
-Comment[ca]=plantilla per a còmics d'estil BD europeu
-Comment[ca@valencia]=plantilla per a còmics d'estil BD europeu
+Comment[ca]=Plantilla per a còmics d'estil BD europeu
+Comment[ca@valencia]=Plantilla per a còmics d'estil BD europeu
Comment[da]=Skabelon til tegneserier i europæisk BD-stil
Comment[de]=Vorlage für Comics im europäischen „Bande Dessinée“-Stil
Comment[el]=πρότυπο για Ευρωπαϊκά BD-style κόμικς
Comment[en_GB]=template for European BD-style comics
Comment[es]=plantilla para cómics de estilo europeo
Comment[et]=Euroopa BD-stiilis koomiksi mall
Comment[eu]=Europako BD-estiloko komikietarako txantiloia
Comment[fi]=Eurooppalaisen BD-tyylin sarjakuvan pohja
Comment[fr]=Modèle européen de bandes dessinées
Comment[gl]=Páxina de banda deseñada de formato europeo, con 2×4 viñetas regulares.
Comment[hu]=sablon az európai BD-stílusú képregényekhez
Comment[is]=Sniðmát fyrir evrópskar BD-teiknimyndir
Comment[it]=modello per fumetti in stile BD europeo
Comment[ja]=バンドデシネ式コミック用テンプレート
Comment[kk]=Еуропалық BD-стильдегі комикс үлгісі
Comment[nb]=mal for europeiske tegneserier i BD-stil
Comment[nds]=BD-Vörlaag för europääsche Comics
Comment[nl]=sjabloon voor Europese strips in BD-stijl
Comment[pl]=szablon dla Europejskiego stylu komików BD
Comment[pt]=modelo de banda desenhada do estilo Europeu
Comment[pt_BR]=Modelo de quadrinhos no estilo Europeu BD
Comment[ru]=Шаблон комиксов в европейском стиле (BD)
Comment[sk]=šablóna pre európske BD komixy
Comment[sl]=predloga za stripe v evropskem slogu BD
Comment[sv]=seriemall med europeisk BD-stil
Comment[tr]=Avrupa BD tarzı çizgi romanlar için şablon
Comment[uk]=шаблон для європейських коміксів у стилі BD
Comment[wa]=Modele po les bindes d' imådje al môde uropeyinne
Comment[x-test]=xxtemplate for European BD-style comicsxx
Comment[zh_CN]=欧式 BD (比利时-法国) 风格漫画模板
Comment[zh_TW]=歐洲 BD-風格的連環漫畫範本
X-Krita-Version=28
diff --git a/krita/data/templates/comics/Comics-USTemplate.desktop b/krita/data/templates/comics/Comics-USTemplate.desktop
index fbe4455b43..9afeddf4c4 100644
--- a/krita/data/templates/comics/Comics-USTemplate.desktop
+++ b/krita/data/templates/comics/Comics-USTemplate.desktop
@@ -1,82 +1,82 @@
[Desktop Entry]
Type=Link
URL=.source/Comics-USTemplate.kra
Icon=template_comics_empty
Name=US-style comics template
Name[ar]=قالب هزليّات بنمط أمريكي
Name[bs]=Američki strip predložak
-Name[ca]=plantilla de còmics d'estil americà
-Name[ca@valencia]=plantilla de còmics d'estil americà
+Name[ca]=Plantilla de còmics d'estil americà
+Name[ca@valencia]=Plantilla de còmics d'estil americà
Name[cs]=Šablona komixu v americkém stylu
Name[da]=Tegneserieskabelon i amerikansk stil
Name[de]=US-Design-Comicvorlage
Name[el]=Πρότυπο κόμικς US-style
Name[en_GB]=US-style comics template
Name[es]=plantilla de cómic de estilo estadounidense
Name[et]=USA stiilis koomiksi mall
Name[eu]=AEB-estiloko komiki-txantiloia
Name[fi]=Yhdysvaltalaistyylinen sarjakuvapohja
Name[fr]=Modèle US de bande dessinée
Name[gl]=Formato estadounidense (2×3 viñetas)
Name[hu]=US-stílusú képregénysablon
Name[is]=Bandarískt teiknimyndasögusniðmát
Name[it]=Modello per fumetti in stile americano
Name[ja]=アメリカ式コミックテンプレート
Name[kk]=АҚШ-стильді комикс үлгісі
Name[ko]=미국식 만화 서식
Name[lt]=JAV stiliaus komiksų šablonas
Name[nb]=Tegneseriemal i USA-stil
Name[nds]=Amerikaansch Comicvörlaag
Name[nl]=sjabloon voor strips in US-stijl
Name[pl]=Szablon komiksów Amerykańskiego stylu
Name[pt]=Modelo de banda desenhada dos EUA
Name[pt_BR]=Modelo de quadrinhos no estilo americano
Name[ru]=Шаблон в американском стиле
Name[sk]=šablóna pre americké komixy
Name[sl]=Predloga za stripe v ameriškem slogu
Name[sv]=Seriemall med amerikansk stil
Name[tr]=US tarzı çizgi roman şablonu
Name[uk]=Шаблон коміксів у американському стилі
Name[wa]=Modele comics a l' amerikinnes
Name[x-test]=xxUS-style comics templatexx
Name[zh_CN]=美式漫画模板
Name[zh_TW]=美式漫畫範本
Comment=template for US-style comics
Comment[ar]=قالب للهزليّات بالنمط الأمريكي
Comment[bs]=predložak za stripove američkog stila
-Comment[ca]=plantilla per a còmics d'estil americà
-Comment[ca@valencia]=plantilla per a còmics d'estil americà
+Comment[ca]=Plantilla per a còmics d'estil americà
+Comment[ca@valencia]=Plantilla per a còmics d'estil americà
Comment[cs]=šablona pro komiksy v americkém stylu
Comment[da]=skabelon til tegneserier i amerikansk stil
Comment[de]=Vorlage für Comics im US-Stil
Comment[el]=πρότυπο για US-style κόμικς
Comment[en_GB]=template for US-style comics
Comment[es]=plantilla para cómics de estilo estadounidense
Comment[et]=USA stiilis koomiksi mall
Comment[eu]=AEB-estiloko komikietarako txantiloia
Comment[fi]=yhdysvaltalaistyylisen sarjakuvan pohja
Comment[fr]=Modèle US de bandes dessinées
Comment[gl]=Páxina de banda deseñada de formato estadounidense, con 2×3 viñetas regulares.
Comment[hu]=sablon a US-stílusú képregényekhez
Comment[is]=Sniðmát fyrir bandarískar comics-teiknimyndir
Comment[it]=modello per fumetti in stile americano
Comment[ja]=アメリカ式コミック用テンプレート
Comment[kk]=АҚШ-стильдегі комикс үлгісі
Comment[ko]=미국식 만화 서식
Comment[nb]=mal for tegneserier i US-stil
Comment[nds]=Vörlaag för amerikaansche Comics
Comment[nl]=sjabloon voor strips in US-stijl
Comment[pl]=szablon dla Amerykańskiego stylu komiksów
Comment[pt]=modelo de banda desenhada do estilo dos EUA
Comment[pt_BR]=Modelo de quadrinhos no estilo americano
Comment[ru]=Шаблон комиксов в американском стиле
Comment[sk]=šablóna pre americké komixy
Comment[sl]=predloga za stripe v ameriškem slogu
Comment[sv]=seriemall med amerikansk stil
Comment[tr]=US tarzı çizgi romanlar için şablon
Comment[uk]=шаблон для коміксів у американському стилі
Comment[wa]=Modele di bindes d' imådje al môde des comics amerikins
Comment[x-test]=xxtemplate for US-style comicsxx
Comment[zh_CN]=美式漫画模板
Comment[zh_TW]=US-風格的連環漫畫範本
X-Krita-Version=28
diff --git a/krita/data/templates/comics/Manga-JpTemplate.desktop b/krita/data/templates/comics/Manga-JpTemplate.desktop
index 5b152f0a3a..205fa78fe0 100644
--- a/krita/data/templates/comics/Manga-JpTemplate.desktop
+++ b/krita/data/templates/comics/Manga-JpTemplate.desktop
@@ -1,83 +1,83 @@
[Desktop Entry]
Type=Link
URL=.source/Manga-JpTemplate.kra
Icon=template_comics_empty
Name=Manga template
Name[ar]=قالب مانغا
Name[bs]=Manga predložak
Name[ca]=Plantilla per a manga
Name[ca@valencia]=Plantilla per a manga
Name[cs]=Šablona Mangy
Name[da]=Manga-skabelon
Name[de]=Manga-Vorlage
Name[el]=Πρότυπο μάνγκα
Name[en_GB]=Manga template
Name[es]=Plantilla manga
Name[et]=Manga mall
Name[eu]=Manga-txantiloia
Name[fi]=Mangapohja
Name[fr]=Modèle de Manga
Name[gl]=Formato Manga
Name[hu]=Manga sablon
Name[ia]=Patrono de Manga
Name[is]=Manga sniðmát
Name[it]=Modello manga
Name[ja]=漫画テンプレート
Name[kk]=Үлгіні басқару
Name[ko]=일본식 만화 서식
Name[lt]=Manga šablonas
Name[nb]=Manga-mal
Name[nds]=Manga-Vörlaag
Name[nl]=Manga-sjabloon
Name[pl]=Szablon Mangi
Name[pt]=Modelo Manga
Name[pt_BR]=Modelo de mangá
Name[ru]=Шаблон манги
Name[sk]=Manga šablóna
Name[sl]=Predloga Manga
Name[sv]=Manga-mall
Name[tr]=Manga şablonu
Name[uk]=Шаблон манґи
Name[wa]=Modele di manga
Name[x-test]=xxManga templatexx
Name[zh_CN]=日式漫画模板
Name[zh_TW]=日本漫畫範本
Comment=template for Japanese Manga-style comics
Comment[ar]=قالب للهزليّات بنمط المانغا اليابانية
Comment[bs]=predložak za japanske Manga stripove
-Comment[ca]=plantilla per a còmics d'estil manga japonès
-Comment[ca@valencia]=plantilla per a còmics d'estil manga japonés
+Comment[ca]=Plantilla per a còmics d'estil manga japonès
+Comment[ca@valencia]=Plantilla per a còmics d'estil manga japonés
Comment[cs]=šablona pro japonské komiksy ve stylu Manga
Comment[da]=skabelon til tegneserier i japansk Manga-stil
Comment[de]=Vorlage für Comics im Stil japanischer Mangas
Comment[el]=Πρότυπο για Ιαπωνικά μάνγκα κόμικς
Comment[en_GB]=template for Japanese Manga-style comics
Comment[es]=plantilla para cómics de estilo manga japonés
Comment[et]=Jaapani manga-stiilis koomiksi mall
Comment[eu]=Japoniako Manga-estiloko komikietarako txantiloia
Comment[fi]=japanilaisen mangatyylisen sarjakuvan pohja
Comment[fr]=Modèle de mangas japonais
Comment[gl]=Páxina de banda deseñada de formato Manga, con 2×3 viñetas non regulares.
Comment[hu]=sablon a japán Manga-stílusú képregényekhez
Comment[is]=Sniðmát fyrir japanskar Manga-teiknimyndir
Comment[it]=modello per fumetti in stile manga giapponese
Comment[ja]=日本式漫画用テンプレート
Comment[kk]=Жапондық манга-стильдегі комикс үлгісі
Comment[ko]=일본식 만화 서식
Comment[nb]=mal for japanske tegneserier i Manga-stil
Comment[nds]=Vörlaag för japaansche Manga-Comics
Comment[nl]=sjabloon voor strips in Japanse Manga-stijl
Comment[pl]=szablon dla Japońskiego stylu komiksów Mangi
Comment[pt]=modelo de banda desenhada Manga do estilo Japonês
Comment[pt_BR]=Modelo de quadrinhos no estilo mangá japonês
Comment[ru]=Шаблон комиксов в японском стиле манга
Comment[sk]=šablóna pre japonské manga komixy
Comment[sl]=predloge za stripe v japonskem slogu Manga
Comment[sv]=seriemall med japansk Manga-stil
Comment[tr]=Japon Manga çizgi romanları için şablon
Comment[uk]=шаблон японських коміксів у стилі манґа
Comment[wa]=Modele di bindes d' imådje al môde des mangas djaponès
Comment[x-test]=xxtemplate for Japanese Manga-style comicsxx
Comment[zh_CN]=日式漫画模板
Comment[zh_TW]=日本 Manga-風格的連環漫畫範本
X-Krita-Version=28
diff --git a/krita/data/templates/comics/a4_waffle_grid.desktop b/krita/data/templates/comics/a4_waffle_grid.desktop
index 701f7e65a7..eb1caa54f7 100644
--- a/krita/data/templates/comics/a4_waffle_grid.desktop
+++ b/krita/data/templates/comics/a4_waffle_grid.desktop
@@ -1,69 +1,71 @@
[Desktop Entry]
Type=Link
URL=.source/a4_waffle_grid.kra
Icon=template_comics_empty
Name=waffle-iron grid
Name[bs]=mreža sječenog željeza
-Name[ca]=graella de ferro
-Name[ca@valencia]=graella de ferro
+Name[ca]=Graella de ferro
+Name[ca@valencia]=Graella de ferro
Name[da]=vaffeljernsgitter
Name[de]=Waffeleisengitter
Name[el]=waffle-iron κάνναβος
Name[en_GB]=waffle-iron grid
Name[es]=rejilla de hierro para gofres
Name[et]=Vahvlimasina ruudustik
Name[eu]=gofreetarako burdinazko sareta
+Name[fi]=vohvelirautaruudukko
Name[fr]=Grille en métal gaufré
Name[gl]=Grade de 3×5 viñetas
Name[is]=vöfflujárnshnit
Name[it]=Griglia a wafer
Name[ja]=格子状コマ
Name[kk]=торлы көзді
Name[nb]=vaffeljern-rutenett
Name[nds]=Wafeliesengadder
Name[nl]=wafelijzer-raster
Name[pl]=siatka gofrownicy
Name[pt]=grelha de ferro para 'waffles'
Name[pt_BR]=Grade de ferro vazia
Name[ru]=Страница с ячейками
Name[sk]=vaflovo-železná mriežka
Name[sv]=våffelmönster
Name[tr]=waffle-çelik ızgara
Name[uk]=сітка з комірками
Name[wa]=grile di fier a wåfes
Name[x-test]=xxwaffle-iron gridxx
Name[zh_CN]=十五宫格
Name[zh_TW]=鬆餅機格線
Comment=300 dpi, A4 waffle-iron grid comic page with ink and color layers
Comment[bs]=300 dpi, A4 mreža sječenog željeza stranica stripa s slojevima za tintu i bojemreža sječenog željeza
Comment[ca]=300 ppp, pàgina de còmic amb graella de ferro amb capes de tinta i color
Comment[ca@valencia]=300 ppp, pàgina de còmic amb graella de ferro amb capes de tinta i color
Comment[da]=300 dpi, A4 tegneserieside i vaffeljernsgitter med blæk og farvelag
Comment[de]=Comicseite mit Waffeleisengitter-Muster, Tinten- und Farbebenen. Format A4, Auflösung 300 dpi.
Comment[el]=300 dpi, σελίδα κόμικ A4 με waffle-iron κάνναβο και στρώματα μελάνης και χρώματος
Comment[en_GB]=300 dpi, A4 waffle-iron grid comic page with ink and colour layers
Comment[es]=página de cómic con rejilla de hierro para gofres de tamaño A4, a 300 ppp, con tinta y capas de colores
Comment[et]=300 DPI A4 vahvlimasina ruudustikuga koomiksilehekülg tindi- ja värvikihiga
Comment[eu]=300 dpi, A4 gofreetarako burdinazko saretadun komiki-orri tintadun eta kolore-geruzaduna
+Comment[fi]=300 dpi:n A4-kokoinen vohvelirautaruudukko sarjakuvalle muste- ja väritasoin
Comment[fr]=Page de bande dessinée avec grille en métal gaufré en A4 300 dpi avec encre et calques de couleurs
Comment[gl]=Páxina de banda deseñada de grade en A4 a 300 dpi con 3×5 viñetas regulares e capas de tinta e cor.
Comment[is]=300 pát, A4 vöfflujárnshnit teiknimyndasíða með lögum fyrir blek og liti
Comment[it]=Pagina di fumetti con griglia a wafer a 300 dpi, A4, con livelli per inchiostro e colore
Comment[ja]=300 dpi A4 サイズの、ペン入れレイヤーと彩色レイヤーを備えた格子状コマテンプレート
Comment[kk]=300 н/д A4 торлы көзді парақтағы комикс
Comment[nb]=300 dpi, A4 tegneserieside med vaffeljern-rutenett, med tusj- og fargelag
Comment[nds]=300 dpi, A4 Wafeliesengadder-Comicsiet mit Dint un Klöörlagen.
Comment[nl]=300 dpi, A4 wafelijzer-raster strippagina met inkt en kleurlagen
Comment[pl]=300 dpi, strona A4 siatki gofrownicy z warstwami tuszu i koloru
Comment[pt]=banda desenhada A4, em grelha de 'waffle' a 300 ppp, com camadas de cores e de pinturas
Comment[pt_BR]=Página de quadrinhos A4, em grade de ferro a 300 ppp, com camadas de cores e de pinturas
Comment[ru]=300 dpi, страница комикса в формате A4 с ячейками и слоями контуров и цветов
Comment[sk]=300 dpi, A4 vaflovo železná mriežka komiksovej strany s atramentom a farebnými vrstvami
Comment[sv]=300 punkter/tum, A4 våffelmönstrad seriesida med bläck- och färglager
Comment[tr]=300 dpi, A4 waffle-çelik ızgara mürekkep ve renk katmanlı çizgi roman sayfası
Comment[uk]=300 т/д, сторінка коміксу у форматі A4 з комірками та шарами контурів та кольорів
Comment[wa]=Pådje A4 di binde d' imådjes avou on discôpaedje come ene grile di fier a wåfes avou des coûtches d' intche eyet d' coleurs.
Comment[x-test]=xx300 dpi, A4 waffle-iron grid comic page with ink and color layersxx
Comment[zh_CN]=300 DPI,A4 尺寸的十五宫格网格漫画页,内置线稿和着色图层
Comment[zh_TW]=300 dpi,A4 大小的烘餅鐵模狀的格線,有墨水與顏色圖層
X-Krita-Version=28
diff --git a/krita/data/templates/design/Designcinema16_10_2484x1200_96dpiRGB_8bit_.desktop b/krita/data/templates/design/Designcinema16_10_2484x1200_96dpiRGB_8bit_.desktop
index 319a23a526..c522a6eed4 100644
--- a/krita/data/templates/design/Designcinema16_10_2484x1200_96dpiRGB_8bit_.desktop
+++ b/krita/data/templates/design/Designcinema16_10_2484x1200_96dpiRGB_8bit_.desktop
@@ -1,38 +1,39 @@
[Desktop Entry]
Icon=template_ratio_1610
Name=Design cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ]
Name[ar]=تصميم سينمائي ١٦:١٠ [ ٢٤٨٤×١٢٠٠ ، ٩٦ نقطة/بوصة ، ٨ بتّ ]
Name[bs]=Design cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ]
-Name[ca]=Disseny de cine 16:10 [2484x1200 / 96ppp RGB / 8bit]
-Name[ca@valencia]=Disseny de cine 16:10 [2484x1200 / 96ppp RGB / 8bit]
+Name[ca]=Disseny de cine 16:10 [2484x1200, 96 ppp amb RGB, 8 bits]
+Name[ca@valencia]=Disseny de cine 16:10 [2484x1200, 96 ppp amb RGB, 8 bits]
Name[cs]=Návrh kino 16:10 [ 2484x1200 , 96dpi RGB , 8bit ]
Name[da]=Design-cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ]
Name[de]=Design-Kino 16:10 [ 2484x1200 , 96dpi RGB , 8bit ]
Name[el]=Design cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ]
Name[en_GB]=Design cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ]
Name[es]=Diseño de cine 16:10 [ 2484x1200 , 96dpi RGB , 8bit ]
Name[et]=Disainkino 16:10 [ 2484x1200 , 96dpi RGB , 8bit ]
Name[eu]=Zinema-diseinua 16:10 [2484 x 1200, 96 dpi GBU, 8 bit]
+Name[fi]=Elokuvasuunnitelma 16 : 10 [2484 × 1200, 96 dpi RGB, 8 bit]
Name[fr]=style cinéma 16:10 [ 2484x1200, 96dpi RGB, 8bit ]
Name[gl]=Deseño de cine 16:10 (2484×1200, 96 dpi RGB, 8 bits)
Name[hu]=Tervező mozi 16:10 [ 2484x1200 , 96dpi RGB , 8bit ]
Name[is]=Hanna kvikmynd 16:10 [ 2484x1200 , 96pát RGB , 8bita ]
Name[it]=Stile cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ]
Name[ja]=映画 16:10 [ 2484x1200、96dpi RGB、8 ビット ]
Name[kk]=Кино пішімі 106:1 [ 2484x1200 , 96 н/д RGB , 8бит ]
Name[nb]=Designkino 16:10 [ 2484x1200 , 96dpi RGB , 8bit ]
Name[nl]=Design cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ]
Name[pl]=Kino projekcyjne 16:10 [ 2484x1200 , 96dpi RGB , 8bit ]
Name[pt]=Desenho de cinema 16:10 [ 2484x1200 , 96ppp RGB , 8-bits ]
Name[pt_BR]=Design de cinema 16:10 [ 2484x1200, 96dpi RGB, 8bits ]
Name[ru]=Дизайн кино 16:10 [ 2484x1200 , 96dpi RGB , 8 бит ]
Name[sk]=Design cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ]
Name[sv]=Design film 16:10 [ 2484x1200, 96 punkter/tum RGB, 8 bitar ]
Name[tr]=Sineme tasarla 16:10 [ 2484x1200 , 96dpi RGB , 8bit ]
Name[uk]=Компонування кіноекрана 16:10 [2484⨯1200, 96 т./д., RGB, 8 бітів]
Name[x-test]=xxDesign cinema 16:10 [ 2484x1200 , 96dpi RGB , 8bit ]xx
Name[zh_CN]=16:10 电影荧幕设计模板 [ 2484x1200 像素, 96dpi RGB , 8 位 ]
Name[zh_TW]=設計電影院 16:10 [ 2484x1200 , 96dpi RGB , 8bit ]
Type=Link
URL[$e]=.source/Designcinema16_10_2484x1200_96dpiRGB_8bit_.kra
X-KDE-Hidden=false
diff --git a/krita/data/templates/design/Designcinema2.39_1_2484x1040_96dpiRGB_8bit_.desktop b/krita/data/templates/design/Designcinema2.39_1_2484x1040_96dpiRGB_8bit_.desktop
index 92ce996fa1..6d76d111a6 100644
--- a/krita/data/templates/design/Designcinema2.39_1_2484x1040_96dpiRGB_8bit_.desktop
+++ b/krita/data/templates/design/Designcinema2.39_1_2484x1040_96dpiRGB_8bit_.desktop
@@ -1,38 +1,39 @@
[Desktop Entry]
Icon=template_ratio_2391
Name=Design cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ]
Name[ar]=تصميم سينمائي ٢٫٣٩:١ [ ٢٤٨٤×١٠٤٠ ، ٩٦ نقطة/بوصة ، ٨ بتّ ]
Name[bs]=Design cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ]
-Name[ca]=Disseny de cine 2,39:1 [2484x1040 / 96ppp RGB / 8bit]
-Name[ca@valencia]=Disseny de cine 2,39:1 [2484x1040 / 96ppp RGB / 8bit]
+Name[ca]=Disseny de cine 2,39:1 [2484x1040, 96 ppp amb RGB, 8 bits]
+Name[ca@valencia]=Disseny de cine 2,39:1 [2484x1040, 96 ppp amb RGB, 8 bits]
Name[cs]=Návrh kino 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ]
Name[da]=Design-cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ]
Name[de]=Design-Kino 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ]
Name[el]=Design cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ]
Name[en_GB]=Design cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ]
Name[es]=Diseño de cine 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ]
Name[et]=Disainkino 2,39:1 [ 2484x1040 , 96dpi RGB , 8bit ]
Name[eu]=Zinema-diseinua 2.39:1 [2484 x 1040, 96 dpi GBU, 8 bit]
+Name[fi]=Elokuvasuunnitelma 2,39 : 1 [2484 × 1040, 96 dpi RGB, 8 bit]
Name[fr]=style cinéma 2.39:1 [ 2484x1040, 96dpi RGB, 8bit ]
Name[gl]=Deseño de cine 2.39:1 (2484×1040, 96 dpi RGB, 8 bits)
Name[hu]=Tervező mozi 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ]
Name[is]=Hanna kvikmynd 2.39:1 [ 2484x1040 , 96pát RGB , 8bita ]
Name[it]=Stile cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ]
Name[ja]=映画 2.39:1 [ 2484x1040、96dpi RGB、8 ビット ]
Name[kk]=Кино пішімі 2.39:1 [ 2484x1040 , 96 н/д RGB , 8бит ]
Name[nb]=Designkino 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ]
Name[nl]=Design cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ]
Name[pl]=Kino projekcyjne 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ]
Name[pt]=Desenho de cinema 2,39:1 [ 2484x1040 , 96ppp RGB , 8-bits ]
Name[pt_BR]=Design de cinema 2.39:1 [ 2484x1040, 96dpi RGB, 8bits ]
Name[ru]=Дизайн кино 2.39:1 [ 2484x1040 , 96dpi RGB , 8 бит ]
Name[sk]=Design cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ]
Name[sv]=Design film 2,39:1 [ 2484x1040, 96 punkter/tum RGB, 8 bitar ]
Name[tr]=Sineme tasarla 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ]
Name[uk]=Компонування кіноекрана 2,39:1 [2484⨯1040, 96 т./д., RGB, 8 бітів]
Name[x-test]=xxDesign cinema 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ]xx
Name[zh_CN]=2.39:1 电影荧幕设计模板 [ 2484x1040 像素, 96dpi RGB , 8 位]
Name[zh_TW]=設計電影院 2.39:1 [ 2484x1040 , 96dpi RGB , 8bit ]
Type=Link
URL[$e]=.source/Designcinema2.39_1_2484x1040_96dpiRGB_8bit_.kra
X-KDE-Hidden=false
diff --git a/krita/data/templates/design/DesignpresentationA3Landscape_4960x3508_300dpiRGB_8bit_.desktop b/krita/data/templates/design/DesignpresentationA3Landscape_4960x3508_300dpiRGB_8bit_.desktop
index c29bbd8694..ed0010c527 100644
--- a/krita/data/templates/design/DesignpresentationA3Landscape_4960x3508_300dpiRGB_8bit_.desktop
+++ b/krita/data/templates/design/DesignpresentationA3Landscape_4960x3508_300dpiRGB_8bit_.desktop
@@ -1,37 +1,38 @@
[Desktop Entry]
Icon=template_DIN_A3_landscape
Name=Design presentation A3 Landscape [ 4960x3508 , 300dpi RGB , 8bit ]
Name[bs]=Design prezentacija A3 položeno [ 4960x3508 , 300dpi RGB , 8bit ]
-Name[ca]=Disseny de presentació A3 apaïsada [4960x3508 / 300ppp RGB / 8bit]
-Name[ca@valencia]=Disseny de presentació A3 apaïsada [4960x3508 / 300ppp RGB / 8bit]
+Name[ca]=Disseny de presentació A3 apaïsada [4960x3508, 300 ppp amb RGB, 8 bits]
+Name[ca@valencia]=Disseny de presentació A3 apaïsada [4960x3508, 300 ppp amb RGB, 8 bits]
Name[cs]=Návrh prezentace A3 vodorovně [ 4960x3508 , 300dpi RGB , 8bit ]
Name[da]=Design-præsentation A3 liggende [ 4960x3508 , 300dpi RGB , 8bit ]
Name[de]=Design-Präsentation A3 Querformat [ 4960x3508 , 300dpi RGB , 8bit ]
Name[el]=Design presentation A3 Landscape [ 4960x3508 , 300dpi RGB , 8bit ]
Name[en_GB]=Design presentation A3 Landscape [ 4960x3508 , 300dpi RGB , 8bit ]
Name[es]=Diseño de presentación A3 apaisado [ 4960x3508 , 300dpi RGB , 8bit ]
Name[et]=Disainesitlus A3 rõhtpaigutusega [ 4960x3508 , 300dpi RGB , 8bit ]
Name[eu]=Aurkezpen-diseinua A3 paisaia [4960 x 3508, 300 dpi GBU, 8 bit]
+Name[fi]=Ruutusuunnitelma vaaka-A3 [4960 × 3508, 300 dpi RGB, 8 bit]
Name[fr]=Style présentation A3 paysage [ 4960x3508, 300dpi RGB, 8bit ]
Name[gl]=Deseño de presentación A3 apaisada (4960×3508, 300 dpi RGB, 8 bits)
Name[hu]=Tervező bemutató A3 fekvő [ 4960x3508 , 300dpi RGB , 8bit ]
Name[is]=Hanna kynningu A3 lárétt : [ 4960x3508 , 300pát RGB , 8bita ]
Name[it]=Stile di presentazione A3 orizzontale [ 4960x3508 , 300dpi RGB , 8bit ]
Name[ja]=プレゼンテーション A3 横向き [ 4960x3508、300dpi RGB、8 ビット ]
Name[kk]=Презентация пішімі A3 жатық [ 4960x3508 , 300 н/д RGB , 8бит ]
Name[nb]=Design presentasjon A3 liggende [ 4960x3508 , 300dpi RGB , 8bit ]
Name[nl]=Design presentatie A3 Landschap [ 4960x3508 , 300dpi RGB , 8bit ]
Name[pl]=Prezentacja projekcyjna A3 poziomo [ 4960x3508 , 300dpi RGB , 8bit ]
Name[pt]=Desenho de apresentação A3 em Paisagem [ 4960x3508 , 300ppp RGB , 8-bits ]
Name[pt_BR]=Design de apresentação A3 paisagem [ 4960x3508, 300dpi RGB, 8bits ]
Name[ru]=Дизайн презентации A3 Ландшафтный [ 4960x3508 , 300dpi RGB , 8bit ]
Name[sk]=Dizajn prezentácia A3 krajinka [ 4960x3508 , 300dpi RGB , 8bit ]
Name[sv]=Design presentation A3 landskap [ 4960x3508, 300 punkter/tum RGB, 8 bitar ]
Name[tr]=A3 Yatay sunum tasarla [ 4960x3508 , 300dpi RGB , 8bit ]
Name[uk]=Компонування презентації, A3, альбомна [4960⨯3508, 300 т./д., RGB, 8 бітів]
Name[x-test]=xxDesign presentation A3 Landscape [ 4960x3508 , 300dpi RGB , 8bit ]xx
Name[zh_CN]=A3 横版设计模板 [ 4960x3508 像素, 300dpi RGB , 8 位]
Name[zh_TW]=設計圖像 A3 風景畫 [ 4960x3508 , 300dpi RGB , 8bit ]
Type=Link
URL[$e]=.source/DesignpresentationA3Landscape_4960x3508_300dpiRGB_8bit_.kra
X-KDE-Hidden=false
diff --git a/krita/data/templates/design/DesignpresentationA4portrait_2480x3508,300dpiRGB_8bit_.desktop b/krita/data/templates/design/DesignpresentationA4portrait_2480x3508,300dpiRGB_8bit_.desktop
index e7a5e6d809..d3a440ace5 100644
--- a/krita/data/templates/design/DesignpresentationA4portrait_2480x3508,300dpiRGB_8bit_.desktop
+++ b/krita/data/templates/design/DesignpresentationA4portrait_2480x3508,300dpiRGB_8bit_.desktop
@@ -1,37 +1,38 @@
[Desktop Entry]
Icon=template_DIN_A4_portrait
Name=Design presentation A4 portrait [ 2480x3508 , 300dpi RGB , 8bit ]
Name[bs]=Design prezentacija A4 uspravno [ 2480x3508 , 300dpi RGB , 8bit ]
-Name[ca]=Disseny de presentació A4 vertical [2480x3508 / 300ppp RGB / 8bit]
-Name[ca@valencia]=Disseny de presentació A4 vertical [2480x3508 / 300ppp RGB / 8bit]
+Name[ca]=Disseny de presentació A4 vertical [2480x3508, 300 ppp amb RGB, 8 bits]
+Name[ca@valencia]=Disseny de presentació A4 vertical [2480x3508, 300 ppp amb RGB, 8 bits]
Name[cs]=Návrh prezentace A4 svisle [ x3508 , 300dpi RGB , 8bit ]
Name[da]=Design-præsentation A4 stående [ x3508 , 300dpi RGB , 8bit ]
Name[de]=Design-Präsentation A4 Hochformat [ 2480x3508 , 300dpi RGB , 8bit ]
Name[el]=Design presentation A4 portrait [ 2480x3508 , 300dpi RGB , 8bit ]
Name[en_GB]=Design presentation A4 portrait [ 2480x3508 , 300dpi RGB , 8bit ]
Name[es]=Diseño de presentación A4 retrato [ 2480x3508 , 300dpi RGB , 8bit ]
Name[et]=Disainesitlus A4 püstpaigutusega [ 2480x3508 , 300dpi RGB , 8bit ]
Name[eu]=Aurkezpen-diseinua A4 erretratua [2480 x 3508, 300 dpi GBU, 8 bit]
+Name[fi]=Ruutusuunnitelma pysty-A4 [2480×3508, 300 dpi RGB, 8 bit]
Name[fr]=Style présentation A4 portrait [ 2480x3508, 300dpi RGB, 8bit ]
Name[gl]=Deseño de presentación A4 vertical (2480×3508, 300 dpi RGB, 8 bits)
Name[hu]=Tervező bemutató A4 álló [ 2480x3508 , 300dpi RGB , 8bit ]
Name[is]=Hanna kynningu A4 lóðrétt : [ 2480x3508 , 300pát RGB , 8bita ]
Name[it]=Stile di presentazione A4 verticale [ 2480x3508 , 300dpi RGB , 8bit ]
Name[ja]=プレゼンテーション A4 縦向き [ 2480x3508、300dpi RGB、8 ビット ]
Name[kk]=Презентация пішімі A4 жатық [ 2460x3508 , 300 н/д RGB , 8бит ]
Name[nb]=Design presentasjon A4 stående [ x3508 , 300dpi RGB , 8bit ]
Name[nl]=Design presentatie A4 portret [ 2480x3508 , 300dpi RGB , 8bit ]
Name[pl]=Prezentacja projekcyjna A4 pionowo [ 2480x3508 , 300dpi RGB , 8bit ]
Name[pt]=Desenho de apresentação A4 em Paisagem [ 2480x3508 , 300ppp RGB , 8-bits ]
Name[pt_BR]=Design de apresentação A4 retrato [ 2480x3508, 300dpi RGB, 8bits ]
Name[ru]=Дизайн презентации A4 Портретный [ 2480x3508 , 300dpi RGB , 8bit ]
Name[sk]=Dizajn prezentácia A4 portrét [ 2480x3508 , 300dpi RGB , 8bit ]
Name[sv]=Design presentation A4 porträtt [ 2480x3508, 300 punkter/tum RGB, 8 bitar ]
Name[tr]=A4 dikey sunum tasarla [ 2480x3508 , 300dpi RGB , 8bit ]
Name[uk]=Компонування презентації, A4, книжкова [2480x3508, 300 т./д., RGB, 8 бітів]
Name[x-test]=xxDesign presentation A4 portrait [ 2480x3508 , 300dpi RGB , 8bit ]xx
Name[zh_CN]=A4 竖版设计模板 [ 2480x3508 像素, 300dpi RGB , 8 位 ]
Name[zh_TW]=設計圖像 A4 肖像 [ 2480x3508 , 300dpi RGB , 8bit ]
Type=Link
URL[$e]=.source/DesignpresentationA4portrait_2480x3508_300dpiRGB_8bit_.kra
X-KDE-Hidden=false
diff --git a/krita/data/templates/design/DesignpresentationA4portrait_2480x3508_300dpiRGB_8bit_.desktop b/krita/data/templates/design/DesignpresentationA4portrait_2480x3508_300dpiRGB_8bit_.desktop
index e7a5e6d809..d3a440ace5 100644
--- a/krita/data/templates/design/DesignpresentationA4portrait_2480x3508_300dpiRGB_8bit_.desktop
+++ b/krita/data/templates/design/DesignpresentationA4portrait_2480x3508_300dpiRGB_8bit_.desktop
@@ -1,37 +1,38 @@
[Desktop Entry]
Icon=template_DIN_A4_portrait
Name=Design presentation A4 portrait [ 2480x3508 , 300dpi RGB , 8bit ]
Name[bs]=Design prezentacija A4 uspravno [ 2480x3508 , 300dpi RGB , 8bit ]
-Name[ca]=Disseny de presentació A4 vertical [2480x3508 / 300ppp RGB / 8bit]
-Name[ca@valencia]=Disseny de presentació A4 vertical [2480x3508 / 300ppp RGB / 8bit]
+Name[ca]=Disseny de presentació A4 vertical [2480x3508, 300 ppp amb RGB, 8 bits]
+Name[ca@valencia]=Disseny de presentació A4 vertical [2480x3508, 300 ppp amb RGB, 8 bits]
Name[cs]=Návrh prezentace A4 svisle [ x3508 , 300dpi RGB , 8bit ]
Name[da]=Design-præsentation A4 stående [ x3508 , 300dpi RGB , 8bit ]
Name[de]=Design-Präsentation A4 Hochformat [ 2480x3508 , 300dpi RGB , 8bit ]
Name[el]=Design presentation A4 portrait [ 2480x3508 , 300dpi RGB , 8bit ]
Name[en_GB]=Design presentation A4 portrait [ 2480x3508 , 300dpi RGB , 8bit ]
Name[es]=Diseño de presentación A4 retrato [ 2480x3508 , 300dpi RGB , 8bit ]
Name[et]=Disainesitlus A4 püstpaigutusega [ 2480x3508 , 300dpi RGB , 8bit ]
Name[eu]=Aurkezpen-diseinua A4 erretratua [2480 x 3508, 300 dpi GBU, 8 bit]
+Name[fi]=Ruutusuunnitelma pysty-A4 [2480×3508, 300 dpi RGB, 8 bit]
Name[fr]=Style présentation A4 portrait [ 2480x3508, 300dpi RGB, 8bit ]
Name[gl]=Deseño de presentación A4 vertical (2480×3508, 300 dpi RGB, 8 bits)
Name[hu]=Tervező bemutató A4 álló [ 2480x3508 , 300dpi RGB , 8bit ]
Name[is]=Hanna kynningu A4 lóðrétt : [ 2480x3508 , 300pát RGB , 8bita ]
Name[it]=Stile di presentazione A4 verticale [ 2480x3508 , 300dpi RGB , 8bit ]
Name[ja]=プレゼンテーション A4 縦向き [ 2480x3508、300dpi RGB、8 ビット ]
Name[kk]=Презентация пішімі A4 жатық [ 2460x3508 , 300 н/д RGB , 8бит ]
Name[nb]=Design presentasjon A4 stående [ x3508 , 300dpi RGB , 8bit ]
Name[nl]=Design presentatie A4 portret [ 2480x3508 , 300dpi RGB , 8bit ]
Name[pl]=Prezentacja projekcyjna A4 pionowo [ 2480x3508 , 300dpi RGB , 8bit ]
Name[pt]=Desenho de apresentação A4 em Paisagem [ 2480x3508 , 300ppp RGB , 8-bits ]
Name[pt_BR]=Design de apresentação A4 retrato [ 2480x3508, 300dpi RGB, 8bits ]
Name[ru]=Дизайн презентации A4 Портретный [ 2480x3508 , 300dpi RGB , 8bit ]
Name[sk]=Dizajn prezentácia A4 portrét [ 2480x3508 , 300dpi RGB , 8bit ]
Name[sv]=Design presentation A4 porträtt [ 2480x3508, 300 punkter/tum RGB, 8 bitar ]
Name[tr]=A4 dikey sunum tasarla [ 2480x3508 , 300dpi RGB , 8bit ]
Name[uk]=Компонування презентації, A4, книжкова [2480x3508, 300 т./д., RGB, 8 бітів]
Name[x-test]=xxDesign presentation A4 portrait [ 2480x3508 , 300dpi RGB , 8bit ]xx
Name[zh_CN]=A4 竖版设计模板 [ 2480x3508 像素, 300dpi RGB , 8 位 ]
Name[zh_TW]=設計圖像 A4 肖像 [ 2480x3508 , 300dpi RGB , 8bit ]
Type=Link
URL[$e]=.source/DesignpresentationA4portrait_2480x3508_300dpiRGB_8bit_.kra
X-KDE-Hidden=false
diff --git a/krita/data/templates/design/Designscreen4_3_2250x1680_96dpiRGB_8bit_.desktop b/krita/data/templates/design/Designscreen4_3_2250x1680_96dpiRGB_8bit_.desktop
index bc79ac93e2..45d7f87e4c 100644
--- a/krita/data/templates/design/Designscreen4_3_2250x1680_96dpiRGB_8bit_.desktop
+++ b/krita/data/templates/design/Designscreen4_3_2250x1680_96dpiRGB_8bit_.desktop
@@ -1,37 +1,38 @@
[Desktop Entry]
Icon=template_ratio_43
Name=Design screen 4:3 [ 2250x1680 , 96dpi RGB , 8bit ]
Name[bs]=Design ekran 4:3 [ 2250x1680 , 96dpi RGB , 8bit ]
-Name[ca]=Disseny de pantalla 4:3 [2250x1680 / 96ppp RGB / 8bit]
-Name[ca@valencia]=Disseny de pantalla 4:3 [2250x1680 / 96ppp RGB / 8bit]
+Name[ca]=Disseny de pantalla 4:3 [2250x1680, 96 ppp amb RGB, 8 bits]
+Name[ca@valencia]=Disseny de pantalla 4:3 [2250x1680, 96 ppp amb RGB, 8 bits]
Name[cs]=Návrh obrazovka 4:3 [ 2250x1680 , 96dpi RGB , 8bit ]
Name[da]=Design-skærm 4:3 [ 2250x1680 , 96dpi RGB , 8bit ]
Name[de]=Design-Bildschirm 4:3 [ 2250x1680 , 96dpi RGB , 8bit ]
Name[el]=Design screen 4:3 [ 2250x1680 , 96dpi RGB , 8bit ]
Name[en_GB]=Design screen 4:3 [ 2250x1680 , 96dpi RGB , 8bit ]
Name[es]=Diseño de pantalla 4:3 [ 2250x1680 , 96dpi RGB , 8bit ]
Name[et]=Disainekraan 4:3 [ 2250x1680 , 96dpi RGB , 8bit ]
Name[eu]=Diseinu-pantaila 4:3 [2250 x 1680, 96 dpi GBU, 8 bit]
+Name[fi]=Ruutusuunnitelma 4 : 3 [2250 × 1680, 96 dpi RGB, 8 bit]
Name[fr]=Style écran 4:3 [ 2250x1680, 96dpi RGB, 8bit ]
Name[gl]=Deseño de pantalla 4:3 (2250×1680, 96 dpi RGB, 8 bits)
Name[hu]=Tervező kijelző 4:3 [ 2250x1680 , 96dpi RGB , 8bit ]
Name[is]=Hanna skjá 4:3 [ 2250x1680 , 96pát RGB , 8bita ]
Name[it]=Stile di disegno 4:3 [ 2250x1680 , 96dpi RGB , 8bit ]
Name[ja]=スクリーン 4:3 [ 2250x1680、96dpi RGB、8ビット ]
Name[kk]=Экран пішімі 4:3 [ 2250x1680 , 96 н/д RGB , 8бит ]
Name[nb]=Design skjerm 4:3 [ 2250x1680 , 96dpi RGB , 8bit ]
Name[nl]=Design screen 4:3 [ 2250x1680 , 96dpi RGB , 8bit ]
Name[pl]=Ekran projekcyjny 4:3 [ 2250x1680 , 96dpi RGB , 8bit ]
Name[pt]=Desenho de ecrã 4:3 [ 2250x1680 , 96ppp RGB , 8-bits ]
Name[pt_BR]=Design de tela 4:3 [ 2250x1680, 96dpi RGB, 8bits ]
Name[ru]=Дизайн экрана 4:3 [ 2250x1680 , 96dpi RGB , 8bit ]
Name[sk]=Dizajn obrazovka 4:3 [ 2250x1680 , 96dpi RGB , 8bit ]
Name[sv]=Design skärm 4:3 [ 2250x1680, 96 punkter/tum RGB, 8 bitar ]
Name[tr]=Ekran tasarla 4:3 [ 2250x1680 , 96dpi RGB , 8bit ]
Name[uk]=Компонування екрана 4:3 [2250⨯1680, 96 т./д., RGB, 8 бітів]
Name[x-test]=xxDesign screen 4:3 [ 2250x1680 , 96dpi RGB , 8bit ]xx
Name[zh_CN]=4:3 屏幕设计模板 [ 2250x1680 像素, 96dpi RGB , 8 位]
Name[zh_TW]=設計螢幕 4:3 [ 2250x1680 , 96dpi RGB , 8bit ]
Type=Link
URL[$e]=.source/Designscreen4_3_2250x1680_96dpiRGB_8bit_.kra
X-KDE-Hidden=false
diff --git a/krita/data/templates/design/web_design.desktop b/krita/data/templates/design/web_design.desktop
index 9567edd22a..b479a76054 100644
--- a/krita/data/templates/design/web_design.desktop
+++ b/krita/data/templates/design/web_design.desktop
@@ -1,36 +1,37 @@
[Desktop Entry]
Icon=template_web_design
Name=Web Design [ 2160x1440 , 72ppi RGB , 8bit ]
Name[ar]=تصميم وبّ [ ٢١٦٠×١٤٤٠ ، ٧٢ بكسل/بوصة ، ٨ بتّ ]
Name[bs]=Web dizajn [ 2160x1440 , 72ppi RGB , 8bit ]
-Name[ca]=Disseny web [2160x1440 / 72ppi RGB / 8bit]
-Name[ca@valencia]=Disseny web [2160x1440 / 72ppi RGB / 8bit]
+Name[ca]=Disseny web [2160x1440, 72 ppp amb RGB, 8 bits]
+Name[ca@valencia]=Disseny web [2160x1440, 72 ppp amb RGB, 8 bits]
Name[cs]=Návrh webu [ 2160x1440 , 72ppi RGB , 8bit ]
Name[da]=Webdesign [ 2160x1440 , 72ppi RGB , 8bit ]
Name[de]=Web-Design [ 2160x1440 , 72ppi RGB , 8bit ]
Name[el]=Σχεδίαση διαδικτυακών τόπων [ 2160x1440 , 72ppi RGB , 8bit ]
Name[en_GB]=Web Design [ 2160x1440 , 72ppi RGB , 8bit ]
Name[es]=Diseño de web 4:3 [ 2160x1440 , 72ppi RGB , 8bit ]
Name[et]=Veebidisain [ 2160x1440, 72ppi RGB, 8-bitine ]
Name[eu]=Web-diseinua [ 2160x1440 , 72ppi GBU , 8bit ]
+Name[fi]=Verkkosuunnitelma [2160 × 1440, 72 ppi RGB, 8 bit]
Name[fr]=Style écran [ 2160x1440, 72ppi RGB, 8bit ]
Name[gl]=Deseño web (2160×1440, 72 ppi RGB, 8 bits)
Name[is]=Vefhönnun [ 2160x1440 , 72pát RGB , 8bita ]
Name[it]=Progettazione web [ 2160x1440 , 72ppi RGB , 8bit ]
Name[ja]=ウェブデザイン [ 2160x1440、72ppi RGB、8 ビット ]
Name[nb]=Web Design [ 2160x1440 , 72ppi RGB , 8bit ]
Name[nl]=Webontwerp [ 2160x1440 , 72ppi RGB , 8bit ]
Name[pl]=Projekt sieciowy [ 2160x1440 , 72ppi RGB , 8bit ]
Name[pt]=Desenho na Web [ 2160x1440 , 72ppp RGB , 8-bits ]
Name[pt_BR]=Web Design [ 2160x1440 , 72ppi RGB , 8bits ]
Name[ru]=Веб-дизайн [ 2160x1440 , 72ppi RGB , 8 бит ]
Name[sk]=Webový dizajn [ 2160x1440 , 72ppi RGB , 8bit ]
Name[sv]=Webbdesign [ 2160x1440, 72 punkter/tum RGB, 8 bitar ]
Name[tr]=Web Tasarımı [ 2160x1440 , 72ppi RGB , 8bit ]
Name[uk]=Вебдизайн [2160⨯1440, 72 т./д., RGB, 8 бітів]
Name[x-test]=xxWeb Design [ 2160x1440 , 72ppi RGB , 8bit ]xx
Name[zh_CN]=网页设计模板 [ 2160x1440 像素, 72ppi RGB , 8 位 ]
Name[zh_TW]=網頁設計 [ 2160x1440 , 72ppi RGB , 8bit ]
Type=Link
URL[$e]=.source/web_design.kra
X-KDE-Hidden=false
diff --git a/krita/data/templates/texture/.directory b/krita/data/templates/texture/.directory
index d91af36cb8..251d14351a 100644
--- a/krita/data/templates/texture/.directory
+++ b/krita/data/templates/texture/.directory
@@ -1,39 +1,39 @@
[Desktop Entry]
Name=Texture Templates
Name[bs]=Predlošci teksture
Name[ca]=Plantilles de textura
Name[ca@valencia]=Plantilles de textura
Name[cs]=Šablony textury
Name[da]=Teksturskabeloner
Name[de]=Textur-Vorlagen
Name[el]=Πρότυπα υφής
Name[en_GB]=Texture Templates
Name[es]=Plantillas de textura
Name[et]=Tekstuurimallid
Name[eu]=Ehundura-txantiloiak
Name[fi]=Tekstuuripohjat
Name[fr]=Modèles de textures
Name[gl]=Modelos de texturas
Name[hu]=Textúrasablonok
Name[ia]=Patronos deTexture
Name[is]=Sniðmát fyrir áferð
Name[it]=Modelli di trama
Name[ja]=テクスチャテンプレート
Name[kk]=Текстура үлгілері
Name[ko]=텍스처 서식
Name[lt]=Tekstūros šablonas
Name[nb]=Tekstur-malrt
Name[nl]=Textuur-sjablonen
-Name[pl]=Szablony teksturowe
+Name[pl]=Szablony tekstur
Name[pt]=Modelos de Texturas
Name[pt_BR]=Modelos de textura
Name[ru]=Шаблоны текстур
Name[sk]=Šablóny textúr
Name[sl]=Predloge tekstur
Name[sv]=Strukturmallar
Name[tr]=Doku Şablonları
Name[uk]=Шаблони текстур
Name[x-test]=xxTexture Templatesxx
Name[zh_CN]=纹理模板
Name[zh_TW]=紋理範本
X-KDE-DefaultTab=true
diff --git a/krita/data/templates/texture/Texture1024x10248bitsrgb.desktop b/krita/data/templates/texture/Texture1024x10248bitsrgb.desktop
index e5920ea503..388131bc8c 100644
--- a/krita/data/templates/texture/Texture1024x10248bitsrgb.desktop
+++ b/krita/data/templates/texture/Texture1024x10248bitsrgb.desktop
@@ -1,35 +1,36 @@
[Desktop Entry]
Icon=template_texture
Name=Texture 1024x1024 8bit srgb
Name[bs]=Tekstura 1024x1024 8bit srgb
-Name[ca]=Textura 1024x1024 8bit SRGB
-Name[ca@valencia]=Textura 1024x1024 8bit SRGB
+Name[ca]=Textura 1024x1024 de 8 bits amb SRGB
+Name[ca@valencia]=Textura 1024x1024 de 8 bits amb SRGB
Name[cs]=Textura 1024x1024 8bit srgb
Name[da]=Tekstur 1024x1024 8bit srgb
Name[de]=Textur 1024x1024 8bit srgb
Name[el]=Υφή 1024x1024 8bit srgb
Name[en_GB]=Texture 1024x1024 8bit srgb
Name[es]=Textura 1024x1024 8bits srgb
Name[et]=Tekstuur 1024x1024 8bit srgb
Name[eu]=Ehundura 1024x1024 8bit sRGB
+Name[fi]=Pintakuvio 1024 × 1024 8 bit SRGB
Name[fr]=Texture 1024x1024 8bit srgb
Name[gl]=Textura de 1024×1024 e 8 bits SRGB
Name[is]=Efnisáferð 1024x1024 8bita srgb
Name[it]=Trama 1024x1024 8bit srgb
Name[ja]=テクスチャ 1024x1024 8 ビット sRGB
Name[nb]=Tekstur 1024x1024 8bit srgb
Name[nl]=Textuur 1024x1024 8bit srgb
Name[pl]=Tekstura 1024x1024 8bit srgb
Name[pt]=Textura 1024x1024 8-bits sRGB
Name[pt_BR]=Textura 1024x1024 8-bits sRGB
Name[ru]=Текстура 1024x1024 8 бит srgb
Name[sk]=Textúra 1024x1024 8bit srgb
Name[sv]=Struktur 1024 x 1024 8-bitar SRGB
Name[tr]=Doku 1024x1024 8bit srgb
Name[uk]=Текстура 1024⨯1024, 8-бітова, srgb
Name[x-test]=xxTexture 1024x1024 8bit srgbxx
Name[zh_CN]=1024x1024 纹理模板 8位 sRGB 色彩空间
Name[zh_TW]=紋理 1024x1024 8位元 srgb
Type=Link
URL[$e]=.source/Texture1024x10248bitsrgb.kra
X-KDE-Hidden=false
diff --git a/krita/data/templates/texture/Texture1k32bitscalar.desktop b/krita/data/templates/texture/Texture1k32bitscalar.desktop
index cf5e01852e..c2de545c0f 100755
--- a/krita/data/templates/texture/Texture1k32bitscalar.desktop
+++ b/krita/data/templates/texture/Texture1k32bitscalar.desktop
@@ -1,37 +1,38 @@
[Desktop Entry]
Icon=template_texture
Name=Texture 1k 32bit scalar
Name[bs]=Tekstura 1k 32bit scalar
-Name[ca]=Textura 1k 32bit escalar
-Name[ca@valencia]=Textura 1k 32bit escalar
+Name[ca]=Textura 1k de 32 bits escalar
+Name[ca@valencia]=Textura 1k de 32 bits escalar
Name[cs]=Textura 1k 32bit skalární
Name[da]=Tekstur 1k 32bit scalar
Name[de]=Textur 1k 32bit scalar
Name[el]=Υφή 1k 32bit βαθμωτό
Name[en_GB]=Texture 1k 32bit scalar
Name[es]=Textura 1k 32 bit escalar
Name[et]=Tekstuur 1k 32bit skalaar
Name[eu]=Ehundura 1k 32bit eskalarra
+Name[fi]=Pintakuvio 1k 32 bit skalaarinen
Name[fr]=Texture 1k 32bit scalaire
Name[gl]=Textura de 1k e 32 bits escalar
Name[hu]=Textúra 1k 32bit skalár
Name[is]=Efnisáferð 1k 32bita scalar
Name[it]=Trama 1k 32bit scalare
Name[ja]=テクスチャ 1k 32 ビットスカラー
Name[kk]=Текстура 1k 32 бит скаляр
Name[nb]=Tekstur 1k 32bit skalar
Name[nl]=Textuur 1k 32bit scalar
Name[pl]=Tekstura 1k 32bit skalar
Name[pt]=Textura 1k 32-bits escalar
Name[pt_BR]=Textura 1k 32bits escalar
Name[ru]=Текстура 1k 32 бит scalar
Name[sk]=Textúra 1k 32bit skalár
Name[sv]=Struktur 1k 32-bitar skalär
Name[tr]=Doku 1k 32bit sayısal
Name[uk]=Текстура 1k, 32-бітова, скалярна
Name[x-test]=xxTexture 1k 32bit scalarxx
Name[zh_CN]=1K 纹理模板 32 位 Scalar 色彩空间
Name[zh_TW]=紋理 1k 32位元 scalar
Type=Link
URL[$e]=.source/Texture1k32bitscalar.kra
X-KDE-Hidden=false
diff --git a/krita/data/templates/texture/Texture1k8bitsrgb.desktop b/krita/data/templates/texture/Texture1k8bitsrgb.desktop
index 0ac82f9702..2d2dd6598c 100755
--- a/krita/data/templates/texture/Texture1k8bitsrgb.desktop
+++ b/krita/data/templates/texture/Texture1k8bitsrgb.desktop
@@ -1,37 +1,38 @@
[Desktop Entry]
Icon=template_texture
Name=Texture 1k 8bit srgb
Name[bs]=Tekstura 1k 8bit srgb
-Name[ca]=Textura 1k 8bit SRGB
-Name[ca@valencia]=Textura 1k 8bit SRGB
+Name[ca]=Textura 1k de 8 bits amb SRGB
+Name[ca@valencia]=Textura 1k de 8 bits amb SRGB
Name[cs]=Textura 1k 8bit srgb
Name[da]=Tekstur 1k 8bit srgb
Name[de]=Textur 1k 8bit srgb
Name[el]=Υφή 1k 8bit srgb
Name[en_GB]=Texture 1k 8bit srgb
Name[es]=Textura 1k 8bit srgb
Name[et]=Tekstuur 1k 8bit srgb
Name[eu]=Ehundura 1k 8bit sGBU
+Name[fi]=Pintakuvio 1k 8 bit SRGB
Name[fr]=Texture 1k 8bit srgb
Name[gl]=Textura de 1k e 8 bits SRGB
Name[hu]=Textúra 1k 8bit srgb
Name[is]=Efnisáferð 1k 8bita srgb
Name[it]=Trama 1k 8bit srgb
Name[ja]=テクスチャ 1k 8 ビット sRGB
Name[kk]=Текстура 1k 8 бит srgb
Name[nb]=Tekstur 1k 8bit srgb
Name[nl]=Textuur 1k 8bit srgb
Name[pl]=Tekstura 1k 8bit srgb
Name[pt]=Textura 1k 8-bits sRGB
Name[pt_BR]=Textura 1k 8bits sRGB
Name[ru]=Текстура 1k 8 бит srgb
Name[sk]=Textúra 1k 8bit srgb
Name[sv]=Struktur 1k 8-bitar SRGB
Name[tr]=Doku 1k 8bit srgb
Name[uk]=Текстура 1k, 8-бітова, srgb
Name[x-test]=xxTexture 1k 8bit srgbxx
Name[zh_CN]=1K 纹理模板 8 位 sRGB 色彩空间
Name[zh_TW]=紋理 1k 8位元 srgb
Type=Link
URL[$e]=.source/Texture1k8bitsrgb.kra
X-KDE-Hidden=false
diff --git a/krita/data/templates/texture/Texture2048x20488bitsrgb.desktop b/krita/data/templates/texture/Texture2048x20488bitsrgb.desktop
index bb0812976a..f92ea653c9 100644
--- a/krita/data/templates/texture/Texture2048x20488bitsrgb.desktop
+++ b/krita/data/templates/texture/Texture2048x20488bitsrgb.desktop
@@ -1,35 +1,36 @@
[Desktop Entry]
Icon=template_texture
Name=Texture 2048x2048 8bit srgb
Name[bs]=Tekstura 2048x2048 8bit srgb
-Name[ca]=Textura 2048x2048 8bit SRGB
-Name[ca@valencia]=Textura 2048x2048 8bit SRGB
+Name[ca]=Textura 2048x2048 de 8 bits amb SRGB
+Name[ca@valencia]=Textura 2048x2048 de 8 bits amb SRGB
Name[cs]=Textura 2048x2048 8bit srgb
Name[da]=Tekstur 2048x2048 8bit srgb
Name[de]=Textur 2048x2048 8bit srgb
Name[el]=Υφή 2048x2048 8bit srgb
Name[en_GB]=Texture 2048x2048 8bit srgb
Name[es]=Textura 2048x2048 8bits srgb
Name[et]=Tekstuur 2048x2048 8bit srgb
Name[eu]=Ehundura 2048x2048 8bit sGBU
+Name[fi]=Pintakuvio 2048 × 2048 8 bit SRGB
Name[fr]=Texture 2048x2048 8bit srgb
Name[gl]=Textura de 2048×2048 e 8 bits SRGB
Name[is]=Efnisáferð 2048x2048 8bita srgb
Name[it]=Trama 2048x2048 8bit srgb
Name[ja]=テクスチャ 2048x2048 8 ビット sRGB
Name[nb]=Tekstur 2048x2048 8bit srgb
Name[nl]=Textuur 2048x2048 8bit srgb
Name[pl]=Tekstura 2048x2048 8bit srgb
Name[pt]=Textura 2048x2048 8-bits sRGB
Name[pt_BR]=Textura 2048x2048 8bits sRGB
Name[ru]=Текстура 2048x2048 8 бит srgb
Name[sk]=Textúra 2048x2048 8bit srgb
Name[sv]=Struktur 2048 x 2048 8-bitar SRGB
Name[tr]=Doku 2048x2048 8bit srgb
Name[uk]=Текстура 2048⨯2048, 8-бітова, srgb
Name[x-test]=xxTexture 2048x2048 8bit srgbxx
Name[zh_CN]=2048x2048 纹理模板 8 位 sRGB 色彩空间
Name[zh_TW]=紋理 2048x2048 8位元 srgb
Type=Link
URL[$e]=.source/Texture2048x20488bitsrgb.kra
X-KDE-Hidden=false
diff --git a/krita/data/templates/texture/Texture256x2568bitsrgb.desktop b/krita/data/templates/texture/Texture256x2568bitsrgb.desktop
index 9ea60f8d4e..24bc6f44ff 100644
--- a/krita/data/templates/texture/Texture256x2568bitsrgb.desktop
+++ b/krita/data/templates/texture/Texture256x2568bitsrgb.desktop
@@ -1,35 +1,36 @@
[Desktop Entry]
Icon=template_texture
Name=Texture 256x256 8bit srgb
Name[bs]=Tekstura 256x256 8bit srgb
-Name[ca]=Textura 256x256 8bit SRGB
-Name[ca@valencia]=Textura 256x256 8bit SRGB
+Name[ca]=Textura 256x256 de 8 bits amb SRGB
+Name[ca@valencia]=Textura 256x256 de 8 bits amb SRGB
Name[cs]=Textura 256x256 8bit srgb
Name[da]=Tekstur 256x256 8bit srgb
Name[de]=Textur 256x256 8bit srgb
Name[el]=Υφή 256x256 8bit srgb
Name[en_GB]=Texture 256x256 8bit srgb
Name[es]=Textura 256x256 8bits srgb
Name[et]=Tekstuur 256x256 8bit srgb
Name[eu]=Ehundura 256x256 8bit sGBU
+Name[fi]=Pintakuvio 256 × 256 8 bit SRGB
Name[fr]=Texture 256x256 8bit srgb
Name[gl]=Textura de 256×256 e 8 bits SRGB
Name[is]=Efnisáferð 256x256 8bita srgb
Name[it]=Trama 256x256 8bit srgb
Name[ja]=テクスチャ 256x256 8 ビット sRGB
Name[nb]=Tekstur 256x256 8bit srgb
Name[nl]=Textuur 256x256 8bit srgb
Name[pl]=Tekstura 256x256 8bit srgb
Name[pt]=Textura 256x256 8-bits sRGB
Name[pt_BR]=Textura 256x256 8bits sRGB
Name[ru]=Текстура 256x256 8 бит srgb
Name[sk]=Textúra 256x256 8bit srgb
Name[sv]=Struktur 256 x 256 8-bitar SRGB
Name[tr]=Doku 256x256 8bit srgb
Name[uk]=Текстура 256⨯256, 8-бітова, srgb
Name[x-test]=xxTexture 256x256 8bit srgbxx
Name[zh_CN]=256x256 纹理模板 8 位 sRGB 色彩空间
Name[zh_TW]=紋理 256x256 8位元 srgb
Type=Link
URL[$e]=.source/Texture256x2568bitsrgb.kra
X-KDE-Hidden=false
diff --git a/krita/data/templates/texture/Texture2k32bitscalar.desktop b/krita/data/templates/texture/Texture2k32bitscalar.desktop
index 625b5dc87e..bbf713eb77 100755
--- a/krita/data/templates/texture/Texture2k32bitscalar.desktop
+++ b/krita/data/templates/texture/Texture2k32bitscalar.desktop
@@ -1,37 +1,38 @@
[Desktop Entry]
Icon=template_texture
Name=Texture 2k 32bit scalar
Name[bs]=Tekstura 2k 32bit scalar
-Name[ca]=Textura 2k 32bit escalar
-Name[ca@valencia]=Textura 2k 32bit escalar
+Name[ca]=Textura 2k de 32 bits escalar
+Name[ca@valencia]=Textura 2k de 32 bits escalar
Name[cs]=Textura 2k 32bit skalární
Name[da]=Tekstur 2k 32bit scalar
Name[de]=Textur 2k 32bit scalar
Name[el]=Υφή 2k 32bit βαθμωτό
Name[en_GB]=Texture 2k 32bit scalar
Name[es]=Textura 2k 32bit escalar
Name[et]=Tekstuur 2k 32bit skalaar
Name[eu]=Ehundura 2k 32bit eskalarra
+Name[fi]=Pintakuvio 2k 32 bit skalaarinen
Name[fr]=Texture 2k 32bit scalaire
Name[gl]=Textura de 2k e 32 bits escalar
Name[hu]=Textúra 2k 32bit skalár
Name[is]=Efnisáferð 2k 32bita scalar
Name[it]=Trama 2k 32bit scalare
Name[ja]=テクスチャ 2k 32 ビットスカラー
Name[kk]=Текстура 2k 32 бит скаляр
Name[nb]=Tekstur 2k 32bit skalar
Name[nl]=Textuur 2k 32bit scalar
Name[pl]=Tekstura 2k 32bit skalar
Name[pt]=Textura 2k 32-bits escalar
Name[pt_BR]=Textura 2k 32bits escalar
Name[ru]=Текстура 2k 32 бит scalar
Name[sk]=Textúra 2k 32bit skalár
Name[sv]=Struktur 2k 32-bitar skalär
Name[tr]=Doku 2k 32bit sayısal
Name[uk]=Текстура 2k, 32-бітова, скалярна
Name[x-test]=xxTexture 2k 32bit scalarxx
Name[zh_CN]=2K 纹理模板 32 位 Scalar 色彩空间
Name[zh_TW]=紋理 2k 32位元 scalar
Type=Link
URL[$e]=.source/Texture2k32bitscalar.kra
X-KDE-Hidden=false
diff --git a/krita/data/templates/texture/Texture2k8bitsrgb.desktop b/krita/data/templates/texture/Texture2k8bitsrgb.desktop
index 6a86b42b36..ead9ba4541 100755
--- a/krita/data/templates/texture/Texture2k8bitsrgb.desktop
+++ b/krita/data/templates/texture/Texture2k8bitsrgb.desktop
@@ -1,37 +1,38 @@
[Desktop Entry]
Icon=template_texture
Name=Texture 2k 8bit srgb
Name[bs]=Tekstura 2k 8bit srgb
-Name[ca]=Textura 2k 8bit SRGB
-Name[ca@valencia]=Textura 2k 8bit SRGB
+Name[ca]=Textura 2k de 8 bits amb SRGB
+Name[ca@valencia]=Textura 2k de 8 bits amb SRGB
Name[cs]=Textura 2k 8bit srgb
Name[da]=Tekstur 2k 8bit srgb
Name[de]=Textur 2k 8bit srgb
Name[el]=Υφή 2k 8bit srgb
Name[en_GB]=Texture 2k 8bit srgb
Name[es]=Textura 2k 8bit srgb
Name[et]=Tekstuur 2k 8bit srgb
Name[eu]=Ehundura 2k 8bit sGBU
+Name[fi]=Pintakuvio 2k 8 bit SRGB
Name[fr]=Texture 2k 8bit srgb
Name[gl]=Textura de 2k e 8 bits SRGB
Name[hu]=Textúra 2k 8bit srgb
Name[is]=Efnisáferð 2k 8bita srgb
Name[it]=Trama 2k 8bit srgb
Name[ja]=テクスチャ 2k 8 ビット sRGB
Name[kk]=Текстура 2k 8 бит srgb
Name[nb]=Tekstur 2k 8bit srgb
Name[nl]=Textuur 2k 8bit srgb
Name[pl]=Tekstura 2k 8bit srgb
Name[pt]=Textura 2k 8-bits sRGB
Name[pt_BR]=Textura 2k 8bits sRGB
Name[ru]=Текстура 2k 8 бит srgb
Name[sk]=Textúra 2k 8bit srgb
Name[sv]=Struktur 2k 8-bitar SRGB
Name[tr]=Doku 2k 8bit srgb
Name[uk]=Текстура 2k, 8-бітова, srgb
Name[x-test]=xxTexture 2k 8bit srgbxx
Name[zh_CN]=2K 纹理模板 8 位 sRGB 色彩空间
Name[zh_TW]=紋理 2k 8位元 srgb
Type=Link
URL[$e]=.source/Texture2k8bitsrgb.kra
X-KDE-Hidden=false
diff --git a/krita/data/templates/texture/Texture4096x40968bitsrgb.desktop b/krita/data/templates/texture/Texture4096x40968bitsrgb.desktop
index d5520df977..48d5250927 100644
--- a/krita/data/templates/texture/Texture4096x40968bitsrgb.desktop
+++ b/krita/data/templates/texture/Texture4096x40968bitsrgb.desktop
@@ -1,35 +1,36 @@
[Desktop Entry]
Icon=template_texture
Name=Texture 4096x4096 8bit srgb
Name[bs]=Tekstura 4096x4096 8bit srgb
-Name[ca]=Textura 4096x4096 8bit SRGB
-Name[ca@valencia]=Textura 4096x4096 8bit SRGB
+Name[ca]=Textura 4096x4096 de 8 bits amb SRGB
+Name[ca@valencia]=Textura 4096x4096 de 8 bits amb SRGB
Name[cs]=Textura 4096x4096 8bit srgb
Name[da]=Tekstur 4096x4096 8bit srgb
Name[de]=Textur 4096x4096 8bit srgb
Name[el]=Υφή 4096x4096 8bit srgb
Name[en_GB]=Texture 4096x4096 8bit srgb
Name[es]=Textura 4096x4096 8bits srgb
Name[et]=Tekstuur 4096x4096 8bit srgb
Name[eu]=Ehundura 4096x4096 8bit sGBU
+Name[fi]=Pintakuvio 4096 × 4096 8 bit SRGB
Name[fr]=Texture 4096x4096 8bit srgb
Name[gl]=Textura de 4096×4096 e 8 bits SRGB
Name[is]=Efnisáferð 4096x4096 8bita srgb
Name[it]=Trama 4096x4096 8bit srgb
Name[ja]=テクスチャ 4096x4096 8 ビット sRGB
Name[nb]=Tekstur 4096x4096 8bit srgb
Name[nl]=Textuur 4096x4096 8bit srgb
Name[pl]=Tekstura 4096x4096 8bit srgb
Name[pt]=Textura 4096x4096 8-bits sRGB
Name[pt_BR]=Textura 4096x4096 8bits sRGB
Name[ru]=Текстура 4096x4096 8 бит srgb
Name[sk]=Textúra 4096x4096 8bit srgb
Name[sv]=Struktur 4096 x 4096 8-bitar SRGB
Name[tr]=Doku 4096x4096 8bit srgb
Name[uk]=Текстура 4096⨯4096, 8-бітова, srgb
Name[x-test]=xxTexture 4096x4096 8bit srgbxx
Name[zh_CN]=4096x4096 纹理模板 8 位 sRGB 色彩空间
Name[zh_TW]=紋理 4096x4096 8位元 srgb
Type=Link
URL[$e]=.source/Texture4096x40968bitsrgb.kra
X-KDE-Hidden=false
diff --git a/krita/data/templates/texture/Texture4k32bitscalar.desktop b/krita/data/templates/texture/Texture4k32bitscalar.desktop
index 3fcb38b683..a6f8f8d1ce 100755
--- a/krita/data/templates/texture/Texture4k32bitscalar.desktop
+++ b/krita/data/templates/texture/Texture4k32bitscalar.desktop
@@ -1,37 +1,38 @@
[Desktop Entry]
Icon=template_texture
Name=Texture 4k 32bit scalar
Name[bs]=Tekstura 4k 32bit scalar
-Name[ca]=Textura 4k 32bit escalar
-Name[ca@valencia]=Textura 4k 32bit escalar
+Name[ca]=Textura 4k de 32 bits escalar
+Name[ca@valencia]=Textura 4k de 32 bits escalar
Name[cs]=Textura 4k 32bit skalární
Name[da]=Tekstur 4k 32bit scalar
Name[de]=Textur 4k 32bit scalar
Name[el]=Υφή 4k 32bit βαθμωτό
Name[en_GB]=Texture 4k 32bit scalar
Name[es]=Textura 4k 32bit escalar
Name[et]=Tekstuur 4k 32bit skalaar
Name[eu]=Ehundura 4k 32bit eskalarra
+Name[fi]=Pintakuvio 4k 32 bit skalaarinen
Name[fr]=Texture 4k 32bit scalaire
Name[gl]=Textura de 4k e 32 bits escalar
Name[hu]=Textúra 4k 32bit skalár
Name[is]=Efnisáferð 4k 32bita scalar
Name[it]=Trama 4k 32bit scalare
Name[ja]=テクスチャ 4k 32 ビットスカラー
Name[kk]=Текстура 4k 32 бит скаляр
Name[nb]=Tekstur 4k 32bit skalar
Name[nl]=Textuur 4k 32bit scalar
Name[pl]=Tekstura 4k 32bit skalar
Name[pt]=Textura 4k 32-bits escalar
Name[pt_BR]=Textura 4k 32bits escalar
Name[ru]=Текстура 4k 32 бит scalar
Name[sk]=Textúra 4k 32bit skalár
Name[sv]=Struktur 4k 32-bitar skalär
Name[tr]=Doku 4k 32bit sayısal
Name[uk]=Текстура 4k, 32-бітова, скалярна
Name[x-test]=xxTexture 4k 32bit scalarxx
Name[zh_CN]=4K 纹理模板 32 位 Scalar 色彩空间
Name[zh_TW]=紋理 4k 32位元 scalar
Type=Link
URL[$e]=.source/Texture4k32bitscalar.kra
X-KDE-Hidden=false
diff --git a/krita/data/templates/texture/Texture4k8bitsrgb.desktop b/krita/data/templates/texture/Texture4k8bitsrgb.desktop
index 30186db77c..81192dd93e 100755
--- a/krita/data/templates/texture/Texture4k8bitsrgb.desktop
+++ b/krita/data/templates/texture/Texture4k8bitsrgb.desktop
@@ -1,37 +1,38 @@
[Desktop Entry]
Icon=template_texture
Name=Texture 4k 8bit srgb
Name[bs]=Tekstura 4k 8bit srgb
-Name[ca]=Textura 4k 8bit SRGB
-Name[ca@valencia]=Textura 4k 8bit SRGB
+Name[ca]=Textura 4k de 8 bits amb SRGB
+Name[ca@valencia]=Textura 4k de 8 bits amb SRGB
Name[cs]=Textura 4k 8bit srgb
Name[da]=Tekstur 4k 8bit srgb
Name[de]=Textur 4k 8bit srgb
Name[el]=Υφή 4k 8bit srgb
Name[en_GB]=Texture 4k 8bit srgb
Name[es]=Textura 4k 8bit srgb
Name[et]=Tekstuur 4k 8bit srgb
Name[eu]=Ehundura 4k 8bit sGBU
+Name[fi]=Pintakuvio 4k 8 bit skalaarinen
Name[fr]=Texture 4k 8bit srgb
Name[gl]=Textura de 4k e 8 bits SRGB
Name[hu]=Textúra 4k 8bit srgb
Name[is]=Efnisáferð 4k 8bita srgb
Name[it]=Trama 4k 8bit srgb
Name[ja]=テクスチャ 4k 8 ビット sRGB
Name[kk]=Текстура 4k 8 бит srgb
Name[nb]=Tekstur 4k 8bit srgb
Name[nl]=Textuur 4k 8bit srgb
Name[pl]=Tekstura 4k 8bit srgb
Name[pt]=Textura 4k 8-bits sRGB
Name[pt_BR]=Textura 4k 8bits sRGB
Name[ru]=Текстура 4k 8 бит srgb
Name[sk]=Textúra 4k 8bit srgb
Name[sv]=Struktur 4k 8-bitar SRGB
Name[tr]=Doku 4k 8bit srgb
Name[uk]=Текстура 4k, 8-бітова, srgb
Name[x-test]=xxTexture 4k 8bit srgbxx
Name[zh_CN]=4K 纹理模板 8 位 sRGB 色彩空间
Name[zh_TW]=紋理 4k 8位元 srgb
Type=Link
URL[$e]=.source/Texture4k8bitsrgb.kra
X-KDE-Hidden=false
diff --git a/krita/data/templates/texture/Texture512x5128bitsrgb.desktop b/krita/data/templates/texture/Texture512x5128bitsrgb.desktop
index 971690f882..8e6ae30ac6 100644
--- a/krita/data/templates/texture/Texture512x5128bitsrgb.desktop
+++ b/krita/data/templates/texture/Texture512x5128bitsrgb.desktop
@@ -1,35 +1,36 @@
[Desktop Entry]
Icon=template_texture
Name=Texture 512x512 8bit srgb
Name[bs]=Tekstura 512x512 8bit srgb
-Name[ca]=Textura 512x512 8bit SRGB
-Name[ca@valencia]=Textura 512x512 8bit SRGB
+Name[ca]=Textura 512x512 de 8 bits amb SRGB
+Name[ca@valencia]=Textura 512x512 de 8 bits amb SRGB
Name[cs]=Textura 512x512 8bit srgb
Name[da]=Tekstur 512x512 8bit srgb
Name[de]=Textur 512x512 8bit srgb
Name[el]=Υφή 512x512 8bit srgb
Name[en_GB]=Texture 512x512 8bit srgb
Name[es]=Textura 512x512 8bits srgb
Name[et]=Tekstuur 512x512 8bit srgb
Name[eu]=Ehundura 512x512 8bit sGBU
+Name[fi]=Pintakuvio 512 × 512 8 bit SRGB
Name[fr]=Texture 512x512 8bit srgb
Name[gl]=Textura de 512×512 e 8 bits SRGB
Name[is]=Efnisáferð 512x512 8bita srgb
Name[it]=Trama 512x512 8bit srgb
Name[ja]=テクスチャ 512x512 8 ビット sRGB
Name[nb]=Tekstur 512x512 8bit srgb
Name[nl]=Textuur 512x512 8bit srgb
Name[pl]=Tekstura 512x512 8bit srgb
Name[pt]=Textura 512x512 8-bits sRGB
Name[pt_BR]=Textura 512x512 8bits sRGB
Name[ru]=Текстура 512x512 8 бит srgb
Name[sk]=Textúra 512x512 8bit srgb
Name[sv]=Struktur 512 x 512 8-bitar SRGB
Name[tr]=Doku 512x512 8bit srgb
Name[uk]=Текстура 512⨯512, 8-бітова, srgb
Name[x-test]=xxTexture 512x512 8bit srgbxx
Name[zh_CN]=512x512 纹理模板 8 位 sRGB 色彩空间
Name[zh_TW]=紋理 512x512 8位元 srgb
Type=Link
URL[$e]=.source/Texture512x5128bitsrgb.kra
X-KDE-Hidden=false
diff --git a/krita/data/templates/texture/Texture8k32bitscalar.desktop b/krita/data/templates/texture/Texture8k32bitscalar.desktop
index d94e7eb3a0..4021ce90dc 100755
--- a/krita/data/templates/texture/Texture8k32bitscalar.desktop
+++ b/krita/data/templates/texture/Texture8k32bitscalar.desktop
@@ -1,37 +1,38 @@
[Desktop Entry]
Icon=template_texture
Name=Texture 8k 32bit scalar
Name[bs]=Tekstura 8k 32bit scalar
-Name[ca]=Textura 8k 32bit escalar
-Name[ca@valencia]=Textura 8k 32bit escalar
+Name[ca]=Textura 8k de 32 bits escalar
+Name[ca@valencia]=Textura 8k de 32 bits escalar
Name[cs]=Textura 8k 32bit skalární
Name[da]=Tekstur 8k 32bit scalar
Name[de]=Textur 8k 32bit scalar
Name[el]=Υφή 8k 32bit βαθμωτό
Name[en_GB]=Texture 8k 32bit scalar
Name[es]=Textura 8k 32 bit escalar
Name[et]=Tekstuur 8k 32bit skalaar
Name[eu]=Ehundura 8k 32bit eskalarra
+Name[fi]=Pintakuvio 8k 32 bit skalaarinen
Name[fr]=Texture 8k 32bit scalaire
Name[gl]=Textura de 8k e 32 bits escalar
Name[hu]=Textúra 8k 32bit skalár
Name[is]=Efnisáferð 8k 32bita scalar
Name[it]=Trama 8k 32bit scalare
Name[ja]=テクスチャ 8k 32 ビットスカラー
Name[kk]=Текстура 8k 32 бит скаляр
Name[nb]=Tekstur 8k 32bit skalar
Name[nl]=Textuur 8k 32bit scalar
Name[pl]=Tekstura 8k 32bit skalar
Name[pt]=Textura 8k 32-bits escalar
Name[pt_BR]=Textura 8k 32bits escalar
Name[ru]=Текстура 8k 32 бит scalar
Name[sk]=Textúra 8k 32bit skalár
Name[sv]=Struktur 8k 32-bitar skalär
Name[tr]=Doku 8k 32bit sayısal
Name[uk]=Текстура 8k, 32-бітова, скалярна
Name[x-test]=xxTexture 8k 32bit scalarxx
Name[zh_CN]=8K 纹理模板 32 位 Scalar 色彩空间
Name[zh_TW]=紋理 8k 32位元 scalar
Type=Link
URL[$e]=.source/Texture8k32bitscalar.kra
X-KDE-Hidden=false
diff --git a/krita/data/templates/texture/Texture8k8bitsrgb.desktop b/krita/data/templates/texture/Texture8k8bitsrgb.desktop
index eaf4531ef4..a426f214ab 100755
--- a/krita/data/templates/texture/Texture8k8bitsrgb.desktop
+++ b/krita/data/templates/texture/Texture8k8bitsrgb.desktop
@@ -1,38 +1,39 @@
[Desktop Entry]
Icon=template_texture
Name=Texture 8k 8bit srgb
Name[bs]=Tekstura 8k 8bit srgb
-Name[ca]=Textura 8k 8bit SRGB
-Name[ca@valencia]=Textura 8k 8bit SRGB
+Name[ca]=Textura 8k de 8 bits amb SRGB
+Name[ca@valencia]=Textura 8k de 8 bits amb SRGB
Name[cs]=Textura 8k 8bit srgb
Name[da]=Tekstur 8k 8bit srgb
Name[de]=Textur 8k 8bit srgb
Name[el]=Υφή 8k 8bit srgb
Name[en_GB]=Texture 8k 8bit srgb
Name[es]=Textura 8k 8bit srgb
Name[et]=Tekstuur 8k 8bit srgb
Name[eu]=Ehundura 8k 8bit sGBU
+Name[fi]=Pintakuvio 8k 8 bit SRGB
Name[fr]=Texture 8k 8bit srgb
Name[gl]=Textura de 8k e 8 bits SRGB
Name[hu]=Textúra 8k 8bit srgb
Name[is]=Efnisáferð 8k 8bita srgb
Name[it]=Trama 8k 8bit srgb
Name[ja]=テクスチャ 8k 8 ビット sRGB
Name[kk]=Текстура 8k 8 бит srgb
Name[nb]=Tekstur 8k 8bit srgb
Name[nl]=Textuur 8k 8bit srgb
Name[pl]=Tekstura 8k 8bit srgb
Name[pt]=Textura 8k 8-bits sRGB
Name[pt_BR]=Textura 8k 8bits sRGB
Name[ru]=Текстура 8k 8 бит srgb
Name[sk]=Textúra 8k 8bit srgb
Name[sl]=Tekstura 8k 8 bitov srgb
Name[sv]=Struktur 8k 8-bitar SRGB
Name[tr]=Doku 8k 8bit srgb
Name[uk]=Текстура 8k, 8-бітова, srgb
Name[x-test]=xxTexture 8k 8bit srgbxx
Name[zh_CN]=8K 纹理模板 8 位 sRGB 色彩空间
Name[zh_TW]=紋理 8k 8位元 srgb
Type=Link
URL[$e]=.source/Texture8k8bitsrgb.kra
X-KDE-Hidden=false
diff --git a/krita/main.cc b/krita/main.cc
index 0e4fc32f9a..aba31a0ad0 100644
--- a/krita/main.cc
+++ b/krita/main.cc
@@ -1,478 +1,499 @@
/*
* Copyright (c) 1999 Matthias Elter <me@kde.org>
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2015 Boudewijn Rempt <boud@valdyas.org>
*
* 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 <stdlib.h>
#include <QString>
#include <QPixmap>
#include <kis_debug.h>
#include <QProcess>
#include <QProcessEnvironment>
#include <QStandardPaths>
#include <QDir>
#include <QDate>
#include <QLocale>
#include <QSettings>
#include <QByteArray>
#include <QMessageBox>
+#include <QThread>
#if QT_VERSION >= 0x050900
#include <QOperatingSystemVersion>
#endif
#include <time.h>
#include <KisApplication.h>
#include <KoConfig.h>
#include <KoResourcePaths.h>
#include <kis_config.h>
#include "data/splash/splash_screen.xpm"
#include "data/splash/splash_holidays.xpm"
#include "data/splash/splash_screen_x2.xpm"
#include "data/splash/splash_holidays_x2.xpm"
#include "KisDocument.h"
#include "kis_splash_screen.h"
#include "KisPart.h"
#include "KisApplicationArguments.h"
#include <opengl/kis_opengl.h>
#include "input/KisQtWidgetsTweaker.h"
+#include <KisUsageLogger.h>
+#include <kis_image_config.h>
#if defined Q_OS_WIN
#include <windows.h>
#include <kis_tablet_support_win.h>
#include <kis_tablet_support_win8.h>
#include <QLibrary>
#elif defined HAVE_X11
#include "config_use_qt_xcb.h"
#ifndef USE_QT_XCB
#include <kis_xi2_event_filter.h>
#endif
#endif
#if defined HAVE_KCRASH
#include <kcrash.h>
#elif defined USE_DRMINGW
namespace
{
void tryInitDrMingw()
{
wchar_t path[MAX_PATH];
QString pathStr = QCoreApplication::applicationDirPath().replace(L'/', L'\\') + QStringLiteral("\\exchndl.dll");
if (pathStr.size() > MAX_PATH - 1) {
return;
}
int pathLen = pathStr.toWCharArray(path);
path[pathLen] = L'\0'; // toWCharArray doesn't add NULL terminator
HMODULE hMod = LoadLibraryW(path);
if (!hMod) {
return;
}
// No need to call ExcHndlInit since the crash handler is installed on DllMain
auto myExcHndlSetLogFileNameA = reinterpret_cast<BOOL (APIENTRY *)(const char *)>(GetProcAddress(hMod, "ExcHndlSetLogFileNameA"));
if (!myExcHndlSetLogFileNameA) {
return;
}
// Set the log file path to %LocalAppData%\kritacrash.log
QString logFile = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation).replace(L'/', L'\\') + QStringLiteral("\\kritacrash.log");
myExcHndlSetLogFileNameA(logFile.toLocal8Bit());
}
typedef enum ORIENTATION_PREFERENCE {
ORIENTATION_PREFERENCE_NONE = 0x0,
ORIENTATION_PREFERENCE_LANDSCAPE = 0x1,
ORIENTATION_PREFERENCE_PORTRAIT = 0x2,
ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED = 0x4,
ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED = 0x8
} ORIENTATION_PREFERENCE;
typedef BOOL WINAPI (*pSetDisplayAutoRotationPreferences_t)(
ORIENTATION_PREFERENCE orientation
);
void resetRotation()
{
QLibrary user32Lib("user32");
if (!user32Lib.load()) {
qWarning() << "Failed to load user32.dll! This really should not happen.";
return;
}
pSetDisplayAutoRotationPreferences_t pSetDisplayAutoRotationPreferences
= reinterpret_cast<pSetDisplayAutoRotationPreferences_t>(user32Lib.resolve("SetDisplayAutoRotationPreferences"));
if (!pSetDisplayAutoRotationPreferences) {
dbgKrita << "Failed to load function SetDisplayAutoRotationPreferences";
return;
}
bool result = pSetDisplayAutoRotationPreferences(ORIENTATION_PREFERENCE_NONE);
dbgKrita << "SetDisplayAutoRotationPreferences(ORIENTATION_PREFERENCE_NONE) returned" << result;
}
} // namespace
#endif
extern "C" int main(int argc, char **argv)
{
// The global initialization of the random generator
qsrand(time(0));
bool runningInKDE = !qgetenv("KDE_FULL_SESSION").isEmpty();
#if defined HAVE_X11
qputenv("QT_QPA_PLATFORM", "xcb");
#endif
// Workaround a bug in QNetworkManager
qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1));
// A per-user unique string, without /, because QLocalServer cannot use names with a / in it
QString key = "Krita4" + QStandardPaths::writableLocation(QStandardPaths::HomeLocation).replace("/", "_");
key = key.replace(":", "_").replace("\\","_");
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true);
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true);
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true);
#if QT_VERSION >= 0x050900
QCoreApplication::setAttribute(Qt::AA_DisableShaderDiskCache, true);
#endif
const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
+ QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat);
bool singleApplication = true;
bool enableOpenGLDebug = false;
bool openGLDebugSynchronous = false;
+ bool logUsage = true;
{
- QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat);
+
singleApplication = kritarc.value("EnableSingleApplication", true).toBool();
-#if QT_VERSION >= 0x050600
if (kritarc.value("EnableHiDPI", true).toBool()) {
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
}
if (!qgetenv("KRITA_HIDPI").isEmpty()) {
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
}
-#endif
+
if (!qgetenv("KRITA_OPENGL_DEBUG").isEmpty()) {
enableOpenGLDebug = true;
} else {
enableOpenGLDebug = kritarc.value("EnableOpenGLDebug", false).toBool();
}
if (enableOpenGLDebug && (qgetenv("KRITA_OPENGL_DEBUG") == "sync" || kritarc.value("OpenGLDebugSynchronous", false).toBool())) {
openGLDebugSynchronous = true;
}
KisConfig::RootSurfaceFormat rootSurfaceFormat = KisConfig::rootSurfaceFormat(&kritarc);
KisOpenGL::OpenGLRenderer preferredRenderer = KisOpenGL::RendererAuto;
+ logUsage = kritarc.value("LogUsage", true).toBool();
+
const QString preferredRendererString = kritarc.value("OpenGLRenderer", "auto").toString();
preferredRenderer = KisOpenGL::convertConfigToOpenGLRenderer(preferredRendererString);
#ifdef Q_OS_WIN
// Force ANGLE to use Direct3D11. D3D9 doesn't support OpenGL ES 3 and WARP
// might get weird crashes atm.
qputenv("QT_ANGLE_PLATFORM", "d3d11");
#endif
const QSurfaceFormat format =
KisOpenGL::selectSurfaceFormat(preferredRenderer, rootSurfaceFormat, enableOpenGLDebug);
if (format.renderableType() == QSurfaceFormat::OpenGLES) {
QCoreApplication::setAttribute(Qt::AA_UseOpenGLES, true);
} else {
QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL, true);
}
KisOpenGL::setDefaultSurfaceFormat(format);
KisOpenGL::setDebugSynchronous(openGLDebugSynchronous);
#ifdef Q_OS_WIN
// HACK: https://bugs.kde.org/show_bug.cgi?id=390651
resetRotation();
#endif
}
+ if (logUsage) {
+ KisUsageLogger::initialize();
+ }
+
QString root;
QString language;
{
// Create a temporary application to get the root
QCoreApplication app(argc, argv);
Q_UNUSED(app);
root = KoResourcePaths::getApplicationRoot();
QSettings languageoverride(configPath + QStringLiteral("/klanguageoverridesrc"), QSettings::IniFormat);
languageoverride.beginGroup(QStringLiteral("Language"));
language = languageoverride.value(qAppName(), "").toString();
}
#ifdef Q_OS_LINUX
{
QByteArray originalXdgDataDirs = qgetenv("XDG_DATA_DIRS");
if (originalXdgDataDirs.isEmpty()) {
// We don't want to completely override the default
originalXdgDataDirs = "/usr/local/share/:/usr/share/";
}
qputenv("XDG_DATA_DIRS", QFile::encodeName(root + "share") + ":" + originalXdgDataDirs);
}
#else
qputenv("XDG_DATA_DIRS", QFile::encodeName(root + "share"));
#endif
dbgKrita << "Setting XDG_DATA_DIRS" << qgetenv("XDG_DATA_DIRS");
// Now that the paths are set, set the language. First check the override from the language
// selection dialog.
dbgKrita << "Override language:" << language;
bool rightToLeft = false;
if (!language.isEmpty()) {
KLocalizedString::setLanguages(language.split(":"));
// And override Qt's locale, too
qputenv("LANG", language.split(":").first().toLocal8Bit());
QLocale locale(language.split(":").first());
QLocale::setDefault(locale);
const QStringList rtlLanguages = QStringList()
<< "ar" << "dv" << "he" << "ha" << "ku" << "fa" << "ps" << "ur" << "yi";
if (rtlLanguages.contains(language.split(':').first())) {
rightToLeft = true;
}
}
else {
dbgKrita << "Qt UI languages:" << QLocale::system().uiLanguages() << qgetenv("LANG");
// And if there isn't one, check the one set by the system.
QLocale locale = QLocale::system();
if (locale.name() != QStringLiteral("en")) {
QStringList uiLanguages = locale.uiLanguages();
for (QString &uiLanguage : uiLanguages) {
// This list of language codes that can have a specifier should
// be extended whenever we have translations that need it; right
// now, only en, pt, zh are in this situation.
if (uiLanguage.startsWith("en") || uiLanguage.startsWith("pt")) {
uiLanguage.replace(QChar('-'), QChar('_'));
}
else if (uiLanguage.startsWith("zh-Hant") || uiLanguage.startsWith("zh-TW")) {
uiLanguage = "zh_TW";
}
else if (uiLanguage.startsWith("zh-Hans") || uiLanguage.startsWith("zh-CN")) {
uiLanguage = "zh_CN";
}
}
for (int i = 0; i < uiLanguages.size(); i++) {
QString uiLanguage = uiLanguages[i];
// Strip the country code
int idx = uiLanguage.indexOf(QChar('-'));
if (idx != -1) {
uiLanguage = uiLanguage.left(idx);
uiLanguages.replace(i, uiLanguage);
}
}
dbgKrita << "Converted ui languages:" << uiLanguages;
qputenv("LANG", uiLanguages.first().toLocal8Bit());
#ifdef Q_OS_MAC
// See https://bugs.kde.org/show_bug.cgi?id=396370
KLocalizedString::setLanguages(QStringList() << uiLanguages.first());
#else
KLocalizedString::setLanguages(QStringList() << uiLanguages);
#endif
}
}
// first create the application so we can create a pixmap
KisApplication app(key, argc, argv);
if (!language.isEmpty()) {
if (rightToLeft) {
app.setLayoutDirection(Qt::RightToLeft);
}
else {
app.setLayoutDirection(Qt::LeftToRight);
}
}
KLocalizedString::setApplicationDomain("krita");
dbgKrita << "Available translations" << KLocalizedString::availableApplicationTranslations();
dbgKrita << "Available domain translations" << KLocalizedString::availableDomainTranslations("krita");
#ifdef Q_OS_WIN
QDir appdir(KoResourcePaths::getApplicationRoot());
QString path = qgetenv("PATH");
qputenv("PATH", QFile::encodeName(appdir.absolutePath() + "/bin" + ";"
+ appdir.absolutePath() + "/lib" + ";"
+ appdir.absolutePath() + "/Frameworks" + ";"
+ appdir.absolutePath() + ";"
+ path));
dbgKrita << "PATH" << qgetenv("PATH");
#endif
if (qApp->applicationDirPath().contains(KRITA_BUILD_DIR)) {
qFatal("FATAL: You're trying to run krita from the build location. You can only run Krita from the installation location.");
}
#if defined HAVE_KCRASH
KCrash::initialize();
#elif defined USE_DRMINGW
tryInitDrMingw();
#endif
// If we should clear the config, it has to be done as soon as possible after
// KisApplication has been created. Otherwise the config file may have been read
// and stored in a KConfig object we have no control over.
app.askClearConfig();
KisApplicationArguments args(app);
if (singleApplication && app.isRunning()) {
// only pass arguments to main instance if they are not for batch processing
// any batch processing would be done in this separate instance
const bool batchRun = args.exportAs();
if (!batchRun) {
QByteArray ba = args.serialize();
if (app.sendMessage(ba)) {
return 0;
}
}
}
if (!runningInKDE) {
// Icons in menus are ugly and distracting
app.setAttribute(Qt::AA_DontShowIconsInMenus);
}
#if defined HAVE_X11
#ifndef USE_QT_XCB
app.installNativeEventFilter(KisXi2EventFilter::instance());
#endif
#endif
app.installEventFilter(KisQtWidgetsTweaker::instance());
if (!args.noSplash()) {
// then create the pixmap from an xpm: we cannot get the
// location of our datadir before we've started our components,
// so use an xpm.
QDate currentDate = QDate::currentDate();
QWidget *splash = 0;
if (currentDate > QDate(currentDate.year(), 12, 4) ||
currentDate < QDate(currentDate.year(), 1, 9)) {
splash = new KisSplashScreen(app.applicationVersion(), QPixmap(splash_holidays_xpm), QPixmap(splash_holidays_x2_xpm));
}
else {
splash = new KisSplashScreen(app.applicationVersion(), QPixmap(splash_screen_xpm), QPixmap(splash_screen_x2_xpm));
}
app.setSplashScreen(splash);
}
#if defined Q_OS_WIN
KisConfig cfg(false);
bool supportedWindowsVersion = true;
#if QT_VERSION >= 0x050900
QOperatingSystemVersion osVersion = QOperatingSystemVersion::current();
if (osVersion.type() == QOperatingSystemVersion::Windows) {
if (osVersion.majorVersion() >= QOperatingSystemVersion::Windows7.majorVersion()) {
supportedWindowsVersion = true;
}
else {
supportedWindowsVersion = false;
if (cfg.readEntry("WarnedAboutUnsupportedWindows", false)) {
QMessageBox::information(0,
i18nc("@title:window", "Krita: Warning"),
i18n("You are running an unsupported version of Windows: %1.\n"
"This is not recommended. Do not report any bugs.\n"
"Please update to a supported version of Windows: Windows 7, 8, 8.1 or 10.", osVersion.name()));
cfg.writeEntry("WarnedAboutUnsupportedWindows", true);
}
}
}
#endif
{
if (cfg.useWin8PointerInput() && !KisTabletSupportWin8::isAvailable()) {
cfg.setUseWin8PointerInput(false);
}
if (!cfg.useWin8PointerInput()) {
bool hasWinTab = KisTabletSupportWin::init();
if (!hasWinTab && supportedWindowsVersion) {
if (KisTabletSupportWin8::isPenDeviceAvailable()) {
// Use WinInk automatically
cfg.setUseWin8PointerInput(true);
} else if (!cfg.readEntry("WarnedAboutMissingWinTab", false)) {
if (KisTabletSupportWin8::isAvailable()) {
QMessageBox::information(nullptr,
i18n("Krita Tablet Support"),
i18n("Cannot load WinTab driver and no Windows Ink pen devices are found. If you have a drawing tablet, please make sure the tablet driver is properly installed."),
QMessageBox::Ok, QMessageBox::Ok);
} else {
QMessageBox::information(nullptr,
i18n("Krita Tablet Support"),
i18n("Cannot load WinTab driver. If you have a drawing tablet, please make sure the tablet driver is properly installed."),
QMessageBox::Ok, QMessageBox::Ok);
}
cfg.writeEntry("WarnedAboutMissingWinTab", true);
}
}
}
if (cfg.useWin8PointerInput()) {
KisTabletSupportWin8 *penFilter = new KisTabletSupportWin8();
if (penFilter->init()) {
// penFilter.registerPointerDeviceNotifications();
app.installNativeEventFilter(penFilter);
dbgKrita << "Using Win8 Pointer Input for tablet support";
} else {
dbgKrita << "No Win8 Pointer Input available";
delete penFilter;
}
}
}
#endif
if (!app.start(args)) {
return 1;
}
#if QT_VERSION >= 0x050700
app.setAttribute(Qt::AA_CompressHighFrequencyEvents, false);
#endif
// Set up remote arguments.
QObject::connect(&app, SIGNAL(messageReceived(QByteArray,QObject*)),
&app, SLOT(remoteArguments(QByteArray,QObject*)));
QObject::connect(&app, SIGNAL(fileOpenRequest(QString)),
&app, SLOT(fileOpenRequested(QString)));
+ // Hardware information
+ KisUsageLogger::write("\nHardware Information\n");
+ KisUsageLogger::write(QString(" GPU Acceleration: %1").arg(kritarc.value("OpenGLRenderer", "auto").toString()));
+ KisUsageLogger::write(QString(" Memory: %1 Mb").arg(KisImageConfig(true).totalRAM()));
+ KisUsageLogger::write(QString(" Number of Cores: %1").arg(QThread::idealThreadCount()));
+ KisUsageLogger::write(QString(" Swap Location: %1\n").arg(KisImageConfig(true).swapDir()));
+
int state = app.exec();
{
QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat);
kritarc.setValue("canvasState", "OPENGL_SUCCESS");
}
+ if (logUsage) {
+ KisUsageLogger::close();
+ }
+
return state;
}
diff --git a/krita/org.kde.krita.appdata.xml b/krita/org.kde.krita.appdata.xml
index d5ce8dd1ef..fd1b6ec2dc 100644
--- a/krita/org.kde.krita.appdata.xml
+++ b/krita/org.kde.krita.appdata.xml
@@ -1,258 +1,262 @@
<?xml version="1.0" encoding="utf-8"?>
<component type="desktop">
<id>org.kde.krita</id>
<launchable type="desktop-id">org.kde.krita.desktop</launchable>
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-3.0-only</project_license>
<developer_name>Krita Foundation</developer_name>
<developer_name xml:lang="ca">Fundació Krita</developer_name>
<developer_name xml:lang="ca-valencia">Fundació Krita</developer_name>
<developer_name xml:lang="cs">Krita Foundation</developer_name>
<developer_name xml:lang="de">Krita Foundation</developer_name>
<developer_name xml:lang="en-GB">Krita Foundation</developer_name>
<developer_name xml:lang="es">Fundación Krita</developer_name>
<developer_name xml:lang="eu">Krita Fundazioa</developer_name>
+ <developer_name xml:lang="fi">Krita Foundation</developer_name>
<developer_name xml:lang="fr">La Fondation Krita</developer_name>
<developer_name xml:lang="gl">Fundación Krita</developer_name>
+ <developer_name xml:lang="id">Asas Krita</developer_name>
<developer_name xml:lang="it">Fondazione Krita</developer_name>
<developer_name xml:lang="nl">Krita Foundation</developer_name>
<developer_name xml:lang="pl">Fundacja Krity</developer_name>
<developer_name xml:lang="pt">Fundação do Krita</developer_name>
<developer_name xml:lang="pt-BR">Krita Foundation</developer_name>
<developer_name xml:lang="sv">Krita-stiftelsen</developer_name>
<developer_name xml:lang="uk">Фундація Krita</developer_name>
<developer_name xml:lang="x-test">xxKrita Foundationxx</developer_name>
<developer_name xml:lang="zh-CN">Krita 基金会</developer_name>
<developer_name xml:lang="zh-TW">Krita 基金會</developer_name>
<update_contact>foundation@krita.org</update_contact>
<name>Krita</name>
<name xml:lang="ar">كريتا</name>
<name xml:lang="ca">Krita</name>
<name xml:lang="ca-valencia">Krita</name>
<name xml:lang="cs">Krita</name>
<name xml:lang="de">Krita</name>
<name xml:lang="el">Krita</name>
<name xml:lang="en-GB">Krita</name>
<name xml:lang="es">Krita</name>
<name xml:lang="eu">Krita</name>
+ <name xml:lang="fi">Krita</name>
<name xml:lang="fr">Krita</name>
<name xml:lang="gl">Krita</name>
<name xml:lang="id">Krita</name>
<name xml:lang="it">Krita</name>
<name xml:lang="nl">Krita</name>
<name xml:lang="pl">Krita</name>
<name xml:lang="pt">Krita</name>
<name xml:lang="pt-BR">Krita</name>
<name xml:lang="ru">Krita</name>
<name xml:lang="sk">Krita</name>
<name xml:lang="sv">Krita</name>
<name xml:lang="tr">Krita</name>
<name xml:lang="uk">Krita</name>
<name xml:lang="x-test">xxKritaxx</name>
<name xml:lang="zh-CN">Krita</name>
<name xml:lang="zh-TW">Krita</name>
<summary>Digital Painting, Creative Freedom</summary>
<summary xml:lang="ar">رسم رقميّ، حريّة إبداعيّة</summary>
<summary xml:lang="bs">Digitalno crtanje, kreativna sloboda</summary>
<summary xml:lang="ca">Dibuix digital, Llibertat creativa</summary>
<summary xml:lang="ca-valencia">Dibuix digital, Llibertat creativa</summary>
<summary xml:lang="cs">Digitální malování, svoboda tvorby</summary>
<summary xml:lang="da">Digital tegning, kunstnerisk frihed</summary>
<summary xml:lang="de">Digitales Malen, kreative Freiheit</summary>
<summary xml:lang="el">Ψηφιακή ζωγραφική, δημιουργική ελευθερία</summary>
<summary xml:lang="en-GB">Digital Painting, Creative Freedom</summary>
<summary xml:lang="es">Pintura digital, libertad creativa</summary>
<summary xml:lang="et">Digitaalne joonistamine, loominguline vabadus</summary>
<summary xml:lang="eu">Margolan digitala, sormen askatasuna</summary>
<summary xml:lang="fi">Digitaalimaalaus, luova vapaus</summary>
<summary xml:lang="fr">Peinture numérique, liberté créatrice</summary>
<summary xml:lang="gl">Debuxo dixital, liberdade creativa</summary>
<summary xml:lang="ia">Pictura digital, Libertate creative</summary>
<summary xml:lang="id">Pelukisan Digital, Kebebasan Berkreatif</summary>
<summary xml:lang="it">Pittura digitale, libertà creativa</summary>
<summary xml:lang="nl">Digital Painting, Creative Freedom</summary>
<summary xml:lang="pl">Cyfrowe malowanie, Wolność Twórcza</summary>
<summary xml:lang="pt">Pintura Digital, Liberdade Criativa</summary>
<summary xml:lang="pt-BR">Pintura digital, liberdade criativa</summary>
<summary xml:lang="ru">Цифровое рисование. Творческая свобода</summary>
<summary xml:lang="sk">Digitálne maľovanie, kreatívna sloboda</summary>
<summary xml:lang="sv">Digital målning, kreativ frihet</summary>
<summary xml:lang="tr">Sayısal Boyama, Yaratıcı Özgürlük</summary>
<summary xml:lang="uk">Цифрове малювання, творча свобода</summary>
<summary xml:lang="x-test">xxDigital Painting, Creative Freedomxx</summary>
<summary xml:lang="zh-CN">自由挥洒数字绘画的无限创意</summary>
<summary xml:lang="zh-TW">數位繪畫,創作自由</summary>
<description>
<p>Krita is the full-featured digital art studio.</p>
<p xml:lang="bs">Krita je potpuni digitalni umjetnički studio.</p>
<p xml:lang="ca">Krita és l'estudi d'art digital ple de funcionalitats.</p>
<p xml:lang="ca-valencia">Krita és l'estudi d'art digital ple de funcionalitats.</p>
<p xml:lang="de">Krita ist ein digitales Designstudio mit umfangreichen Funktionen.</p>
<p xml:lang="el">Το Krita είναι ένα πλήρες χαρακτηριστικών ψηφιακό ατελιέ.</p>
<p xml:lang="en-GB">Krita is the full-featured digital art studio.</p>
<p xml:lang="es">Krita es un estudio de arte digital completo</p>
<p xml:lang="et">Krita on rohkete võimalustega digitaalkunstistuudio.</p>
<p xml:lang="eu">Krita arte lantegi digital osoa da.</p>
<p xml:lang="fi">Krita on täyspiirteinen digitaiteen ateljee.</p>
<p xml:lang="fr">Krita est le studio d'art numérique complet.</p>
<p xml:lang="gl">Krita é un estudio completo de arte dixital.</p>
<p xml:lang="ia">Krita es le studio de arte digital complete.</p>
<p xml:lang="id">Krita adalah studio seni digital yang penuh dengan fitur.</p>
<p xml:lang="it">Krita è uno studio d'arte digitale completo.</p>
<p xml:lang="ja">Krita は、フル機能を備えたデジタルなアートスタジオです。</p>
<p xml:lang="nl">Krita is de digitale kunststudio vol mogelijkheden.</p>
<p xml:lang="pl">Krita jest pełnowymiarowym, cyfrowym studiem artystycznym</p>
<p xml:lang="pt">O Krita é o estúdio de arte digital completo.</p>
<p xml:lang="pt-BR">O Krita é o estúdio de arte digital completo.</p>
<p xml:lang="ru">Krita — полнофункциональный инструмент для создания цифровой графики.</p>
<p xml:lang="sk">Krita je plne vybavené digitálne umelecké štúdio.</p>
<p xml:lang="sv">Krita är den fullfjädrade digitala konststudion.</p>
<p xml:lang="tr">Krita, tam özellikli dijital sanat stüdyosudur.</p>
<p xml:lang="uk">Krita — повноцінний комплекс для створення цифрових художніх творів.</p>
<p xml:lang="x-test">xxKrita is the full-featured digital art studio.xx</p>
<p xml:lang="zh-CN">Krita 是一款功能齐全的数字绘画工作室软件。</p>
<p xml:lang="zh-TW">Krita 是全功能的數位藝術工作室。</p>
<p>It is perfect for sketching and painting, and presents an end–to–end solution for creating digital painting files from scratch by masters.</p>
<p xml:lang="bs">On je savršen za skiciranje i slikanje i predstavlja finalno rješenje za kreiranje digitalnih slika od nule s majstorima</p>
<p xml:lang="ca">És perfecte per fer esbossos i pintar, i presenta una solució final per crear fitxers de dibuix digital des de zero per a mestres.</p>
<p xml:lang="ca-valencia">És perfecte per fer esbossos i pintar, i presenta una solució final per crear fitxers de dibuix digital des de zero per a mestres.</p>
<p xml:lang="el">Είναι ιδανικό για σκιτσογραφία και ζωγραφική, και παρουσιάζει μια από άκρη σε άκρη λύση για τη δημιουργία από το μηδέν αρχείων ψηφιακης ζωγραφικής από τους δασκάλους της τέχνης.</p>
<p xml:lang="en-GB">It is perfect for sketching and painting, and presents an end–to–end solution for creating digital painting files from scratch by masters.</p>
<p xml:lang="es">Es perfecto para diseñar y pintar, y ofrece una solución completa para crear desde cero archivos de pintura digital apta para profesionales.</p>
<p xml:lang="et">See on suurepärane töövahend visandite ja joonistuste valmistamiseks ning annab andekatele kunstnikele võimaluse luua digitaalpilt algusest lõpuni just oma käe järgi.</p>
<p xml:lang="eu">Zirriborratzeko eta margotzeko ezin hobea da, eta margolan digitalen fitxategiak hutsetik sortzeko muturretik-muturrera konponbide bat aurkezten du, maisuentzako mailakoa.</p>
<p xml:lang="fi">Se on täydellinen luonnosteluun ja maalaukseen ja tarjoaa kokonaisratkaisun digitaalisten kuvatiedostojen luomiseen alusta alkaen.</p>
<p xml:lang="fr">Il est parfait pour crayonner et peindre, et constitue une solution de bout en bout pour créer des fichier de peinture numérique depuis la feuille blanche jusqu'au épreuves finales.</p>
<p xml:lang="gl">Resulta perfecto para debuxar e pintar, e presenta unha solución completa que permite aos mestres crear ficheiros de debuxo dixital desde cero.</p>
<p xml:lang="ia">Illo es perfecte pro schizzar e pinger, e presenta un solution ab fin al fin pro crear files de pictura digital ab grattamentos per maestros.</p>
<p xml:lang="id">Ini adalah sempurna untuk mensketsa dan melukis, dan menghadirkan sebuah solusi untuk menciptakan file-file pelukisan digital dari goresan si pelukis ulung.</p>
<p xml:lang="it">Perfetto per fare schizzi e dipingere, prevede una soluzione completa che consente agli artisti di creare file di dipinti digitali partendo da zero.</p>
<p xml:lang="nl">Het is perfect voor schetsen en schilderen en zet een end–to–end oplossing voor het maken van digitale bestanden voor schilderingen vanuit het niets door meesters.</p>
<p xml:lang="pl">Nadaje się perfekcyjnie do szkicowania i malowania i dostarcza zupełnego rozwiązania dla tworzenia plików malowideł cyfrowych od zalążka.</p>
<p xml:lang="pt">É perfeito para desenhos e pinturas, oferecendo uma solução final para criar ficheiros de pintura digital do zero por mestres.</p>
<p xml:lang="pt-BR">É perfeito para desenhos e pinturas, oferecendo uma solução final para criar arquivos de desenho digital feitos a partir do zero por mestres.</p>
<p xml:lang="ru">Она превосходно подходит для набросков и рисования, предоставляя мастерам самодостаточный инструмент для создания цифровой живописи с нуля.</p>
<p xml:lang="sk">Je ideálna na skicovanie a maľovanie a poskytuje end-to-end riešenie na vytváranie súborov digitálneho maľovania od základu od profesionálov.</p>
<p xml:lang="sv">Den är perfekt för att skissa och måla, samt erbjuder en helomfattande lösning för att skapa digitala målningsfiler från grunden av mästare.</p>
<p xml:lang="tr">Eskiz ve boyama için mükemmeldir ve ustaların sıfırdan dijital boyama dosyaları oluşturmak için uçtan-uca bir çözüm sunar.</p>
<p xml:lang="uk">Цей комплекс чудово пасує для створення ескізів та художніх зображень і є самодостатнім набором для створення файлів цифрових полотен «з нуля» для справжніх художників.</p>
<p xml:lang="x-test">xxIt is perfect for sketching and painting, and presents an end–to–end solution for creating digital painting files from scratch by masters.xx</p>
<p xml:lang="zh-CN">它专门为数字绘画设计,为美术工作者提供了一个从起草、上色到完成作品等整个创作流程的完整解决方案。</p>
<p xml:lang="zh-TW">它是素描和繪畫的完美選擇,並提供了一個從零開始建立數位繪畫檔的端到端解決方案。</p>
<p>
Krita is a great choice for creating concept art, comics, textures for rendering and matte paintings. Krita supports many colorspaces like RGB and CMYK
at 8 and 16 bits integer channels, as well as 16 and 32 bits floating point channels.
</p>
<p xml:lang="bs">Krita je odličan izbor za kreiranje konceptualne umjetnosti, stripove, teksture za obradu i mat slike. Krita podržava mnoge prostore boja kao RGB i CMIK na 8 i 16 bitnim cjelobrojnim kanalimaa, kao i 16 i 32 bita floating point kanalima.</p>
<p xml:lang="ca">El Krita és una gran elecció per crear art conceptual, còmics, textures per renderitzar i pintures «matte». El Krita permet molts espais de color com el RGB i el CMYK a 8 i 16 bits de canals sencers, així com 16 i 32 bits de canals de coma flotant.</p>
<p xml:lang="ca-valencia">El Krita és una gran elecció per crear art conceptual, còmics, textures per renderitzar i pintures «matte». El Krita permet molts espais de color com el RGB i el CMYK a 8 i 16 bits de canals sencers, així com 16 i 32 bits de canals de coma flotant.</p>
<p xml:lang="el">Το Krita είναι μια εξαιρετική επιλογή για τη δημιουργία αφηρημένης τέχνης, ιστοριών με εικόνες, υφής για ζωγραφική αποτύπωσης και διάχυσης φωτός. Το Krita υποστηρίζει πολλούς χρωματικούς χώρους όπως τα RGB και CMYK σε 8 και 16 bit κανάλια ακεραίων καθώς επίσης και σε 16 και 32 bit κανάλια κινητής υποδιαστολής,</p>
<p xml:lang="en-GB">Krita is a great choice for creating concept art, comics, textures for rendering and matte paintings. Krita supports many colourspaces like RGB and CMYK at 8 and 16 bits integer channels, as well as 16 and 32 bits floating point channels.</p>
<p xml:lang="es">Krita es una gran elección para crear arte conceptual, cómics, texturas para renderizar y «matte paintings». Krita permite el uso de muchos espacios de color, como, por ejemplo, RGB y CMYK, tanto en canales de enteros de 8 y 16 bits, así como en canales de coma flotante de 16 y 32 bits.</p>
<p xml:lang="et">Krita on üks paremaid valikuid kontseptuaalkunsti, koomiksite, tekstuuride ja digitaalmaalide loomiseks. Krita toetab paljusid värviruume, näiteks RGB ja CMYK 8 ja 16 täisarvulise bitiga kanali kohta, samuti 16 ja 32 ujukomabitiga kanali kohta.</p>
<p xml:lang="eu">Krita aukera bikaina da kontzeptuzko artea, komikiak, errendatzeko ehundurak eta «matte» margolanak sortzeko. Kritak kolore-espazio ugari onartzen ditu hala nola GBU eta CMYK, 8 eta 16 biteko osoko kanaletan, baita 16 eta 32 biteko koma-higikorreko kanaletan.</p>
<p xml:lang="fi">Krita on hyvä valinta konseptikuvituksen, sarjakuvien, pintakuvioiden ja maalausten luomiseen. Krita tukee useita väriavaruuksia kuten RGB:tä ja CMYK:ta 8 ja 16 bitin kokonaisluku- samoin kuin 16 ja 32 bitin liukulukukanavin.</p>
<p xml:lang="fr">Krita est un très bon choix pour créer des concepts arts, des bandes-dessinées, des textures de rendu et des peintures. Krita prend en charge plusieurs espaces de couleurs comme RVB et CMJN avec les canaux de 8 et 16 bits entiers ainsi que les canaux de 16 et 32 bits flottants.</p>
<p xml:lang="gl">Krita é unha gran opción para crear arte conceptual, texturas para renderización e pinturas mate. Krita permite usar moitos espazos de cores como RGB e CMYK con canles de 8 e 16 bits, así como canles de coma flotante de 16 e 32 bits.</p>
<p xml:lang="ia">Krita es un grande selection pro crear arte de concepto, comics, texturas pro rendering e picturas opac. Krita supporta multe spatios de colores como RGB e CMYK con canales de integer a 8 e 16 bits, como anque canales floating point a 16 e 32 bits.</p>
- <p xml:lang="id">Krita adalah pilihan yang cocok untuk menciptakan konsep seni, komik, tekstur untuk rendering dan lukisan matte. Krita mendukung banyak ruang warna seperti RGB dan CMYK pada channel integer 8 dan 16 bit, serta 16 dan 32 bit channel titik mengambang.</p>
+ <p xml:lang="id">Krita adalah pilihan yang cocok untuk menciptakan konsep seni, komik, tekstur untuk rendering dan lukisan matte. Krita mendukung banyak ruang warna seperti RGB dan CMYK pada channel integer 8 dan 16 bit, serta channel floating point 16 dan 32 bit.</p>
<p xml:lang="it">Krita rappresenta una scelta ottimale per la creazione di arte concettuale, fumetti e texture per il rendering e il matte painting. Krita supporta molti spazi colori come RGB e CMYK a 8 e 16 bit per canali interi e 16 e 32 bit per canali a virgola mobile.</p>
<p xml:lang="ja">コンセプトアート、コミック、3DCG 用テクスチャ、マットペイントを制作する方にとって、Krita は最適な選択です。Krita は、8/16 ビット整数/チャンネル、および 16/32 ビット浮動小数点/チャンネルの RGB や CMYK をはじめ、さまざまな色空間をサポートしています。</p>
<p xml:lang="nl">Krita is een goede keuze voor het maken van kunstconcepten, strips, textuur voor weergeven en matte schilderijen. Krita ondersteunt vele kleurruimten zoals RGB en CMYK in 8 en 16 bits kanalen met gehele getallen, evenals 16 en 32 bits kanalen met drijvende komma.</p>
<p xml:lang="pl">Krita jest świetnym wyborem przy tworzeniu koncepcyjnej sztuki, komiksów, tekstur do wyświetlania i kaszet. Krita obsługuje wiele przestrzeni barw takich jak RGB oraz CMYK dla kanałów 8 oraz 16 bitowych wyrażonych w l. całkowitych, a także 16 oraz 32 bitowych wyrażonych w l. zmiennoprzecinkowych.</p>
<p xml:lang="pt">O Krita é uma óptima escolha para criar arte conceptual, banda desenhada, texturas para desenho e pinturas. O Krita suporta diversos espaços de cores como o RGB e o CMYK com canais de cores inteiros a 8 e 16 bits, assim como canais de vírgula flutuante a 16 e a 32 bits.</p>
<p xml:lang="pt-BR">O Krita é uma ótima escolha para criação de arte conceitual, histórias em quadrinhos, texturas para desenhos e pinturas. O Krita tem suporte a diversos espaços de cores como RGB e CMYK com canais de cores inteiros de 8 e 16 bits, assim como canais de ponto flutuante de 16 e 32 bits.</p>
<p xml:lang="ru">Krita — отличный выбор для создания концепт-артов, комиксов, текстур для рендеринга и рисования. Она поддерживает множество цветовых пространств включая RGB и CMYK с 8 и 16 целыми битами на канал, а также 16 и 32 битами с плавающей запятой на канал.</p>
<p xml:lang="sk">Krita je výborná voľba pre vytváranie konceptového umenia, textúr na renderovanie a matné kresby. Krita podporuje mnoho farebných priestorov ako RGB a CMYK na 8 a 16 bitových celočíselných kanáloch ako aj 16 a 32 bitových reálnych kanáloch.</p>
<p xml:lang="sv">Krita är ett utmärkt val för att skapa concept art, serier, strukturer för återgivning och bakgrundsmålningar. Krita stöder många färgrymder som RGB och CMYK med 8- och 16-bitars heltal, samt 16- och 32-bitars flyttal.</p>
<p xml:lang="tr">Krita, konsept sanat, çizgi roman, kaplama ve mat resimler için dokular oluşturmak için mükemmel bir seçimdir. Krita, 8 ve 16 bit tamsayı kanallarında RGB ve CMYK gibi birçok renk alanını ve 16 ve 32 bit kayan nokta kanallarını desteklemektedir.</p>
<p xml:lang="uk">Krita — чудовий інструмент для створення концептуального живопису, коміксів, текстур для моделей та декорацій. У Krita передбачено підтримку багатьох просторів кольорів, зокрема RGB та CMYK з 8-бітовими та 16-бітовими цілими значеннями, а також 16-бітовими та 32-бітовими значеннями з рухомою крапкою для каналів кольорів.</p>
<p xml:lang="x-test">xxKrita is a great choice for creating concept art, comics, textures for rendering and matte paintings. Krita supports many colorspaces like RGB and CMYK at 8 and 16 bits integer channels, as well as 16 and 32 bits floating point channels.xx</p>
<p xml:lang="zh-CN">Krita 是绘制概念美术、漫画、纹理和电影布景的理想选择。Krita 支持多种色彩空间,如 8 位和 16 位整数及 16 位和 32 位浮点的 RGB 和 CMYK 颜色模型。</p>
<p xml:lang="zh-TW">Krita 是創造概念藝術、漫畫、彩現紋理和場景繪畫的絕佳選擇。Krita 在 8 位元和 16 位元整數色版,以及 16 位元和 32 位元浮點色版中支援 RGB 和 CMYK 等多種色彩空間。</p>
<p>Have fun painting with the advanced brush engines, amazing filters and many handy features that make Krita enormously productive.</p>
<p xml:lang="bs">Zabavite se kreirajući napredne pogone četki, filtere i mnoge praktične osobine koje čine Krita vrlo produktivnim.</p>
<p xml:lang="ca">Gaudiu pintant amb els motors de pinzells avançats, els filtres impressionants i moltes funcionalitats útils que fan el Krita molt productiu.</p>
<p xml:lang="ca-valencia">Gaudiu pintant amb els motors de pinzells avançats, els filtres impressionants i moltes funcionalitats útils que fan el Krita molt productiu.</p>
<p xml:lang="el">Διασκεδάστε ζωγραφίζοντας με τις προηγμένες μηχανές πινέλων, με εκπληκτικά φίλτρα και πολλά εύκολης χρήσης χαρακτηριστικά που παρέχουν στο Krita εξαιρετικά αυξημένη παραγωγικότητα.</p>
<p xml:lang="en-GB">Have fun painting with the advanced brush engines, amazing filters and many handy features that make Krita enormously productive.</p>
<p xml:lang="es">Diviértase pintando con los avanzados motores de pinceles, los espectaculares filtros y muchas funcionalidades prácticas que hacen que Krita sea enormemente productivo.</p>
<p xml:lang="et">Joonistamise muudavad tunduvalt lõbusamaks võimsad pintslimootorid, imetabased filtrid ja veel paljud käepärased võimalused, mis muudavad Krita kasutaja tohutult tootlikuks.</p>
<p xml:lang="eu">Marrazten ondo pasa ezazu, isipu motor aurreratuekin, iragazki txundigarriekin eta eginbide praktiko ugariekin, zeintzuek Krita ikaragarri emankorra egiten duten.</p>
<p xml:lang="fi">Pidä hauskaa maalatessasi edistyneillä sivellinmoottoreilla, hämmästyttävillä suotimilla ja monilla muilla kätevillä ominaisuuksilla, jotka tekevät Kritasta tavattoman tehokkaan.</p>
<p xml:lang="fr">Amusez-vous à peindre avec les outils de brosse avancés, les filtres incroyables et les nombreuses fonctionnalités pratiques qui rendent Krita extrêmement productif.</p>
<p xml:lang="gl">Goza debuxando con motores de pincel avanzados, filtros fantásticos e moitas outras funcionalidades útiles que fan de Krita un programa extremadamente produtivo.</p>
<p xml:lang="ia">Amusa te a pinger con le motores de pincel avantiate, filtros stupende e multe characteristicas amical que face Krita enormemente productive.</p>
<p xml:lang="id">Bersenang-senanglah melukis dengan mesin kuas canggih, filter luar biasa dan banyak fitur berguna yang membuat Krita sangat produktif.</p>
<p xml:lang="it">Divertiti a dipingere con gli avanzati sistemi di pennelli, i sorprendenti filtri e molte altre utili caratteristiche che fanno di Krita un software enormemente produttivo.</p>
<p xml:lang="ja">Krita のソフトウェアとしての生産性を高めている先進的なブラシエンジンや素晴らしいフィルタのほか、便利な機能の数々をお楽しみください。</p>
<p xml:lang="nl">Veel plezier met schilderen met the geavanceerde penseel-engines, filters vol verbazing en vele handige mogelijkheden die maken dat Krita enorm productief is.</p>
<p xml:lang="pl">Baw się przy malowaniu przy użyciu zaawansowanych silników pędzli, zadziwiających filtrów i wielu innych przydatnych cech, które czynią z Krity bardzo produktywną.</p>
<p xml:lang="pt">Divirta-se a pintar com os motores de pincéis avançados, os filtros espantosos e muitas outras funcionalidades úteis que tornam o Krita altamente produtivo.</p>
<p xml:lang="pt-BR">Divirta-se pintando com os mecanismos de pincéis avançados, filtros maravilhosos e muitas outras funcionalidades úteis que tornam o Krita altamente produtivo.</p>
<p xml:lang="ru">Получайте удовольствие от использования особых кистевых движков, впечатляющих фильтров и множества других функций, делающих Krita сверхпродуктивной.</p>
<p xml:lang="sk">Užívajte si maľovanie s pokročilými kresliacimi enginmi, úžasnými filtrami a mnohými užitočnými funkciami, ktoré robia Kritu veľmi produktívnu.</p>
<p xml:lang="sv">Ha det så kul vid målning med de avancerade penselfunktionerna, fantastiska filtren och många praktiska funktioner som gör Krita så enormt produktiv.</p>
<p xml:lang="tr">Gelişmiş fırça motorları, şaşırtıcı filtreler ve Krita'yı son derece üretken yapan bir çok kullanışlı özellikli boya ile iyi eğlenceler.</p>
<p xml:lang="uk">Отримуйте задоволення від малювання за допомогою пензлів з найширшими можливостями, чудових фільтрів та багатьох зручних можливостей, які роблять Krita надзвичайно продуктивним засобом малювання.</p>
<p xml:lang="x-test">xxHave fun painting with the advanced brush engines, amazing filters and many handy features that make Krita enormously productive.xx</p>
<p xml:lang="zh-CN">Krita 具有功能强大的笔刷引擎、种类繁多的滤镜以及便于操作的交互设计,可让你尽情、高效地挥洒无限创意。</p>
<p xml:lang="zh-TW">使用先進的筆刷引擎、驚人的濾鏡和許多方便的功能來開心地繪畫,讓 Krita 擁有巨大的生產力。</p>
</description>
<url type="homepage">https://www.krita.org/</url>
<url type="faq">https://krita.org/about/faq/</url>
<url type="donation">https://krita.org/support-us/donations/</url>
<url type="help">https://docs.krita.org/Category:Tutorials</url>
<screenshots>
<screenshot type="default">
<image>https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_001.png</image>
</screenshot>
<screenshot type="default">
<image>https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_002.png</image>
</screenshot>
<screenshot type="default">
<image>https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_003.png</image>
</screenshot>
<screenshot type="default">
<image>https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_004.png</image>
</screenshot>
<screenshot type="default">
<image>https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_005.png</image>
</screenshot>
<screenshot type="default">
<image>https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_006.png</image>
</screenshot>
</screenshots>
<content_rating type="oars-1.0">
<content_attribute id="violence-cartoon">none</content_attribute>
<content_attribute id="violence-fantasy">none</content_attribute>
<content_attribute id="violence-realistic">none</content_attribute>
<content_attribute id="violence-bloodshed">none</content_attribute>
<content_attribute id="violence-sexual">none</content_attribute>
<content_attribute id="drugs-alcohol">none</content_attribute>
<content_attribute id="drugs-narcotics">none</content_attribute>
<content_attribute id="drugs-tobacco">none</content_attribute>
<content_attribute id="sex-nudity">none</content_attribute>
<content_attribute id="sex-themes">none</content_attribute>
<content_attribute id="language-profanity">none</content_attribute>
<content_attribute id="language-humor">none</content_attribute>
<content_attribute id="language-discrimination">none</content_attribute>
<content_attribute id="social-chat">none</content_attribute>
<content_attribute id="social-info">none</content_attribute>
<content_attribute id="social-audio">none</content_attribute>
<content_attribute id="social-location">none</content_attribute>
<content_attribute id="social-contacts">none</content_attribute>
<content_attribute id="money-purchasing">none</content_attribute>
<content_attribute id="money-gambling">none</content_attribute>
</content_rating>
<categories>
<category>Graphics</category>
</categories>
<project_group>KDE</project_group>
<provides>
<binary>krita</binary>
+ <id>org.kde.krita.desktop</id>
</provides>
<releases>
<release date="2018-01-11" version="4.0.0.51 (beta 1)"/>
</releases>
</component>
diff --git a/krita/org.kde.krita.desktop b/krita/org.kde.krita.desktop
index 0acd1b67a5..b4bf6bfeb3 100644
--- a/krita/org.kde.krita.desktop
+++ b/krita/org.kde.krita.desktop
@@ -1,151 +1,154 @@
[Desktop Entry]
Name=Krita
Name[af]=Krita
Name[ar]=كريتا
Name[bg]=Krita
Name[br]=Krita
Name[bs]=Krita
Name[ca]=Krita
Name[ca@valencia]=Krita
Name[cs]=Krita
Name[cy]=Krita
Name[da]=Krita
Name[de]=Krita
Name[el]=Krita
Name[en_GB]=Krita
Name[eo]=Krita
Name[es]=Krita
Name[et]=Krita
Name[eu]=Krita
Name[fi]=Krita
Name[fr]=Krita
Name[fy]=Krita
Name[ga]=Krita
Name[gl]=Krita
Name[he]=Krita
Name[hi]=केरिता
Name[hne]=केरिता
Name[hr]=Krita
Name[hu]=Krita
Name[ia]=Krita
Name[is]=Krita
Name[it]=Krita
Name[ja]=Krita
Name[kk]=Krita
Name[ko]=Krita
Name[lt]=Krita
Name[lv]=Krita
Name[mr]=क्रिटा
Name[ms]=Krita
Name[nb]=Krita
Name[nds]=Krita
Name[ne]=क्रिता
Name[nl]=Krita
Name[pl]=Krita
Name[pt]=Krita
Name[pt_BR]=Krita
Name[ro]=Krita
Name[ru]=Krita
Name[se]=Krita
Name[sk]=Krita
Name[sl]=Krita
Name[sv]=Krita
Name[ta]=கிரிட்டா
Name[tg]=Krita
Name[tr]=Krita
Name[ug]=Krita
Name[uk]=Krita
Name[uz]=Krita
Name[uz@cyrillic]=Krita
Name[wa]=Krita
Name[xh]=Krita
Name[x-test]=xxKritaxx
Name[zh_CN]=Krita
Name[zh_TW]=Krita
Exec=krita %F
GenericName=Digital Painting
GenericName[ar]=رسم رقمي
GenericName[bs]=Digitalno Bojenje
GenericName[ca]=Dibuix digital
GenericName[ca@valencia]=Dibuix digital
GenericName[cs]=Digitální malování
GenericName[da]=Digital tegning
GenericName[de]=Digitales Malen
GenericName[el]=Ψηφιακή ζωγραφική
GenericName[en_GB]=Digital Painting
GenericName[es]=Pintura digital
GenericName[et]=Digitaalne joonistamine
GenericName[eu]=Margolan digitala
GenericName[fi]=Digitaalimaalaus
GenericName[fr]=Peinture numérique
GenericName[gl]=Debuxo dixital
GenericName[hu]=Digitális festészet
GenericName[ia]=Pintura Digital
GenericName[is]=Stafræn málun
GenericName[it]=Pittura digitale
GenericName[ja]=デジタルペインティング
GenericName[kk]=Цифрлық сурет салу
GenericName[lt]=Skaitmeninis piešimas
GenericName[mr]=डिजिटल पेंटिंग
GenericName[nb]=Digital maling
GenericName[nl]=Digitaal schilderen
GenericName[pl]=Cyfrowe malowanie
GenericName[pt]=Pintura Digital
GenericName[pt_BR]=Pintura digital
GenericName[ru]=Цифровая живопись
GenericName[sk]=Digitálne maľovanie
GenericName[sl]=Digitalno slikanje
GenericName[sv]=Digital målning
GenericName[tr]=Sayısal Boyama
GenericName[ug]=سىفىرلىق رەسىم سىزغۇ
GenericName[uk]=Цифрове малювання
GenericName[x-test]=xxDigital Paintingxx
GenericName[zh_CN]=数字绘画
GenericName[zh_TW]=數位繪畫
MimeType=application/x-krita;image/openraster;application/x-krita-paintoppreset;
Comment=Digital Painting
Comment[ar]=رسم رقمي
Comment[bs]=Digitalno Bojenje
Comment[ca]=Dibuix digital
Comment[ca@valencia]=Dibuix digital
Comment[cs]=Digitální malování
Comment[da]=Digital tegning
Comment[de]=Digitales Malen
Comment[el]=Ψηφιακή ζωγραφική
Comment[en_GB]=Digital Painting
Comment[es]=Pintura digital
Comment[et]=Digitaalne joonistamine
Comment[eu]=Margolan digitala
Comment[fi]=Digitaalimaalaus
Comment[fr]=Peinture numérique
Comment[gl]=Debuxo dixital.
Comment[hu]=Digitális festészet
Comment[ia]=Pintura Digital
Comment[is]=Stafræn málun
Comment[it]=Pittura digitale
Comment[ja]=デジタルペインティング
Comment[kk]=Цифрлық сурет салу
Comment[lt]=Skaitmeninis piešimas
Comment[mr]=डिजिटल पेंटिंग
Comment[nb]=Digital maling
Comment[nl]=Digitaal schilderen
Comment[pl]=Cyfrowe malowanie
Comment[pt]=Pintura Digital
Comment[pt_BR]=Pintura digital
Comment[ru]=Цифровая живопись
Comment[sk]=Digitálne maľovanie
Comment[sl]=Digitalno slikanje
Comment[sv]=Digitalt målningsverktyg
Comment[tr]=Sayısal Boyama
Comment[ug]=سىفىرلىق رەسىم سىزغۇ
Comment[uk]=Цифрове малювання
Comment[x-test]=xxDigital Paintingxx
Comment[zh_CN]=数字绘画
Comment[zh_TW]=數位繪畫
Type=Application
Icon=calligrakrita
Categories=Qt;KDE;Graphics;
X-KDE-NativeMimeType=application/x-krita
X-KDE-ExtraNativeMimeTypes=
StartupNotify=true
X-Krita-Version=28
+StartupWMClass=krita
+# Always be the preferred handler for .kra files
+InitialPreference=99
diff --git a/libs/brush/kis_abr_brush_collection.cpp b/libs/brush/kis_abr_brush_collection.cpp
index cf1a48404d..e0d08671aa 100644
--- a/libs/brush/kis_abr_brush_collection.cpp
+++ b/libs/brush/kis_abr_brush_collection.cpp
@@ -1,627 +1,628 @@
/*
* Copyright (c) 2010 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
* Copyright (c) 2007 Eric Lamarque <eric.lamarque@free.fr>
*
* 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 <QtEndian>
#include "kis_abr_brush_collection.h"
#include "kis_abr_brush.h"
#include <QDomElement>
#include <QFile>
#include <QImage>
#include <QPoint>
#include <QColor>
#include <QByteArray>
#include <kis_debug.h>
#include <QString>
#include <QBuffer>
#include <klocalizedstring.h>
#include <KoColor.h>
struct AbrInfo {
//big endian
short version;
short subversion;
// count of the images (brushes) in the abr file
short count;
};
/// save the QImages as png files to directory image_tests
static QImage convertToQImage(char * buffer, qint32 width, qint32 height)
{
// create 8-bit indexed image
QImage img(width, height, QImage::Format_RGB32);
int pos = 0;
int value = 0;
for (int y = 0; y < height; y++) {
QRgb *pixel = reinterpret_cast<QRgb *>(img.scanLine(y));
for (int x = 0; x < width; x++, pos++) {
value = 255 - buffer[pos];
pixel[x] = qRgb(value, value , value);
}
}
return img;
}
static qint32 rle_decode(QDataStream & abr, char *buffer, qint32 height)
{
qint32 n;
char ptmp;
char ch;
int i, j, c;
short *cscanline_len;
char *data = buffer;
// read compressed size foreach scanline
cscanline_len = new short[ height ];
for (i = 0; i < height; i++) {
// short
abr >> cscanline_len[i];
}
// unpack each scanline data
for (i = 0; i < height; i++) {
for (j = 0; j < cscanline_len[i];) {
// char
if (!abr.device()->getChar(&ptmp)) {
break;
}
n = ptmp;
j++;
if (n >= 128) // force sign
n -= 256;
if (n < 0) { // copy the following char -n + 1 times
if (n == -128) // it's a nop
continue;
n = -n + 1;
// char
if (!abr.device()->getChar(&ch)) {
break;
}
j++;
for (c = 0; c < n; c++, data++) {
*data = ch;
}
}
else {
// read the following n + 1 chars (no compr)
for (c = 0; c < n + 1; c++, j++, data++) {
// char
if (!abr.device()->getChar(data)) {
break;
}
}
}
}
}
delete [] cscanline_len;
return 0;
}
static QString abr_v1_brush_name(const QString filename, qint32 id)
{
QString result = filename;
int pos = filename.lastIndexOf('.');
result.remove(pos, 4);
QTextStream(&result) << "_" << id;
return result;
}
static bool abr_supported_content(AbrInfo *abr_hdr)
{
switch (abr_hdr->version) {
case 1:
case 2:
return true;
break;
case 6:
if (abr_hdr->subversion == 1 || abr_hdr->subversion == 2)
return true;
break;
}
return false;
}
static bool abr_reach_8BIM_section(QDataStream & abr, const QString name)
{
char tag[4];
char tagname[5];
qint32 section_size = 0;
int r;
// find 8BIMname section
while (!abr.atEnd()) {
r = abr.readRawData(tag, 4);
if (r != 4) {
warnKrita << "Error: Cannot read 8BIM tag ";
return false;
}
if (strncmp(tag, "8BIM", 4)) {
warnKrita << "Error: Start tag not 8BIM but " << (int)tag[0] << (int)tag[1] << (int)tag[2] << (int)tag[3] << " at position " << abr.device()->pos();
return false;
}
r = abr.readRawData(tagname, 4);
if (r != 4) {
warnKrita << "Error: Cannot read 8BIM tag name";
return false;
}
tagname[4] = '\0';
QString s1 = QString::fromLatin1(tagname, 4);
if (!s1.compare(name)) {
return true;
}
// long
abr >> section_size;
abr.device()->seek(abr.device()->pos() + section_size);
}
return true;
}
static qint32 find_sample_count_v6(QDataStream & abr, AbrInfo *abr_info)
{
qint64 origin;
qint32 sample_section_size;
qint32 sample_section_end;
qint32 samples = 0;
qint32 data_start;
qint32 brush_size;
qint32 brush_end;
if (!abr_supported_content(abr_info))
return 0;
origin = abr.device()->pos();
if (!abr_reach_8BIM_section(abr, "samp")) {
// reset to origin
abr.device()->seek(origin);
return 0;
}
// long
abr >> sample_section_size;
sample_section_end = sample_section_size + abr.device()->pos();
if(sample_section_end < 0 || sample_section_end > abr.device()->size())
return 0;
data_start = abr.device()->pos();
while ((!abr.atEnd()) && (abr.device()->pos() < sample_section_end)) {
// read long
abr >> brush_size;
brush_end = brush_size;
// complement to 4
while (brush_end % 4 != 0) brush_end++;
qint64 newPos = abr.device()->pos() + brush_end;
if(newPos > 0 && newPos < abr.device()->size()) {
abr.device()->seek(newPos);
}
else
return 0;
samples++;
}
// set stream to samples data
abr.device()->seek(data_start);
//dbgKrita <<"samples : "<< samples;
return samples;
}
static bool abr_read_content(QDataStream & abr, AbrInfo *abr_hdr)
{
abr >> abr_hdr->version;
abr_hdr->subversion = 0;
abr_hdr->count = 0;
switch (abr_hdr->version) {
case 1:
case 2:
abr >> abr_hdr->count;
break;
case 6:
abr >> abr_hdr->subversion;
abr_hdr->count = find_sample_count_v6(abr, abr_hdr);
break;
default:
// unknown versions
break;
}
// next bytes in abr are samples data
return true;
}
static QString abr_read_ucs2_text(QDataStream & abr)
{
quint32 name_size;
quint32 buf_size;
uint i;
/* two-bytes characters encoded (UCS-2)
* format:
* long : size - number of characters in string
* data : zero terminated UCS-2 string
*/
// long
abr >> name_size;
if (name_size == 0) {
return QString();
}
//buf_size = name_size * 2;
buf_size = name_size;
//name_ucs2 = (char*) malloc (buf_size * sizeof (char));
//name_ucs2 = new char[buf_size];
ushort * name_ucs2 = new ushort[buf_size];
for (i = 0; i < buf_size ; i++) {
//* char*/
//abr >> name_ucs2[i];
// I will use ushort as that is input to fromUtf16
abr >> name_ucs2[i];
}
QString name_utf8 = QString::fromUtf16(name_ucs2, buf_size);
delete [] name_ucs2;
return name_utf8;
}
quint32 KisAbrBrushCollection::abr_brush_load_v6(QDataStream & abr, AbrInfo *abr_hdr, const QString filename, qint32 image_ID, qint32 id)
{
Q_UNUSED(image_ID);
qint32 brush_size = 0;
qint32 brush_end = 0;
qint32 next_brush = 0;
qint32 top, left, bottom, right;
top = left = bottom = right = 0;
short depth;
char compression;
qint32 width = 0;
qint32 height = 0;
qint32 size = 0;
qint32 layer_ID = -1;
char *buffer;
abr >> brush_size;
brush_end = brush_size;
// complement to 4
while (brush_end % 4 != 0) {
brush_end++;
}
next_brush = abr.device()->pos() + brush_end;
// discard key
abr.device()->seek(abr.device()->pos() + 37);
if (abr_hdr->subversion == 1)
// discard short coordinates and unknown short
abr.device()->seek(abr.device()->pos() + 10);
else
// discard unknown bytes
abr.device()->seek(abr.device()->pos() + 264);
// long
abr >> top;
abr >> left;
abr >> bottom;
abr >> right;
// short
abr >> depth;
// char
abr.device()->getChar(&compression);
width = right - left;
height = bottom - top;
size = width * (depth >> 3) * height;
// remove .abr and add some id, so something like test.abr -> test_12345
QString name = abr_v1_brush_name(filename, id);
buffer = (char*)malloc(size);
// data decoding
if (!compression) {
// not compressed - read raw bytes as brush data
//fread (buffer, size, 1, abr);
abr.readRawData(buffer, size);
} else {
rle_decode(abr, buffer, height);
}
if (width < quint16_MAX && height < quint16_MAX) {
// filename - filename of the file , e.g. test.abr
// name - test_number_of_the_brush, e.g test_1, test_2
KisAbrBrush* abrBrush = 0;
if (m_abrBrushes.contains(name)) {
abrBrush = m_abrBrushes[name];
}
else {
abrBrush = new KisAbrBrush(name, this);
abrBrush->setMD5(md5());
}
abrBrush->setBrushTipImage(convertToQImage(buffer, width, height));
// XXX: call extra setters on abrBrush for other options of ABR brushes
abrBrush->setValid(true);
abrBrush->setName(name);
m_abrBrushes[name] = abrBrush;
}
free(buffer);
abr.device()->seek(next_brush);
layer_ID = id;
return layer_ID;
}
qint32 KisAbrBrushCollection::abr_brush_load_v12(QDataStream & abr, AbrInfo *abr_hdr, const QString filename, qint32 image_ID, qint32 id)
{
Q_UNUSED(image_ID);
short brush_type;
qint32 brush_size;
qint32 next_brush;
qint32 top, left, bottom, right;
qint16 depth;
char compression;
QString name;
qint32 width, height;
qint32 size;
qint32 layer_ID = -1;
char *buffer;
// short
abr >> brush_type;
// long
abr >> brush_size;
next_brush = abr.device()->pos() + brush_size;
if (brush_type == 1) {
// computed brush
// FIXME: support it!
warnKrita << "WARNING: computed brush unsupported, skipping.";
abr.device()->seek(abr.device()->pos() + next_brush);
// TODO: test also this one abr.skipRawData(next_brush);
}
else if (brush_type == 2) {
// sampled brush
// discard 4 misc bytes and 2 spacing bytes
abr.device()->seek(abr.device()->pos() + 6);
if (abr_hdr->version == 2)
name = abr_read_ucs2_text(abr);
if (name.isNull()) {
name = abr_v1_brush_name(filename, id);
}
// discard 1 byte for antialiasing and 4 x short for short bounds
abr.device()->seek(abr.device()->pos() + 9);
// long
abr >> top;
abr >> left;
abr >> bottom;
abr >> right;
// short
abr >> depth;
// char
abr.device()->getChar(&compression);
width = right - left;
height = bottom - top;
size = width * (depth >> 3) * height;
/* FIXME: support wide brushes */
if (height > 16384) {
warnKrita << "WARNING: wide brushes not supported";
abr.device()->seek(next_brush);
}
else {
buffer = (char*)malloc(size);
if (!compression) {
// not compressed - read raw bytes as brush data
abr.readRawData(buffer, size);
} else {
rle_decode(abr, buffer, height);
}
KisAbrBrush* abrBrush = 0;
if (m_abrBrushes.contains(name)) {
abrBrush = m_abrBrushes[name];
}
else {
abrBrush = new KisAbrBrush(name, this);
abrBrush->setMD5(md5());
}
abrBrush->setBrushTipImage(convertToQImage(buffer, width, height));
// XXX: call extra setters on abrBrush for other options of ABR brushes free (buffer);
abrBrush->setValid(true);
abrBrush->setName(name);
m_abrBrushes[name] = abrBrush;
layer_ID = 1;
}
}
else {
warnKrita << "Unknown ABR brush type, skipping.";
abr.device()->seek(next_brush);
}
return layer_ID;
}
qint32 KisAbrBrushCollection::abr_brush_load(QDataStream & abr, AbrInfo *abr_hdr, const QString filename, qint32 image_ID, qint32 id)
{
qint32 layer_ID = -1;
switch (abr_hdr->version) {
case 1:
+ Q_FALLTHROUGH();
// fall through, version 1 and 2 are compatible
case 2:
layer_ID = abr_brush_load_v12(abr, abr_hdr, filename, image_ID, id);
break;
case 6:
layer_ID = abr_brush_load_v6(abr, abr_hdr, filename, image_ID, id);
break;
}
return layer_ID;
}
KisAbrBrushCollection::KisAbrBrushCollection(const QString& filename)
: KisScalingSizeBrush(filename)
{
}
KisAbrBrushCollection::KisAbrBrushCollection(const KisAbrBrushCollection& rhs)
: KisScalingSizeBrush(rhs)
{
for (auto it = rhs.m_abrBrushes.begin();
it != rhs.m_abrBrushes.end();
++it) {
m_abrBrushes.insert(it.key(), new KisAbrBrush(*it.value(), this));
}
}
KisBrush* KisAbrBrushCollection::clone() const
{
return new KisAbrBrushCollection(*this);
}
bool KisAbrBrushCollection::load()
{
QFile file(filename());
// check if the file is open correctly
if (!file.open(QIODevice::ReadOnly)) {
warnKrita << "Can't open file " << filename();
return false;
}
bool res = loadFromDevice(&file);
file.close();
return res;
}
bool KisAbrBrushCollection::loadFromDevice(QIODevice *dev)
{
AbrInfo abr_hdr;
qint32 image_ID;
int i;
qint32 layer_ID;
QByteArray ba = dev->readAll();
QBuffer buf(&ba);
buf.open(QIODevice::ReadOnly);
QDataStream abr(&buf);
if (!abr_read_content(abr, &abr_hdr)) {
warnKrita << "Error: cannot parse ABR file: " << filename();
return false;
}
if (!abr_supported_content(&abr_hdr)) {
warnKrita << "ERROR: unable to decode abr format version " << abr_hdr.version << "(subver " << abr_hdr.subversion << ")";
return false;
}
if (abr_hdr.count == 0) {
errKrita << "ERROR: no sample brush found in " << filename();
return false;
}
image_ID = 123456;
for (i = 0; i < abr_hdr.count; i++) {
layer_ID = abr_brush_load(abr, &abr_hdr, shortFilename(), image_ID, i + 1);
if (layer_ID == -1) {
warnKrita << "Warning: problem loading brush #" << i << " in " << filename();
}
}
return true;
}
bool KisAbrBrushCollection::save()
{
return false;
}
bool KisAbrBrushCollection::saveToDevice(QIODevice */*dev*/) const
{
return false;
}
QImage KisAbrBrushCollection::image() const
{
return QImage();
}
void KisAbrBrushCollection::toXML(QDomDocument& d, QDomElement& e) const
{
Q_UNUSED(d);
Q_UNUSED(e);
// Do nothing...
}
QString KisAbrBrushCollection::defaultFileExtension() const
{
return QString(".abr");
}
diff --git a/libs/brush/kis_brush.h b/libs/brush/kis_brush.h
index 42935fe01b..b7c8af4a68 100644
--- a/libs/brush/kis_brush.h
+++ b/libs/brush/kis_brush.h
@@ -1,397 +1,397 @@
/*
* Copyright (c) 1999 Matthias Elter <me@kde.org>
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
*
* 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_BRUSH_
#define KIS_BRUSH_
#include <QImage>
#include <resources/KoResource.h>
#include <kis_types.h>
#include <kis_shared.h>
#include <kis_dab_shape.h>
#include <kritabrush_export.h>
class KisQImagemask;
typedef KisSharedPtr<KisQImagemask> KisQImagemaskSP;
class QString;
class KoColor;
class KoColorSpace;
class KisPaintInformation;
class KisBoundary;
class KisPaintopLodLimitations;
enum enumBrushType {
INVALID,
MASK,
IMAGE,
PIPE_MASK,
PIPE_IMAGE
};
static const qreal DEFAULT_SOFTNESS_FACTOR = 1.0;
class KisBrush;
typedef KisSharedPtr<KisBrush> KisBrushSP;
/**
* KisBrush is the base class for brush resources. A brush resource
* defines one or more images that are used to potato-stamp along
* the drawn path. The brush type defines how this brush is used --
* the important difference is between masks (which take the current
* painting color) and images (which do not). It is up to the paintop
* to make use of this feature.
*
* Brushes must be serializable to an xml representation and provide
* a factory class that can recreate or retrieve the brush based on
* this representation.
*
* XXX: This api is still a big mess -- it needs a good refactoring.
* And the whole KoResource architecture is way over-designed.
*/
class BRUSH_EXPORT KisBrush : public KoResource, public KisShared
{
public:
class ColoringInformation
{
public:
virtual ~ColoringInformation();
virtual const quint8* color() const = 0;
virtual void nextColumn() = 0;
virtual void nextRow() = 0;
};
protected:
class PlainColoringInformation : public ColoringInformation
{
public:
PlainColoringInformation(const quint8* color);
~PlainColoringInformation() override;
const quint8* color() const override ;
void nextColumn() override;
void nextRow() override;
private:
const quint8* m_color;
};
class PaintDeviceColoringInformation : public ColoringInformation
{
public:
PaintDeviceColoringInformation(const KisPaintDeviceSP source, int width);
~PaintDeviceColoringInformation() override;
const quint8* color() const override ;
void nextColumn() override;
void nextRow() override;
private:
const KisPaintDeviceSP m_source;
KisHLineConstIteratorSP m_iterator;
};
public:
KisBrush();
KisBrush(const QString& filename);
~KisBrush() override;
virtual qreal userEffectiveSize() const = 0;
virtual void setUserEffectiveSize(qreal value) = 0;
bool load() override {
return false;
}
bool loadFromDevice(QIODevice *) override {
return false;
}
bool save() override {
return false;
}
bool saveToDevice(QIODevice* ) const override {
return false;
}
/**
* @brief brushImage the image the brush tip can paint with. Not all brush types have a single
* image.
* @return a valid QImage.
*/
virtual QImage brushTipImage() const;
/**
* Change the spacing of the brush.
* @param spacing a spacing of 1.0 means that strokes will be separated from one time the size
* of the brush.
*/
virtual void setSpacing(double spacing);
/**
* @return the spacing between two strokes for this brush
*/
double spacing() const;
void setAutoSpacing(bool active, qreal coeff);
bool autoSpacingActive() const;
qreal autoSpacingCoeff() const;
/**
* @return the width (for scale == 1.0)
*/
qint32 width() const;
/**
* @return the height (for scale == 1.0)
*/
qint32 height() const;
/**
* @return the width of the mask for the given scale and angle
*/
virtual qint32 maskWidth(KisDabShape const&, qreal subPixelX, qreal subPixelY, const KisPaintInformation& info) const;
/**
* @return the height of the mask for the given scale and angle
*/
virtual qint32 maskHeight(KisDabShape const&, qreal subPixelX, qreal subPixelY, const KisPaintInformation& info) const;
/**
* @return the logical size of the brush, that is the size measured
* in floating point value.
*
* This value should not be used for calculating future dab sizes
* because it doesn't take any rounding into account. The only use
* of this metric is calculation of brush-size derivatives like
* hotspots and spacing.
*/
virtual QSizeF characteristicSize(KisDabShape const&) const;
/**
* @return the angle of the mask adding the given angle
*/
double maskAngle(double angle = 0) const;
/**
* @return the index of the brush
* if the brush consists of multiple images
*/
virtual quint32 brushIndex(const KisPaintInformation& info) const;
/**
* The brush type defines how the brush is used.
*/
virtual enumBrushType brushType() const;
QPointF hotSpot(KisDabShape const&, const KisPaintInformation& info) const;
/**
* Returns true if this brush can return something useful for the info. This is used
* by Pipe Brushes that can't paint sometimes
**/
virtual bool canPaintFor(const KisPaintInformation& /*info*/);
/**
* Is called by the paint op when a paintop starts a stroke. The
* point is that we store brushes a server while the paint ops are
* are recreated all the time. Is means that upon a stroke start
* the brushes may need to clear its state.
*/
virtual void notifyStrokeStarted();
/**
* Is called by the cache, when cache hit has happened.
* Having got this notification the brush can update the counters
* of dabs, generate some new random values if needed.
*
* * NOTE: one should use **either** notifyCachedDabPainted() or prepareForSeqNo()
*
* Currently, this is used by pipe'd brushes to implement
* incremental and random parasites
*/
virtual void notifyCachedDabPainted(const KisPaintInformation& info);
/**
* Is called by the multithreaded queue to prepare a specific brush
* tip for the particular seqNo.
*
* NOTE: one should use **either** notifyCachedDabPainted() or prepareForSeqNo()
*
* Currently, this is used by pipe'd brushes to implement
* incremental and random parasites
*/
virtual void prepareForSeqNo(const KisPaintInformation& info, int seqNo);
/**
* Notify the brush if it can use QtConcurrent's threading capabilities in its
* internal routines. By default it is allowed, but some paintops (who do their
* own multithreading) may ask the brush to avoid internal threading.
*/
void setThreadingAllowed(bool value);
/**
* \see setThreadingAllowed() for details
*/
bool threadingAllowed() const;
/**
* Return a fixed paint device that contains a correctly scaled image dab.
*/
virtual KisFixedPaintDeviceSP paintDevice(const KoColorSpace * colorSpace,
KisDabShape const&,
const KisPaintInformation& info,
double subPixelX = 0, double subPixelY = 0) const;
/**
* clear dst fill it with a mask colored with KoColor
*/
void mask(KisFixedPaintDeviceSP dst,
const KoColor& color,
KisDabShape const& shape,
const KisPaintInformation& info,
double subPixelX = 0, double subPixelY = 0, qreal softnessFactor = DEFAULT_SOFTNESS_FACTOR) const;
/**
* clear dst and fill it with a mask colored with the corresponding colors of src
*/
void mask(KisFixedPaintDeviceSP dst,
const KisPaintDeviceSP src,
KisDabShape const& shape,
const KisPaintInformation& info,
double subPixelX = 0, double subPixelY = 0, qreal softnessFactor = DEFAULT_SOFTNESS_FACTOR) const;
virtual bool hasColor() const;
/**
* Create a mask and either mask dst (that is, change all alpha values of the
* existing pixels to those of the mask) or, if coloringInfo is present, clear
* dst and fill dst with pixels according to coloringInfo, masked according to the
* generated mask.
*
* @param dst the destination that will be draw on the image, and this function
* will edit its alpha channel
* @param coloringInfo coloring information that will be copied on the dab, it can be null
- * @param scale a scale applied on the alpha mask
- * @param angle a rotation applied on the alpha mask
+ * @param shape a shape applied on the alpha mask
* @param info the painting information (this is only and should only be used by
* KisImagePipeBrush and only to be backward compatible with the Gimp,
* KisImagePipeBrush is ignoring scale and angle information)
* @param subPixelX sub position of the brush (contained between 0.0 and 1.0)
* @param subPixelY sub position of the brush (contained between 0.0 and 1.0)
+ * @param softnessFactor softness factor of the brush
*
* @return a mask computed from the grey-level values of the
* pixels in the brush.
*/
virtual void generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst,
ColoringInformation* coloringInfo,
KisDabShape const&,
const KisPaintInformation& info,
double subPixelX = 0, double subPixelY = 0, qreal softnessFactor = DEFAULT_SOFTNESS_FACTOR) const;
/**
* Serialize this brush to XML.
*/
virtual void toXML(QDomDocument& , QDomElement&) const;
static KisBrushSP fromXML(const QDomElement& element);
virtual const KisBoundary* boundary() const;
virtual QPainterPath outline() const;
virtual void setScale(qreal _scale);
qreal scale() const;
virtual void setAngle(qreal _angle);
qreal angle() const;
void clearBrushPyramid();
virtual void lodLimitations(KisPaintopLodLimitations *l) const;
virtual KisBrush* clone() const = 0;
protected:
KisBrush(const KisBrush& rhs);
void setWidth(qint32 width);
void setHeight(qint32 height);
void setHotSpot(QPointF);
/**
* XXX
*/
virtual void setBrushType(enumBrushType type);
virtual void setHasColor(bool hasColor);
public:
/**
* The image is used to represent the brush in the gui, and may also, depending on the brush type
* be used to define the actual brush instance.
*/
virtual void setBrushTipImage(const QImage& image);
/**
* Returns true if the brush has a bunch of pixels almost
* fully transparent in the very center. If the brush is pierced,
* then dulling mode may not work correctly due to empty samples.
*
* WARNING: this method is relatively expensive since it iterates
* up to 100 pixels of the brush.
*/
bool isPiercedApprox() const;
protected:
void resetBoundary();
void predefinedBrushToXML(const QString &type, QDomElement& e) const;
private:
// Initialize our boundary
void generateBoundary() const;
struct Private;
Private* const d;
};
#endif // KIS_BRUSH_
diff --git a/libs/command/kis_command_utils.h b/libs/command/kis_command_utils.h
index 098a893f14..3e3512073c 100644
--- a/libs/command/kis_command_utils.h
+++ b/libs/command/kis_command_utils.h
@@ -1,147 +1,147 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_COMMAND_UTILS_H
#define __KIS_COMMAND_UTILS_H
#include "kundo2command.h"
#include "kis_undo_stores.h"
#include "kritacommand_export.h"
#include <functional>
namespace KisCommandUtils
{
/**
* @brief The AggregateCommand struct is a command with delayed
* initialization. On first redo() populateChildCommands() is called
* and the descendants add the desired commands to the internal list.
* After that, the added commands are executed on every undo()/redo().
*
* This structure is used when the commands should be populated from
* the context of the stroke, not from the GUI thread.
*/
struct KRITACOMMAND_EXPORT AggregateCommand : public KUndo2Command {
AggregateCommand(KUndo2Command *parent = 0);
AggregateCommand(const KUndo2MagicString &text,
KUndo2Command *parent = 0);
void redo() override;
void undo() override;
protected:
virtual void populateChildCommands() = 0;
void addCommand(KUndo2Command *cmd);
private:
bool m_firstRedo;
KisSurrogateUndoStore m_store;
};
/**
* @brief The LambdaCommand struct is a shorthand for creation of
* AggregateCommand commands using C++ lambda feature. Just pass
* a lambda object into a command and it will be called from within
* the context of the strokes thread to populate the command content.
*/
struct KRITACOMMAND_EXPORT LambdaCommand : public AggregateCommand {
LambdaCommand(std::function<KUndo2Command*()> createCommandFunc);
LambdaCommand(const KUndo2MagicString &text,
std::function<KUndo2Command*()> createCommandFunc);
LambdaCommand(const KUndo2MagicString &text,
KUndo2Command *parent,
std::function<KUndo2Command*()> createCommandFunc);
LambdaCommand(KUndo2Command *parent,
std::function<KUndo2Command*()> createCommandFunc);
protected:
void populateChildCommands() override;
private:
std::function<KUndo2Command*()> m_createCommandFunc;
};
struct KRITACOMMAND_EXPORT SkipFirstRedoWrapper : public KUndo2Command {
SkipFirstRedoWrapper(KUndo2Command *child = 0, KUndo2Command *parent = 0);
void redo() override;
void undo() override;
private:
bool m_firstRedo;
QScopedPointer<KUndo2Command> m_child;
};
struct KRITACOMMAND_EXPORT SkipFirstRedoBase : public KUndo2Command {
SkipFirstRedoBase(bool skipFirstRedo, KUndo2Command *parent = 0);
SkipFirstRedoBase(bool skipFirstRedo, const KUndo2MagicString &text, KUndo2Command *parent = 0);
- void redo() final;
- void undo() final;
+ void redo() override final;
+ void undo() override final;
void setSkipOneRedo(bool value);
protected:
virtual void redoImpl() = 0;
virtual void undoImpl() = 0;
private:
bool m_firstRedo;
};
struct KRITACOMMAND_EXPORT FlipFlopCommand : public KUndo2Command {
enum State {
INITIALIZING, // Before redo; after undo.
FINALIZING // After redo; before undo.
};
FlipFlopCommand(State initialState, KUndo2Command *parent = 0);
FlipFlopCommand(bool finalizing = false, KUndo2Command *parent = 0);
void redo() override; // partA -> redo command -> partB
void undo() override; // partB -> undo command -> partA
protected:
virtual void partA();
virtual void partB();
State getState() const { return m_currentState; }
bool isFirstRedo() const { return m_firstRedo; }
private:
State m_currentState;
bool m_firstRedo;
};
struct KRITACOMMAND_EXPORT CompositeCommand : public KUndo2Command {
CompositeCommand(KUndo2Command *parent = 0);
~CompositeCommand() override;
void addCommand(KUndo2Command *cmd);
void redo() override;
void undo() override;
private:
QVector<KUndo2Command*> m_commands;
};
}
#endif /* __KIS_COMMAND_UTILS_H */
diff --git a/libs/flake/KoCanvasControllerWidget.h b/libs/flake/KoCanvasControllerWidget.h
index 37777b6c6f..f134fed94c 100644
--- a/libs/flake/KoCanvasControllerWidget.h
+++ b/libs/flake/KoCanvasControllerWidget.h
@@ -1,191 +1,193 @@
/* This file is part of the KDE project
* Copyright (C) 2006, 2008 Thomas Zander <zander@kde.org>
* Copyright (C) 2007-2010 Boudewijn Rempt <boud@valdyas.org>
* Copyright (C) 2007-2008 C. Boemann <cbo@boemann.dk>
* Copyright (C) 2006-2007 Jan Hambrecht <jaham@gmx.net>
* Copyright (C) 2009 Thorsten Zachmann <zachmann@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOCANVASCONTROLLERWIDGET_H
#define KOCANVASCONTROLLERWIDGET_H
#include "kritaflake_export.h"
#include <QAbstractScrollArea>
#include <QPointer>
#include "KoCanvasController.h"
class KoShape;
class KoCanvasBase;
/**
* KoCanvasController implementation for QWidget based canvases
*/
class KRITAFLAKE_EXPORT KoCanvasControllerWidget : public QAbstractScrollArea, public KoCanvasController
{
Q_OBJECT
public:
/**
* Constructor.
+ * @param actionCollection the action collection for this widget
* @param parent the parent this widget will belong to
*/
explicit KoCanvasControllerWidget(KActionCollection * actionCollection, QWidget *parent = 0);
~KoCanvasControllerWidget() override;
/**
* Reimplemented from QAbstractScrollArea.
*/
void scrollContentsBy(int dx, int dy) override;
QSize viewportSize() const override;
/// Reimplemented from KoCanvasController
/**
* Activate this canvascontroller
*/
virtual void activate();
void setCanvas(KoCanvasBase *canvas) override;
KoCanvasBase *canvas() const override;
/**
* Change the actual canvas widget used by the current canvas. This allows the canvas widget
* to be changed while keeping the current KoCanvasBase canvas and its associated resources as
* they are. This might be used, for example, to switch from a QWidget to a QOpenGLWidget canvas.
* @param widget the new canvas widget.
*/
virtual void changeCanvasWidget(QWidget *widget);
int visibleHeight() const override;
int visibleWidth() const override;
int canvasOffsetX() const override;
int canvasOffsetY() const override;
void ensureVisible(const QRectF &rect, bool smooth = false) override;
void ensureVisible(KoShape *shape) override;
/**
* will cause the toolOptionWidgetsChanged to be emitted and all
* listeners to be updated to the new widget.
*
* FIXME: This doesn't belong her and it does an
* inherits("KoView") so it too much tied to komain
*
* @param widgets the map of widgets
*/
void setToolOptionWidgets(const QList<QPointer<QWidget> > &widgets);
void zoomIn(const QPoint &center) override;
void zoomOut(const QPoint &center) override;
void zoomBy(const QPoint &center, qreal zoom) override;
void zoomTo(const QRect &rect) override;
/**
* Zoom document keeping point \p widgetPoint unchanged
* \param widgetPoint sticky point in widget pixels
+ * \param zoomCoeff the zoom
*/
virtual void zoomRelativeToPoint(const QPoint &widgetPoint, qreal zoomCoeff);
void recenterPreferred() override;
void setPreferredCenter(const QPointF &viewPoint) override;
/// Returns the currently set preferred center point in view coordinates (pixels)
QPointF preferredCenter() const override;
void pan(const QPoint &distance) override;
virtual void panUp() override;
virtual void panDown() override;
virtual void panLeft() override;
virtual void panRight() override;
void setMargin(int margin) override;
QPoint scrollBarValue() const override;
/**
* Used by KisCanvasController to correct the scrollbars position
* after the rotation.
*/
void setScrollBarValue(const QPoint &value) override;
void updateDocumentSize(const QSize &sz, bool recalculateCenter = true) override;
/**
* Set mouse wheel to zoom behaviour
* @param zoom if true wheel will zoom instead of scroll, control modifier will scroll
*/
void setZoomWithWheel(bool zoom) override;
void setVastScrolling(qreal factor) override;
QPointF currentCursorPosition() const override;
void resetScrollBars() override;
/**
* \internal
*/
class Private;
KoCanvasControllerWidget::Private *priv();
private Q_SLOTS:
/// Called by the horizontal scrollbar when its value changes
void updateCanvasOffsetX();
/// Called by the vertical scrollbar when its value changes
void updateCanvasOffsetY();
protected:
friend class KisZoomAndPanTest;
qreal vastScrollingFactor() const;
/// reimplemented from QWidget
void paintEvent(QPaintEvent *event) override;
/// reimplemented from QWidget
void resizeEvent(QResizeEvent *resizeEvent) override;
/// reimplemented from QWidget
void dragEnterEvent(QDragEnterEvent *event) override;
/// reimplemented from QWidget
void dropEvent(QDropEvent *event) override;
/// reimplemented from QWidget
void dragMoveEvent(QDragMoveEvent *event) override;
/// reimplemented from QWidget
void dragLeaveEvent(QDragLeaveEvent *event) override;
/// reimplemented from QWidget
void wheelEvent(QWheelEvent *event) override;
/// reimplemented from QWidget
bool focusNextPrevChild(bool next) override;
private:
Q_PRIVATE_SLOT(d, void activate())
Private * const d;
};
#endif
diff --git a/libs/flake/KoFilterEffectRegistry.h b/libs/flake/KoFilterEffectRegistry.h
index 594d3d2116..f2f0da880e 100644
--- a/libs/flake/KoFilterEffectRegistry.h
+++ b/libs/flake/KoFilterEffectRegistry.h
@@ -1,61 +1,62 @@
/* This file is part of the KDE project
* Copyright (c) 2009 Jan Hambrecht <jaham@gmx.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOFILTEREFFECTREGISTRY_H
#define KOFILTEREFFECTREGISTRY_H
#include <KoGenericRegistry.h>
#include <KoFilterEffectFactoryBase.h>
#include "kritaflake_export.h"
#include <KoXmlReaderForward.h>
class KoFilterEffectLoadingContext;
class KoFilterEffect;
class KRITAFLAKE_EXPORT KoFilterEffectRegistry : public KoGenericRegistry<KoFilterEffectFactoryBase*>
{
public:
KoFilterEffectRegistry();
~KoFilterEffectRegistry() override;
/**
* Return the only instance of KoFilterEffectRegistry.
* Creates an instance on the first call.
*/
static KoFilterEffectRegistry *instance();
/**
* Creates filter effect from given xml element.
- * @param element the xml element to load form
+ * @param element the xml element to load from
+ * @param context the loading context
* @return the created filter effect if successful, otherwise returns 0
*/
KoFilterEffect *createFilterEffectFromXml(const KoXmlElement &element, const KoFilterEffectLoadingContext &context);
private:
KoFilterEffectRegistry(const KoFilterEffectRegistry&);
KoFilterEffectRegistry operator=(const KoFilterEffectRegistry&);
void init();
class Private;
Private * const d;
};
#endif // KOFILTEREFFECTREGISTRY_H
diff --git a/libs/flake/KoImageData.cpp b/libs/flake/KoImageData.cpp
index a0d08369e4..006aa419fd 100644
--- a/libs/flake/KoImageData.cpp
+++ b/libs/flake/KoImageData.cpp
@@ -1,379 +1,379 @@
/* This file is part of the KDE project
* Copyright (C) 2007, 2009 Thomas Zander <zander@kde.org>
* Copyright (C) 2007 Jan Hambrecht <jaham@gmx.net>
* Copyright (C) 2008 C. Boemann <cbo@boemann.dk>
* Copyright (C) 2008 Thorsten Zachmann <zachmann@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoImageData.h"
#include "KoImageData_p.h"
#include "KoImageCollection.h"
#include <KoUnit.h>
#include <KoStore.h>
#include <KoStoreDevice.h>
#include <FlakeDebug.h>
#include <QBuffer>
#include <QCryptographicHash>
#include <QTemporaryFile>
#include <QPainter>
/// the maximum amount of bytes the image can be while we store it in memory instead of
/// spooling it to disk in a temp-file.
#define MAX_MEMORY_IMAGESIZE 90000
KoImageData::KoImageData()
: d(0)
{
}
KoImageData::KoImageData(const KoImageData &imageData)
: KoShapeUserData(),
d(imageData.d)
{
if (d)
d->refCount.ref();
}
KoImageData::KoImageData(KoImageDataPrivate *priv)
: d(priv)
{
d->refCount.ref();
}
KoImageData::~KoImageData()
{
if (d && !d->refCount.deref())
delete d;
}
QPixmap KoImageData::pixmap(const QSize &size)
{
if (!d) return QPixmap();
QSize wantedSize = size;
if (! wantedSize.isValid()) {
if (d->pixmap.isNull()) // we have a problem, Houston..
wantedSize = QSize(100, 100);
else
wantedSize = d->pixmap.size();
}
if (d->pixmap.isNull() || d->pixmap.size() != wantedSize) {
switch (d->dataStoreState) {
case KoImageDataPrivate::StateEmpty: {
#if 0 // this is not possible as it gets called during the paint method
// and will crash. Therefore create a tmp pixmap and return it.
d->pixmap = QPixmap(1, 1);
QPainter p(&d->pixmap);
p.setPen(QPen(Qt::gray, 0));
p.drawPoint(0, 0);
p.end();
break;
#endif
QPixmap tmp(1, 1);
tmp.fill(Qt::gray);
return tmp;
}
case KoImageDataPrivate::StateNotLoaded:
image(); // forces load
- // fall through
+ Q_FALLTHROUGH();
case KoImageDataPrivate::StateImageLoaded:
case KoImageDataPrivate::StateImageOnly:
if (!d->image.isNull()) {
// create pixmap from image.
// this is the highest quality and lowest memory usage way of doing the conversion.
d->pixmap = QPixmap::fromImage(d->image.scaled(wantedSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
}
}
if (d->dataStoreState == KoImageDataPrivate::StateImageLoaded) {
if (d->cleanCacheTimer.isActive())
d->cleanCacheTimer.stop();
// schedule an auto-unload of the big QImage in a second.
d->cleanCacheTimer.start();
}
}
return d->pixmap;
}
bool KoImageData::hasCachedPixmap() const
{
return d && !d->pixmap.isNull();
}
QSizeF KoImageData::imageSize()
{
if (!d->imageSize.isValid()) {
// The imagesize have not yet been calculated
if (image().isNull()) // auto loads the image
return QSizeF(100, 100);
if (d->image.dotsPerMeterX())
d->imageSize.setWidth(DM_TO_POINT(d->image.width() / (qreal) d->image.dotsPerMeterX() * 10.0));
else
d->imageSize.setWidth(d->image.width() / 72.0);
if (d->image.dotsPerMeterY())
d->imageSize.setHeight(DM_TO_POINT(d->image.height() / (qreal) d->image.dotsPerMeterY() * 10.0));
else
d->imageSize.setHeight(d->image.height() / 72.0);
}
return d->imageSize;
}
QImage KoImageData::image() const
{
if (d->dataStoreState == KoImageDataPrivate::StateNotLoaded) {
// load image
if (d->temporaryFile) {
bool r = d->temporaryFile->open();
if (!r) {
d->errorCode = OpenFailed;
}
else if (d->errorCode == Success && !d->image.load(d->temporaryFile->fileName(), d->suffix.toLatin1())) {
d->errorCode = OpenFailed;
}
d->temporaryFile->close();
} else {
if (d->errorCode == Success && !d->image.load(d->imageLocation.toLocalFile())) {
d->errorCode = OpenFailed;
}
}
if (d->errorCode == Success) {
d->dataStoreState = KoImageDataPrivate::StateImageLoaded;
}
}
return d->image;
}
bool KoImageData::hasCachedImage() const
{
return d && !d->image.isNull();
}
void KoImageData::setImage(const QImage &image, KoImageCollection *collection)
{
qint64 oldKey = 0;
if (d) {
oldKey = d->key;
}
Q_ASSERT(!image.isNull());
if (collection) {
// let the collection first check if it already has one. If it doesn't it'll call this method
// again and well go to the other clause
KoImageData *other = collection->createImageData(image);
this->operator=(*other);
delete other;
} else {
if (d == 0) {
d = new KoImageDataPrivate(this);
d->refCount.ref();
}
delete d->temporaryFile;
d->temporaryFile = 0;
d->clear();
d->suffix = "png"; // good default for non-lossy storage.
if (image.byteCount() > MAX_MEMORY_IMAGESIZE) {
// store image
QBuffer buffer;
buffer.open(QIODevice::WriteOnly);
if (!image.save(&buffer, d->suffix.toLatin1())) {
warnFlake << "Write temporary file failed";
d->errorCode = StorageFailed;
delete d->temporaryFile;
d->temporaryFile = 0;
return;
}
buffer.close();
buffer.open(QIODevice::ReadOnly);
d->copyToTemporary(buffer);
} else {
d->image = image;
d->dataStoreState = KoImageDataPrivate::StateImageOnly;
QByteArray ba;
QBuffer buffer(&ba);
buffer.open(QIODevice::WriteOnly);
image.save(&buffer, "PNG"); // use .png for images we get as QImage
QCryptographicHash md5(QCryptographicHash::Md5);
md5.addData(ba);
d->key = KoImageDataPrivate::generateKey(md5.result());
}
if (oldKey != 0 && d->collection) {
d->collection->update(oldKey, d->key);
}
}
}
void KoImageData::setImage(const QString &url, KoStore *store, KoImageCollection *collection)
{
if (collection) {
// Let the collection first check if it already has one. If it
// doesn't it'll call this method again and we'll go to the
// other clause.
KoImageData *other = collection->createImageData(url, store);
this->operator=(*other);
delete other;
} else {
if (d == 0) {
d = new KoImageDataPrivate(this);
d->refCount.ref();
} else {
d->clear();
}
d->setSuffix(url);
if (store->open(url)) {
struct Finalizer {
~Finalizer() { store->close(); }
KoStore *store;
};
Finalizer closer;
closer.store = store;
KoStoreDevice device(store);
const bool lossy = url.endsWith(".jpg", Qt::CaseInsensitive) || url.endsWith(".gif", Qt::CaseInsensitive);
if (!lossy && device.size() < MAX_MEMORY_IMAGESIZE) {
QByteArray data = device.readAll();
if (d->image.loadFromData(data)) {
QCryptographicHash md5(QCryptographicHash::Md5);
md5.addData(data);
qint64 oldKey = d->key;
d->key = KoImageDataPrivate::generateKey(md5.result());
if (oldKey != 0 && d->collection) {
d->collection->update(oldKey, d->key);
}
d->dataStoreState = KoImageDataPrivate::StateImageOnly;
return;
}
}
if (!device.open(QIODevice::ReadOnly)) {
warnFlake << "open file from store " << url << "failed";
d->errorCode = OpenFailed;
return;
}
d->copyToTemporary(device);
} else {
warnFlake << "Find file in store " << url << "failed";
d->errorCode = OpenFailed;
return;
}
}
}
void KoImageData::setImage(const QByteArray &imageData, KoImageCollection *collection)
{
if (collection) {
// let the collection first check if it already has one. If it doesn't it'll call this method
// again and we'll go to the other clause
KoImageData *other = collection->createImageData(imageData);
this->operator=(*other);
delete other;
}
else {
if (d == 0) {
d = new KoImageDataPrivate(this);
d->refCount.ref();
}
d->suffix = "png"; // good default for non-lossy storage.
if (imageData.size() <= MAX_MEMORY_IMAGESIZE) {
QImage image;
if (!image.loadFromData(imageData)) {
// mark the image as invalid, but keep the data in memory
// even if Calligra cannot handle the format, the data should
// be retained
d->errorCode = OpenFailed;
}
d->image = image;
d->dataStoreState = KoImageDataPrivate::StateImageOnly;
}
if (imageData.size() > MAX_MEMORY_IMAGESIZE
|| d->errorCode == OpenFailed) {
d->image = QImage();
// store image data
QBuffer buffer;
buffer.setData(imageData);
buffer.open(QIODevice::ReadOnly);
d->copyToTemporary(buffer);
}
QCryptographicHash md5(QCryptographicHash::Md5);
md5.addData(imageData);
qint64 oldKey = d->key;
d->key = KoImageDataPrivate::generateKey(md5.result());
if (oldKey != 0 && d->collection) {
d->collection->update(oldKey, d->key);
}
}
}
bool KoImageData::isValid() const
{
return d && d->dataStoreState != KoImageDataPrivate::StateEmpty
&& d->errorCode == Success;
}
bool KoImageData::operator==(const KoImageData &other) const
{
return other.d == d;
}
KoImageData &KoImageData::operator=(const KoImageData &other)
{
if (other.d)
other.d->refCount.ref();
if (d && !d->refCount.deref())
delete d;
d = other.d;
return *this;
}
KoShapeUserData *KoImageData::clone() const
{
return new KoImageData(*this);
}
qint64 KoImageData::key() const
{
return d->key;
}
QString KoImageData::suffix() const
{
return d->suffix;
}
KoImageData::ErrorCode KoImageData::errorCode() const
{
return d->errorCode;
}
bool KoImageData::saveData(QIODevice &device)
{
return d->saveData(device);
}
//have to include this because of Q_PRIVATE_SLOT
#include "moc_KoImageData.cpp"
diff --git a/libs/flake/KoParameterShape.h b/libs/flake/KoParameterShape.h
index ec308f1b81..f20502f9fb 100644
--- a/libs/flake/KoParameterShape.h
+++ b/libs/flake/KoParameterShape.h
@@ -1,165 +1,162 @@
/* This file is part of the KDE project
Copyright (C) 2006 Thorsten Zachmann <zachmann@kde.org>
Copyright (C) 2007 Thomas Zander <zander@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOPARAMETERSHAPE_H
#define KOPARAMETERSHAPE_H
#include "KoPathShape.h"
#include "kritaflake_export.h"
class KoParameterShapePrivate;
class KisHandlePainterHelper;
/**
* KoParameterShape is the base class for all parametric shapes
* in flake.
* Parametric shapes are those whose appearance can be completely
* defined by a few numerical parameters. Rectangle, ellipse and star
* are examples of parametric shapes.
* In flake, these shape parameters can be manipulated visually by means
* of control points. These control points can be moved with the mouse
* on the canvas which changes the shapes parameter values and hence the
* shapes appearance in realtime.
* KoParameterShape is derived from the KoPathShape class that means
* by changing the shape parameters, the underlying path is manipulated.
* A parametric shape can be converted into a path shape by simply calling
* the setModified method. This makes the path tool know that it can handle
* the shape like a path shape, so that modifying the single path points
* is possible.
*/
class KRITAFLAKE_EXPORT KoParameterShape : public KoPathShape
{
public:
KoParameterShape();
~KoParameterShape() override;
/**
* @brief Move handle to point
*
* This method calls moveHandleAction. Overload moveHandleAction to get the behaviour you want.
* After that updatePath and a repaint is called.
*
* @param handleId the id of the handle to move
* @param point the point to move the handle to in document coordinates
* @param modifiers the keyboard modifiers used during moving the handle
*/
void moveHandle(int handleId, const QPointF &point, Qt::KeyboardModifiers modifiers = Qt::NoModifier);
/**
* @brief Get the id of the handle within the given rect
*
* @param rect the rect in shape coordinates
* @return id of the found handle or -1 if none was found
*/
int handleIdAt(const QRectF &rect) const;
/**
* @brief Get the handle position
*
* @param handleId the id of the handle for which to get the position in shape coordinates
*/
QPointF handlePosition(int handleId) const;
/**
* @brief Paint the handles
*
- * @param painter the painter to paint the handles on
- * @param converter the view converter for applying the actual zoom
- * @param handleRadius the radius of the handles used for painting
+ * @param handlesHelper the helper of the handles used for painting
+ * @sa KisHandlePainterHelper
*/
void paintHandles(KisHandlePainterHelper &handlesHelper);
/**
* @brief Paint the given handles
*
- * @param painter the painter to paint the handles on
- * @param converter the view converter for applying the actual zoom
+ * @param handlesHelper the helper of the handle used for painting
* @param handleId of the handle which should be repainted
- * @param handleRadius the radius of the handle used for painting
*/
void paintHandle(KisHandlePainterHelper &handlesHelper, int handleId);
/// reimplemented from KoShape
void setSize(const QSizeF &size) override;
/**
* @brief Check if object is a parametric shape
*
* It is no longer a parametric shape when the path was manipulated
*
* @return true if it is a parametic shape, false otherwise
*/
bool isParametricShape() const;
/**
* @brief Set if the shape can be modified using parameters
*
* After the state is set to false it is no longer possible to work
* with parameters on this shape.
*
* @param parametric the new state
* @see isParametricShape
*/
void setParametricShape(bool parametric);
QPointF normalize() override;
/// return the number of handles set on the shape
int handleCount() const;
protected:
/**
* Get the handle positions for manipulating the parameters.
* @see setHandles, handleCount()
*/
QList<QPointF> handles() const;
/**
* Set the new handle positions which are used by the user to manipulate the parameters.
* @see handles(), handleCount()
*/
void setHandles(const QList<QPointF> &handles);
/// constructor
KoParameterShape(KoParameterShapePrivate *);
/**
* @brief Updates the internal state of a KoParameterShape.
*
* This method is called from moveHandle.
*
* @param handleId of the handle
* @param point to move the handle to in shape coordinates
* @param modifiers used during move to point
*/
virtual void moveHandleAction(int handleId, const QPointF & point, Qt::KeyboardModifiers modifiers = Qt::NoModifier) = 0;
/**
* @brief Update the path of the parameter shape
*
* @param size of the shape
*/
virtual void updatePath(const QSizeF &size) = 0;
protected:
Q_DECLARE_PRIVATE(KoParameterShape)
};
#endif /* KOPARAMETERSHAPE_H */
diff --git a/libs/flake/KoPathShape.h b/libs/flake/KoPathShape.h
index 6a788ae89b..8a79086661 100644
--- a/libs/flake/KoPathShape.h
+++ b/libs/flake/KoPathShape.h
@@ -1,522 +1,523 @@
/* This file is part of the KDE project
Copyright (C) 2006, 2011 Thorsten Zachmann <zachmann@kde.org>
Copyright (C) 2007,2009 Thomas Zander <zander@kde.org>
Copyright (C) 2006-2008 Jan Hambrecht <jaham@gmx.net>
Copyright (C) 2011 Jean-Nicolas Artaud <jeannicolasartaud@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOPATHSHAPE_H
#define KOPATHSHAPE_H
#include "kritaflake_export.h"
#include <QMetaType>
#include <QTransform>
#include "KoTosContainer.h"
#define KoPathShapeId "KoPathShape"
class KoPathSegment;
class KoPathPoint;
class KoPathShapePrivate;
class KoMarker;
class KisHandlePainterHelper;
typedef QPair<int, int> KoPathPointIndex;
/// a KoSubpath contains a path from a moveTo until a close or a new moveTo
typedef QList<KoPathPoint *> KoSubpath;
typedef QList<KoSubpath *> KoSubpathList;
/// The position of a path point within a path shape
/**
* @brief This is the base for all graphical objects.
*
* All graphical objects are based on this object e.g. lines, rectangulars, pies
* and so on.
*
* The KoPathShape uses KoPathPoint's to describe the path of the shape.
*
* Here a short example:
* 3 points connected by a curveTo's described by the following svg:
* M 100,200 C 100,100 250,100 250,200 C 250,200 400,300 400,200.
*
* This will be stored in 3 KoPathPoint's as
* The first point contains in
* point 100,200
* controlPoint2 100,100
* The second point contains in
* point 250,200
* controlPoint1 250,100
* controlPoint2 250,300
* The third point contains in
* point 400,300
* controlPoint1 400,200
*
* Not the segments are stored but the points. Out of the points the segments are
* generated. See the outline method. The reason for storing it like that is that
* it is the points that are modified by the user and not the segments.
*/
class KRITAFLAKE_EXPORT KoPathShape : public KoTosContainer
{
public:
/**
* @brief constructor
*/
KoPathShape();
/**
* @brief
*/
~KoPathShape() override;
KoShape *cloneShape() const override;
/// reimplemented
void paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext) override;
virtual void paintPoints(KisHandlePainterHelper &handlesHelper);
/// reimplemented
QRectF outlineRect() const override;
/// reimplemented
QPainterPath outline() const override;
/// reimplemented
QRectF boundingRect() const override;
/// reimplemented
QSizeF size() const override;
QPainterPath pathStroke(const QPen &pen) const;
/**
* Resize the shape
*
* This makes sure that the pathshape will not be resized to 0 if the new size
* is null as that makes it impossible to undo the change.
*
* All functions that overwrite this function should also use the resizeMatrix
* function to get and use the same data in resizing.
*
* @see resizeMatrix()
*/
void setSize(const QSizeF &size) override;
/// reimplemented
bool hitTest(const QPointF &position) const override;
// reimplemented
void saveOdf(KoShapeSavingContext &context) const override;
// reimplemented
bool loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context) override;
// basically the same as loadOdf but adapted to the contour cases
// tag needs to be either contour-polygon or contour-path from another shape
bool loadContourOdf(const KoXmlElement & element, KoShapeLoadingContext &context, const QSizeF &scaleFactor);
/** basically the equivalent saveOdf but adapted to the contour cases
+ * @param context the saving context
* @param originalSize the original size of the unscaled image.
*/
void saveContourOdf(KoShapeSavingContext &context, const QSizeF &originalSize) const;
/// Removes all subpaths and their points from the path
void clear();
/**
* @brief Starts a new Subpath
*
* Moves the pen to p and starts a new subpath.
*
* @return the newly created point
*/
KoPathPoint *moveTo(const QPointF &p);
/**
* @brief Adds a new line segment
*
* Adds a straight line between the last point and the given point p.
*
* @return the newly created point
*/
KoPathPoint *lineTo(const QPointF &p);
/**
* @brief Adds a new cubic Bezier curve segment.
*
* Adds a cubic Bezier curve between the last point and the given point p,
* using the control points specified by c1 and c2.
*
* @param c1 control point1
* @param c2 control point2
* @param p the endpoint of this curve segment
*
* @return The newly created point
*/
KoPathPoint *curveTo(const QPointF &c1, const QPointF &c2, const QPointF &p);
/**
* @brief Adds a new quadratic Bezier curve segment.
*
* Adds a quadratic Bezier curve between the last point and the given point p,
* using the control point specified by c.
*
* @param c control point
* @param p the endpoint of this curve segment
*
* @return The newly created point
*/
KoPathPoint *curveTo(const QPointF &c, const QPointF &p);
/**
* @brief Add an arc.
*
* Adds an arc starting at the current point. The arc will be converted to bezier curves.
*
* @param rx x radius of the ellipse
* @param ry y radius of the ellipse
* @param startAngle the angle where the arc will be started
* @param sweepAngle the length of the angle
* TODO add param to have angle of the ellipse
*
* @return The newly created point
*/
KoPathPoint *arcTo(qreal rx, qreal ry, qreal startAngle, qreal sweepAngle);
/**
* @brief Closes the current subpath
*/
void close();
/**
* @brief Closes the current subpath
*
* It tries to merge the last and first point of the subpath
* to one point and then closes the subpath. If merging is not
* possible as the two point are to far from each other a close
* will be done.
* TODO define a maximum distance between the two points until this is working
*/
void closeMerge();
/**
* @brief Normalizes the path data.
*
* The path points are transformed so that the top-left corner
* of the bounding rect is at (0,0).
* This should be called after adding points to the path or changing
* positions of path points.
* @return the offset by which the points are moved in shape coordinates.
*/
virtual QPointF normalize();
/**
* @brief Returns the path points within the given rectangle.
* @param rect the rectangle the requested points are in
* @return list of points within the rectangle
*/
QList<KoPathPoint*> pointsAt(const QRectF &rect) const;
/**
* @brief Returns the list of path segments within the given rectangle.
* @param rect the rectangle the requested segments are in
* @return list of segments within the rectangle
*/
QList<KoPathSegment> segmentsAt(const QRectF &rect) const;
/**
* @brief Returns the path point index of a given path point
*
* @param point the point for which you want to get the index
* @return path point index of the point if it exists
* otherwise KoPathPointIndex( -1, -1 )
*/
KoPathPointIndex pathPointIndex(const KoPathPoint *point) const;
/**
* @brief Returns the path point specified by a path point index
*
* @param pointIndex index of the point to get
*
* @return KoPathPoint on success, 0 otherwise e.g. out of bounds
*/
KoPathPoint *pointByIndex(const KoPathPointIndex &pointIndex) const;
/**
* @brief Returns the segment specified by a path point index
*
* A segment is defined by the point index of the first point in the segment.
* A segment contains the defined point and its following point. If the subpath is
* closed and the and the pointIndex point to the last point in the subpath, the
* following point is the first point in the subpath.
*
* @param pointIndex index of the first point of the segment
*
* @return Segment containing both points of the segment or KoPathSegment( 0, 0 ) on error e.g. out of bounds
*/
KoPathSegment segmentByIndex(const KoPathPointIndex &pointIndex) const;
/**
* @brief Returns the number of points in the path
*
* @return The number of points in the path
*/
int pointCount() const;
/**
* @brief Returns the number of subpaths in the path
*
* @return The number of subpaths in the path
*/
int subpathCount() const;
/**
* @brief Returns the number of points in a subpath
*
* @return The number of points in the subpath or -1 if subpath out of bounds
*/
int subpathPointCount(int subpathIndex) const;
/**
* @brief Checks if a subpath is closed
*
* @param subpathIndex index of the subpath to check
*
* @return true when the subpath is closed, false otherwise
*/
bool isClosedSubpath(int subpathIndex) const;
/**
* @brief Inserts a new point into the given subpath at the specified position
*
* This method keeps the subpath closed if it is closed, and open when it was
* open. So it can change the properties of the point inserted.
* You might need to update the point before/after to get the desired result
* e.g. when you insert the point into a curve.
*
* @param point to insert
* @param pointIndex index at which the point should be inserted
*
* @return true on success,
* false when pointIndex is out of bounds
*/
bool insertPoint(KoPathPoint *point, const KoPathPointIndex &pointIndex);
/**
* @brief Removes a point from the path.
*
* Note that the ownership of the point will pass to the caller.
*
* @param pointIndex index of the point which should be removed
*
* @return The removed point on success,
* otherwise 0
*/
KoPathPoint *removePoint(const KoPathPointIndex &pointIndex);
/**
* @brief Breaks the path after the point index
*
* The new subpath will be behind the one that was broken. The segment between
* the given point and the one behind will be removed. If you want to split at
* one point insert first a copy of the point behind it.
* This does not work when the subpath is closed. Use openSubpath for this.
* It does not break at the last position of a subpath or if there is only one
* point in the subpath.
*
* @param pointIndex index of the point after which the path should be broken
*
* @return true if the subpath was broken, otherwise false
*/
bool breakAfter(const KoPathPointIndex &pointIndex);
/**
* @brief Joins the given subpath with the following one
*
* Joins the given subpath with the following one by inserting a segment between
* the two subpaths.
* This does nothing if the specified subpath is the last subpath
* or one of both subpaths is closed.
*
* @param subpathIndex index of the subpath being joined with the following subpath
*
* @return true if the subpath was joined, otherwise false
*/
bool join(int subpathIndex);
/**
* @brief Moves the position of a subpath within a path
*
* @param oldSubpathIndex old index of the subpath
* @param newSubpathIndex new index of the subpath
*
* @return true if the subpath was moved, otherwise false e.g. if an index is out of bounds
*/
bool moveSubpath(int oldSubpathIndex, int newSubpathIndex);
/**
* @brief Opens a closed subpath
*
* The subpath is opened by removing the segment before the given point, making
* the given point the new start point of the subpath.
*
* @param pointIndex the index of the point at which to open the closed subpath
* @return the new position of the old first point in the subpath
* otherwise KoPathPointIndex( -1, -1 )
*/
KoPathPointIndex openSubpath(const KoPathPointIndex &pointIndex);
/**
* @brief Close a open subpath
*
* The subpath is closed be inserting a segment between the start and end point, making
* the given point the new start point of the subpath.
*
* @return the new position of the old first point in the subpath
* otherwise KoPathPointIndex( -1, -1 )
*/
KoPathPointIndex closeSubpath(const KoPathPointIndex &pointIndex);
/**
* @brief Reverse subpath
*
* The last point becomes the first point and the first one becomes the last one.
*
* @param subpathIndex the index of the subpath to reverse
*/
bool reverseSubpath(int subpathIndex);
/**
* @brief Removes subpath from the path
* @param subpathIndex the index of the subpath to remove
* @return the removed subpath on success, 0 otherwise.
*/
KoSubpath *removeSubpath(int subpathIndex);
/**
* @brief Adds a subpath at the given index to the path
* @param subpath the subpath to add
* @param subpathIndex the index at which the new subpath should be inserted
* @return true on success, false otherwise e.g. subpathIndex out of bounds
*/
bool addSubpath(KoSubpath *subpath, int subpathIndex);
/**
* @brief Combines two path shapes by appending the data of the specified path.
* @param path the path to combine with
* @return index of the first segment inserted or -1 on failure
*/
int combine(KoPathShape *path);
/**
* @brief Creates separate path shapes, one for each existing subpath.
* @param separatedPaths the list which contains the separated path shapes
* @return true if separating the path was successful, false otherwise
*/
bool separate(QList<KoPathShape*> &separatedPaths);
/**
* Returns the specific path shape id.
*
* Path shape derived shapes have a different shape id which link them
* to their respective shape factories. In most cases they do not have
* a special tool for editing them.
* This function returns the specific shape id for finding the shape
* factory from KoShapeRegistry. The default KoPathShapeId is returned
* from KoShape::shapeId() so that the generic path editing tool gets
* activated when the shape is selected.
*
* @return the specific shape id
*/
virtual QString pathShapeId() const;
/// Returns a odf/svg string representation of the path data with the given matrix applied.
QString toString(const QTransform &matrix = QTransform()) const;
/// Returns the fill rule for the path object
Qt::FillRule fillRule() const;
/// Sets the fill rule to be used for painting the background
void setFillRule(Qt::FillRule fillRule);
/// Creates path shape from given QPainterPath
static KoPathShape *createShapeFromPainterPath(const QPainterPath &path);
/// Returns the viewbox from the given xml element.
static QRect loadOdfViewbox(const KoXmlElement &element);
void setMarker(KoMarker *marker, KoFlake::MarkerPosition pos);
KoMarker* marker(KoFlake::MarkerPosition pos) const;
bool hasMarkers() const;
bool autoFillMarkers() const;
void setAutoFillMarkers(bool value);
public:
struct KRITAFLAKE_EXPORT PointSelectionChangeListener : public ShapeChangeListener {
void notifyShapeChanged(ChangeType type, KoShape *shape) override;
virtual void recommendPointSelectionChange(KoPathShape *shape, const QList<KoPathPointIndex> &newSelection) = 0;
virtual void notifyPathPointsChanged(KoPathShape *shape) = 0;
};
void recommendPointSelectionChange(const QList<KoPathPointIndex> &newSelection);
protected:
void notifyPointsChanged();
private:
/// constructor: to be used in cloneShape(), not in descendants!
/// \internal
KoPathShape(const KoPathShape &rhs);
protected:
/// constructor:to be used in descendant shapes
/// \internal
KoPathShape(KoPathShapePrivate *);
/// reimplemented
QString saveStyle(KoGenStyle &style, KoShapeSavingContext &context) const override;
/// reimplemented
void loadStyle(const KoXmlElement &element, KoShapeLoadingContext &context) override;
/**
* @brief Add an arc.
*
* Adds an arc starting at the current point. The arc will be converted to bezier curves.
* @param rx x radius of the ellipse
* @param ry y radius of the ellipse
* @param startAngle the angle where the arc will be started
* @param sweepAngle the length of the angle
* TODO add param to have angle of the ellipse
* @param offset to the first point in the arc
* @param curvePoints a array which take the curve points, pass a 'QPointF curvePoins[12]';
*
* @return number of points created by the curve
*/
int arcToCurve(qreal rx, qreal ry, qreal startAngle, qreal sweepAngle, const QPointF &offset, QPointF *curvePoints) const;
/**
* Get the resize matrix
*
* This makes sure that also if the newSize isNull that there will be a
* very small size of 0.000001 pixels
*/
QTransform resizeMatrix( const QSizeF &newSize ) const;
private:
Q_DECLARE_PRIVATE(KoPathShape)
};
Q_DECLARE_METATYPE(KoPathShape*)
#endif /* KOPATHSHAPE_H */
diff --git a/libs/flake/KoPathShapeLoader.cpp b/libs/flake/KoPathShapeLoader.cpp
index f8307b9087..a3478344b5 100644
--- a/libs/flake/KoPathShapeLoader.cpp
+++ b/libs/flake/KoPathShapeLoader.cpp
@@ -1,648 +1,648 @@
/* This file is part of the KDE project
* Copyright (C) 2007 Jan Hambrecht <jaham@gmx.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoPathShapeLoader.h"
#include "KoPathShape.h"
#include <math.h>
#include <FlakeDebug.h>
class KoPathShapeLoaderPrivate
{
public:
KoPathShapeLoaderPrivate(KoPathShape * p) : path(p) {
Q_ASSERT(path);
path->clear();
}
void parseSvg(const QString &svgInputData, bool process = false);
void svgMoveTo(qreal x1, qreal y1, bool abs = true);
void svgLineTo(qreal x1, qreal y1, bool abs = true);
void svgLineToHorizontal(qreal x, bool abs = true);
void svgLineToVertical(qreal y, bool abs = true);
void svgCurveToCubic(qreal x1, qreal y1, qreal x2, qreal y2, qreal x, qreal y, bool abs = true);
void svgCurveToCubicSmooth(qreal x, qreal y, qreal x2, qreal y2, bool abs = true);
void svgCurveToQuadratic(qreal x, qreal y, qreal x1, qreal y1, bool abs = true);
void svgCurveToQuadraticSmooth(qreal x, qreal y, bool abs = true);
void svgArcTo(qreal x, qreal y, qreal r1, qreal r2, qreal angle, bool largeArcFlag, bool sweepFlag, bool abs = true);
void svgClosePath();
const char *getCoord(const char *, qreal &);
void calculateArc(bool relative, qreal &curx, qreal &cury, qreal angle, qreal x, qreal y, qreal r1, qreal r2, bool largeArcFlag, bool sweepFlag);
KoPathShape * path; ///< the path shape to work on
QPointF lastPoint;
};
void KoPathShapeLoaderPrivate::parseSvg(const QString &s, bool process)
{
if (!s.isEmpty()) {
QString d = s;
d.replace(',', ' ');
d = d.simplified();
const QByteArray buffer = d.toLatin1();
const char *ptr = buffer.constData();
const char *end = buffer.constData() + buffer.length() + 1;
qreal curx = 0.0;
qreal cury = 0.0;
qreal contrlx, contrly, subpathx, subpathy, tox, toy, x1, y1, x2, y2, xc, yc;
qreal px1, py1, px2, py2, px3, py3;
bool relative;
char command = *(ptr++), lastCommand = ' ';
subpathx = subpathy = curx = cury = contrlx = contrly = 0.0;
while (ptr < end) {
if (*ptr == ' ')
++ptr;
relative = false;
switch (command) {
case 'm':
relative = true;
- /* Falls through. */
+ Q_FALLTHROUGH();
case 'M': {
ptr = getCoord(ptr, tox);
ptr = getCoord(ptr, toy);
if (process) {
subpathx = curx = relative ? curx + tox : tox;
subpathy = cury = relative ? cury + toy : toy;
svgMoveTo(curx, cury);
} else
svgMoveTo(tox, toy, !relative);
break;
}
case 'l':
relative = true;
- /* Falls through. */
+ Q_FALLTHROUGH();
case 'L': {
ptr = getCoord(ptr, tox);
ptr = getCoord(ptr, toy);
if (process) {
curx = relative ? curx + tox : tox;
cury = relative ? cury + toy : toy;
svgLineTo(curx, cury);
} else
svgLineTo(tox, toy, !relative);
break;
}
case 'h': {
ptr = getCoord(ptr, tox);
if (process) {
curx = curx + tox;
svgLineTo(curx, cury);
} else
svgLineToHorizontal(tox, false);
break;
}
case 'H': {
ptr = getCoord(ptr, tox);
if (process) {
curx = tox;
svgLineTo(curx, cury);
} else
svgLineToHorizontal(tox);
break;
}
case 'v': {
ptr = getCoord(ptr, toy);
if (process) {
cury = cury + toy;
svgLineTo(curx, cury);
} else
svgLineToVertical(toy, false);
break;
}
case 'V': {
ptr = getCoord(ptr, toy);
if (process) {
cury = toy;
svgLineTo(curx, cury);
} else
svgLineToVertical(toy);
break;
}
case 'z':
- /* Falls through. */
+ Q_FALLTHROUGH();
case 'Z': {
// reset curx, cury for next path
if (process) {
curx = subpathx;
cury = subpathy;
}
svgClosePath();
break;
}
case 'c':
relative = true;
- /* Falls through. */
+ Q_FALLTHROUGH();
case 'C': {
ptr = getCoord(ptr, x1);
ptr = getCoord(ptr, y1);
ptr = getCoord(ptr, x2);
ptr = getCoord(ptr, y2);
ptr = getCoord(ptr, tox);
ptr = getCoord(ptr, toy);
if (process) {
px1 = relative ? curx + x1 : x1;
py1 = relative ? cury + y1 : y1;
px2 = relative ? curx + x2 : x2;
py2 = relative ? cury + y2 : y2;
px3 = relative ? curx + tox : tox;
py3 = relative ? cury + toy : toy;
svgCurveToCubic(px1, py1, px2, py2, px3, py3);
contrlx = relative ? curx + x2 : x2;
contrly = relative ? cury + y2 : y2;
curx = relative ? curx + tox : tox;
cury = relative ? cury + toy : toy;
} else
svgCurveToCubic(x1, y1, x2, y2, tox, toy, !relative);
break;
}
case 's':
relative = true;
- /* Falls through. */
+ Q_FALLTHROUGH();
case 'S': {
ptr = getCoord(ptr, x2);
ptr = getCoord(ptr, y2);
ptr = getCoord(ptr, tox);
ptr = getCoord(ptr, toy);
if (!(lastCommand == 'c' || lastCommand == 'C' ||
lastCommand == 's' || lastCommand == 'S')) {
contrlx = curx;
contrly = cury;
}
if (process) {
px1 = 2 * curx - contrlx;
py1 = 2 * cury - contrly;
px2 = relative ? curx + x2 : x2;
py2 = relative ? cury + y2 : y2;
px3 = relative ? curx + tox : tox;
py3 = relative ? cury + toy : toy;
svgCurveToCubic(px1, py1, px2, py2, px3, py3);
contrlx = relative ? curx + x2 : x2;
contrly = relative ? cury + y2 : y2;
curx = relative ? curx + tox : tox;
cury = relative ? cury + toy : toy;
} else
svgCurveToCubicSmooth(x2, y2, tox, toy, !relative);
break;
}
case 'q':
relative = true;
- /* Falls through. */
+ Q_FALLTHROUGH();
case 'Q': {
ptr = getCoord(ptr, x1);
ptr = getCoord(ptr, y1);
ptr = getCoord(ptr, tox);
ptr = getCoord(ptr, toy);
if (process) {
px1 = relative ? (curx + 2 * (x1 + curx)) * (1.0 / 3.0) : (curx + 2 * x1) * (1.0 / 3.0);
py1 = relative ? (cury + 2 * (y1 + cury)) * (1.0 / 3.0) : (cury + 2 * y1) * (1.0 / 3.0);
px2 = relative ? ((curx + tox) + 2 * (x1 + curx)) * (1.0 / 3.0) : (tox + 2 * x1) * (1.0 / 3.0);
py2 = relative ? ((cury + toy) + 2 * (y1 + cury)) * (1.0 / 3.0) : (toy + 2 * y1) * (1.0 / 3.0);
px3 = relative ? curx + tox : tox;
py3 = relative ? cury + toy : toy;
svgCurveToCubic(px1, py1, px2, py2, px3, py3);
contrlx = relative ? curx + x1 : x1;
contrly = relative ? cury + y1 : y1;
curx = relative ? curx + tox : tox;
cury = relative ? cury + toy : toy;
} else
svgCurveToQuadratic(x1, y1, tox, toy, !relative);
break;
}
case 't':
relative = true;
- /* Falls through. */
+ Q_FALLTHROUGH();
case 'T': {
ptr = getCoord(ptr, tox);
ptr = getCoord(ptr, toy);
if (!(lastCommand == 'q' || lastCommand == 'Q' ||
lastCommand == 't' || lastCommand == 'T')) {
contrlx = curx;
contrly = cury;
}
if (process) {
xc = 2 * curx - contrlx;
yc = 2 * cury - contrly;
px1 = (curx + 2 * xc) * (1.0 / 3.0);
py1 = (cury + 2 * yc) * (1.0 / 3.0);
px2 = relative ? ((curx + tox) + 2 * xc) * (1.0 / 3.0) : (tox + 2 * xc) * (1.0 / 3.0);
py2 = relative ? ((cury + toy) + 2 * yc) * (1.0 / 3.0) : (toy + 2 * yc) * (1.0 / 3.0);
px3 = relative ? curx + tox : tox;
py3 = relative ? cury + toy : toy;
svgCurveToCubic(px1, py1, px2, py2, px3, py3);
contrlx = xc;
contrly = yc;
curx = relative ? curx + tox : tox;
cury = relative ? cury + toy : toy;
} else
svgCurveToQuadraticSmooth(tox, toy, !relative);
break;
}
case 'a':
relative = true;
- /* Falls through. */
+ Q_FALLTHROUGH();
case 'A': {
bool largeArc, sweep;
qreal angle, rx, ry;
ptr = getCoord(ptr, rx);
ptr = getCoord(ptr, ry);
ptr = getCoord(ptr, angle);
ptr = getCoord(ptr, tox);
largeArc = tox == 1;
ptr = getCoord(ptr, tox);
sweep = tox == 1;
ptr = getCoord(ptr, tox);
ptr = getCoord(ptr, toy);
// Spec: radii are nonnegative numbers
rx = fabs(rx);
ry = fabs(ry);
if (process)
calculateArc(relative, curx, cury, angle, tox, toy, rx, ry, largeArc, sweep);
else
svgArcTo(tox, toy, rx, ry, angle, largeArc, sweep, !relative);
break;
}
default: {
// when svg parser is used for a parsing an odf path an unknown command
// can be encountered, so we stop parsing here
debugFlake << "KoSvgPathParser::parseSVG(): unknown command \"" << command << "\"";
return;
}
}
lastCommand = command;
if (*ptr == '+' || *ptr == '-' || (*ptr >= '0' && *ptr <= '9')) {
// there are still coords in this command
if (command == 'M')
command = 'L';
else if (command == 'm')
command = 'l';
} else
command = *(ptr++);
if (lastCommand != 'C' && lastCommand != 'c' &&
lastCommand != 'S' && lastCommand != 's' &&
lastCommand != 'Q' && lastCommand != 'q' &&
lastCommand != 'T' && lastCommand != 't') {
contrlx = curx;
contrly = cury;
}
}
}
}
// parses the coord into number and forwards to the next token
const char * KoPathShapeLoaderPrivate::getCoord(const char *ptr, qreal &number)
{
int integer, exponent;
qreal decimal, frac;
int sign, expsign;
exponent = 0;
integer = 0;
frac = 1.0;
decimal = 0;
sign = 1;
expsign = 1;
// read the sign
if (*ptr == '+')
++ptr;
else if (*ptr == '-') {
++ptr;
sign = -1;
}
// read the integer part
while (*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
integer = (integer * 10) + *(ptr++) - '0';
if (*ptr == '.') { // read the decimals
++ptr;
while (*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
decimal += (*(ptr++) - '0') * (frac *= 0.1);
}
if (*ptr == 'e' || *ptr == 'E') { // read the exponent part
++ptr;
// read the sign of the exponent
if (*ptr == '+')
++ptr;
else if (*ptr == '-') {
++ptr;
expsign = -1;
}
exponent = 0;
while (*ptr != '\0' && *ptr >= '0' && *ptr <= '9') {
exponent *= 10;
exponent += *ptr - '0';
++ptr;
}
}
number = integer + decimal;
number *= sign * pow((qreal)10, qreal(expsign * exponent));
// skip the following space
if (*ptr == ' ')
++ptr;
return ptr;
}
// This works by converting the SVG arc to "simple" beziers.
// For each bezier found a svgToCurve call is done.
// Adapted from Niko's code in kdelibs/kdecore/svgicons.
// Maybe this can serve in some shared lib? (Rob)
void KoPathShapeLoaderPrivate::calculateArc(bool relative, qreal &curx, qreal &cury, qreal angle, qreal x, qreal y, qreal r1, qreal r2, bool largeArcFlag, bool sweepFlag)
{
qreal sin_th, cos_th;
qreal a00, a01, a10, a11;
qreal x0, y0, x1, y1, xc, yc;
qreal d, sfactor, sfactor_sq;
qreal th0, th1, th_arc;
int i, n_segs;
sin_th = sin(angle * (M_PI / 180.0));
cos_th = cos(angle * (M_PI / 180.0));
qreal dx;
if (!relative)
dx = (curx - x) / 2.0;
else
dx = -x / 2.0;
qreal dy;
if (!relative)
dy = (cury - y) / 2.0;
else
dy = -y / 2.0;
qreal _x1 = cos_th * dx + sin_th * dy;
qreal _y1 = -sin_th * dx + cos_th * dy;
qreal Pr1 = r1 * r1;
qreal Pr2 = r2 * r2;
qreal Px = _x1 * _x1;
qreal Py = _y1 * _y1;
// Spec : check if radii are large enough
qreal check = Px / Pr1 + Py / Pr2;
if (check > 1) {
r1 = r1 * sqrt(check);
r2 = r2 * sqrt(check);
}
a00 = cos_th / r1;
a01 = sin_th / r1;
a10 = -sin_th / r2;
a11 = cos_th / r2;
x0 = a00 * curx + a01 * cury;
y0 = a10 * curx + a11 * cury;
if (!relative)
x1 = a00 * x + a01 * y;
else
x1 = a00 * (curx + x) + a01 * (cury + y);
if (!relative)
y1 = a10 * x + a11 * y;
else
y1 = a10 * (curx + x) + a11 * (cury + y);
/* (x0, y0) is current point in transformed coordinate space.
(x1, y1) is new point in transformed coordinate space.
The arc fits a unit-radius circle in this space.
*/
d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
sfactor_sq = 1.0 / d - 0.25;
if (sfactor_sq < 0)
sfactor_sq = 0;
sfactor = sqrt(sfactor_sq);
if (sweepFlag == largeArcFlag)
sfactor = -sfactor;
xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
/* (xc, yc) is center of the circle. */
th0 = atan2(y0 - yc, x0 - xc);
th1 = atan2(y1 - yc, x1 - xc);
th_arc = th1 - th0;
if (th_arc < 0 && sweepFlag)
th_arc += 2 * M_PI;
else if (th_arc > 0 && !sweepFlag)
th_arc -= 2 * M_PI;
n_segs = (int)(int) ceil(fabs(th_arc / (M_PI * 0.5 + 0.001)));
for (i = 0; i < n_segs; ++i) {
{
qreal sin_th, cos_th;
qreal a00, a01, a10, a11;
qreal x1, y1, x2, y2, x3, y3;
qreal t;
qreal th_half;
qreal _th0 = th0 + i * th_arc / n_segs;
qreal _th1 = th0 + (i + 1) * th_arc / n_segs;
sin_th = sin(angle * (M_PI / 180.0));
cos_th = cos(angle * (M_PI / 180.0));
/* inverse transform compared with rsvg_path_arc */
a00 = cos_th * r1;
a01 = -sin_th * r2;
a10 = sin_th * r1;
a11 = cos_th * r2;
th_half = 0.5 * (_th1 - _th0);
t = (8.0 / 3.0) * sin(th_half * 0.5) * sin(th_half * 0.5) / sin(th_half);
x1 = xc + cos(_th0) - t * sin(_th0);
y1 = yc + sin(_th0) + t * cos(_th0);
x3 = xc + cos(_th1);
y3 = yc + sin(_th1);
x2 = x3 + t * sin(_th1);
y2 = y3 - t * cos(_th1);
svgCurveToCubic(a00 * x1 + a01 * y1, a10 * x1 + a11 * y1, a00 * x2 + a01 * y2, a10 * x2 + a11 * y2, a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
}
}
if (!relative)
curx = x;
else
curx += x;
if (!relative)
cury = y;
else
cury += y;
}
void KoPathShapeLoaderPrivate::svgMoveTo(qreal x1, qreal y1, bool abs)
{
if (abs)
lastPoint = QPointF(x1, y1);
else
lastPoint += QPointF(x1, y1);
path->moveTo(lastPoint);
}
void KoPathShapeLoaderPrivate::svgLineTo(qreal x1, qreal y1, bool abs)
{
if (abs)
lastPoint = QPointF(x1, y1);
else
lastPoint += QPointF(x1, y1);
path->lineTo(lastPoint);
}
void KoPathShapeLoaderPrivate::svgLineToHorizontal(qreal x, bool abs)
{
if (abs)
lastPoint.setX(x);
else
lastPoint.rx() += x;
path->lineTo(lastPoint);
}
void KoPathShapeLoaderPrivate::svgLineToVertical(qreal y, bool abs)
{
if (abs)
lastPoint.setY(y);
else
lastPoint.ry() += y;
path->lineTo(lastPoint);
}
void KoPathShapeLoaderPrivate::svgCurveToCubic(qreal x1, qreal y1, qreal x2, qreal y2, qreal x, qreal y, bool abs)
{
QPointF p1, p2;
if (abs) {
p1 = QPointF(x1, y1);
p2 = QPointF(x2, y2);
lastPoint = QPointF(x, y);
} else {
p1 = lastPoint + QPointF(x1, y1);
p2 = lastPoint + QPointF(x2, y2);
lastPoint += QPointF(x, y);
}
path->curveTo(p1, p2, lastPoint);
}
void KoPathShapeLoaderPrivate::svgCurveToCubicSmooth(qreal x, qreal y, qreal x2, qreal y2, bool abs)
{
Q_UNUSED(x);
Q_UNUSED(y);
Q_UNUSED(x2);
Q_UNUSED(y2);
Q_UNUSED(abs);
// TODO implement
}
void KoPathShapeLoaderPrivate::svgCurveToQuadratic(qreal x, qreal y, qreal x1, qreal y1, bool abs)
{
Q_UNUSED(x);
Q_UNUSED(y);
Q_UNUSED(x1);
Q_UNUSED(y1);
Q_UNUSED(abs);
// TODO implement
}
void KoPathShapeLoaderPrivate::svgCurveToQuadraticSmooth(qreal x, qreal y, bool abs)
{
Q_UNUSED(x);
Q_UNUSED(y);
Q_UNUSED(abs);
// TODO implement
}
void KoPathShapeLoaderPrivate::svgArcTo(qreal x, qreal y, qreal r1, qreal r2, qreal angle, bool largeArcFlag, bool sweepFlag, bool abs)
{
Q_UNUSED(x);
Q_UNUSED(y);
Q_UNUSED(r1);
Q_UNUSED(r2);
Q_UNUSED(angle);
Q_UNUSED(largeArcFlag);
Q_UNUSED(sweepFlag);
Q_UNUSED(abs);
// TODO implement
}
void KoPathShapeLoaderPrivate::svgClosePath()
{
path->closeMerge();
}
KoPathShapeLoader::KoPathShapeLoader(KoPathShape *path)
: d(new KoPathShapeLoaderPrivate(path))
{
}
KoPathShapeLoader::~KoPathShapeLoader()
{
delete d;
}
void KoPathShapeLoader::parseSvg(const QString &s, bool process)
{
d->parseSvg(s, process);
}
diff --git a/libs/flake/KoResourceManager_p.h b/libs/flake/KoResourceManager_p.h
index af17591a57..e4d2140baa 100644
--- a/libs/flake/KoResourceManager_p.h
+++ b/libs/flake/KoResourceManager_p.h
@@ -1,220 +1,220 @@
/*
Copyright (c) 2006, 2011 Boudewijn Rempt (boud@valdyas.org)
Copyright (C) 2007, 2009, 2010 Thomas Zander <zander@kde.org>
Copyright (c) 2008 Carlos Licea <carlos.licea@kdemail.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KO_RESOURCEMANAGER_P_H
#define KO_RESOURCEMANAGER_P_H
#include <QObject>
#include <QSizeF>
#include <QHash>
#include "kritaflake_export.h"
#include <KoColor.h>
#include <KoUnit.h>
#include "KoDerivedResourceConverter.h"
#include "KoResourceUpdateMediator.h"
class KoShape;
class QVariant;
class KRITAFLAKE_EXPORT KoResourceManager : public QObject
{
Q_OBJECT
public:
KoResourceManager() {}
/**
* Set a resource of any type.
* @param key the integer key
* @param value the new value for the key.
* @see KoCanvasResourceProvider::CanvasResource KoDocumentResourceManager::DocumentResource
*/
void setResource(int key, const QVariant &value);
/**
* Set a resource of type KoColor.
* @param key the integer key
* @param color the new value for the key.
* @see KoCanvasResourceProvider::CanvasResource KoDocumentResourceManager::DocumentResource
*/
void setResource(int key, const KoColor &color);
/**
* Set a resource of type KoShape*.
* @param key the integer key
- * @param id the new value for the key.
+ * @param shape the new shape for the key.
* @see KoCanvasResourceProvider::CanvasResource KoDocumentResourceManager::DocumentResource
*/
void setResource(int key, KoShape *shape);
/**
* Set a resource of type KoUnit
* @param key the integer key
- * @param id the new value for the key.
+ * @param unit the unit for the key.
* @see KoCanvasResourceProvider::CanvasResource KoDocumentResourceManager::DocumentResource
*/
void setResource(int key, const KoUnit &unit);
/**
* Returns a qvariant containing the specified resource or a standard one if the
* specified resource does not exist.
* @param key the key
* @see KoCanvasResourceProvider::CanvasResource KoDocumentResourceManager::DocumentResource
*/
QVariant resource(int key) const;
/**
* Return the resource determined by param key as a boolean.
* @param key the identifying key for the resource
* @see KoCanvasResourceProvider::CanvasResource KoDocumentResourceManager::DocumentResource
*/
bool boolResource(int key) const;
/**
* Return the resource determined by param key as an integer.
* @param key the identifying key for the resource
* @see KoCanvasResourceProvider::CanvasResource KoDocumentResourceManager::DocumentResource
*/
int intResource(int key) const;
/**
* Return the resource determined by param key as a KoColor.
* @param key the identifying key for the resource
* @see KoCanvasResourceProvider::CanvasResource KoDocumentResourceManager::DocumentResource
*/
KoColor koColorResource(int key) const;
/**
* Return the resource determined by param key as a pointer to a KoShape.
* @param key the identifying key for the resource
* @see KoCanvasResourceProvider::CanvasResource KoDocumentResourceManager::DocumentResource
*/
KoShape *koShapeResource(int key) const;
/**
* Return the resource determined by param key as a QString .
* @param key the identifying key for the resource
* @see KoCanvasResourceProvider::CanvasResource KoDocumentResourceManager::DocumentResource
*/
QString stringResource(int key) const;
/**
* Return the resource determined by param key as a QSizeF.
* @param key the identifying key for the resource
* @see KoCanvasResourceProvider::CanvasResource KoDocumentResourceManager::DocumentResource
*/
QSizeF sizeResource(int key) const;
/**
* Return the resource determined by param key as a KoUnit.
* @param key the identifying key for the resource
* @see KoCanvasResourceProvider::CanvasResource KoDocumentResourceManager::DocumentResource
*/
KoUnit unitResource(int key) const;
/**
* Returns true if there is a resource set with the requested key.
* @param key the identifying key for the resource
* @see KoCanvasResourceProvider::CanvasResource KoDocumentResourceManager::DocumentResource
*/
bool hasResource(int key) const;
/**
* Remove the resource with @p key from the provider.
* @param key the key that will be used to remove the resource
* @see KoCanvasResourceProvider::CanvasResource KoDocumentResourceManager::DocumentResource
*/
void clearResource(int key);
/**
* Some of the resources may be "derived" from the others. For
* example opacity, composite op and erase mode properties are
* contained inside a paintop preset, so we need not create a
* separate resource for them. Instead we created a derived resource,
* that loads/saves values from/to another resource, but has its own
* "resource changed" signal (via a different key).
*
* When a parent resource changes, the resource manager emits
* update signals for all its derived resources.
*/
void addDerivedResourceConverter(KoDerivedResourceConverterSP converter);
/**
* @return true if the resource with \p key is a derived resource
* (has a converter installed)
*
* @see addDerivedResourceConverter()
*/
bool hasDerivedResourceConverter(int key);
/**
* Removes a derived resource converter. If you rty to add a
* resource with \p key it will be treated as a usual resource.
*
* @see addDerivedResourceConverter()
*/
void removeDerivedResourceConverter(int key);
/**
* Some resources can "mutate", that is their value doesn't change
* (a pointer), whereas the contents changes. But we should still
* notify all the derived resources subscribers that the resource
* has changed. For that purpose we use a special mediator class
* that connects the resource (which is not a QObject at all) and
* the resource manager controls that connection.
*/
void addResourceUpdateMediator(KoResourceUpdateMediatorSP mediator);
/**
* \see addResourceUpdateMediator()
*/
bool hasResourceUpdateMediator(int key);
/**
* \see addResourceUpdateMediator()
*/
void removeResourceUpdateMediator(int key);
Q_SIGNALS:
void resourceChanged(int key, const QVariant &value);
private:
void notifyResourceChanged(int key, const QVariant &value);
void notifyDerivedResourcesChanged(int key, const QVariant &value);
private Q_SLOTS:
void slotResourceInternalsChanged(int key);
private:
KoResourceManager(const KoResourceManager&);
KoResourceManager& operator=(const KoResourceManager&);
QHash<int, QVariant> m_resources;
QHash<int, KoDerivedResourceConverterSP> m_derivedResources;
QMultiHash<int, KoDerivedResourceConverterSP> m_derivedFromSource;
QHash<int, KoResourceUpdateMediatorSP> m_updateMediators;
};
#endif
diff --git a/libs/flake/KoSelection.h b/libs/flake/KoSelection.h
index 0444813f75..b3e86b1729 100644
--- a/libs/flake/KoSelection.h
+++ b/libs/flake/KoSelection.h
@@ -1,169 +1,165 @@
/* This file is part of the KDE project
Copyright (C) 2006 Boudewijn Rempt <boud@valdyas.org>
Copyright (C) 2006 Thorsten Zachmann <zachmann@kde.org>
Copyright (C) 2007,2009 Thomas Zander <zander@kde.org>
Copyright (C) 2006,2007 Jan Hambrecht <jaham@gmx.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOSELECTION_H
#define KOSELECTION_H
#include <QObject>
#include "KoShape.h"
#include "KoFlake.h"
#include "kritaflake_export.h"
class KoViewConverter;
class KoShapeLayer;
class KoSelectionPrivate;
/**
* A selection is a shape that contains a number of references
* to shapes. That means that a selection can be manipulated in
* the same way as a single shape.
*
* Note that a single shape can be selected in one view, and not in
* another, and that in a single view, more than one selection can be
* present. So selections should not be seen as singletons, or as
* something completely transient.
*
* A selection, however, should not be selectable. We need to think
* a little about the interaction here.
*/
class KRITAFLAKE_EXPORT KoSelection : public QObject, public KoShape, public KoShape::ShapeChangeListener
{
Q_OBJECT
public:
KoSelection();
~KoSelection() override;
void paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintcontext) override;
void setSize(const QSizeF &size) override;
QSizeF size() const override;
QRectF outlineRect() const override;
QRectF boundingRect() const override;
/**
* Adds a shape to the selection.
*
* If the shape is a KoShapeGroup all of its child shapes are automatically added
* to the selection.
* If the shape has no parent or is not a KoShapeGroup, only the given shape is
* added to the selection.
* If the given shape is a child of a KoShapeGroup and recursive selection is enabled
* the all parents and their child shapes up to the toplevel KoShapeGroup are added to
* the selection.
*
* @param shape the shape to add to the selection
- * @param recursive enables recursively selecting shapes of parent groups
*/
void select(KoShape *shape);
/**
* Removes a selected shape.
*
* If the shape is a KoShapeGroup all of its child shapes are automatically removed
* from the selection.
* If the shape has no parent or is not a KoShapeGroup, only the given shape is
* removed from the selection.
* If the given shape is a child of a KoShapeGroup and recursive selection is enabled
* the all parents and their child shape up to the toplevel KoShapeGroup are removed
* from the selection.
*
* @param shape the shape to remove from the selection
- * @param recursive enables recursively deselecting shapes of parent groups
*/
void deselect(KoShape *shape);
/// clear the selections list
void deselectAll();
/**
* Return the list of selected shapes
* @return the list of selected shapes
*/
const QList<KoShape*> selectedShapes() const;
/**
* Same as selectedShapes() but only for shapes in visible state. Used by
* the algorithms that draw shapes on the image
*/
const QList<KoShape*> selectedVisibleShapes() const;
/**
* Same as selectedShapes() but only for editable shapes. Used by
* the algorithms that modify the image
*/
const QList<KoShape*> selectedEditableShapes() const;
/**
* Same as selectedEditableShapes() but also includes shapes delegates.
* Used for
*/
const QList<KoShape*> selectedEditableShapesAndDelegates() const;
/**
* Return the first selected shape, or 0 if there is nothing selected.
- * @param strip if StrippedSelection, the returned list will not include any children
- * of a grouped shape if the group-parent is itself also in the set.
*/
KoShape *firstSelectedShape() const;
/// return true if the shape is selected
bool isSelected(const KoShape *shape) const;
/// return the selection count, i.e. the number of all selected shapes
int count() const;
bool hitTest(const QPointF &position) const override;
/**
* Sets the currently active layer.
* @param layer the new active layer
*/
void setActiveLayer(KoShapeLayer *layer);
/**
* Returns a currently active layer.
*
* @return the currently active layer, or zero if there is none
*/
KoShapeLayer *activeLayer() const;
void notifyShapeChanged(ChangeType type, KoShape *shape) override;
Q_SIGNALS:
/// emitted when the selection is changed
void selectionChanged();
/// emitted when the current layer is changed
void currentLayerChanged(const KoShapeLayer *layer);
private:
void saveOdf(KoShapeSavingContext &) const override;
bool loadOdf(const KoXmlElement &, KoShapeLoadingContext &) override;
Q_DECLARE_PRIVATE_D(KoShape::d_ptr, KoSelection)
};
#endif
diff --git a/libs/flake/KoShape.cpp b/libs/flake/KoShape.cpp
index 2a523c80d0..89a4acb18a 100644
--- a/libs/flake/KoShape.cpp
+++ b/libs/flake/KoShape.cpp
@@ -1,2543 +1,2541 @@
/* This file is part of the KDE project
Copyright (C) 2006 C. Boemann Rasmussen <cbo@boemann.dk>
Copyright (C) 2006-2010 Thomas Zander <zander@kde.org>
Copyright (C) 2006-2010 Thorsten Zachmann <zachmann@kde.org>
Copyright (C) 2007-2009,2011 Jan Hambrecht <jaham@gmx.net>
CopyRight (C) 2010 Boudewijn Rempt <boud@valdyas.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <limits>
#include "KoShape.h"
#include "KoShape_p.h"
#include "KoShapeContainer.h"
#include "KoShapeLayer.h"
#include "KoShapeContainerModel.h"
#include "KoSelection.h"
#include "KoPointerEvent.h"
#include "KoInsets.h"
#include "KoShapeStrokeModel.h"
#include "KoShapeBackground.h"
#include "KoColorBackground.h"
#include "KoHatchBackground.h"
#include "KoGradientBackground.h"
#include "KoPatternBackground.h"
#include "KoShapeManager.h"
#include "KoShapeUserData.h"
#include "KoShapeApplicationData.h"
#include "KoShapeSavingContext.h"
#include "KoShapeLoadingContext.h"
#include "KoViewConverter.h"
#include "KoShapeStroke.h"
#include "KoShapeShadow.h"
#include "KoClipPath.h"
#include "KoPathShape.h"
#include "KoOdfWorkaround.h"
#include "KoFilterEffectStack.h"
#include <KoSnapData.h>
#include <KoElementReference.h>
#include <KoXmlReader.h>
#include <KoXmlWriter.h>
#include <KoXmlNS.h>
#include <KoGenStyle.h>
#include <KoGenStyles.h>
#include <KoUnit.h>
#include <KoOdfStylesReader.h>
#include <KoOdfGraphicStyles.h>
#include <KoOdfLoadingContext.h>
#include <KoStyleStack.h>
#include <KoBorder.h>
#include <QPainter>
#include <QVariant>
#include <QPainterPath>
#include <QList>
#include <QMap>
#include <QByteArray>
#include <FlakeDebug.h>
#include "kis_assert.h"
#include "KoOdfGradientBackground.h"
#include <KisHandlePainterHelper.h>
// KoShapePrivate
KoShapePrivate::KoShapePrivate(KoShape *shape)
: q_ptr(shape),
size(50, 50),
parent(0),
shadow(0),
border(0),
filterEffectStack(0),
transparency(0.0),
zIndex(0),
runThrough(0),
visible(true),
printable(true),
geometryProtected(false),
keepAspect(false),
selectable(true),
detectCollision(false),
protectContent(false),
textRunAroundSide(KoShape::BiggestRunAroundSide),
textRunAroundDistanceLeft(0.0),
textRunAroundDistanceTop(0.0),
textRunAroundDistanceRight(0.0),
textRunAroundDistanceBottom(0.0),
textRunAroundThreshold(0.0),
textRunAroundContour(KoShape::ContourFull)
{
connectors[KoConnectionPoint::TopConnectionPoint] = KoConnectionPoint::defaultConnectionPoint(KoConnectionPoint::TopConnectionPoint);
connectors[KoConnectionPoint::RightConnectionPoint] = KoConnectionPoint::defaultConnectionPoint(KoConnectionPoint::RightConnectionPoint);
connectors[KoConnectionPoint::BottomConnectionPoint] = KoConnectionPoint::defaultConnectionPoint(KoConnectionPoint::BottomConnectionPoint);
connectors[KoConnectionPoint::LeftConnectionPoint] = KoConnectionPoint::defaultConnectionPoint(KoConnectionPoint::LeftConnectionPoint);
connectors[KoConnectionPoint::FirstCustomConnectionPoint] = KoConnectionPoint(QPointF(0.5, 0.5), KoConnectionPoint::AllDirections, KoConnectionPoint::AlignCenter);
}
KoShapePrivate::KoShapePrivate(const KoShapePrivate &rhs, KoShape *q)
: q_ptr(q),
size(rhs.size),
shapeId(rhs.shapeId),
name(rhs.name),
localMatrix(rhs.localMatrix),
connectors(rhs.connectors),
parent(0), // to be initialized later
shapeManagers(), // to be initialized later
toolDelegates(), // FIXME: how to initialize them?
userData(rhs.userData ? rhs.userData->clone() : 0),
stroke(rhs.stroke),
fill(rhs.fill),
inheritBackground(rhs.inheritBackground),
inheritStroke(rhs.inheritStroke),
dependees(), // FIXME: how to initialize them?
shadow(0), // WARNING: not implemented in Krita
border(0), // WARNING: not implemented in Krita
clipPath(rhs.clipPath ? rhs.clipPath->clone() : 0),
clipMask(rhs.clipMask ? rhs.clipMask->clone() : 0),
additionalAttributes(rhs.additionalAttributes),
additionalStyleAttributes(rhs.additionalStyleAttributes),
filterEffectStack(0), // WARNING: not implemented in Krita
transparency(rhs.transparency),
hyperLink(rhs.hyperLink),
zIndex(rhs.zIndex),
runThrough(rhs.runThrough),
visible(rhs.visible),
printable(rhs.visible),
geometryProtected(rhs.geometryProtected),
keepAspect(rhs.keepAspect),
selectable(rhs.selectable),
detectCollision(rhs.detectCollision),
protectContent(rhs.protectContent),
textRunAroundSide(rhs.textRunAroundSide),
textRunAroundDistanceLeft(rhs.textRunAroundDistanceLeft),
textRunAroundDistanceTop(rhs.textRunAroundDistanceTop),
textRunAroundDistanceRight(rhs.textRunAroundDistanceRight),
textRunAroundDistanceBottom(rhs.textRunAroundDistanceBottom),
textRunAroundThreshold(rhs.textRunAroundThreshold),
textRunAroundContour(rhs.textRunAroundContour)
{
}
KoShapePrivate::~KoShapePrivate()
{
Q_Q(KoShape);
/**
* The shape must have already been detached from all the parents and
* shape managers. Otherwise we migh accidentally request some RTTI
* information, which is not available anymore (we are in d-tor).
*
* TL;DR: fix the code that caused this destruction without unparenting
* instead of trying to remove these assert!
*/
KIS_SAFE_ASSERT_RECOVER (!parent) {
parent->removeShape(q);
}
KIS_SAFE_ASSERT_RECOVER (shapeManagers.isEmpty()) {
Q_FOREACH (KoShapeManager *manager, shapeManagers) {
manager->shapeInterface()->notifyShapeDestructed(q);
}
shapeManagers.clear();
}
if (shadow && !shadow->deref())
delete shadow;
if (filterEffectStack && !filterEffectStack->deref())
delete filterEffectStack;
}
void KoShapePrivate::shapeChanged(KoShape::ChangeType type)
{
Q_Q(KoShape);
if (parent)
parent->model()->childChanged(q, type);
q->shapeChanged(type);
Q_FOREACH (KoShape * shape, dependees) {
shape->shapeChanged(type, q);
}
Q_FOREACH (KoShape::ShapeChangeListener *listener, listeners) {
listener->notifyShapeChangedImpl(type, q);
}
}
void KoShapePrivate::addShapeManager(KoShapeManager *manager)
{
shapeManagers.insert(manager);
}
void KoShapePrivate::removeShapeManager(KoShapeManager *manager)
{
shapeManagers.remove(manager);
}
void KoShapePrivate::convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
{
switch(point.alignment) {
case KoConnectionPoint::AlignNone:
point.position = KoFlake::toRelative(point.position, shapeSize);
point.position.rx() = qBound<qreal>(0.0, point.position.x(), 1.0);
point.position.ry() = qBound<qreal>(0.0, point.position.y(), 1.0);
break;
case KoConnectionPoint::AlignRight:
point.position.rx() -= shapeSize.width();
break;
case KoConnectionPoint::AlignLeft:
point.position.ry() = 0.5*shapeSize.height();
break;
case KoConnectionPoint::AlignBottom:
point.position.ry() -= shapeSize.height();
break;
case KoConnectionPoint::AlignTop:
point.position.rx() = 0.5*shapeSize.width();
break;
case KoConnectionPoint::AlignTopLeft:
// nothing to do here
break;
case KoConnectionPoint::AlignTopRight:
point.position.rx() -= shapeSize.width();
break;
case KoConnectionPoint::AlignBottomLeft:
point.position.ry() -= shapeSize.height();
break;
case KoConnectionPoint::AlignBottomRight:
point.position.rx() -= shapeSize.width();
point.position.ry() -= shapeSize.height();
break;
case KoConnectionPoint::AlignCenter:
point.position.rx() -= 0.5 * shapeSize.width();
point.position.ry() -= 0.5 * shapeSize.height();
break;
}
}
void KoShapePrivate::convertToShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const
{
switch(point.alignment) {
case KoConnectionPoint::AlignNone:
point.position = KoFlake::toAbsolute(point.position, shapeSize);
break;
case KoConnectionPoint::AlignRight:
point.position.rx() += shapeSize.width();
break;
case KoConnectionPoint::AlignLeft:
point.position.ry() = 0.5*shapeSize.height();
break;
case KoConnectionPoint::AlignBottom:
point.position.ry() += shapeSize.height();
break;
case KoConnectionPoint::AlignTop:
point.position.rx() = 0.5*shapeSize.width();
break;
case KoConnectionPoint::AlignTopLeft:
// nothing to do here
break;
case KoConnectionPoint::AlignTopRight:
point.position.rx() += shapeSize.width();
break;
case KoConnectionPoint::AlignBottomLeft:
point.position.ry() += shapeSize.height();
break;
case KoConnectionPoint::AlignBottomRight:
point.position.rx() += shapeSize.width();
point.position.ry() += shapeSize.height();
break;
case KoConnectionPoint::AlignCenter:
point.position.rx() += 0.5 * shapeSize.width();
point.position.ry() += 0.5 * shapeSize.height();
break;
}
}
// static
QString KoShapePrivate::getStyleProperty(const char *property, KoShapeLoadingContext &context)
{
KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
QString value;
if (styleStack.hasProperty(KoXmlNS::draw, property)) {
value = styleStack.property(KoXmlNS::draw, property);
}
return value;
}
// ======== KoShape
const qint16 KoShape::maxZIndex = std::numeric_limits<qint16>::max();
const qint16 KoShape::minZIndex = std::numeric_limits<qint16>::min();
KoShape::KoShape()
: d_ptr(new KoShapePrivate(this))
{
notifyChanged();
}
KoShape::KoShape(KoShapePrivate *dd)
: d_ptr(dd)
{
}
KoShape::~KoShape()
{
Q_D(KoShape);
d->shapeChanged(Deleted);
d->listeners.clear();
delete d_ptr;
}
KoShape *KoShape::cloneShape() const
{
KIS_SAFE_ASSERT_RECOVER_NOOP(0 && "not implemented!");
qWarning() << shapeId() << "cannot be cloned";
return 0;
}
void KoShape::paintStroke(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintcontext)
{
Q_UNUSED(paintcontext);
if (stroke()) {
stroke()->paint(this, painter, converter);
}
}
void KoShape::scale(qreal sx, qreal sy)
{
Q_D(KoShape);
QPointF pos = position();
QTransform scaleMatrix;
scaleMatrix.translate(pos.x(), pos.y());
scaleMatrix.scale(sx, sy);
scaleMatrix.translate(-pos.x(), -pos.y());
d->localMatrix = d->localMatrix * scaleMatrix;
notifyChanged();
d->shapeChanged(ScaleChanged);
}
void KoShape::rotate(qreal angle)
{
Q_D(KoShape);
QPointF center = d->localMatrix.map(QPointF(0.5 * size().width(), 0.5 * size().height()));
QTransform rotateMatrix;
rotateMatrix.translate(center.x(), center.y());
rotateMatrix.rotate(angle);
rotateMatrix.translate(-center.x(), -center.y());
d->localMatrix = d->localMatrix * rotateMatrix;
notifyChanged();
d->shapeChanged(RotationChanged);
}
void KoShape::shear(qreal sx, qreal sy)
{
Q_D(KoShape);
QPointF pos = position();
QTransform shearMatrix;
shearMatrix.translate(pos.x(), pos.y());
shearMatrix.shear(sx, sy);
shearMatrix.translate(-pos.x(), -pos.y());
d->localMatrix = d->localMatrix * shearMatrix;
notifyChanged();
d->shapeChanged(ShearChanged);
}
void KoShape::setSize(const QSizeF &newSize)
{
Q_D(KoShape);
QSizeF oldSize(size());
// always set size, as d->size and size() may vary
d->size = newSize;
if (oldSize == newSize)
return;
notifyChanged();
d->shapeChanged(SizeChanged);
}
void KoShape::setPosition(const QPointF &newPosition)
{
Q_D(KoShape);
QPointF currentPos = position();
if (newPosition == currentPos)
return;
QTransform translateMatrix;
translateMatrix.translate(newPosition.x() - currentPos.x(), newPosition.y() - currentPos.y());
d->localMatrix = d->localMatrix * translateMatrix;
notifyChanged();
d->shapeChanged(PositionChanged);
}
bool KoShape::hitTest(const QPointF &position) const
{
Q_D(const KoShape);
if (d->parent && d->parent->isClipped(this) && !d->parent->hitTest(position))
return false;
QPointF point = absoluteTransformation(0).inverted().map(position);
QRectF bb = outlineRect();
if (d->stroke) {
KoInsets insets;
d->stroke->strokeInsets(this, insets);
bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
}
if (bb.contains(point))
return true;
// if there is no shadow we can as well just leave
if (! d->shadow)
return false;
// the shadow has an offset to the shape, so we simply
// check if the position minus the shadow offset hits the shape
point = absoluteTransformation(0).inverted().map(position - d->shadow->offset());
return bb.contains(point);
}
QRectF KoShape::boundingRect() const
{
Q_D(const KoShape);
QTransform transform = absoluteTransformation(0);
QRectF bb = outlineRect();
if (d->stroke) {
KoInsets insets;
d->stroke->strokeInsets(this, insets);
bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
}
bb = transform.mapRect(bb);
if (d->shadow) {
KoInsets insets;
d->shadow->insets(insets);
bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
}
if (d->filterEffectStack) {
QRectF clipRect = d->filterEffectStack->clipRectForBoundingRect(outlineRect());
bb |= transform.mapRect(clipRect);
}
return bb;
}
QRectF KoShape::boundingRect(const QList<KoShape *> &shapes)
{
QRectF boundingRect;
Q_FOREACH (KoShape *shape, shapes) {
boundingRect |= shape->boundingRect();
}
return boundingRect;
}
QRectF KoShape::absoluteOutlineRect(KoViewConverter *converter) const
{
return absoluteTransformation(converter).map(outline()).boundingRect();
}
QRectF KoShape::absoluteOutlineRect(const QList<KoShape *> &shapes, KoViewConverter *converter)
{
QRectF absoluteOutlineRect;
Q_FOREACH (KoShape *shape, shapes) {
absoluteOutlineRect |= shape->absoluteOutlineRect(converter);
}
return absoluteOutlineRect;
}
QTransform KoShape::absoluteTransformation(const KoViewConverter *converter) const
{
Q_D(const KoShape);
QTransform matrix;
// apply parents matrix to inherit any transformations done there.
KoShapeContainer * container = d->parent;
if (container) {
if (container->inheritsTransform(this)) {
// We do need to pass the converter here, otherwise the parent's
// translation is not inherited.
matrix = container->absoluteTransformation(converter);
} else {
QSizeF containerSize = container->size();
QPointF containerPos = container->absolutePosition() - QPointF(0.5 * containerSize.width(), 0.5 * containerSize.height());
if (converter)
containerPos = converter->documentToView(containerPos);
matrix.translate(containerPos.x(), containerPos.y());
}
}
if (converter) {
QPointF pos = d->localMatrix.map(QPointF());
QPointF trans = converter->documentToView(pos) - pos;
matrix.translate(trans.x(), trans.y());
}
return d->localMatrix * matrix;
}
void KoShape::applyAbsoluteTransformation(const QTransform &matrix)
{
QTransform globalMatrix = absoluteTransformation(0);
// the transformation is relative to the global coordinate system
// but we want to change the local matrix, so convert the matrix
// to be relative to the local coordinate system
QTransform transformMatrix = globalMatrix * matrix * globalMatrix.inverted();
applyTransformation(transformMatrix);
}
void KoShape::applyTransformation(const QTransform &matrix)
{
Q_D(KoShape);
d->localMatrix = matrix * d->localMatrix;
notifyChanged();
d->shapeChanged(GenericMatrixChange);
}
void KoShape::setTransformation(const QTransform &matrix)
{
Q_D(KoShape);
d->localMatrix = matrix;
notifyChanged();
d->shapeChanged(GenericMatrixChange);
}
QTransform KoShape::transformation() const
{
Q_D(const KoShape);
return d->localMatrix;
}
KoShape::ChildZOrderPolicy KoShape::childZOrderPolicy()
{
return ChildZDefault;
}
bool KoShape::compareShapeZIndex(KoShape *s1, KoShape *s2)
{
/**
* WARNING: Our definition of zIndex is not yet compatible with SVG2's
* definition. In SVG stacking context of groups with the same
* zIndex are **merged**, while in Krita the contents of groups
* is never merged. One group will always below than the other.
* Therefore, when zIndex of two groups inside the same parent
* coincide, the resulting painting order in Krita is
* **UNDEFINED**.
*
* To avoid this trouble we use KoShapeReorderCommand::mergeInShape()
* inside KoShapeCreateCommand.
*/
/**
* The algorithm below doesn't correctly handle the case when the two pointers actually
* point to the same shape. So just check it in advance to guarantee strict weak ordering
* relation requirement
*/
if (s1 == s2) return false;
// First sort according to runThrough which is sort of a master level
KoShape *parentShapeS1 = s1->parent();
KoShape *parentShapeS2 = s2->parent();
int runThrough1 = s1->runThrough();
int runThrough2 = s2->runThrough();
while (parentShapeS1) {
if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
runThrough1 = parentShapeS1->runThrough();
} else {
runThrough1 = runThrough1 + parentShapeS1->runThrough();
}
parentShapeS1 = parentShapeS1->parent();
}
while (parentShapeS2) {
if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) {
runThrough2 = parentShapeS2->runThrough();
} else {
runThrough2 = runThrough2 + parentShapeS2->runThrough();
}
parentShapeS2 = parentShapeS2->parent();
}
if (runThrough1 > runThrough2) {
return false;
}
if (runThrough1 < runThrough2) {
return true;
}
// If on the same runThrough level then the zIndex is all that matters.
//
// We basically walk up through the parents until we find a common base parent
// To do that we need two loops where the inner loop walks up through the parents
// of s2 every time we step up one parent level on s1
//
// We don't update the index value until after we have seen that it's not a common base
// That way we ensure that two children of a common base are sorted according to their respective
// z value
bool foundCommonParent = false;
int index1 = s1->zIndex();
int index2 = s2->zIndex();
parentShapeS1 = s1;
parentShapeS2 = s2;
while (parentShapeS1 && !foundCommonParent) {
parentShapeS2 = s2;
index2 = parentShapeS2->zIndex();
while (parentShapeS2) {
if (parentShapeS2 == parentShapeS1) {
foundCommonParent = true;
break;
}
if (parentShapeS2->childZOrderPolicy() == KoShape::ChildZParentChild) {
index2 = parentShapeS2->zIndex();
}
parentShapeS2 = parentShapeS2->parent();
}
if (!foundCommonParent) {
if (parentShapeS1->childZOrderPolicy() == KoShape::ChildZParentChild) {
index1 = parentShapeS1->zIndex();
}
parentShapeS1 = parentShapeS1->parent();
}
}
// If the one shape is a parent/child of the other then sort so.
if (s1 == parentShapeS2) {
return true;
}
if (s2 == parentShapeS1) {
return false;
}
// If we went that far then the z-Index is used for sorting.
return index1 < index2;
}
void KoShape::setParent(KoShapeContainer *parent)
{
Q_D(KoShape);
if (d->parent == parent) {
return;
}
KoShapeContainer *oldParent = d->parent;
d->parent = 0; // avoids recursive removing
if (oldParent) {
oldParent->shapeInterface()->removeShape(this);
}
KIS_SAFE_ASSERT_RECOVER_NOOP(parent != this);
if (parent && parent != this) {
d->parent = parent;
parent->shapeInterface()->addShape(this);
}
notifyChanged();
d->shapeChanged(ParentChanged);
}
bool KoShape::inheritsTransformFromAny(const QList<KoShape *> ancestorsInQuestion) const
{
bool result = false;
KoShape *shape = const_cast<KoShape*>(this);
while (shape) {
KoShapeContainer *parent = shape->parent();
if (parent && !parent->inheritsTransform(shape)) {
break;
}
if (ancestorsInQuestion.contains(shape)) {
result = true;
break;
}
shape = parent;
}
return result;
}
bool KoShape::hasCommonParent(const KoShape *shape) const
{
const KoShape *thisShape = this;
while (thisShape) {
const KoShape *otherShape = shape;
while (otherShape) {
if (thisShape == otherShape) {
return true;
}
otherShape = otherShape->parent();
}
thisShape = thisShape->parent();
}
return false;
}
qint16 KoShape::zIndex() const
{
Q_D(const KoShape);
return d->zIndex;
}
void KoShape::update() const
{
Q_D(const KoShape);
if (!d->shapeManagers.empty()) {
QRectF rect(boundingRect());
Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
manager->update(rect, this, true);
}
}
}
void KoShape::updateAbsolute(const QRectF &rect) const
{
if (rect.isEmpty() && !rect.isNull()) {
return;
}
Q_D(const KoShape);
if (!d->shapeManagers.empty() && isVisible()) {
Q_FOREACH (KoShapeManager *manager, d->shapeManagers) {
manager->update(rect);
}
}
}
QPainterPath KoShape::outline() const
{
QPainterPath path;
path.addRect(outlineRect());
return path;
}
QRectF KoShape::outlineRect() const
{
const QSizeF s = size();
return QRectF(QPointF(0, 0), QSizeF(qMax(s.width(), qreal(0.0001)),
qMax(s.height(), qreal(0.0001))));
}
QPainterPath KoShape::shadowOutline() const
{
if (background()) {
return outline();
}
return QPainterPath();
}
QPointF KoShape::absolutePosition(KoFlake::AnchorPosition anchor) const
{
const QRectF rc = outlineRect();
QPointF point = rc.topLeft();
bool valid = false;
QPointF anchoredPoint = KoFlake::anchorToPoint(anchor, rc, &valid);
if (valid) {
point = anchoredPoint;
}
return absoluteTransformation(0).map(point);
}
void KoShape::setAbsolutePosition(const QPointF &newPosition, KoFlake::AnchorPosition anchor)
{
Q_D(KoShape);
QPointF currentAbsPosition = absolutePosition(anchor);
QPointF translate = newPosition - currentAbsPosition;
QTransform translateMatrix;
translateMatrix.translate(translate.x(), translate.y());
applyAbsoluteTransformation(translateMatrix);
notifyChanged();
d->shapeChanged(PositionChanged);
}
void KoShape::copySettings(const KoShape *shape)
{
Q_D(KoShape);
d->size = shape->size();
d->connectors.clear();
Q_FOREACH (const KoConnectionPoint &point, shape->connectionPoints())
addConnectionPoint(point);
d->zIndex = shape->zIndex();
d->visible = shape->isVisible(false);
// Ensure printable is true by default
if (!d->visible)
d->printable = true;
else
d->printable = shape->isPrintable();
d->geometryProtected = shape->isGeometryProtected();
d->protectContent = shape->isContentProtected();
d->selectable = shape->isSelectable();
d->keepAspect = shape->keepAspectRatio();
d->localMatrix = shape->d_ptr->localMatrix;
}
void KoShape::notifyChanged()
{
Q_D(KoShape);
Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
manager->notifyShapeChanged(this);
}
}
void KoShape::setUserData(KoShapeUserData *userData)
{
Q_D(KoShape);
d->userData.reset(userData);
}
KoShapeUserData *KoShape::userData() const
{
Q_D(const KoShape);
return d->userData.data();
}
bool KoShape::hasTransparency() const
{
Q_D(const KoShape);
QSharedPointer<KoShapeBackground> bg = background();
return !bg || bg->hasTransparency() || d->transparency > 0.0;
}
void KoShape::setTransparency(qreal transparency)
{
Q_D(KoShape);
d->transparency = qBound<qreal>(0.0, transparency, 1.0);
d->shapeChanged(TransparencyChanged);
notifyChanged();
}
qreal KoShape::transparency(bool recursive) const
{
Q_D(const KoShape);
if (!recursive || !parent()) {
return d->transparency;
} else {
const qreal parentOpacity = 1.0-parent()->transparency(recursive);
const qreal childOpacity = 1.0-d->transparency;
return 1.0-(parentOpacity*childOpacity);
}
}
KoInsets KoShape::strokeInsets() const
{
Q_D(const KoShape);
KoInsets answer;
if (d->stroke)
d->stroke->strokeInsets(this, answer);
return answer;
}
qreal KoShape::rotation() const
{
Q_D(const KoShape);
// try to extract the rotation angle out of the local matrix
// if it is a pure rotation matrix
// check if the matrix has shearing mixed in
if (fabs(fabs(d->localMatrix.m12()) - fabs(d->localMatrix.m21())) > 1e-10)
return std::numeric_limits<qreal>::quiet_NaN();
// check if the matrix has scaling mixed in
if (fabs(d->localMatrix.m11() - d->localMatrix.m22()) > 1e-10)
return std::numeric_limits<qreal>::quiet_NaN();
// calculate the angle from the matrix elements
qreal angle = atan2(-d->localMatrix.m21(), d->localMatrix.m11()) * 180.0 / M_PI;
if (angle < 0.0)
angle += 360.0;
return angle;
}
QSizeF KoShape::size() const
{
Q_D(const KoShape);
return d->size;
}
QPointF KoShape::position() const
{
Q_D(const KoShape);
QPointF center = outlineRect().center();
return d->localMatrix.map(center) - center;
}
int KoShape::addConnectionPoint(const KoConnectionPoint &point)
{
Q_D(KoShape);
// get next glue point id
int nextConnectionPointId = KoConnectionPoint::FirstCustomConnectionPoint;
if (d->connectors.size())
nextConnectionPointId = qMax(nextConnectionPointId, (--d->connectors.end()).key()+1);
KoConnectionPoint p = point;
d->convertFromShapeCoordinates(p, size());
d->connectors[nextConnectionPointId] = p;
return nextConnectionPointId;
}
bool KoShape::setConnectionPoint(int connectionPointId, const KoConnectionPoint &point)
{
Q_D(KoShape);
if (connectionPointId < 0)
return false;
const bool insertPoint = !hasConnectionPoint(connectionPointId);
switch(connectionPointId) {
case KoConnectionPoint::TopConnectionPoint:
case KoConnectionPoint::RightConnectionPoint:
case KoConnectionPoint::BottomConnectionPoint:
case KoConnectionPoint::LeftConnectionPoint:
{
KoConnectionPoint::PointId id = static_cast<KoConnectionPoint::PointId>(connectionPointId);
d->connectors[id] = KoConnectionPoint::defaultConnectionPoint(id);
break;
}
default:
{
KoConnectionPoint p = point;
d->convertFromShapeCoordinates(p, size());
d->connectors[connectionPointId] = p;
break;
}
}
if(!insertPoint)
d->shapeChanged(ConnectionPointChanged);
return true;
}
bool KoShape::hasConnectionPoint(int connectionPointId) const
{
Q_D(const KoShape);
return d->connectors.contains(connectionPointId);
}
KoConnectionPoint KoShape::connectionPoint(int connectionPointId) const
{
Q_D(const KoShape);
KoConnectionPoint p = d->connectors.value(connectionPointId, KoConnectionPoint());
// convert glue point to shape coordinates
d->convertToShapeCoordinates(p, size());
return p;
}
KoConnectionPoints KoShape::connectionPoints() const
{
Q_D(const KoShape);
QSizeF s = size();
KoConnectionPoints points = d->connectors;
KoConnectionPoints::iterator point = points.begin();
KoConnectionPoints::iterator lastPoint = points.end();
// convert glue points to shape coordinates
for(; point != lastPoint; ++point) {
d->convertToShapeCoordinates(point.value(), s);
}
return points;
}
void KoShape::removeConnectionPoint(int connectionPointId)
{
Q_D(KoShape);
d->connectors.remove(connectionPointId);
d->shapeChanged(ConnectionPointChanged);
}
void KoShape::clearConnectionPoints()
{
Q_D(KoShape);
d->connectors.clear();
}
KoShape::TextRunAroundSide KoShape::textRunAroundSide() const
{
Q_D(const KoShape);
return d->textRunAroundSide;
}
void KoShape::setTextRunAroundSide(TextRunAroundSide side, RunThroughLevel runThrought)
{
Q_D(KoShape);
if (side == RunThrough) {
if (runThrought == Background) {
setRunThrough(-1);
} else {
setRunThrough(1);
}
} else {
setRunThrough(0);
}
if ( d->textRunAroundSide == side) {
return;
}
d->textRunAroundSide = side;
notifyChanged();
d->shapeChanged(TextRunAroundChanged);
}
qreal KoShape::textRunAroundDistanceTop() const
{
Q_D(const KoShape);
return d->textRunAroundDistanceTop;
}
void KoShape::setTextRunAroundDistanceTop(qreal distance)
{
Q_D(KoShape);
d->textRunAroundDistanceTop = distance;
}
qreal KoShape::textRunAroundDistanceLeft() const
{
Q_D(const KoShape);
return d->textRunAroundDistanceLeft;
}
void KoShape::setTextRunAroundDistanceLeft(qreal distance)
{
Q_D(KoShape);
d->textRunAroundDistanceLeft = distance;
}
qreal KoShape::textRunAroundDistanceRight() const
{
Q_D(const KoShape);
return d->textRunAroundDistanceRight;
}
void KoShape::setTextRunAroundDistanceRight(qreal distance)
{
Q_D(KoShape);
d->textRunAroundDistanceRight = distance;
}
qreal KoShape::textRunAroundDistanceBottom() const
{
Q_D(const KoShape);
return d->textRunAroundDistanceBottom;
}
void KoShape::setTextRunAroundDistanceBottom(qreal distance)
{
Q_D(KoShape);
d->textRunAroundDistanceBottom = distance;
}
qreal KoShape::textRunAroundThreshold() const
{
Q_D(const KoShape);
return d->textRunAroundThreshold;
}
void KoShape::setTextRunAroundThreshold(qreal threshold)
{
Q_D(KoShape);
d->textRunAroundThreshold = threshold;
}
KoShape::TextRunAroundContour KoShape::textRunAroundContour() const
{
Q_D(const KoShape);
return d->textRunAroundContour;
}
void KoShape::setTextRunAroundContour(KoShape::TextRunAroundContour contour)
{
Q_D(KoShape);
d->textRunAroundContour = contour;
}
void KoShape::setBackground(QSharedPointer<KoShapeBackground> fill)
{
Q_D(KoShape);
d->inheritBackground = false;
d->fill = fill;
d->shapeChanged(BackgroundChanged);
notifyChanged();
}
QSharedPointer<KoShapeBackground> KoShape::background() const
{
Q_D(const KoShape);
QSharedPointer<KoShapeBackground> bg;
if (!d->inheritBackground) {
bg = d->fill;
} else if (parent()) {
bg = parent()->background();
}
return bg;
}
void KoShape::setInheritBackground(bool value)
{
Q_D(KoShape);
d->inheritBackground = value;
if (d->inheritBackground) {
d->fill.clear();
}
}
bool KoShape::inheritBackground() const
{
Q_D(const KoShape);
return d->inheritBackground;
}
void KoShape::setZIndex(qint16 zIndex)
{
Q_D(KoShape);
if (d->zIndex == zIndex)
return;
d->zIndex = zIndex;
notifyChanged();
}
int KoShape::runThrough()
{
Q_D(const KoShape);
return d->runThrough;
}
void KoShape::setRunThrough(short int runThrough)
{
Q_D(KoShape);
d->runThrough = runThrough;
}
void KoShape::setVisible(bool on)
{
Q_D(KoShape);
int _on = (on ? 1 : 0);
if (d->visible == _on) return;
d->visible = _on;
}
bool KoShape::isVisible(bool recursive) const
{
Q_D(const KoShape);
if (!recursive)
return d->visible;
if (!d->visible)
return false;
KoShapeContainer * parentShape = parent();
if (parentShape) {
return parentShape->isVisible(true);
}
return true;
}
void KoShape::setPrintable(bool on)
{
Q_D(KoShape);
d->printable = on;
}
bool KoShape::isPrintable() const
{
Q_D(const KoShape);
if (d->visible)
return d->printable;
else
return false;
}
void KoShape::setSelectable(bool selectable)
{
Q_D(KoShape);
d->selectable = selectable;
}
bool KoShape::isSelectable() const
{
Q_D(const KoShape);
return d->selectable;
}
void KoShape::setGeometryProtected(bool on)
{
Q_D(KoShape);
d->geometryProtected = on;
}
bool KoShape::isGeometryProtected() const
{
Q_D(const KoShape);
return d->geometryProtected;
}
void KoShape::setContentProtected(bool protect)
{
Q_D(KoShape);
d->protectContent = protect;
}
bool KoShape::isContentProtected() const
{
Q_D(const KoShape);
return d->protectContent;
}
KoShapeContainer *KoShape::parent() const
{
Q_D(const KoShape);
return d->parent;
}
void KoShape::setKeepAspectRatio(bool keepAspect)
{
Q_D(KoShape);
d->keepAspect = keepAspect;
d->shapeChanged(KeepAspectRatioChange);
notifyChanged();
}
bool KoShape::keepAspectRatio() const
{
Q_D(const KoShape);
return d->keepAspect;
}
QString KoShape::shapeId() const
{
Q_D(const KoShape);
return d->shapeId;
}
void KoShape::setShapeId(const QString &id)
{
Q_D(KoShape);
d->shapeId = id;
}
void KoShape::setCollisionDetection(bool detect)
{
Q_D(KoShape);
d->detectCollision = detect;
}
bool KoShape::collisionDetection()
{
Q_D(KoShape);
return d->detectCollision;
}
KoShapeStrokeModelSP KoShape::stroke() const
{
Q_D(const KoShape);
KoShapeStrokeModelSP stroke;
if (!d->inheritStroke) {
stroke = d->stroke;
} else if (parent()) {
stroke = parent()->stroke();
}
return stroke;
}
void KoShape::setStroke(KoShapeStrokeModelSP stroke)
{
Q_D(KoShape);
d->inheritStroke = false;
d->stroke = stroke;
d->shapeChanged(StrokeChanged);
notifyChanged();
}
void KoShape::setInheritStroke(bool value)
{
Q_D(KoShape);
d->inheritStroke = value;
if (d->inheritStroke) {
d->stroke.clear();
}
}
bool KoShape::inheritStroke() const
{
Q_D(const KoShape);
return d->inheritStroke;
}
void KoShape::setShadow(KoShapeShadow *shadow)
{
Q_D(KoShape);
if (d->shadow)
d->shadow->deref();
d->shadow = shadow;
if (d->shadow) {
d->shadow->ref();
// TODO update changed area
}
d->shapeChanged(ShadowChanged);
notifyChanged();
}
KoShapeShadow *KoShape::shadow() const
{
Q_D(const KoShape);
return d->shadow;
}
void KoShape::setBorder(KoBorder *border)
{
Q_D(KoShape);
if (d->border) {
// The shape owns the border.
delete d->border;
}
d->border = border;
d->shapeChanged(BorderChanged);
notifyChanged();
}
KoBorder *KoShape::border() const
{
Q_D(const KoShape);
return d->border;
}
void KoShape::setClipPath(KoClipPath *clipPath)
{
Q_D(KoShape);
d->clipPath.reset(clipPath);
d->shapeChanged(ClipPathChanged);
notifyChanged();
}
KoClipPath * KoShape::clipPath() const
{
Q_D(const KoShape);
return d->clipPath.data();
}
void KoShape::setClipMask(KoClipMask *clipMask)
{
Q_D(KoShape);
d->clipMask.reset(clipMask);
}
KoClipMask* KoShape::clipMask() const
{
Q_D(const KoShape);
return d->clipMask.data();
}
QTransform KoShape::transform() const
{
Q_D(const KoShape);
return d->localMatrix;
}
QString KoShape::name() const
{
Q_D(const KoShape);
return d->name;
}
void KoShape::setName(const QString &name)
{
Q_D(KoShape);
d->name = name;
}
void KoShape::waitUntilReady(const KoViewConverter &converter, bool asynchronous) const
{
Q_UNUSED(converter);
Q_UNUSED(asynchronous);
}
bool KoShape::isShapeEditable(bool recursive) const
{
Q_D(const KoShape);
if (!d->visible || d->geometryProtected)
return false;
if (recursive && d->parent) {
return d->parent->isShapeEditable(true);
}
return true;
}
// painting
void KoShape::paintBorder(QPainter &painter, const KoViewConverter &converter)
{
Q_UNUSED(converter);
KoBorder *bd = border();
if (!bd) {
return;
}
QRectF borderRect = QRectF(QPointF(0, 0), size());
// Paint the border.
bd->paint(painter, borderRect, KoBorder::PaintInsideLine);
}
// loading & saving methods
QString KoShape::saveStyle(KoGenStyle &style, KoShapeSavingContext &context) const
{
Q_D(const KoShape);
// and fill the style
KoShapeStrokeModelSP sm = stroke();
if (sm) {
sm->fillStyle(style, context);
}
else {
style.addProperty("draw:stroke", "none", KoGenStyle::GraphicType);
}
KoShapeShadow *s = shadow();
if (s)
s->fillStyle(style, context);
QSharedPointer<KoShapeBackground> bg = background();
if (bg) {
bg->fillStyle(style, context);
}
else {
style.addProperty("draw:fill", "none", KoGenStyle::GraphicType);
}
KoBorder *b = border();
if (b) {
b->saveOdf(style);
}
if (context.isSet(KoShapeSavingContext::AutoStyleInStyleXml)) {
style.setAutoStyleInStylesDotXml(true);
}
QString value;
if (isGeometryProtected()) {
value = "position size";
}
if (isContentProtected()) {
if (! value.isEmpty())
value += ' ';
value += "content";
}
if (!value.isEmpty()) {
style.addProperty("style:protect", value, KoGenStyle::GraphicType);
}
QMap<QByteArray, QString>::const_iterator it(d->additionalStyleAttributes.constBegin());
for (; it != d->additionalStyleAttributes.constEnd(); ++it) {
style.addProperty(it.key(), it.value());
}
if (parent() && parent()->isClipped(this)) {
/*
* In Calligra clipping is done using a parent shape which can be rotated, sheared etc
* and even non-square. So the ODF interoperability version we write here is really
* just a very simple version of that...
*/
qreal top = -position().y();
qreal left = -position().x();
qreal right = parent()->size().width() - size().width() - left;
qreal bottom = parent()->size().height() - size().height() - top;
style.addProperty("fo:clip", QString("rect(%1pt, %2pt, %3pt, %4pt)")
.arg(top, 10, 'f').arg(right, 10, 'f')
.arg(bottom, 10, 'f').arg(left, 10, 'f'), KoGenStyle::GraphicType);
}
QString wrap;
switch (textRunAroundSide()) {
case BiggestRunAroundSide:
wrap = "biggest";
break;
case LeftRunAroundSide:
wrap = "left";
break;
case RightRunAroundSide:
wrap = "right";
break;
case EnoughRunAroundSide:
wrap = "dynamic";
break;
case BothRunAroundSide:
wrap = "parallel";
break;
case NoRunAround:
wrap = "none";
break;
case RunThrough:
wrap = "run-through";
break;
}
style.addProperty("style:wrap", wrap, KoGenStyle::GraphicType);
switch (textRunAroundContour()) {
case ContourBox:
style.addProperty("style:wrap-contour", "false", KoGenStyle::GraphicType);
break;
case ContourFull:
style.addProperty("style:wrap-contour", "true", KoGenStyle::GraphicType);
style.addProperty("style:wrap-contour-mode", "full", KoGenStyle::GraphicType);
break;
case ContourOutside:
style.addProperty("style:wrap-contour", "true", KoGenStyle::GraphicType);
style.addProperty("style:wrap-contour-mode", "outside", KoGenStyle::GraphicType);
break;
}
style.addPropertyPt("style:wrap-dynamic-threshold", textRunAroundThreshold(), KoGenStyle::GraphicType);
if ((textRunAroundDistanceLeft() == textRunAroundDistanceRight())
&& (textRunAroundDistanceTop() == textRunAroundDistanceBottom())
&& (textRunAroundDistanceLeft() == textRunAroundDistanceTop())) {
style.addPropertyPt("fo:margin", textRunAroundDistanceLeft(), KoGenStyle::GraphicType);
} else {
style.addPropertyPt("fo:margin-left", textRunAroundDistanceLeft(), KoGenStyle::GraphicType);
style.addPropertyPt("fo:margin-top", textRunAroundDistanceTop(), KoGenStyle::GraphicType);
style.addPropertyPt("fo:margin-right", textRunAroundDistanceRight(), KoGenStyle::GraphicType);
style.addPropertyPt("fo:margin-bottom", textRunAroundDistanceBottom(), KoGenStyle::GraphicType);
}
return context.mainStyles().insert(style, context.isSet(KoShapeSavingContext::PresentationShape) ? "pr" : "gr");
}
void KoShape::loadStyle(const KoXmlElement &element, KoShapeLoadingContext &context)
{
Q_D(KoShape);
KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
styleStack.setTypeProperties("graphic");
d->fill.clear();
d->stroke.clear();
if (d->shadow && !d->shadow->deref()) {
delete d->shadow;
d->shadow = 0;
}
setBackground(loadOdfFill(context));
setStroke(loadOdfStroke(element, context));
setShadow(d->loadOdfShadow(context));
setBorder(d->loadOdfBorder(context));
QString protect(styleStack.property(KoXmlNS::style, "protect"));
setGeometryProtected(protect.contains("position") || protect.contains("size"));
setContentProtected(protect.contains("content"));
QString margin = styleStack.property(KoXmlNS::fo, "margin");
if (!margin.isEmpty()) {
setTextRunAroundDistanceLeft(KoUnit::parseValue(margin));
setTextRunAroundDistanceTop(KoUnit::parseValue(margin));
setTextRunAroundDistanceRight(KoUnit::parseValue(margin));
setTextRunAroundDistanceBottom(KoUnit::parseValue(margin));
}
margin = styleStack.property(KoXmlNS::fo, "margin-left");
if (!margin.isEmpty()) {
setTextRunAroundDistanceLeft(KoUnit::parseValue(margin));
}
margin = styleStack.property(KoXmlNS::fo, "margin-top");
if (!margin.isEmpty()) {
setTextRunAroundDistanceTop(KoUnit::parseValue(margin));
}
margin = styleStack.property(KoXmlNS::fo, "margin-right");
if (!margin.isEmpty()) {
setTextRunAroundDistanceRight(KoUnit::parseValue(margin));
}
margin = styleStack.property(KoXmlNS::fo, "margin-bottom");
if (!margin.isEmpty()) {
setTextRunAroundDistanceBottom(KoUnit::parseValue(margin));
}
QString wrap;
if (styleStack.hasProperty(KoXmlNS::style, "wrap")) {
wrap = styleStack.property(KoXmlNS::style, "wrap");
} else {
// no value given in the file, but guess biggest
wrap = "biggest";
}
if (wrap == "none") {
setTextRunAroundSide(KoShape::NoRunAround);
} else if (wrap == "run-through") {
QString runTrought = styleStack.property(KoXmlNS::style, "run-through", "background");
if (runTrought == "background") {
setTextRunAroundSide(KoShape::RunThrough, KoShape::Background);
} else {
setTextRunAroundSide(KoShape::RunThrough, KoShape::Foreground);
}
} else {
if (wrap == "biggest")
setTextRunAroundSide(KoShape::BiggestRunAroundSide);
else if (wrap == "left")
setTextRunAroundSide(KoShape::LeftRunAroundSide);
else if (wrap == "right")
setTextRunAroundSide(KoShape::RightRunAroundSide);
else if (wrap == "dynamic")
setTextRunAroundSide(KoShape::EnoughRunAroundSide);
else if (wrap == "parallel")
setTextRunAroundSide(KoShape::BothRunAroundSide);
}
if (styleStack.hasProperty(KoXmlNS::style, "wrap-dynamic-threshold")) {
QString wrapThreshold = styleStack.property(KoXmlNS::style, "wrap-dynamic-threshold");
if (!wrapThreshold.isEmpty()) {
setTextRunAroundThreshold(KoUnit::parseValue(wrapThreshold));
}
}
if (styleStack.property(KoXmlNS::style, "wrap-contour", "false") == "true") {
if (styleStack.property(KoXmlNS::style, "wrap-contour-mode", "full") == "full") {
setTextRunAroundContour(KoShape::ContourFull);
} else {
setTextRunAroundContour(KoShape::ContourOutside);
}
} else {
setTextRunAroundContour(KoShape::ContourBox);
}
}
bool KoShape::loadOdfAttributes(const KoXmlElement &element, KoShapeLoadingContext &context, int attributes)
{
if (attributes & OdfPosition) {
QPointF pos(position());
if (element.hasAttributeNS(KoXmlNS::svg, "x"))
pos.setX(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "x", QString())));
if (element.hasAttributeNS(KoXmlNS::svg, "y"))
pos.setY(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "y", QString())));
setPosition(pos);
}
if (attributes & OdfSize) {
QSizeF s(size());
if (element.hasAttributeNS(KoXmlNS::svg, "width"))
s.setWidth(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "width", QString())));
if (element.hasAttributeNS(KoXmlNS::svg, "height"))
s.setHeight(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "height", QString())));
setSize(s);
}
if (attributes & OdfLayer) {
if (element.hasAttributeNS(KoXmlNS::draw, "layer")) {
KoShapeLayer *layer = context.layer(element.attributeNS(KoXmlNS::draw, "layer"));
if (layer) {
setParent(layer);
}
}
}
if (attributes & OdfId) {
KoElementReference ref;
ref.loadOdf(element);
if (ref.isValid()) {
context.addShapeId(this, ref.toString());
}
}
if (attributes & OdfZIndex) {
if (element.hasAttributeNS(KoXmlNS::draw, "z-index")) {
setZIndex(element.attributeNS(KoXmlNS::draw, "z-index").toInt());
} else {
setZIndex(context.zIndex());
}
}
if (attributes & OdfName) {
if (element.hasAttributeNS(KoXmlNS::draw, "name")) {
setName(element.attributeNS(KoXmlNS::draw, "name"));
}
}
if (attributes & OdfStyle) {
KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
styleStack.save();
if (element.hasAttributeNS(KoXmlNS::draw, "style-name")) {
context.odfLoadingContext().fillStyleStack(element, KoXmlNS::draw, "style-name", "graphic");
}
if (element.hasAttributeNS(KoXmlNS::presentation, "style-name")) {
context.odfLoadingContext().fillStyleStack(element, KoXmlNS::presentation, "style-name", "presentation");
}
loadStyle(element, context);
styleStack.restore();
}
if (attributes & OdfTransformation) {
QString transform = element.attributeNS(KoXmlNS::draw, "transform", QString());
if (! transform.isEmpty())
applyAbsoluteTransformation(parseOdfTransform(transform));
}
if (attributes & OdfAdditionalAttributes) {
QSet<KoShapeLoadingContext::AdditionalAttributeData> additionalAttributeData = KoShapeLoadingContext::additionalAttributeData();
Q_FOREACH (const KoShapeLoadingContext::AdditionalAttributeData &attributeData, additionalAttributeData) {
if (element.hasAttributeNS(attributeData.ns, attributeData.tag)) {
QString value = element.attributeNS(attributeData.ns, attributeData.tag);
//debugFlake << "load additional attribute" << attributeData.tag << value;
setAdditionalAttribute(attributeData.name, value);
}
}
}
if (attributes & OdfCommonChildElements) {
// load glue points (connection points)
loadOdfGluePoints(element, context);
}
return true;
}
QSharedPointer<KoShapeBackground> KoShape::loadOdfFill(KoShapeLoadingContext &context) const
{
QString fill = KoShapePrivate::getStyleProperty("fill", context);
QSharedPointer<KoShapeBackground> bg;
if (fill == "solid") {
bg = QSharedPointer<KoShapeBackground>(new KoColorBackground());
}
else if (fill == "hatch") {
bg = QSharedPointer<KoShapeBackground>(new KoHatchBackground());
}
else if (fill == "gradient") {
QString styleName = KoShapePrivate::getStyleProperty("fill-gradient-name", context);
KoXmlElement *e = context.odfLoadingContext().stylesReader().drawStyles("gradient")[styleName];
QString style;
if (e) {
style = e->attributeNS(KoXmlNS::draw, "style", QString());
}
if ((style == "rectangular") || (style == "square")) {
bg = QSharedPointer<KoShapeBackground>(new KoOdfGradientBackground());
} else {
QGradient *gradient = new QLinearGradient();
gradient->setCoordinateMode(QGradient::ObjectBoundingMode);
bg = QSharedPointer<KoShapeBackground>(new KoGradientBackground(gradient));
}
} else if (fill == "bitmap") {
bg = QSharedPointer<KoShapeBackground>(new KoPatternBackground(context.imageCollection()));
#ifndef NWORKAROUND_ODF_BUGS
} else if (fill.isEmpty()) {
bg = QSharedPointer<KoShapeBackground>(KoOdfWorkaround::fixBackgroundColor(this, context));
return bg;
#endif
} else {
return QSharedPointer<KoShapeBackground>(0);
}
if (!bg->loadStyle(context.odfLoadingContext(), size())) {
return QSharedPointer<KoShapeBackground>(0);
}
return bg;
}
KoShapeStrokeModelSP KoShape::loadOdfStroke(const KoXmlElement &element, KoShapeLoadingContext &context) const
{
KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
KoOdfStylesReader &stylesReader = context.odfLoadingContext().stylesReader();
QString stroke = KoShapePrivate::getStyleProperty("stroke", context);
if (stroke == "solid" || stroke == "dash") {
QPen pen = KoOdfGraphicStyles::loadOdfStrokeStyle(styleStack, stroke, stylesReader);
QSharedPointer<KoShapeStroke> stroke(new KoShapeStroke());
if (styleStack.hasProperty(KoXmlNS::calligra, "stroke-gradient")) {
QString gradientName = styleStack.property(KoXmlNS::calligra, "stroke-gradient");
QBrush brush = KoOdfGraphicStyles::loadOdfGradientStyleByName(stylesReader, gradientName, size());
stroke->setLineBrush(brush);
} else {
stroke->setColor(pen.color());
}
#ifndef NWORKAROUND_ODF_BUGS
KoOdfWorkaround::fixPenWidth(pen, context);
#endif
stroke->setLineWidth(pen.widthF());
stroke->setJoinStyle(pen.joinStyle());
stroke->setLineStyle(pen.style(), pen.dashPattern());
stroke->setCapStyle(pen.capStyle());
return stroke;
#ifndef NWORKAROUND_ODF_BUGS
} else if (stroke.isEmpty()) {
QPen pen = KoOdfGraphicStyles::loadOdfStrokeStyle(styleStack, "solid", stylesReader);
if (KoOdfWorkaround::fixMissingStroke(pen, element, context, this)) {
QSharedPointer<KoShapeStroke> stroke(new KoShapeStroke());
#ifndef NWORKAROUND_ODF_BUGS
KoOdfWorkaround::fixPenWidth(pen, context);
#endif
stroke->setLineWidth(pen.widthF());
stroke->setJoinStyle(pen.joinStyle());
stroke->setLineStyle(pen.style(), pen.dashPattern());
stroke->setCapStyle(pen.capStyle());
stroke->setColor(pen.color());
return stroke;
}
#endif
}
return KoShapeStrokeModelSP();
}
KoShapeShadow *KoShapePrivate::loadOdfShadow(KoShapeLoadingContext &context) const
{
KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
QString shadowStyle = KoShapePrivate::getStyleProperty("shadow", context);
if (shadowStyle == "visible" || shadowStyle == "hidden") {
KoShapeShadow *shadow = new KoShapeShadow();
QColor shadowColor(styleStack.property(KoXmlNS::draw, "shadow-color"));
qreal offsetX = KoUnit::parseValue(styleStack.property(KoXmlNS::draw, "shadow-offset-x"));
qreal offsetY = KoUnit::parseValue(styleStack.property(KoXmlNS::draw, "shadow-offset-y"));
shadow->setOffset(QPointF(offsetX, offsetY));
qreal blur = KoUnit::parseValue(styleStack.property(KoXmlNS::calligra, "shadow-blur-radius"));
shadow->setBlur(blur);
QString opacity = styleStack.property(KoXmlNS::draw, "shadow-opacity");
if (! opacity.isEmpty() && opacity.right(1) == "%")
shadowColor.setAlphaF(opacity.left(opacity.length() - 1).toFloat() / 100.0);
shadow->setColor(shadowColor);
shadow->setVisible(shadowStyle == "visible");
return shadow;
}
return 0;
}
KoBorder *KoShapePrivate::loadOdfBorder(KoShapeLoadingContext &context) const
{
KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
KoBorder *border = new KoBorder();
if (border->loadOdf(styleStack)) {
return border;
}
delete border;
return 0;
}
void KoShape::loadOdfGluePoints(const KoXmlElement &element, KoShapeLoadingContext &context)
{
Q_D(KoShape);
KoXmlElement child;
bool hasCenterGluePoint = false;
forEachElement(child, element) {
if (child.namespaceURI() != KoXmlNS::draw)
continue;
if (child.localName() != "glue-point")
continue;
// NOTE: this uses draw:id, but apparently while ODF 1.2 has deprecated
// all use of draw:id for xml:id, it didn't specify that here, so it
// doesn't support xml:id (and so, maybe, shouldn't use KoElementReference.
const QString id = child.attributeNS(KoXmlNS::draw, "id", QString());
const int index = id.toInt();
// connection point in center should be default but odf doesn't support,
// in new shape, first custom point is in center, it's okay to replace that point
// with point from xml now, we'll add it back later
if(id.isEmpty() || index < KoConnectionPoint::FirstCustomConnectionPoint ||
(index != KoConnectionPoint::FirstCustomConnectionPoint && d->connectors.contains(index))) {
warnFlake << "glue-point with no or invalid id";
continue;
}
QString xStr = child.attributeNS(KoXmlNS::svg, "x", QString()).simplified();
QString yStr = child.attributeNS(KoXmlNS::svg, "y", QString()).simplified();
if(xStr.isEmpty() || yStr.isEmpty()) {
warnFlake << "glue-point with invald position";
continue;
}
KoConnectionPoint connector;
const QString align = child.attributeNS(KoXmlNS::draw, "align", QString());
if (align.isEmpty()) {
#ifndef NWORKAROUND_ODF_BUGS
KoOdfWorkaround::fixGluePointPosition(xStr, context);
KoOdfWorkaround::fixGluePointPosition(yStr, context);
#endif
if(!xStr.endsWith('%') || !yStr.endsWith('%')) {
warnFlake << "glue-point with invald position";
continue;
}
// x and y are relative to drawing object center
connector.position.setX(xStr.remove('%').toDouble()/100.0);
connector.position.setY(yStr.remove('%').toDouble()/100.0);
// convert position to be relative to top-left corner
connector.position += QPointF(0.5, 0.5);
connector.position.rx() = qBound<qreal>(0.0, connector.position.x(), 1.0);
connector.position.ry() = qBound<qreal>(0.0, connector.position.y(), 1.0);
} else {
// absolute distances to the edge specified by align
connector.position.setX(KoUnit::parseValue(xStr));
connector.position.setY(KoUnit::parseValue(yStr));
if (align == "top-left") {
connector.alignment = KoConnectionPoint::AlignTopLeft;
} else if (align == "top") {
connector.alignment = KoConnectionPoint::AlignTop;
} else if (align == "top-right") {
connector.alignment = KoConnectionPoint::AlignTopRight;
} else if (align == "left") {
connector.alignment = KoConnectionPoint::AlignLeft;
} else if (align == "center") {
connector.alignment = KoConnectionPoint::AlignCenter;
} else if (align == "right") {
connector.alignment = KoConnectionPoint::AlignRight;
} else if (align == "bottom-left") {
connector.alignment = KoConnectionPoint::AlignBottomLeft;
} else if (align == "bottom") {
connector.alignment = KoConnectionPoint::AlignBottom;
} else if (align == "bottom-right") {
connector.alignment = KoConnectionPoint::AlignBottomRight;
}
debugFlake << "using alignment" << align;
}
const QString escape = child.attributeNS(KoXmlNS::draw, "escape-direction", QString());
if (!escape.isEmpty()) {
if (escape == "horizontal") {
connector.escapeDirection = KoConnectionPoint::HorizontalDirections;
} else if (escape == "vertical") {
connector.escapeDirection = KoConnectionPoint::VerticalDirections;
} else if (escape == "left") {
connector.escapeDirection = KoConnectionPoint::LeftDirection;
} else if (escape == "right") {
connector.escapeDirection = KoConnectionPoint::RightDirection;
} else if (escape == "up") {
connector.escapeDirection = KoConnectionPoint::UpDirection;
} else if (escape == "down") {
connector.escapeDirection = KoConnectionPoint::DownDirection;
}
debugFlake << "using escape direction" << escape;
}
d->connectors[index] = connector;
debugFlake << "loaded glue-point" << index << "at position" << connector.position;
if (d->connectors[index].position == QPointF(0.5, 0.5)) {
hasCenterGluePoint = true;
debugFlake << "center glue-point found at id " << index;
}
}
if (!hasCenterGluePoint) {
d->connectors[d->connectors.count()] = KoConnectionPoint(QPointF(0.5, 0.5),
KoConnectionPoint::AllDirections, KoConnectionPoint::AlignCenter);
}
debugFlake << "shape has now" << d->connectors.count() << "glue-points";
}
void KoShape::loadOdfClipContour(const KoXmlElement &element, KoShapeLoadingContext &context, const QSizeF &scaleFactor)
{
Q_D(KoShape);
KoXmlElement child;
forEachElement(child, element) {
if (child.namespaceURI() != KoXmlNS::draw)
continue;
if (child.localName() != "contour-polygon")
continue;
debugFlake << "shape loads contour-polygon";
KoPathShape *ps = new KoPathShape();
ps->loadContourOdf(child, context, scaleFactor);
ps->setTransformation(transformation());
KoClipPath *clipPath = new KoClipPath({ps}, KoFlake::UserSpaceOnUse);
d->clipPath.reset(clipPath);
}
}
QTransform KoShape::parseOdfTransform(const QString &transform)
{
QTransform matrix;
// Split string for handling 1 transform statement at a time
QStringList subtransforms = transform.split(')', QString::SkipEmptyParts);
QStringList::ConstIterator it = subtransforms.constBegin();
QStringList::ConstIterator end = subtransforms.constEnd();
for (; it != end; ++it) {
QStringList subtransform = (*it).split('(', QString::SkipEmptyParts);
subtransform[0] = subtransform[0].trimmed().toLower();
subtransform[1] = subtransform[1].simplified();
QRegExp reg("[,( ]");
QStringList params = subtransform[1].split(reg, QString::SkipEmptyParts);
if (subtransform[0].startsWith(';') || subtransform[0].startsWith(','))
subtransform[0] = subtransform[0].right(subtransform[0].length() - 1);
QString cmd = subtransform[0].toLower();
if (cmd == "rotate") {
QTransform rotMatrix;
if (params.count() == 3) {
qreal x = KoUnit::parseValue(params[1]);
qreal y = KoUnit::parseValue(params[2]);
rotMatrix.translate(x, y);
// oo2 rotates by radians
rotMatrix.rotate(-params[0].toDouble()*180.0 / M_PI);
rotMatrix.translate(-x, -y);
} else {
// oo2 rotates by radians
rotMatrix.rotate(-params[0].toDouble()*180.0 / M_PI);
}
matrix = matrix * rotMatrix;
} else if (cmd == "translate") {
QTransform moveMatrix;
if (params.count() == 2) {
qreal x = KoUnit::parseValue(params[0]);
qreal y = KoUnit::parseValue(params[1]);
moveMatrix.translate(x, y);
} else // Spec : if only one param given, assume 2nd param to be 0
moveMatrix.translate(KoUnit::parseValue(params[0]) , 0);
matrix = matrix * moveMatrix;
} else if (cmd == "scale") {
QTransform scaleMatrix;
if (params.count() == 2)
scaleMatrix.scale(params[0].toDouble(), params[1].toDouble());
else // Spec : if only one param given, assume uniform scaling
scaleMatrix.scale(params[0].toDouble(), params[0].toDouble());
matrix = matrix * scaleMatrix;
} else if (cmd == "skewx") {
QPointF p = absolutePosition(KoFlake::TopLeft);
QTransform shearMatrix;
shearMatrix.translate(p.x(), p.y());
shearMatrix.shear(tan(-params[0].toDouble()), 0.0F);
shearMatrix.translate(-p.x(), -p.y());
matrix = matrix * shearMatrix;
} else if (cmd == "skewy") {
QPointF p = absolutePosition(KoFlake::TopLeft);
QTransform shearMatrix;
shearMatrix.translate(p.x(), p.y());
shearMatrix.shear(0.0F, tan(-params[0].toDouble()));
shearMatrix.translate(-p.x(), -p.y());
matrix = matrix * shearMatrix;
} else if (cmd == "matrix") {
QTransform m;
if (params.count() >= 6) {
m.setMatrix(params[0].toDouble(), params[1].toDouble(), 0,
params[2].toDouble(), params[3].toDouble(), 0,
KoUnit::parseValue(params[4]), KoUnit::parseValue(params[5]), 1);
}
matrix = matrix * m;
}
}
return matrix;
}
void KoShape::saveOdfAttributes(KoShapeSavingContext &context, int attributes) const
{
Q_D(const KoShape);
if (attributes & OdfStyle) {
KoGenStyle style;
// all items that should be written to 'draw:frame' and any other 'draw:' object that inherits this shape
if (context.isSet(KoShapeSavingContext::PresentationShape)) {
style = KoGenStyle(KoGenStyle::PresentationAutoStyle, "presentation");
context.xmlWriter().addAttribute("presentation:style-name", saveStyle(style, context));
} else {
style = KoGenStyle(KoGenStyle::GraphicAutoStyle, "graphic");
context.xmlWriter().addAttribute("draw:style-name", saveStyle(style, context));
}
}
if (attributes & OdfId) {
if (context.isSet(KoShapeSavingContext::DrawId)) {
KoElementReference ref = context.xmlid(this, "shape", KoElementReference::Counter);
ref.saveOdf(&context.xmlWriter(), KoElementReference::DrawId);
}
}
if (attributes & OdfName) {
if (! name().isEmpty())
context.xmlWriter().addAttribute("draw:name", name());
}
if (attributes & OdfLayer) {
KoShape *parent = d->parent;
while (parent) {
if (dynamic_cast<KoShapeLayer*>(parent)) {
context.xmlWriter().addAttribute("draw:layer", parent->name());
break;
}
parent = parent->parent();
}
}
if (attributes & OdfZIndex && context.isSet(KoShapeSavingContext::ZIndex)) {
context.xmlWriter().addAttribute("draw:z-index", zIndex());
}
if (attributes & OdfSize) {
QSizeF s(size());
if (parent() && parent()->isClipped(this)) { // being clipped shrinks our visible size
// clipping in ODF is done using a combination of visual size and content cliprect.
// A picture of 10cm x 10cm displayed in a box of 2cm x 4cm will be scaled (out
// of proportion in this case). If we then add a fo:clip like;
// fo:clip="rect(2cm, 3cm, 4cm, 5cm)" (top, right, bottom, left)
// our original 10x10 is clipped to 2cm x 4cm and *then* fitted in that box.
// TODO do this properly by subtracting rects
s = parent()->size();
}
context.xmlWriter().addAttribute("svg:width", s.width());
context.xmlWriter().addAttribute("svg:height", s.height());
}
// The position is implicitly stored in the transformation matrix
// if the transformation is saved as well
if ((attributes & OdfPosition) && !(attributes & OdfTransformation)) {
const QPointF p(position() * context.shapeOffset(this));
context.xmlWriter().addAttribute("svg:x", p.x());
context.xmlWriter().addAttribute("svg:y", p.y());
}
if (attributes & OdfTransformation) {
QTransform matrix = absoluteTransformation(0) * context.shapeOffset(this);
if (! matrix.isIdentity()) {
if (qAbs(matrix.m11() - 1) < 1E-5 // 1
&& qAbs(matrix.m12()) < 1E-5 // 0
&& qAbs(matrix.m21()) < 1E-5 // 0
&& qAbs(matrix.m22() - 1) < 1E-5) { // 1
context.xmlWriter().addAttribute("svg:x", matrix.dx());
context.xmlWriter().addAttribute("svg:y", matrix.dy());
} else {
QString m = QString("matrix(%1 %2 %3 %4 %5pt %6pt)")
.arg(matrix.m11(), 0, 'f', 11)
.arg(matrix.m12(), 0, 'f', 11)
.arg(matrix.m21(), 0, 'f', 11)
.arg(matrix.m22(), 0, 'f', 11)
.arg(matrix.dx(), 0, 'f', 11)
.arg(matrix.dy(), 0, 'f', 11);
context.xmlWriter().addAttribute("draw:transform", m);
}
}
}
if (attributes & OdfViewbox) {
const QSizeF s(size());
QString viewBox = QString("0 0 %1 %2").arg(qRound(s.width())).arg(qRound(s.height()));
context.xmlWriter().addAttribute("svg:viewBox", viewBox);
}
if (attributes & OdfAdditionalAttributes) {
QMap<QString, QString>::const_iterator it(d->additionalAttributes.constBegin());
for (; it != d->additionalAttributes.constEnd(); ++it) {
context.xmlWriter().addAttribute(it.key().toUtf8(), it.value());
}
}
}
void KoShape::saveOdfCommonChildElements(KoShapeSavingContext &context) const
{
Q_D(const KoShape);
// save glue points see ODF 9.2.19 Glue Points
if(d->connectors.count()) {
KoConnectionPoints::const_iterator cp = d->connectors.constBegin();
KoConnectionPoints::const_iterator lastCp = d->connectors.constEnd();
for(; cp != lastCp; ++cp) {
// do not save default glue points
if(cp.key() < 4)
continue;
context.xmlWriter().startElement("draw:glue-point");
context.xmlWriter().addAttribute("draw:id", QString("%1").arg(cp.key()));
if (cp.value().alignment == KoConnectionPoint::AlignNone) {
// convert to percent from center
const qreal x = cp.value().position.x() * 100.0 - 50.0;
const qreal y = cp.value().position.y() * 100.0 - 50.0;
context.xmlWriter().addAttribute("svg:x", QString("%1%").arg(x));
context.xmlWriter().addAttribute("svg:y", QString("%1%").arg(y));
} else {
context.xmlWriter().addAttribute("svg:x", cp.value().position.x());
context.xmlWriter().addAttribute("svg:y", cp.value().position.y());
}
QString escapeDirection;
switch(cp.value().escapeDirection) {
case KoConnectionPoint::HorizontalDirections:
escapeDirection = "horizontal";
break;
case KoConnectionPoint::VerticalDirections:
escapeDirection = "vertical";
break;
case KoConnectionPoint::LeftDirection:
escapeDirection = "left";
break;
case KoConnectionPoint::RightDirection:
escapeDirection = "right";
break;
case KoConnectionPoint::UpDirection:
escapeDirection = "up";
break;
case KoConnectionPoint::DownDirection:
escapeDirection = "down";
break;
default:
- // fall through
break;
}
if(!escapeDirection.isEmpty()) {
context.xmlWriter().addAttribute("draw:escape-direction", escapeDirection);
}
QString alignment;
switch(cp.value().alignment) {
case KoConnectionPoint::AlignTopLeft:
alignment = "top-left";
break;
case KoConnectionPoint::AlignTop:
alignment = "top";
break;
case KoConnectionPoint::AlignTopRight:
alignment = "top-right";
break;
case KoConnectionPoint::AlignLeft:
alignment = "left";
break;
case KoConnectionPoint::AlignCenter:
alignment = "center";
break;
case KoConnectionPoint::AlignRight:
alignment = "right";
break;
case KoConnectionPoint::AlignBottomLeft:
alignment = "bottom-left";
break;
case KoConnectionPoint::AlignBottom:
alignment = "bottom";
break;
case KoConnectionPoint::AlignBottomRight:
alignment = "bottom-right";
break;
default:
- // fall through
break;
}
if(!alignment.isEmpty()) {
context.xmlWriter().addAttribute("draw:align", alignment);
}
context.xmlWriter().endElement();
}
}
}
void KoShape::saveOdfClipContour(KoShapeSavingContext &context, const QSizeF &originalSize) const
{
Q_D(const KoShape);
debugFlake << "shape saves contour-polygon";
if (d->clipPath && !d->clipPath->clipPathShapes().isEmpty()) {
// This will loose data as odf can only save one set of contour whereas
// svg loading and at least karbon editing can produce more than one
// TODO, FIXME see if we can save more than one clipshape to odf
d->clipPath->clipPathShapes().first()->saveContourOdf(context, originalSize);
}
}
// end loading & saving methods
// static
void KoShape::applyConversion(QPainter &painter, const KoViewConverter &converter)
{
qreal zoomX, zoomY;
converter.zoom(&zoomX, &zoomY);
painter.scale(zoomX, zoomY);
}
KisHandlePainterHelper KoShape::createHandlePainterHelper(QPainter *painter, KoShape *shape, const KoViewConverter &converter, qreal handleRadius)
{
const QTransform originalPainterTransform = painter->transform();
painter->setTransform(shape->absoluteTransformation(&converter) * painter->transform());
KoShape::applyConversion(*painter, converter);
// move c-tor
return KisHandlePainterHelper(painter, originalPainterTransform, handleRadius);
}
QPointF KoShape::shapeToDocument(const QPointF &point) const
{
return absoluteTransformation(0).map(point);
}
QRectF KoShape::shapeToDocument(const QRectF &rect) const
{
return absoluteTransformation(0).mapRect(rect);
}
QPointF KoShape::documentToShape(const QPointF &point) const
{
return absoluteTransformation(0).inverted().map(point);
}
QRectF KoShape::documentToShape(const QRectF &rect) const
{
return absoluteTransformation(0).inverted().mapRect(rect);
}
bool KoShape::addDependee(KoShape *shape)
{
Q_D(KoShape);
if (! shape)
return false;
// refuse to establish a circular dependency
if (shape->hasDependee(this))
return false;
if (! d->dependees.contains(shape))
d->dependees.append(shape);
return true;
}
void KoShape::removeDependee(KoShape *shape)
{
Q_D(KoShape);
int index = d->dependees.indexOf(shape);
if (index >= 0)
d->dependees.removeAt(index);
}
bool KoShape::hasDependee(KoShape *shape) const
{
Q_D(const KoShape);
return d->dependees.contains(shape);
}
QList<KoShape*> KoShape::dependees() const
{
Q_D(const KoShape);
return d->dependees;
}
void KoShape::shapeChanged(ChangeType type, KoShape *shape)
{
Q_UNUSED(type);
Q_UNUSED(shape);
}
KoSnapData KoShape::snapData() const
{
return KoSnapData();
}
void KoShape::setAdditionalAttribute(const QString &name, const QString &value)
{
Q_D(KoShape);
d->additionalAttributes.insert(name, value);
}
void KoShape::removeAdditionalAttribute(const QString &name)
{
Q_D(KoShape);
d->additionalAttributes.remove(name);
}
bool KoShape::hasAdditionalAttribute(const QString &name) const
{
Q_D(const KoShape);
return d->additionalAttributes.contains(name);
}
QString KoShape::additionalAttribute(const QString &name) const
{
Q_D(const KoShape);
return d->additionalAttributes.value(name);
}
void KoShape::setAdditionalStyleAttribute(const char *name, const QString &value)
{
Q_D(KoShape);
d->additionalStyleAttributes.insert(name, value);
}
void KoShape::removeAdditionalStyleAttribute(const char *name)
{
Q_D(KoShape);
d->additionalStyleAttributes.remove(name);
}
KoFilterEffectStack *KoShape::filterEffectStack() const
{
Q_D(const KoShape);
return d->filterEffectStack;
}
void KoShape::setFilterEffectStack(KoFilterEffectStack *filterEffectStack)
{
Q_D(KoShape);
if (d->filterEffectStack)
d->filterEffectStack->deref();
d->filterEffectStack = filterEffectStack;
if (d->filterEffectStack) {
d->filterEffectStack->ref();
}
notifyChanged();
}
QSet<KoShape*> KoShape::toolDelegates() const
{
Q_D(const KoShape);
return d->toolDelegates;
}
void KoShape::setToolDelegates(const QSet<KoShape*> &delegates)
{
Q_D(KoShape);
d->toolDelegates = delegates;
}
QString KoShape::hyperLink () const
{
Q_D(const KoShape);
return d->hyperLink;
}
void KoShape::setHyperLink(const QString &hyperLink)
{
Q_D(KoShape);
d->hyperLink = hyperLink;
}
KoShapePrivate *KoShape::priv()
{
Q_D(KoShape);
return d;
}
KoShape::ShapeChangeListener::~ShapeChangeListener()
{
Q_FOREACH(KoShape *shape, m_registeredShapes) {
shape->removeShapeChangeListener(this);
}
}
void KoShape::ShapeChangeListener::registerShape(KoShape *shape)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(!m_registeredShapes.contains(shape));
m_registeredShapes.append(shape);
}
void KoShape::ShapeChangeListener::unregisterShape(KoShape *shape)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(m_registeredShapes.contains(shape));
m_registeredShapes.removeAll(shape);
}
void KoShape::ShapeChangeListener::notifyShapeChangedImpl(KoShape::ChangeType type, KoShape *shape)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(m_registeredShapes.contains(shape));
notifyShapeChanged(type, shape);
if (type == KoShape::Deleted) {
unregisterShape(shape);
}
}
void KoShape::addShapeChangeListener(KoShape::ShapeChangeListener *listener)
{
Q_D(KoShape);
KIS_SAFE_ASSERT_RECOVER_RETURN(!d->listeners.contains(listener));
listener->registerShape(this);
d->listeners.append(listener);
}
void KoShape::removeShapeChangeListener(KoShape::ShapeChangeListener *listener)
{
Q_D(KoShape);
KIS_SAFE_ASSERT_RECOVER_RETURN(d->listeners.contains(listener));
d->listeners.removeAll(listener);
listener->unregisterShape(this);
}
QList<KoShape *> KoShape::linearizeSubtree(const QList<KoShape *> &shapes)
{
QList<KoShape *> result;
Q_FOREACH (KoShape *shape, shapes) {
result << shape;
KoShapeContainer *container = dynamic_cast<KoShapeContainer*>(shape);
if (container) {
result << linearizeSubtree(container->shapes());
}
}
return result;
}
diff --git a/libs/flake/KoShape.h b/libs/flake/KoShape.h
index 8786cd679f..50fb9ee339 100644
--- a/libs/flake/KoShape.h
+++ b/libs/flake/KoShape.h
@@ -1,1303 +1,1305 @@
/* This file is part of the KDE project
Copyright (C) 2006-2008 Thorsten Zachmann <zachmann@kde.org>
Copyright (C) 2006, 2008 C. Boemann <cbo@boemann.dk>
Copyright (C) 2006-2010 Thomas Zander <zander@kde.org>
Copyright (C) 2007-2009,2011 Jan Hambrecht <jaham@gmx.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOSHAPE_H
#define KOSHAPE_H
#include "KoFlake.h"
#include "KoFlakeTypes.h"
#include "KoConnectionPoint.h"
#include <QSharedPointer>
#include <QSet>
#include <QMetaType>
#include <KoXmlReaderForward.h>
#include "kritaflake_export.h"
class QPainter;
class QRectF;
class QPainterPath;
class QTransform;
class KoShapeContainer;
class KoShapeStrokeModel;
class KoShapeUserData;
class KoViewConverter;
class KoShapeApplicationData;
class KoShapeSavingContext;
class KoShapeLoadingContext;
class KoGenStyle;
class KoShapeShadow;
class KoShapePrivate;
class KoFilterEffectStack;
class KoSnapData;
class KoClipPath;
class KoClipMask;
class KoShapePaintingContext;
class KoShapeAnchor;
class KoBorder;
struct KoInsets;
class KoShapeBackground;
class KisHandlePainterHelper;
/**
* Base class for all flake shapes. Shapes extend this class
* to allow themselves to be manipulated. This class just represents
* a graphical shape in the document and can be manipulated by some default
* tools in this library.
*
* Due to the limited responsibility of this class, the extending object
* can have any data backend and is responsible for painting itself.
*
* We strongly suggest that any extending class will use a Model View
* Controller (MVC) design where the View part is all in this class, as well
* as the one that inherits from this one. This allows the data that rests
* in the model to be reused in different parts of the document. For example
* by having two flake objects that show that same data. Or each showing a section of it.
*
* The KoShape data is completely in postscript-points (pt) (see KoUnit
* for conversion methods to and from points).
* This image will explain the real-world use of the shape and its options.
* <img src="../flake_shape_coords.png" align=center><br>
* The Rotation center can be returned with absolutePosition()
*
* <p>Flake objects can be created in three ways:
* <ul>
* <li>a simple new KoDerivedFlake(),
* <li>through an associated tool,
* <li>through a factory
* </ul>
*
* <h1>Shape interaction notifications</h1>
* We had several notification methods that allow your shape to be notified of changes in other
* shapes positions or rotation etc.
* <ol><li>The most general is KoShape::shapeChanged().<br>
* a virtual method that you can use to check various changed to your shape made by tools or otherwise.</li>
* <li>for shape hierarchies the parent may receive a notification when a child was modified.
* This is done though KoShapeContainerModel::childChanged()</li>
* <li>any shape that is at a similar position as another shape there is collision detection.
* You can register your shape to be sensitive to any changes like moving or whatever to
* <b>other</b> shapes that intersect yours.
* Such changes will then be notified to your shape using the method from (1) You should call
* KoShape::setCollisionDetection(bool) to enable this.
* </ol>
*/
class KRITAFLAKE_EXPORT KoShape
{
public:
/// Used by shapeChanged() to select which change was made
enum ChangeType {
PositionChanged, ///< used after a setPosition()
RotationChanged, ///< used after a setRotation()
ScaleChanged, ///< used after a scale()
ShearChanged, ///< used after a shear()
SizeChanged, ///< used after a setSize()
GenericMatrixChange, ///< used after the matrix was changed without knowing which property explicitly changed
KeepAspectRatioChange, ///< used after setKeepAspectRatio()
ParentChanged, ///< used after a setParent()
CollisionDetected, ///< used when another shape moved in our boundingrect
Deleted, ///< the shape was deleted
StrokeChanged, ///< the shapes stroke has changed
BackgroundChanged, ///< the shapes background has changed
ShadowChanged, ///< the shapes shadow has changed
BorderChanged, ///< the shapes border has changed
ParameterChanged, ///< the shapes parameter has changed (KoParameterShape only)
ContentChanged, ///< the content of the shape changed e.g. a new image inside a pixmap/text change inside a textshape
TextRunAroundChanged, ///< used after a setTextRunAroundSide()
ChildChanged, ///< a child of a container was changed/removed. This is propagated to all parents
ConnectionPointChanged, ///< a connection point has changed
ClipPathChanged, ///< the shapes clip path has changed
TransparencyChanged ///< the shapetransparency value has changed
};
/// The behavior text should do when intersecting this shape.
enum TextRunAroundSide {
BiggestRunAroundSide, ///< Run other text around the side that has the most space
LeftRunAroundSide, ///< Run other text around the left side of the frame
RightRunAroundSide, ///< Run other text around the right side of the frame
EnoughRunAroundSide, ///< Run other text dynamically around both sides of the shape, provided there is sufficient space left
BothRunAroundSide, ///< Run other text around both sides of the shape
NoRunAround, ///< The text will be completely avoiding the frame by keeping the horizontal space that this frame occupies blank.
RunThrough ///< The text will completely ignore the frame and layout as if it was not there
};
/// The behavior text should do when intersecting this shape.
enum TextRunAroundContour {
ContourBox, /// Run other text around a bounding rect of the outline
ContourFull, ///< Run other text around also on the inside
ContourOutside ///< Run other text around only on the outside
};
/**
* TODO
*/
enum RunThroughLevel {
Background,
Foreground
};
/**
* @brief Constructor
*/
KoShape();
/**
* @brief Destructor
*/
virtual ~KoShape();
/**
* @brief creates a deep copy of the shape or shape's subtree
* @return a cloned shape
*/
virtual KoShape* cloneShape() const;
/**
* @brief Paint the shape fill
* The class extending this one is responsible for painting itself. Since we do not
* assume the shape is square the paint must also clear its background if it will draw
* something transparent on top.
* This can be done with a method like:
* <code>
painter.fillRect(converter.normalToView(QRectF(QPointF(0.0,0.0), size())), background());</code>
* Or equivalent for non-square objects.
* Do note that a shape's top-left is always at coordinate 0,0. Even if the shape itself is rotated
* or translated.
* @param painter used for painting the shape
* @param converter to convert between internal and view coordinates.
* @see applyConversion()
* @param paintcontext the painting context.
*/
virtual void paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintcontext) = 0;
/**
* @brief paintStroke paints the shape's stroked outline
* @param painter used for painting the shape
* @param converter to convert between internal and view coordinates.
* @see applyConversion()
* @param paintcontext the painting context.
*/
virtual void paintStroke(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintcontext);
/**
* @brief Paint the shape's border
* This is a helper function that could be called from the paint() method of all shapes.
* @param painter used for painting the shape
* @param converter to convert between internal and view coordinates.
* @see applyConversion()
*/
virtual void paintBorder(QPainter &painter, const KoViewConverter &converter);
/**
* Load a shape from odf
*
* @param context the KoShapeLoadingContext used for loading
* @param element element which represents the shape in odf
*
* @return false if loading failed
*/
virtual bool loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context) = 0;
/**
* @brief store the shape data as ODF XML.
* This is the method that will be called when saving a shape as a described in
* OpenDocument 9.2 Drawing Shapes.
* @see saveOdfAttributes
*/
virtual void saveOdf(KoShapeSavingContext &context) const = 0;
/**
* This method can be used while saving the shape as ODF to add the data
* stored on this shape to the current element.
*
* @param context the context for the current save.
* @param attributes a number of OdfAttribute items to state which attributes to save.
* @see saveOdf
*/
void saveOdfAttributes(KoShapeSavingContext &context, int attributes) const;
/**
* This method can be used while saving the shape as Odf to add common child elements
*
* The office:event-listeners and draw:glue-point are saved.
* @param context the context for the current save.
*/
void saveOdfCommonChildElements(KoShapeSavingContext &context) const;
/**
* This method can be used to save contour data from the clipPath()
*
* The draw:contour-polygon or draw:contour-path elements are saved.
* @param context the context for the current save.
* @param originalSize the original size of the unscaled image.
*/
void saveOdfClipContour(KoShapeSavingContext &context, const QSizeF &originalSize) const;
/**
* @brief Scale the shape using the zero-point which is the top-left corner.
* @see position()
*
* @param sx scale in x direction
* @param sy scale in y direction
*/
void scale(qreal sx, qreal sy);
/**
* @brief Rotate the shape (relative)
*
* The shape will be rotated from the current rotation using the center of the shape using the size()
*
* @param angle change the angle of rotation increasing it with 'angle' degrees
*/
void rotate(qreal angle);
/**
* Return the current rotation in degrees.
* It returns NaN if the shape has a shearing or scaling transformation applied.
*/
qreal rotation() const;
/**
* @brief Shear the shape
* The shape will be sheared using the zero-point which is the top-left corner.
* @see position()
*
* @param sx shear in x direction
* @param sy shear in y direction
*/
void shear(qreal sx, qreal sy);
/**
* @brief Resize the shape
*
* @param size the new size of the shape. This is different from scaling as
* scaling is a so called secondary operation which is comparable to zooming in
* instead of changing the size of the basic shape.
* Easiest example of this difference is that using this method will not distort the
* size of pattern-fills and strokes.
*/
virtual void setSize(const QSizeF &size);
/**
* @brief Get the size of the shape in pt.
*
* The size is in shape coordinates.
*
* @return the size of the shape as set by setSize()
*/
virtual QSizeF size() const;
/**
* @brief Set the position of the shape in pt
*
* @param position the new position of the shape
*/
virtual void setPosition(const QPointF &position);
/**
* @brief Get the position of the shape in pt
*
* @return the position of the shape
*/
QPointF position() const;
/**
* @brief Check if the shape is hit on position
* @param position the position where the user clicked.
* @return true when it hits.
*/
virtual bool hitTest(const QPointF &position) const;
/**
* @brief Get the bounding box of the shape
*
* This includes the line width and the shadow of the shape
*
* @return the bounding box of the shape
*/
virtual QRectF boundingRect() const;
/**
* Get the united bounding box of a group of shapes. This is a utility
* function used in many places in Krita.
*/
static QRectF boundingRect(const QList<KoShape*> &shapes);
/**
* @return the bounding rect of the outline of the shape measured
* in absolute coordinate system. Please note that in contrast to
* boundingRect() this rect doesn't include the stroke and other
* insets.
*/
QRectF absoluteOutlineRect(KoViewConverter *converter = 0) const;
/**
* Same as a member function, but applies to a list of shapes and returns a
* united rect.
*/
static QRectF absoluteOutlineRect(const QList<KoShape*> &shapes, KoViewConverter *converter = 0);
/**
* @brief Add a connector point to the shape
*
* A connector is a place on the shape that allows a graphical connection to be made
* using a line, for example.
*
* @param point the connection point to add
* @return the id of the new connection point
*/
int addConnectionPoint(const KoConnectionPoint &point);
/**
* Sets data of connection point with specified id.
*
* The position of the connector is restricted to the bounding rectangle of the shape.
* When setting a default connection point, the new position is ignored, as these
* are fixed at their default position.
* The function will insert a new connection point if the specified id was not used
* before.
*
* @param connectionPointId the id of the connection point to set
* @param point the connection point data
* @return false if specified connection point id is invalid, else true
*/
bool setConnectionPoint(int connectionPointId, const KoConnectionPoint &point);
/// Checks if a connection point with the specified id exists
bool hasConnectionPoint(int connectionPointId) const;
/// Returns connection point with specified connection point id
KoConnectionPoint connectionPoint(int connectionPointId) const;
/**
* Return a list of the connection points that have been added to this shape.
* All the points are relative to the shape position, see absolutePosition().
* @return a list of the connectors that have been added to this shape.
*/
KoConnectionPoints connectionPoints() const;
/// Removes connection point with specified id
void removeConnectionPoint(int connectionPointId);
/// Removes all connection points
void clearConnectionPoints();
/**
* Return the side text should flow around this shape. This implements the ODF style:wrap
* attribute that specifies how text is displayed around a frame or graphic object.
*/
TextRunAroundSide textRunAroundSide() const;
/**
* Set the side text should flow around this shape.
* @param side the requested side
- * @param runThrought run through the foreground or background or...
+ * @param runThrough run through the foreground or background or...
*/
void setTextRunAroundSide(TextRunAroundSide side, RunThroughLevel runThrough = Background);
/**
* The space between this shape's left edge and text that runs around this shape.
* @return the space around this shape to keep free from text
*/
qreal textRunAroundDistanceLeft() const;
/**
* Set the space between this shape's left edge and the text that run around this shape.
* @param distance the space around this shape to keep free from text
*/
void setTextRunAroundDistanceLeft(qreal distance);
/**
* The space between this shape's top edge and text that runs around this shape.
* @return the space around this shape to keep free from text
*/
qreal textRunAroundDistanceTop() const;
/**
* Set the space between this shape's top edge and the text that run around this shape.
* @param distance the space around this shape to keep free from text
*/
void setTextRunAroundDistanceTop(qreal distance);
/**
* The space between this shape's right edge and text that runs around this shape.
* @return the space around this shape to keep free from text
*/
qreal textRunAroundDistanceRight() const;
/**
* Set the space between this shape's right edge and the text that run around this shape.
* @param distance the space around this shape to keep free from text
*/
void setTextRunAroundDistanceRight(qreal distance);
/**
* The space between this shape's bottom edge and text that runs around this shape.
* @return the space around this shape to keep free from text
*/
qreal textRunAroundDistanceBottom() const;
/**
* Set the space between this shape's bottom edge and the text that run around this shape.
* @param distance the space around this shape to keep free from text
*/
void setTextRunAroundDistanceBottom(qreal distance);
/**
* Return the threshold above which text should flow around this shape.
* The text will not flow around the shape on a side unless the space available on that side
* is above this threshold. Only used when the text run around side is EnoughRunAroundSide.
* @return threshold the threshold
*/
qreal textRunAroundThreshold() const;
/**
* Set the threshold above which text should flow around this shape.
* The text will not flow around the shape on a side unless the space available on that side
* is above this threshold. Only used when the text run around side is EnoughRunAroundSide.
* @param threshold the new threshold
*/
void setTextRunAroundThreshold(qreal threshold);
/**
* Return the how tight text run around is done around this shape.
* @return the contour
*/
TextRunAroundContour textRunAroundContour() const;
/**
* Set how tight text run around is done around this shape.
* @param contour the new contour
*/
void setTextRunAroundContour(TextRunAroundContour contour);
/**
* Set the KoShapeAnchor
*/
void setAnchor(KoShapeAnchor *anchor);
/**
* Return the KoShapeAnchor, or 0
*/
KoShapeAnchor *anchor() const;
/**
* Set the minimum height of the shape.
* Currently it's not respected but only for informational purpose
- * @param minimumShapeHeight the minimum height of the frame.
+ * @param height the minimum height of the frame.
*/
void setMinimumHeight(qreal height);
/**
* Return the minimum height of the shape.
* @return the minimum height of the shape. Default is 0.0.
*/
qreal minimumHeight() const;
/**
* Set the background of the shape.
* A shape background can be a plain color, a gradient, a pattern, be fully transparent
* or have a complex fill.
* Setting such a background will allow the shape to be filled and will be able to tell
* if it is transparent or not.
*
* If the shape inherited the background from its parent, its stops inheriting it, that
* is inheritBackground property resets to false.
*
* @param background the new shape background.
*/
void setBackground(QSharedPointer<KoShapeBackground> background);
/**
* return the brush used to paint te background of this shape with.
* A QBrush can have a plain color, be fully transparent or have a complex fill.
* setting such a brush will allow the shape to fill itself using that brush and
* will be able to tell if its transparent or not.
* @return the background-brush
*/
QSharedPointer<KoShapeBackground> background() const;
/**
* @brief setInheritBackground marks a shape as inhiriting the background
* from the parent shape. NOTE: The currently selected background is destroyed.
* @param value true if the shape should inherit the filling background
*/
void setInheritBackground(bool value);
/**
* @brief inheritBackground shows if the shape inherits background from its parent
* @return true if the shape inherits the fill
*/
bool inheritBackground() const;
/**
* Returns true if there is some transparency, false if the shape is fully opaque.
* The default implementation will just return if the background has some transparency,
* you should override it and always return true if your shape is not square.
* @return if the shape is (partly) transparent.
*/
virtual bool hasTransparency() const;
/**
* Sets shape level transparency.
* @param transparency the new shape level transparency
*/
void setTransparency(qreal transparency);
/**
* Returns the shape level transparency.
* @param recursive when true takes the parents transparency into account
*/
qreal transparency(bool recursive=false) const;
/**
* Retrieve the z-coordinate of this shape.
* The zIndex property is used to determine which shape lies on top of other objects.
* An shape with a higher z-order is on top, and can obscure another shape.
* @return the z-index of this shape.
* @see setZIndex()
*/
qint16 zIndex() const;
/**
* Set the z-coordinate of this shape.
* The zIndex property is used to determine which shape lies on top of other objects.
* An shape with a higher z-order is on top, and can obscure, another shape.
* <p>Just like two objects having the same x or y coordinate will make them 'touch',
* so will two objects with the same z-index touch on the z plane. In layering the
* shape this, however, can cause a little confusion as one always has to be on top.
* The layering if two overlapping objects have the same index is implementation dependent
* and probably depends on the order in which they are added to the shape manager.
* @param zIndex the new z-index;
*/
void setZIndex(qint16 zIndex);
/**
* Maximum value of z-index
*/
static const qint16 maxZIndex;
/**
* Minimum value of z-index
*/
static const qint16 minZIndex;
/**
* Retrieve the run through property of this shape.
* The run through property is used to determine if the shape is behind, inside or before text.
* @return the run through of this shape.
*/
int runThrough();
/**
* Set the run through property of this shape.
* The run through property is used to determine if the shape is behind, inside or before text.
* @param runThrough the new run through;
*/
virtual void setRunThrough(short int runThrough);
/**
* Changes the Shape to be visible or invisible.
* Being visible means being painted, as well as being used for
* things like guidelines or searches.
* @param on when true; set the shape to be visible.
* @see setGeometryProtected(), setContentProtected(), setSelectable()
*/
void setVisible(bool on);
/**
* Returns current visibility state of this shape.
* Being visible means being painted, as well as being used for
* things like guidelines or searches.
* @param recursive when true, checks visibility recursively
* @return current visibility state of this shape.
* @see isGeometryProtected(), isContentProtected(), isSelectable()
*/
bool isVisible(bool recursive = true) const;
/**
* Changes the shape to be printable or not. The default is true.
*
* If a Shape's print flag is true, the shape will be printed. If
* false, the shape will not be printed. If a shape is not visible (@see isVisible),
* it isPrinted will return false, too.
*/
void setPrintable(bool on);
/**
* Returns the current printable state of this shape.
*
* A shape can be visible but not printable, not printable and not visible
* or visible and printable, but not invisible and still printable.
*
* @return current printable state of this shape.
*/
bool isPrintable() const;
/**
* Makes it possible for the user to select this shape.
* This parameter defaults to true.
* @param selectable when true; set the shape to be selectable by the user.
* @see setGeometryProtected(), setContentProtected(), setVisible()
*/
void setSelectable(bool selectable);
/**
* Returns if this shape can be selected by the user.
* @return true only when the object is selectable.
* @see isGeometryProtected(), isContentProtected(), isVisible()
*/
bool isSelectable() const;
/**
* Tells the shape to have its position/rotation and size protected from user-changes.
* The geometry being protected means the user can not change shape or position of the
* shape. This includes any matrix operation such as rotation.
* @param on when true; set the shape to have its geometry protected.
* @see setContentProtected(), setSelectable(), setVisible()
*/
void setGeometryProtected(bool on);
/**
* Returns current geometry protection state of this shape.
* The geometry being protected means the user can not change shape or position of the
* shape. This includes any matrix operation such as rotation.
* @return current geometry protection state of this shape.
* @see isContentProtected(), isSelectable(), isVisible()
*/
bool isGeometryProtected() const;
/**
* Marks the shape to have its content protected against editing.
* Content protection is a hint for tools to disallow the user editing the content.
* @param protect when true set the shapes content to be protected from user modification.
* @see setGeometryProtected(), setSelectable(), setVisible()
*/
void setContentProtected(bool protect);
/**
* Returns current content protection state of this shape.
* Content protection is a hint for tools to disallow the user editing the content.
* @return current content protection state of this shape.
* @see isGeometryProtected(), isSelectable(), isVisible()
*/
bool isContentProtected() const;
/**
* Returns the parent, or 0 if there is no parent.
* @return the parent, or 0 if there is no parent.
*/
KoShapeContainer *parent() const;
/**
* Set the parent of this shape.
* @param parent the new parent of this shape. Can be 0 if the shape has no parent anymore.
*/
void setParent(KoShapeContainer *parent);
/**
* @brief inheritsTransformFromAny checks if the shape inherits transformation from
* any of the shapes listed in \p ancestorsInQuestion. The inheritance is checked
* in recursive way.
* @return true if there is a (transitive) transformation-wise parent found in \p ancestorsInQuestion
*/
bool inheritsTransformFromAny(const QList<KoShape*> ancestorsInQuestion) const;
/**
* @return true if this shape has a common parent with \p shape
*/
bool hasCommonParent(const KoShape *shape) const;
/**
* Request a repaint to be queued.
* The repaint will be of the entire Shape, including its selection handles should this
* shape be selected.
* <p>This method will return immediately and only request a repaint. Successive calls
* will be merged into an appropriate repaint action.
*/
virtual void update() const;
/**
* Request a repaint to be queued.
* The repaint will be restricted to the parameters rectangle, which is expected to be
* in absolute coordinates of the canvas and it is expected to be
* normalized.
* <p>This method will return immediately and only request a repaint. Successive calls
* will be merged into an appropriate repaint action.
* @param rect the rectangle (in pt) to queue for repaint.
*/
virtual void updateAbsolute(const QRectF &rect) const;
/// Used by compareShapeZIndex() to order shapes
enum ChildZOrderPolicy {
ChildZDefault,
ChildZParentChild = ChildZDefault, ///< normal parent/child ordering
ChildZPassThrough ///< children are considered equal to this shape
};
/**
* Returns if during compareShapeZIndex() how this shape portrays the values
* of its children. The default behaviour is to let this shape's z values take
* the place of its childrens values, so you get a parent/child relationship.
* The children are naturally still ordered relatively to their z values
*
* But for special cases (like Calligra's TextShape) it can be overloaded to return
* ChildZPassThrough which means the children keep their own z values
* @returns the z order policy of this shape
*/
virtual ChildZOrderPolicy childZOrderPolicy();
/**
* This is a method used to sort a list using the STL sorting methods.
* @param s1 the first shape
* @param s2 the second shape
*/
static bool compareShapeZIndex(KoShape *s1, KoShape *s2);
/**
* returns the outline of the shape in the form of a path.
* The outline returned will always be relative to the position() of the shape, so
* moving the shape will not alter the result. The outline is used to draw the stroke
* on, for example.
* @returns the outline of the shape in the form of a path.
*/
virtual QPainterPath outline() const;
/**
* returns the outline of the shape in the form of a rect.
* The outlineRect returned will always be relative to the position() of the shape, so
* moving the shape will not alter the result. The outline is used to calculate
* the boundingRect.
* @returns the outline of the shape in the form of a rect.
*/
virtual QRectF outlineRect() const;
/**
* returns the outline of the shape in the form of a path for the use of painting a shadow.
*
* Normally this would be the same as outline() if there is a fill (background) set on the
* shape and empty if not. However, a shape could reimplement this to return an outline
* even if no fill is defined. A typical example of this would be the picture shape
* which has a picture but almost never a background.
*
* @returns the outline of the shape in the form of a path.
*/
virtual QPainterPath shadowOutline() const;
/**
* Returns the currently set stroke, or 0 if there is no stroke.
* @return the currently set stroke, or 0 if there is no stroke.
*/
KoShapeStrokeModelSP stroke() const;
/**
* Set a new stroke, removing the old one. The stroke inheritance becomes disabled.
* @param stroke the new stroke, or 0 if there should be no stroke.
*/
void setStroke(KoShapeStrokeModelSP stroke);
/**
* @brief setInheritStroke marks a shape as inhiriting the stroke
* from the parent shape. NOTE: The currently selected stroke is destroyed.
* @param value true if the shape should inherit the stroke style
*/
void setInheritStroke(bool value);
/**
* @brief inheritStroke shows if the shape inherits the stroke from its parent
* @return true if the shape inherits the stroke style
*/
bool inheritStroke() const;
/**
* Return the insets of the stroke.
* Convenience method for KoShapeStrokeModel::strokeInsets()
*/
KoInsets strokeInsets() const;
/// Sets the new shadow, removing the old one
void setShadow(KoShapeShadow *shadow);
/// Returns the currently set shadow or 0 if there is no shadow set
KoShapeShadow *shadow() const;
/// Sets the new border, removing the old one.
void setBorder(KoBorder *border);
/// Returns the currently set border or 0 if there is no border set
KoBorder *border() const;
/// Sets a new clip path, removing the old one
void setClipPath(KoClipPath *clipPath);
/// Returns the currently set clip path or 0 if there is no clip path set
KoClipPath * clipPath() const;
/// Sets a new clip mask, removing the old one. The mask is owned by the shape.
void setClipMask(KoClipMask *clipMask);
/// Returns the currently set clip mask or 0 if there is no clip mask set
KoClipMask* clipMask() const;
/**
* Setting the shape to keep its aspect-ratio has the effect that user-scaling will
* keep the width/height ratio intact so as not to distort shapes that rely on that
* ratio.
* @param keepAspect the new value
*/
void setKeepAspectRatio(bool keepAspect);
/**
* Setting the shape to keep its aspect-ratio has the effect that user-scaling will
* keep the width/height ratio intact so as not to distort shapes that rely on that
* ratio.
* @return whether to keep aspect ratio of this shape
*/
bool keepAspectRatio() const;
/**
* Return the position of this shape regardless of rotation/skew/scaling and regardless of
* this shape having a parent (being in a group) or not.<br>
* @param anchor The place on the (unaltered) shape that you want the position of.
* @return the point that is the absolute, centered position of this shape.
*/
QPointF absolutePosition(KoFlake::AnchorPosition anchor = KoFlake::Center) const;
/**
* Move this shape to an absolute position where the end location will be the same
* regardless of the shape's rotation/skew/scaling and regardless of this shape having
* a parent (being in a group) or not.<br>
* The newPosition is going to be the center of the shape.
* This has the convenient effect that: <pre>
shape-&gt;setAbsolutePosition(QPointF(0,0));
shape-&gt;rotate(45);</pre>
Will result in the same visual position of the shape as the opposite:<pre>
shape-&gt;rotate(45);
shape-&gt;setAbsolutePosition(QPointF(0,0));</pre>
* @param newPosition the new absolute center of the shape.
* @param anchor The place on the (unaltered) shape that you set the position of.
*/
void setAbsolutePosition(const QPointF &newPosition, KoFlake::AnchorPosition anchor = KoFlake::Center);
/**
* Set a data object on the shape to be used by an application.
* This is specifically useful when a shape is created in a plugin and that data from that
* shape should be accessible outside the plugin.
* @param userData the new user data, or 0 to delete the current one.
*/
void setUserData(KoShapeUserData *userData);
/**
* Return the current userData.
*/
KoShapeUserData *userData() const;
/**
* Return the Id of this shape, identifying the type of shape by the id of the factory.
* @see KoShapeFactoryBase::shapeId()
* @return the id of the shape-type
*/
QString shapeId() const;
/**
* Set the Id of this shape. A shapeFactory is expected to set the Id at creation
* so applications can find out what kind of shape this is.
* @see KoShapeFactoryBase::shapeId()
* @param id the ID from the factory that created this shape
*/
void setShapeId(const QString &id);
/**
* Create a matrix that describes all the transformations done on this shape.
*
* The absolute transformation is the combined transformation of this shape
* and all its parents and grandparents.
*
* @param converter if not null, this method uses the converter to mark the right
* offsets in the current view.
*/
QTransform absoluteTransformation(const KoViewConverter *converter) const;
/**
* Applies a transformation to this shape.
*
* The transformation given is relative to the global coordinate system, i.e. the document.
* This is a convenience function to apply a global transformation to this shape.
* @see applyTransformation
*
* @param matrix the transformation matrix to apply
*/
void applyAbsoluteTransformation(const QTransform &matrix);
/**
* Sets a new transformation matrix describing the local transformations on this shape.
* @param matrix the new transformation matrix
*/
void setTransformation(const QTransform &matrix);
/// Returns the shapes local transformation matrix
QTransform transformation() const;
/**
* Applies a transformation to this shape.
*
* The transformation given is relative to the shape coordinate system.
*
* @param matrix the transformation matrix to apply
*/
void applyTransformation(const QTransform &matrix);
/**
* Copy all the settings from the parameter shape and apply them to this shape.
* Settings like the position and rotation to visible and locked. The parent
* is a notable exclusion.
* @param shape the shape to use as original
*/
void copySettings(const KoShape *shape);
/**
* Convenience method that allows people implementing paint() to use the shape
* internal coordinate system directly to paint itself instead of considering the
* views zoom.
* @param painter the painter to alter the zoom level of.
* @param converter the converter for the current views zoom.
*/
static void applyConversion(QPainter &painter, const KoViewConverter &converter);
/**
* A convenience method that creates a handles helper with applying transformations at
* the same time. Please note that you shouldn't save/restore additionally. All the work
* on restoring original painter's transformations is done by the helper.
*/
static KisHandlePainterHelper createHandlePainterHelper(QPainter *painter, KoShape *shape, const KoViewConverter &converter, qreal handleRadius = 0.0);
/**
* @brief Transforms point from shape coordinates to document coordinates
* @param point in shape coordinates
* @return point in document coordinates
*/
QPointF shapeToDocument(const QPointF &point) const;
/**
* @brief Transforms rect from shape coordinates to document coordinates
* @param rect in shape coordinates
* @return rect in document coordinates
*/
QRectF shapeToDocument(const QRectF &rect) const;
/**
* @brief Transforms point from document coordinates to shape coordinates
* @param point in document coordinates
* @return point in shape coordinates
*/
QPointF documentToShape(const QPointF &point) const;
/**
* @brief Transform rect from document coordinates to shape coordinates
* @param rect in document coordinates
* @return rect in shape coordinates
*/
QRectF documentToShape(const QRectF &rect) const;
/**
* Returns the name of the shape.
* @return the shapes name
*/
QString name() const;
/**
* Sets the name of the shape.
* @param name the new shape name
*/
void setName(const QString &name);
/**
* Update the position of the shape in the tree of the KoShapeManager.
*/
void notifyChanged();
/**
* A shape can be in a state that it is doing processing data like loading or text layout.
* In this case it can be shown on screen probably partially but it should really not be printed
* until it is fully done processing.
* Warning! This method can be blocking for a long time
+ * @param converter The converter
* @param asynchronous If set to true the processing will can take place in a different thread and the
* function will not block until the shape is finished.
* In case of printing Flake will call this method from a non-main thread and only
* start printing it when the in case of printing method returned.
* If set to false the processing needs to be done synchronously and will
* block until the result is finished.
*/
virtual void waitUntilReady(const KoViewConverter &converter, bool asynchronous = true) const;
/// checks recursively if the shape or one of its parents is not visible or locked
virtual bool isShapeEditable(bool recursive = true) const;
/**
* Adds a shape which depends on this shape.
* Making a shape dependent on this one means it will get shapeChanged() called
* on each update of this shape.
*
* If this shape already depends on the given shape, establishing the
* dependency is refused to prevent circular dependencies.
*
* @param shape the shape which depends on this shape
* @return true if dependency could be established, otherwise false
* @see removeDependee(), hasDependee()
*/
bool addDependee(KoShape *shape);
/**
* Removes as shape depending on this shape.
* @see addDependee(), hasDependee()
*/
void removeDependee(KoShape *shape);
/// Returns if the given shape is dependent on this shape
bool hasDependee(KoShape *shape) const;
/// Returns list of shapes depending on this shape
QList<KoShape*> dependees() const;
/// Returns additional snap data the shape wants to have snapping to
virtual KoSnapData snapData() const;
/**
* Set additional attribute
*
* This can be used to attach additional attributes to a shape for attributes
* that are application specific like presentation:placeholder
*
* @param name The name of the attribute in the following form prefix:tag e.g. presentation:placeholder
* @param value The value of the attribute
*/
void setAdditionalAttribute(const QString &name, const QString &value);
/**
* Remove additional attribute
*
* @param name The name of the attribute in the following form prefix:tag e.g. presentation:placeholder
*/
void removeAdditionalAttribute(const QString &name);
/**
* Check if additional attribute is set
*
* @param name The name of the attribute in the following form prefix:tag e.g. presentation:placeholder
*
* @return true if there is a attribute with prefix:tag set, false otherwise
*/
bool hasAdditionalAttribute(const QString &name) const;
/**
* Get additional attribute
*
* @param name The name of the attribute in the following form prefix:tag e.g. presentation:placeholder
*
* @return The value of the attribute if it exists or a null string if not found.
*/
QString additionalAttribute(const QString &name) const;
void setAdditionalStyleAttribute(const char *name, const QString &value);
void removeAdditionalStyleAttribute(const char *name);
/**
* Returns the filter effect stack of the shape
*
* @return the list of filter effects applied on the shape when rendering.
*/
KoFilterEffectStack *filterEffectStack() const;
/// Sets the new filter effect stack, removing the old one
void setFilterEffectStack(KoFilterEffectStack *filterEffectStack);
/**
* Set the property collision detection.
* Setting this to true will result in calls to shapeChanged() with the CollisionDetected
* parameter whenever either this or another shape is moved/rotated etc and intersects this shape.
* @param detect if true detect collisions.
*/
void setCollisionDetection(bool detect);
/**
* get the property collision detection.
* @returns true if collision detection is on.
*/
bool collisionDetection();
/**
* Return the tool delegates for this shape.
* In Flake a shape being selected will cause the tool manager to make available all tools that
* can edit the selected shapes. In some cases selecting one shape should allow the tool to
* edit a related shape be available too. The tool delegates allows this to happen by taking
* all the shapes in the set into account on tool selection.
* Notice that if the set is non-empty 'this' shape is no longer looked at. You can choose
* to add itself to the set too.
*/
QSet<KoShape*> toolDelegates() const;
/**
* Set the tool delegates.
* @param delegates the new delegates.
* @see toolDelegates()
*/
void setToolDelegates(const QSet<KoShape*> &delegates);
/**
* Return the hyperlink for this shape.
*/
QString hyperLink () const;
/**
* Set hyperlink for this shape.
* @param hyperLink name.
*/
void setHyperLink(const QString &hyperLink);
/**
* \internal
* Returns the private object for use within the flake lib
*/
KoShapePrivate *priv();
public:
struct KRITAFLAKE_EXPORT ShapeChangeListener {
virtual ~ShapeChangeListener();
virtual void notifyShapeChanged(ChangeType type, KoShape *shape) = 0;
private:
friend class KoShape;
friend class KoShapePrivate;
void registerShape(KoShape *shape);
void unregisterShape(KoShape *shape);
void notifyShapeChangedImpl(ChangeType type, KoShape *shape);
QList<KoShape*> m_registeredShapes;
};
void addShapeChangeListener(ShapeChangeListener *listener);
void removeShapeChangeListener(ShapeChangeListener *listener);
public:
static QList<KoShape*> linearizeSubtree(const QList<KoShape*> &shapes);
protected:
/// constructor
KoShape(KoShapePrivate *);
/* ** loading saving helper methods */
/// attributes from ODF 1.1 chapter 9.2.15 Common Drawing Shape Attributes
enum OdfAttribute {
OdfTransformation = 1, ///< Store transformation information
OdfSize = 2, ///< Store size information
OdfPosition = 8, ///< Store position
OdfAdditionalAttributes = 4, ///< Store additional attributes of the shape
OdfCommonChildElements = 16, ///< Event actions and connection points
OdfLayer = 64, ///< Store layer name
OdfStyle = 128, ///< Store the style
OdfId = 256, ///< Store the unique ID
OdfName = 512, ///< Store the name of the shape
OdfZIndex = 1024, ///< Store the z-index
OdfViewbox = 2048, ///< Store the viewbox
/// A mask for all mandatory attributes
OdfMandatories = OdfLayer | OdfStyle | OdfId | OdfName | OdfZIndex,
/// A mask for geometry attributes
OdfGeometry = OdfPosition | OdfSize,
/// A mask for all the attributes
OdfAllAttributes = OdfTransformation | OdfGeometry | OdfAdditionalAttributes | OdfMandatories | OdfCommonChildElements
};
/**
* This method is used during loading of the shape to load common attributes
*
* @param context the KoShapeLoadingContext used for loading
* @param element element which represents the shape in odf
* @param attributes a number of OdfAttribute items to state which attributes to load.
*/
bool loadOdfAttributes(const KoXmlElement &element, KoShapeLoadingContext &context, int attributes);
/**
* Parses the transformation attribute from the given string
* @param transform the transform attribute string
* @return the resulting transformation matrix
*/
QTransform parseOdfTransform(const QString &transform);
/**
* @brief Saves the style used for the shape
*
* This method fills the given style object with the stroke and
* background properties and then adds the style to the context.
*
* @param style the style object to fill
* @param context used for saving
* @return the name of the style
* @see saveOdf
*/
virtual QString saveStyle(KoGenStyle &style, KoShapeSavingContext &context) const;
/**
* Loads the stroke and fill style from the given element.
*
* @param element the xml element to load the style from
* @param context the loading context used for loading
*/
virtual void loadStyle(const KoXmlElement &element, KoShapeLoadingContext &context);
/// Loads the stroke style
KoShapeStrokeModelSP loadOdfStroke(const KoXmlElement &element, KoShapeLoadingContext &context) const;
/// Loads the fill style
QSharedPointer<KoShapeBackground> loadOdfFill(KoShapeLoadingContext &context) const;
/// Loads the connection points
void loadOdfGluePoints(const KoXmlElement &element, KoShapeLoadingContext &context);
/// Loads the clip contour
void loadOdfClipContour(const KoXmlElement &element, KoShapeLoadingContext &context, const QSizeF &scaleFactor);
/* ** end loading saving */
/**
* A hook that allows inheriting classes to do something after a KoShape property changed
* This is called whenever the shape, position rotation or scale properties were altered.
* @param type an indicator which type was changed.
+ * @param shape the shape.
*/
virtual void shapeChanged(ChangeType type, KoShape *shape = 0);
/// return the current matrix that contains the rotation/scale/position of this shape
QTransform transform() const;
KoShapePrivate *d_ptr;
private:
Q_DECLARE_PRIVATE(KoShape)
};
Q_DECLARE_METATYPE(KoShape*)
#endif
diff --git a/libs/flake/KoShapeContainer.h b/libs/flake/KoShapeContainer.h
index c1b934dd07..3f31b9d8fc 100644
--- a/libs/flake/KoShapeContainer.h
+++ b/libs/flake/KoShapeContainer.h
@@ -1,251 +1,252 @@
/* This file is part of the KDE project
* Copyright (C) 2006-2010 Thomas Zander <zander@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOSHAPECONTAINER_H
#define KOSHAPECONTAINER_H
#include "KoShape.h"
#include <QList>
#include "kritaflake_export.h"
class QPainter;
class KoShapeContainerModel;
class KoShapeContainerPrivate;
class KoViewConverter;
/**
* This is the base class that all Flake group-shapes are based on.
* Extending from this class allows you to have child-shapes.
* Like the KoShape class, this shape is a visible class with
* a position and a size. It can paint itself as well if you implement
* the paintComponent() method.
*
* <p>The most important feature of this class is that you can make
* other KoShape classes to be children of this container.
*
* <p>The effect of grouping those shapes is that their position
* is relative to the position of the container. Move the container and
* all children move with it.
*
* <p>Each child can optionally be said to be 'clipped' by the container.
* This feature will give the effect that if the child has a size and
* position outside the container, parts outside the container will not be shown.
* This is especially useful
* for showing cutouts of content, like images, without changing the actual content.
*
* <p>For so called clipped children any modification made to the container is
* propagated to the child. This includes rotation as well as scaling
* and shearing.
*
* <p>Maintaining the list of children can be done using the supplied methods
* addChild() and removeChild(). However, they only forward their requests to the
* data model KoShapeContainerModel and if you provide a custom implementation
* of that model any means can be used to maintain a list of children, as long as
* you will take care to register them with the appropriate shape manager.
*
* <p>An example usage where a custom model might be useful is when you have a
* container for text areas which are split into columns. If you resize the container
* and the width of the individual columns gets too small, the model can choose to
* remove a child or add one when the width allows another column.
*/
class KRITAFLAKE_EXPORT KoShapeContainer : public KoShape
{
public:
/**
* Constructor with custom model to be used for maintaining the list of children.
* For all the normal cases you don't need a custom model. Only when you want to respond
* to moves of the container to do something special, or disable one of the features the
* container normally has (like clipping). Use the default constructor in those cases.
* @param model the custom model to be used for maintaining the list of children.
*/
explicit KoShapeContainer(KoShapeContainerModel *model = 0);
/**
* Destructor for the shape container.
* All children will be orphaned by calling a KoShape::setParent(0)
*/
~KoShapeContainer() override;
/**
* Add a child to this container.
*
* This container will NOT take over ownership of the shape. The caller or those creating
* the shape is responsible to delete it if not needed any longer.
*
* @param shape the child to be managed in the container.
*/
void addShape(KoShape *shape);
/**
* Remove a child to be completely separated from the container.
*
* The shape will only be removed from this container but not be deleted.
*
* @param shape the child to be removed.
*/
void removeShape(KoShape *shape);
/**
* Return the current number of children registered.
* @return the current number of children registered.
*/
int shapeCount() const;
/**
* Set the argument child to have its 'clipping' property set.
*
* A shape that is clipped by the container will have its visible portion
* limited to the area where it intersects with the container.
* If a shape is positioned or sized such that it would be painted outside
* of the KoShape::outline() of its parent container, setting this property
* to true will clip the shape painting to the container outline.
*
* @param child the child for which the property will be changed.
* @param clipping the property
*/
void setClipped(const KoShape *child, bool clipping);
/**
* Returns if the argument child has its 'clipping' property set.
*
* A shape that is clipped by the container will have its visible portion
* limited to the area where it intersects with the container.
* If a shape is positioned or sized such that it would be painted outside
* of the KoShape::outline() of its parent container, setting this property
* to true will clip the shape painting to the container outline.
*
* @return if the argument child has its 'clipping' property set.
* @param child the child for which the property will be returned.
*/
bool isClipped(const KoShape *child) const;
/**
* Set the shape to inherit the container transform.
*
* A shape that inherits the transform of the parent container will have its
* share / rotation / skew etc be calculated as being the product of both its
* own local transformation and also that of its parent container.
* If you set this to true and rotate the container, the shape will get that
* rotation as well automatically.
*
* @param shape the shape for which the property will be changed.
* @param inherit the new value
*/
void setInheritsTransform(const KoShape *shape, bool inherit);
/**
* Returns if the shape inherits the container transform.
*
* A shape that inherits the transform of the parent container will have its
* share / rotation / skew etc be calculated as being the product of both its
* own local transformation and also that of its parent container.
* If you set this to true and rotate the container, the shape will get that
* rotation as well automatically.
*
* @return if the argument shape has its 'inherits transform' property set.
* @param shape the shape for which the property will be returned.
*/
bool inheritsTransform(const KoShape *shape) const;
/// reimplemented
void paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintcontext) override;
/**
* @brief Paint the component
* Implement this method to allow the shape to paint itself, just like the KoShape::paint()
* method does.
*
* @param painter used for painting the shape
* @param converter to convert between internal and view coordinates.
+ * @param paintcontext the painting context
* @see applyConversion()
*/
virtual void paintComponent(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintcontext) = 0;
using KoShape::update;
/// reimplemented
void update() const override;
/**
* Return the list of all child shapes.
* @return the list of all child shapes
*/
QList<KoShape*> shapes() const;
/**
* return the model for this container
*/
KoShapeContainerModel *model() const;
/**
* A special interface for KoShape to use during setParent call. Don't use
* these method directly for managing shapes hierarchy! Use shape->setParent()
* instead.
*/
struct ShapeInterface {
ShapeInterface(KoShapeContainer *_q);
/**
* Add a child to this container.
*
* This container will NOT take over ownership of the shape. The caller or those creating
* the shape is responsible to delete it if not needed any longer.
*
* @param shape the child to be managed in the container.
*/
void addShape(KoShape *shape);
/**
* Remove a child to be completely separated from the container.
*
* The shape will only be removed from this container but not be deleted.
*
* @param shape the child to be removed.
*/
void removeShape(KoShape *shape);
protected:
KoShapeContainer *q;
};
ShapeInterface* shapeInterface();
protected:
/**
* This hook is for inheriting classes that need to do something on adding/removing
* of children.
* This method will be called just after the child has been added/removed.
* The default implementation is empty.
*/
virtual void shapeCountChanged() { }
void shapeChanged(ChangeType type, KoShape *shape = 0) override;
/// constructor
KoShapeContainer(KoShapeContainerPrivate *);
private:
Q_DECLARE_PRIVATE(KoShapeContainer)
};
#endif
diff --git a/libs/flake/KoShapeController.h b/libs/flake/KoShapeController.h
index cde79865e5..9e60ff550a 100644
--- a/libs/flake/KoShapeController.h
+++ b/libs/flake/KoShapeController.h
@@ -1,170 +1,173 @@
/* This file is part of the KDE project
*
* Copyright (C) 2006-2007, 2010 Thomas Zander <zander@kde.org>
* Copyright (C) 2006-2008 Thorsten Zachmann <zachmann@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOSHAPECONTROLLER_H
#define KOSHAPECONTROLLER_H
#include "kritaflake_export.h"
#include <QObject>
#include <QList>
#include <QMetaType>
class KoCanvasBase;
class KoShape;
class KoShapeContainer;
class KoShapeControllerBase;
class KUndo2Command;
class KoDocumentResourceManager;
/**
* Class used by tools to maintain the list of shapes.
* All applications have some sort of list of all shapes that belong to the document.
* The applications implement the KoShapeControllerBase interface (all pure virtuals)
* to add and remove shapes from the document. To ensure that an application can expect
* a certain protocol to be adhered to when adding/removing shapes, all tools use the API
* from this class for maintaining the list of shapes in the document. So no tool gets
* to access the application directly.
*/
class KRITAFLAKE_EXPORT KoShapeController : public QObject
{
Q_OBJECT
public:
/**
* Create a new Controller; typically not called by applications, only
* by the KonCanvasBase constructor.
* @param canvas the canvas this controller works for. The canvas can be 0
* @param shapeController the application provided shapeController that we can call.
*/
KoShapeController(KoCanvasBase *canvas, KoShapeControllerBase *shapeController);
/// destructor
~KoShapeController() override;
/**
* @brief reset sets the canvas and shapebased document to 0.
*/
void reset();
/**
* @brief Add a shape to the document.
* If the shape has no parent, the active layer will become its parent.
*
* @param shape to add to the document
+ * @param parentShape the parent shape
* @param parent the parent command if the resulting command is a compound undo command.
*
* @return command which will insert the shape into the document or 0 if the
* insertion was cancelled. The command is not yet executed.
*/
KUndo2Command* addShape(KoShape *shape, KoShapeContainer *parentShape, KUndo2Command *parent = 0);
/**
* @brief Add a shape to the document, skipping any dialogs or other user interaction.
*
* @param shape to add to the document
+ * @param parentShape the parent shape
* @param parent the parent command if the resulting command is a compound undo command.
*
* @return command which will insert the shape into the document. The command is not yet executed.
*/
KUndo2Command* addShapeDirect(KoShape *shape, KoShapeContainer *parentShape, KUndo2Command *parent = 0);
/**
* @brief Add shapes to the document, skipping any dialogs or other user interaction.
*
- * @param shapes to add to the document
+ * @param shape the shape to add to the document
+ * @param parentShape the parent shape
* @param parent the parent command if the resulting command is a compound undo command.
*
* @return command which will insert the shapes into the document. The command is not yet executed.
*/
KUndo2Command* addShapesDirect(const QList<KoShape*> shape, KoShapeContainer *parentShape, KUndo2Command *parent = 0);
/**
* @brief Remove a shape from the document.
*
* @param shape to remove from the document
* @param parent the parent command if the resulting command is a compound undo command.
*
* @return command which will remove the shape from the document.
* The command is not yet executed.
*/
KUndo2Command* removeShape(KoShape *shape, KUndo2Command *parent = 0);
/**
* Remove a shape from the document.
*
* @param shapes the set of shapes to remove from the document
* @param parent the parent command if the resulting command is a compound undo command.
*
* @return command which will remove the shape from the document.
* The command is not yet executed.
*/
KUndo2Command* removeShapes(const QList<KoShape*> &shapes, KUndo2Command *parent = 0);
/**
* @brief Set the KoShapeControllerBase used to add/remove shapes.
*
* NOTE: only Sheets uses this method. Do not use it in your application. Sheets
* has to also call:
* <code>KoToolManager::instance()->updateShapeControllerBase(shapeController, canvas->canvasController());</code>
*
* @param shapeController the new shapeController.
*/
void setShapeControllerBase(KoShapeControllerBase *shapeController);
/**
* The size of the document measured in rasterized pixels. This information is needed for loading
* SVG documents that use 'px' as the default unit.
*/
QRectF documentRectInPixels() const;
/**
* Resolution of the rasterized representation of the document. Used to load SVG documents correctly.
*/
qreal pixelsPerInch() const;
/**
* Document rect measured in 'pt'
*/
QRectF documentRect() const;
/**
* Return a pointer to the resource manager associated with the
* shape-set (typically a document). The resource manager contains
* document wide resources * such as variable managers, the image
* collection and others.
*/
KoDocumentResourceManager *resourceManager() const;
/**
* @brief Returns the KoShapeControllerBase used to add/remove shapes.
*
* @return the KoShapeControllerBase
*/
KoShapeControllerBase *documentBase() const;
private:
class Private;
Private * const d;
};
Q_DECLARE_METATYPE(KoShapeController *)
#endif
diff --git a/libs/flake/KoShapeControllerBase.h b/libs/flake/KoShapeControllerBase.h
index c11dc0b4be..f14eee34a5 100644
--- a/libs/flake/KoShapeControllerBase.h
+++ b/libs/flake/KoShapeControllerBase.h
@@ -1,110 +1,110 @@
/* This file is part of the KDE project
Copyright (C) 2006 Jan Hambrecht <jaham@gmx.net>
Copyright (C) 2006, 2010 Thomas Zander <zander@kde.org>
Copyright (C) 2008 C. Boemann <cbo@boemann.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOshapeControllerBASE_H
#define KOshapeControllerBASE_H
#include "kritaflake_export.h"
#include <QList>
class QRectF;
class KoShape;
class KoshapeControllerBasePrivate;
class KoDocumentResourceManager;
class KUndo2Command;
/**
* The KoshapeControllerBase is an abstract interface that the application's class
* that owns the shapes should implement. This tends to be the document.
* @see KoShapeDeleteCommand, KoShapeCreateCommand
*/
class KRITAFLAKE_EXPORT KoShapeControllerBase
{
public:
KoShapeControllerBase();
virtual ~KoShapeControllerBase();
/**
* Add a shape to the shape controller, allowing it to be seen and saved.
* The controller should add the shape to the ShapeManager instance(s) manually
* if the shape is one that should be currently shown on screen.
* @param shape the new shape
*/
void addShape(KoShape *shape);
/**
* Add shapes to the shape controller, allowing it to be seen and saved.
* The controller should add the shape to the ShapeManager instance(s) manually
* if the shape is one that should be currently shown on screen.
- * @param shape the new shape
+ * @param shapes the shapes to add
*/
virtual void addShapes(const QList<KoShape*> shapes) = 0;
/**
* Remove a shape from the shape controllers control, allowing it to be deleted shortly after
* The controller should remove the shape from all the ShapeManager instance(s) manually
* @param shape the shape to remove
*/
virtual void removeShape(KoShape *shape) = 0;
/**
* This method gets called after the KoShapeDeleteCommand is executed
*
* This passes the KoShapeDeleteCommand as the command parameter. This makes it possible
* for applications that need to do something after the KoShapeDeleteCommand is done, e.g.
* adding one commands that need to be executed when a shape was deleted.
* The default implementation is empty.
* @param shapes The list of shapes that got removed.
* @param command The command that was used to remove the shapes from the document.
*/
virtual void shapesRemoved(const QList<KoShape*> &shapes, KUndo2Command *command);
/**
* Return a pointer to the resource manager associated with the
* shape-set (typically a document). The resource manager contains
* document wide resources * such as variable managers, the image
* collection and others.
*/
virtual KoDocumentResourceManager *resourceManager() const;
/**
* The size of the document measured in rasterized pixels. This information is needed for loading
* SVG documents that use 'px' as the default unit.
*/
virtual QRectF documentRectInPixels() const = 0;
/**
* The size of the document measured in 'pt'
*/
QRectF documentRect() const;
/**
* Resolution of the rasterized representation of the document. Used to load SVG documents correctly.
*/
virtual qreal pixelsPerInch() const = 0;
private:
KoshapeControllerBasePrivate * const d;
};
#endif
diff --git a/libs/flake/KoShapeManager.h b/libs/flake/KoShapeManager.h
index 8b9600658d..9b21d5af37 100644
--- a/libs/flake/KoShapeManager.h
+++ b/libs/flake/KoShapeManager.h
@@ -1,212 +1,214 @@
/* This file is part of the KDE project
Copyright (C) 2006-2008 Thorsten Zachmann <zachmann@kde.org>
Copyright (C) 2007, 2009 Thomas Zander <zander@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOSHAPEMANAGER_H
#define KOSHAPEMANAGER_H
#include <QList>
#include <QObject>
#include <QSet>
#include "KoFlake.h"
#include "kritaflake_export.h"
class KoShape;
class KoSelection;
class KoViewConverter;
class KoCanvasBase;
class KoPointerEvent;
class KoShapePaintingContext;
class QPainter;
class QPointF;
class QRectF;
/**
* The shape manager hold a list of all shape which are in scope.
* There is one shape manager per canvas. This makes the shape manager
* different from QGraphicsScene, which contains the datamodel for all
* graphics items: KoShapeManager only contains the subset of shapes
* that are shown in its canvas.
*
* The selection in the different views can be different.
*/
class KRITAFLAKE_EXPORT KoShapeManager : public QObject
{
Q_OBJECT
public:
/// enum for add()
enum Repaint {
PaintShapeOnAdd, ///< Causes each shapes 'update()' to be called after being added to the shapeManager
AddWithoutRepaint ///< Avoids each shapes 'update()' to be called for faster addition when its possible.
};
/**
* Constructor.
*/
explicit KoShapeManager(KoCanvasBase *canvas);
/**
* Constructor that takes a list of shapes, convenience version.
* @param shapes the shapes to start out with, see also setShapes()
* @param canvas the canvas this shape manager is working on.
*/
KoShapeManager(KoCanvasBase *canvas, const QList<KoShape *> &shapes);
~KoShapeManager() override;
/**
* Remove all previously owned shapes and make the argument list the new shapes
* to be managed by this manager.
* @param shapes the new shapes to manage.
* @param repaint if true it will trigger a repaint of the shapes
*/
void setShapes(const QList<KoShape *> &shapes, Repaint repaint = PaintShapeOnAdd);
/// returns the list of maintained shapes
QList<KoShape*> shapes() const;
/**
* Get a list of all shapes that don't have a parent.
*/
QList<KoShape*> topLevelShapes() const;
public Q_SLOTS:
/**
* Add a KoShape to be displayed and managed by this manager.
* This will trigger a repaint of the shape.
* @param shape the shape to add
* @param repaint if true it will trigger a repaint of the shape
*/
void addShape(KoShape *shape, KoShapeManager::Repaint repaint = PaintShapeOnAdd);
/**
* Remove a KoShape from this manager
* @param shape the shape to remove
*/
void remove(KoShape *shape);
public:
/// return the selection shapes for this shapeManager
KoSelection *selection() const;
/**
* Paint all shapes and their selection handles etc.
* @param painter the painter to paint to.
* @param forPrint if true, make sure only actual content is drawn and no decorations.
* @param converter to convert between document and view coordinates.
*/
void paint(QPainter &painter, const KoViewConverter &converter, bool forPrint);
/**
* Returns the shape located at a specific point in the document.
* If more than one shape is located at the specific point, the given selection type
* controls which of them is returned.
* @param position the position in the document coordinate system.
* @param selection controls which shape is returned when more than one shape is at the specific point
* @param omitHiddenShapes if true, only visible shapes are considered
*/
KoShape *shapeAt(const QPointF &position, KoFlake::ShapeSelection selection = KoFlake::ShapeOnTop, bool omitHiddenShapes = true);
/**
* Returns the shapes which intersects the specific rect in the document.
* @param rect the rectangle in the document coordinate system.
- * @param omitHiddenShapes if true, only visible shapes are considered
+ * @param omitHiddenShapes if @c true, only visible shapes are considered
+ * @param containedMode if @c true use contained mode
*/
QList<KoShape *> shapesAt(const QRectF &rect, bool omitHiddenShapes = true, bool containedMode = false);
/**
* Request a repaint to be queued.
* The repaint will be restricted to the parameters rectangle, which is expected to be
* in points (the document coordinates system of KoShape) and it is expected to be
* normalized and based in the global coordinates, not any local coordinates.
* <p>This method will return immediately and only request a repaint. Successive calls
* will be merged into an appropriate repaint action.
* @param rect the rectangle (in pt) to queue for repaint.
* @param shape the shape that is going to be redrawn; only needed when selectionHandles=true
* @param selectionHandles if true; find out if the shape is selected and repaint its
* selection handles at the same time.
*/
void update(const QRectF &rect, const KoShape *shape = 0, bool selectionHandles = false);
/**
* Update the tree for finding the shapes.
* This will remove the shape from the tree and will reinsert it again.
* The update to the tree will be posponed until it is needed so that successive calls
* will be merged into one.
* @param shape the shape to updated its position in the tree.
*/
void notifyShapeChanged(KoShape *shape);
/**
* Paint a shape
*
* @param shape the shape to paint
* @param painter the painter to paint to.
* @param converter to convert between document and view coordinates.
+ * @param paintContext the painting context
*/
static void paintShape(KoShape *shape, QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext);
/**
* @brief renderSingleShape renders a shape on \p painter. This method includes all the
* needed steps for painting a single shape: setting transformations, clipping and masking.
*/
static void renderSingleShape(KoShape *shape, QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext);
/**
* A special interface for KoShape to use during shape destruction. Don't use this
* interface directly unless you are KoShape.
*/
struct ShapeInterface {
ShapeInterface(KoShapeManager *_q);
/**
* Called by a shape when it is destructed. Please note that you cannot access
* any shape's method type or information during this call because the shape might be
* semi-destroyed.
*/
void notifyShapeDestructed(KoShape *shape);
protected:
KoShapeManager *q;
};
ShapeInterface* shapeInterface();
Q_SIGNALS:
/// emitted when the selection is changed
void selectionChanged();
/// emitted when an object in the selection is changed (moved/rotated etc)
void selectionContentChanged();
/// emitted when any object changed (moved/rotated etc)
void contentChanged();
private:
KoCanvasBase *canvas();
class Private;
Private * const d;
Q_PRIVATE_SLOT(d, void updateTree())
};
#endif
diff --git a/libs/flake/KoToolBase.cpp b/libs/flake/KoToolBase.cpp
index 7d1f94c20c..66fc5910d5 100644
--- a/libs/flake/KoToolBase.cpp
+++ b/libs/flake/KoToolBase.cpp
@@ -1,373 +1,377 @@
/* This file is part of the KDE project
* Copyright (C) 2006, 2010 Thomas Zander <zander@kde.org>
* Copyright (C) 2011 Jan Hambrecht <jaham@gmx.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <QDebug>
#include <QAction>
#include "KoToolBase.h"
#include "KoToolBase_p.h"
#include "KoCanvasBase.h"
#include "KoPointerEvent.h"
#include "KoDocumentResourceManager.h"
#include "KoCanvasResourceProvider.h"
#include "KoViewConverter.h"
#include "KoShapeController.h"
#include "KoShapeControllerBase.h"
#include "KoToolSelection.h"
#include "KoCanvasController.h"
#include <klocalizedstring.h>
#include <kactioncollection.h>
#include <QWidget>
#include <QFile>
#include <QDomDocument>
#include <QDomElement>
KoToolBase::KoToolBase(KoCanvasBase *canvas)
: d_ptr(new KoToolBasePrivate(this, canvas))
{
Q_D(KoToolBase);
d->connectSignals();
}
KoToolBase::KoToolBase(KoToolBasePrivate &dd)
: d_ptr(&dd)
{
Q_D(KoToolBase);
d->connectSignals();
}
KoToolBase::~KoToolBase()
{
qDeleteAll(d_ptr->optionWidgets);
delete d_ptr;
}
bool KoToolBase::isActivated() const
{
Q_D(const KoToolBase);
return d->isActivated;
}
void KoToolBase::activate(KoToolBase::ToolActivation toolActivation, const QSet<KoShape *> &shapes)
{
Q_UNUSED(toolActivation);
Q_UNUSED(shapes);
Q_D(KoToolBase);
d->isActivated = true;
}
void KoToolBase::deactivate()
{
Q_D(KoToolBase);
d->isActivated = false;
}
void KoToolBase::canvasResourceChanged(int key, const QVariant & res)
{
Q_UNUSED(key);
Q_UNUSED(res);
}
void KoToolBase::documentResourceChanged(int key, const QVariant &res)
{
Q_UNUSED(key);
Q_UNUSED(res);
}
bool KoToolBase::wantsAutoScroll() const
{
return true;
}
void KoToolBase::mouseDoubleClickEvent(KoPointerEvent *event)
{
event->ignore();
}
void KoToolBase::mouseTripleClickEvent(KoPointerEvent *event)
{
event->ignore();
}
void KoToolBase::keyPressEvent(QKeyEvent *e)
{
e->ignore();
}
void KoToolBase::keyReleaseEvent(QKeyEvent *e)
{
e->ignore();
}
void KoToolBase::explicitUserStrokeEndRequest()
{
}
QVariant KoToolBase::inputMethodQuery(Qt::InputMethodQuery query, const KoViewConverter &) const
{
Q_D(const KoToolBase);
if (d->canvas->canvasWidget() == 0)
return QVariant();
switch (query) {
case Qt::ImMicroFocus:
return QRect(d->canvas->canvasWidget()->width() / 2, 0, 1, d->canvas->canvasWidget()->height());
case Qt::ImFont:
return d->canvas->canvasWidget()->font();
default:
return QVariant();
}
}
void KoToolBase::inputMethodEvent(QInputMethodEvent * event)
{
if (! event->commitString().isEmpty()) {
QKeyEvent ke(QEvent::KeyPress, -1, 0, event->commitString());
keyPressEvent(&ke);
}
event->accept();
}
void KoToolBase::customPressEvent(KoPointerEvent * event)
{
event->ignore();
}
void KoToolBase::customReleaseEvent(KoPointerEvent * event)
{
event->ignore();
}
void KoToolBase::customMoveEvent(KoPointerEvent * event)
{
event->ignore();
}
void KoToolBase::useCursor(const QCursor &cursor)
{
Q_D(KoToolBase);
d->currentCursor = cursor;
emit cursorChanged(d->currentCursor);
}
QList<QPointer<QWidget> > KoToolBase::optionWidgets()
{
Q_D(KoToolBase);
- if (d->optionWidgets.empty()) {
+ if (!d->optionWidgetsCreated) {
d->optionWidgets = createOptionWidgets();
+ d->optionWidgetsCreated = true;
}
return d->optionWidgets;
}
QAction *KoToolBase::action(const QString &name) const
{
Q_D(const KoToolBase);
- return d->canvas->canvasController()->actionCollection()->action(name);
+ if (d->canvas && d->canvas->canvasController() && d->canvas->canvasController()) {
+ return d->canvas->canvasController()->actionCollection()->action(name);
+ }
+ return 0;
}
QWidget * KoToolBase::createOptionWidget()
{
return 0;
}
QList<QPointer<QWidget> > KoToolBase::createOptionWidgets()
{
QList<QPointer<QWidget> > ow;
if (QWidget *widget = createOptionWidget()) {
if (widget->objectName().isEmpty()) {
widget->setObjectName(toolId());
}
ow.append(widget);
}
return ow;
}
void KoToolBase::setToolId(const QString &id)
{
Q_D(KoToolBase);
d->toolId = id;
}
QString KoToolBase::toolId() const
{
Q_D(const KoToolBase);
return d->toolId;
}
QCursor KoToolBase::cursor() const
{
Q_D(const KoToolBase);
return d->currentCursor;
}
void KoToolBase::deleteSelection()
{
}
void KoToolBase::cut()
{
copy();
deleteSelection();
}
QMenu *KoToolBase::popupActionsMenu()
{
return 0;
}
KoCanvasBase * KoToolBase::canvas() const
{
Q_D(const KoToolBase);
return d->canvas;
}
void KoToolBase::setStatusText(const QString &statusText)
{
emit statusTextChanged(statusText);
}
uint KoToolBase::handleRadius() const
{
Q_D(const KoToolBase);
if(d->canvas->shapeController()->resourceManager())
{
return d->canvas->shapeController()->resourceManager()->handleRadius();
} else {
return 3;
}
}
uint KoToolBase::grabSensitivity() const
{
Q_D(const KoToolBase);
if(d->canvas->shapeController()->resourceManager())
{
return d->canvas->shapeController()->resourceManager()->grabSensitivity();
} else {
return 3;
}
}
QRectF KoToolBase::handleGrabRect(const QPointF &position) const
{
Q_D(const KoToolBase);
const KoViewConverter * converter = d->canvas->viewConverter();
uint handleSize = 2*grabSensitivity();
QRectF r = converter->viewToDocument(QRectF(0, 0, handleSize, handleSize));
r.moveCenter(position);
return r;
}
QRectF KoToolBase::handlePaintRect(const QPointF &position) const
{
Q_D(const KoToolBase);
const KoViewConverter * converter = d->canvas->viewConverter();
uint handleSize = 2*handleRadius();
QRectF r = converter->viewToDocument(QRectF(0, 0, handleSize, handleSize));
r.moveCenter(position);
return r;
}
void KoToolBase::setTextMode(bool value)
{
Q_D(KoToolBase);
d->isInTextMode=value;
}
bool KoToolBase::paste()
{
return false;
}
void KoToolBase::copy() const
{
}
void KoToolBase::dragMoveEvent(QDragMoveEvent *event, const QPointF &point)
{
Q_UNUSED(event);
Q_UNUSED(point);
}
void KoToolBase::dragLeaveEvent(QDragLeaveEvent *event)
{
Q_UNUSED(event);
}
void KoToolBase::dropEvent(QDropEvent *event, const QPointF &point)
{
Q_UNUSED(event);
Q_UNUSED(point);
}
bool KoToolBase::hasSelection()
{
KoToolSelection *sel = selection();
return (sel && sel->hasSelection());
}
KoToolSelection *KoToolBase::selection()
{
return 0;
}
void KoToolBase::repaintDecorations()
{
}
bool KoToolBase::isInTextMode() const
{
Q_D(const KoToolBase);
return d->isInTextMode;
}
void KoToolBase::requestUndoDuringStroke()
{
/**
* Default implementation just cancels the stroke
*/
requestStrokeCancellation();
}
void KoToolBase::requestStrokeCancellation()
{
}
void KoToolBase::requestStrokeEnd()
{
}
bool KoToolBase::maskSyntheticEvents() const
{
Q_D(const KoToolBase);
return d->maskSyntheticEvents;
}
void KoToolBase::setMaskSyntheticEvents(bool value)
{
Q_D(KoToolBase);
d->maskSyntheticEvents = value;
}
diff --git a/libs/flake/KoToolBase_p.h b/libs/flake/KoToolBase_p.h
index 4e60c8b0d0..8e8d2c2251 100644
--- a/libs/flake/KoToolBase_p.h
+++ b/libs/flake/KoToolBase_p.h
@@ -1,88 +1,89 @@
/* This file is part of the KDE project
* Copyright (C) 2006-2010 Thomas Zander <zander@kde.org>
* Copyright (C) 2010 KO GmbH <boud@valdyas.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOTOOLBASE_P_H
#define KOTOOLBASE_P_H
#include "KoDocumentResourceManager.h"
#include "KoCanvasResourceProvider.h"
#include "KoCanvasBase.h"
#include "KoShapeController.h"
#include <QHash>
#include <QWidget>
#include <QString>
#include <QPointer>
#include <string.h> // for the qt version check
class QAction;
class KoToolBase;
class KoToolBasePrivate
{
public:
KoToolBasePrivate(KoToolBase *qq, KoCanvasBase *canvas_)
: currentCursor(Qt::ArrowCursor),
q(qq),
canvas(canvas_),
isInTextMode(false),
isActivated(false)
{
}
~KoToolBasePrivate()
{
Q_FOREACH (QPointer<QWidget> optionWidget, optionWidgets) {
if (optionWidget) {
optionWidget->setParent(0);
delete optionWidget;
}
}
optionWidgets.clear();
}
void connectSignals()
{
if (canvas) { // in the case of KoToolManagers dummytool it can be zero :(
KoCanvasResourceProvider * crp = canvas->resourceManager();
Q_ASSERT_X(crp, "KoToolBase::KoToolBase", "No Canvas KoResourceManager");
if (crp)
q->connect(crp, SIGNAL(canvasResourceChanged(int, const QVariant &)),
SLOT(canvasResourceChanged(int, const QVariant &)));
// can be 0 in the case of Calligra Sheets
KoDocumentResourceManager *scrm = canvas->shapeController()->resourceManager();
if (scrm) {
q->connect(scrm, SIGNAL(resourceChanged(int, const QVariant &)),
SLOT(documentResourceChanged(int, const QVariant &)));
}
}
}
QList<QPointer<QWidget> > optionWidgets; ///< the optionwidgets associated with this tool
+ bool optionWidgetsCreated {false};
QCursor currentCursor;
QString toolId;
KoToolBase *q;
KoCanvasBase *canvas; ///< the canvas interface this tool will work for.
bool isInTextMode;
bool maskSyntheticEvents{false}; ///< Whether this tool masks synthetic events
bool isActivated;
};
#endif
diff --git a/libs/flake/KoToolFactoryBase.h b/libs/flake/KoToolFactoryBase.h
index db827364a8..a6a691b5d5 100644
--- a/libs/flake/KoToolFactoryBase.h
+++ b/libs/flake/KoToolFactoryBase.h
@@ -1,262 +1,263 @@
/* This file is part of the KDE project
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
* Copyright (C) 2006 Thomas Zander <zander@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KO_TOOL_FACTORY_H
#define KO_TOOL_FACTORY_H
#include "kritaflake_export.h"
#include <QString>
#include <QList>
class KoCanvasBase;
class KoToolBase;
class QKeySequence;
class KActionCollection;
class QAction;
/**
* A factory for KoToolBase objects.
*
* The baseclass for all tool plugins. Each plugin that ships a KoToolBase should also
* ship a factory. That factory will extend this class and set variable data like
* a toolTip and icon in the constructor of that extending class.
*
* An example usage would be:<pre>
* class MyToolFactory : public KoToolFactoryBase {
* public:
* MyToolFactory(const QStringList&)
* : KoToolFactoryBase("MyTool") {
* setToolTip(i18n("Create object"));
* setToolType("dynamic");
* setPriority(5);
* }
* ~MyToolFactory() {}
* KoToolBase *createTool(KoCanvasBase *canvas);
* };
* K_PLUGIN_FACTORY_WITH_JSON((MyToolFactoryFactory, "mytool.json", registerPlugin<MyToolFactory>();)
</pre>
*/
class KRITAFLAKE_EXPORT KoToolFactoryBase
{
public:
/**
* Create the new factory
* @param id a string that will be used internally for referencing the tool, for
* example for use by the KoToolBase::activateTemporary.
*/
explicit KoToolFactoryBase(const QString &id);
virtual ~KoToolFactoryBase();
/**
* Create the actions for this tool. Actions are unique per window, not per
* tool instance; tool instances are unique per view/canvas.
*/
QList<QAction *> createActions(KActionCollection *actionCollection);
/**
* Instantiate a new tool
* @param canvas the canvas that the new tool will work on. Should be passed
* to the constructor of the tool.
* @return a new KoToolBase instance, or zero if the tool doesn't want to show up.
*/
virtual KoToolBase *createTool(KoCanvasBase *canvas) = 0;
/**
* return the id for the tool this factory creates.
* @return the id for the tool this factory creates.
*/
QString id() const;
/**
* Returns The priority of this tool in its section in the toolbox
* @return The priority of this tool.
*/
int priority() const;
/**
* returns the type of tool, used to group tools in the toolbox
* @return the type of tool
*/
QString section() const;
/**
* return a translated tooltip Text
* @return a translated tooltip Text
*/
QString toolTip() const;
/**
* return the basename of the icon for this tool
* @return the basename of the icon for this tool
*/
QString iconName() const;
/**
* Return the id of the shape we can process.
* This is the shape Id the tool we create is associated with. So a TextTool for a TextShape.
* In combination with the toolType the following situations can occur;
<table><tr><th>Type</th><th>shapeId</th><th>Result</th></tr>
<tr>
<td>'main'</td>
<td>Foo</td>
<td>Tool will always be visible, but only active when shape with shapeId 'Foo' is in the selection.</td></tr>
<tr>
<td>'main'</td>
<td>''</td>
<td>Tool will always be visible, but only active when at least one shape is selected</td></tr>
<tr>
<td>'main'</td>
<td>'flake/always'</td>
<td>Tool will always be visible and enabled.</td></tr>
<tr>
<td>'main'</td>
<td>'flake/edit'</td>
<td>Tool will be visible no matter which shape is selected (if any), but only
be enabled when the current layer is editable.</td></tr>
<tr>
<td>'dynamic'</td>
<td>Foo</td>
<td>Tool will only be visible when shape with shapeId 'Foo' is in the selection.</td></tr>
<tr>
<td>'dynamic'</td>
<td>''</td>
<td>Tool will always be visible. We recommend you don't use this one.</td></tr>
<tr>
<td>"comma separated list of application names"</td>
<td>see main type</td>
<td>Similar to the 'main' item if the application name matches with the current application. Otherwise it's similar to 'dynamic', but segmented in its own section. If the list includes 'dynamic' it's even added to the dynamic section, when not matching the application name</td></tr>
<tr>
<td>'other'</td>
<td>any</td>
<td>similar to the 'dynamic' items, but segmented in its own section.</td></tr>
<tr>
<td>n/a</td>
<td>/always</td>
<td>An activation shape id ending with '/always' will make the tool always visible and enabled.</td></tr>
</table>
* @see KoShapeFactoryBase::shapeId()
* @see setActivationShapeId()
* @return the id of a shape, or an empty string for all shapes.
*/
QString activationShapeId() const;
/**
* Return the default keyboard shortcut for activation of this tool (if
* the shape this tool belongs to is active).
*
* See KoToolManager for use.
*
* @return the shortcut
*/
QKeySequence shortcut() const;
/**
* Returns the main toolType
* Each tool has a toolType which it uses to be grouped in the toolbox.
* The predefined areas are main and dynamic. "main" tools are always
* shown.
*
* @see toolType()
* @see setToolType()
*/
static QString mainToolType() {
return "main";
}
/**
* Returns the navigation toolType
* Each tool has a toolType which it uses to be grouped in the toolbox.
* The predefined areas are main and dynamic. "navigation" tools are always
* shown and are for tools that change the settings of the canvas, zoom, pan...
*
* @see toolType()
* @see setToolType()
*/
static QString navigationToolType() {
return "navigation";
}
/**
* Returns the dynamic toolType
* Each tool has a toolType which it uses to be grouped in the toolbox.
* The predefined areas are main and dynamic. Dynamic tools are hidden
* until the shape they belong to is activated.
*
* @see toolType()
* @see setToolType()
*/
static QString dynamicToolType() {
return "dynamic";
}
+protected:
+
+
/**
* Set the default shortcut for activation of this tool.
*/
void setShortcut(const QKeySequence & shortcut);
-protected:
-
/**
* Set the tooltip to be used for this tool
* @param tooltip the tooltip
*/
void setToolTip(const QString &tooltip);
/**
* Set the toolType. used to group tools in the toolbox
* @param toolType the toolType
*/
void setSection(const QString &section);
/**
* Set an icon to be used in the toolBox.
* @param iconName the basename (without extension) of the icon
*/
void setIconName(const char *iconName);
void setIconName(const QString &iconName);
/**
* Set the priority of this tool, as it is shown in the toolBox; lower number means
* it will be show more to the front of the list.
* @param newPriority the priority
*/
void setPriority(int newPriority);
/**
* Set the id of the shape we can process.
* This is the Id, as passed to the constructor of a KoShapeFactoryBase, that the tool
* we create is associated with. This means that if a KoTextShape is selected, then
* all tools that have its id set here will be added to the dynamic part of the toolbox.
* @param activationShapeId the Id of the shape
* @see activationShapeId()
*/
void setActivationShapeId(const QString &activationShapeId);
/**
* @brief createActionsImpl should be reimplemented if the tool needs any actions.
* The actions should have a valid objectName().
*
* @return the list of actions this tool wishes to be available.
*/
virtual QList<QAction *> createActionsImpl();
private:
class Private;
Private * const d;
};
#endif
diff --git a/libs/flake/KoToolManager.h b/libs/flake/KoToolManager.h
index f99dc52d34..77dbe4edd1 100644
--- a/libs/flake/KoToolManager.h
+++ b/libs/flake/KoToolManager.h
@@ -1,335 +1,335 @@
/* This file is part of the KDE project
* Copyright (c) 2005-2006 Boudewijn Rempt <boud@valdyas.org>
* Copyright (C) 2006, 2008 Thomas Zander <zander@kde.org>
* Copyright (C) 2006 Thorsten Zachmann <zachmann@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KO_TOOL_MANAGER
#define KO_TOOL_MANAGER
#include "KoInputDevice.h"
#include "kritaflake_export.h"
#include <QObject>
#include <QList>
class KoCanvasController;
class KoShapeControllerBase;
class KoToolFactoryBase;
class KoCanvasBase;
class KoToolBase;
class KoCreateShapesTool;
class KActionCollection;
class KoShape;
class KoInputDeviceHandlerEvent;
class KoShapeLayer;
class ToolHelper;
class QKeySequence;
class QCursor;
/**
* This class serves as a QAction-like control object for activation of a tool.
*
* It allows to implement a custom UI to control the activation of tools.
* See KoToolBox & KoModeBox in the kowidgets library.
*
* KoToolAction objects are indirectly owned by the KoToolManager singleton
* and live until the end of its lifetime.
*/
class KRITAFLAKE_EXPORT KoToolAction : public QObject
{
Q_OBJECT
public:
// toolHelper takes over ownership, and those live till the end of KoToolManager.
explicit KoToolAction(ToolHelper *toolHelper);
~KoToolAction() override;
public:
QString id() const; ///< The id of the tool
QString iconText() const; ///< The icontext of the tool
QString toolTip() const; ///< The tooltip of the tool
QString iconName() const; ///< The icon name of the tool
QKeySequence shortcut() const; ///< The shortcut to activate the tool
QString section() const; ///< The section the tool wants to be in.
int priority() const; ///< Lower number (higher priority) means coming first in the section.
int buttonGroupId() const; ///< A unique ID for this tool as passed by changedTool(), >= 0
QString visibilityCode() const; ///< This tool should become visible when we emit this string in toolCodesSelected()
public Q_SLOTS:
void trigger(); ///< Request the activation of the tool
Q_SIGNALS:
void changed(); ///< Emitted when a property changes (shortcut ATM)
private:
friend class ToolHelper;
class Private;
Private *const d;
};
/**
* This class manages the activation and deactivation of tools for
* each input device.
*
* Managing the active tool and switching tool based on various variables.
*
* The state of the toolbox will be the same for all views in the process so practically
* you can say we have one toolbox per application instance (process). Implementation
* does not allow one widget to be in more then one view, so we just make sure the toolbox
* is hidden in not-in-focus views.
*
* The ToolManager is a singleton and will manage all views in all applications that
* are loaded in this process. This means you will have to register and unregister your view.
* When creating your new view you should use a KoCanvasController() and register that
* with the ToolManager like this:
@code
MyGuiWidget::MyGuiWidget() {
m_canvasController = new KoCanvasController(this);
m_canvasController->setCanvas(m_canvas);
KoToolManager::instance()->addControllers(m_canvasController));
}
MyGuiWidget::~MyGuiWidget() {
KoToolManager::instance()->removeCanvasController(m_canvasController);
}
@endcode
*
* For a new view that extends KoView all you need to do is implement KoView::createToolBox()
*
* KoToolManager also keeps track of the current tool based on a
complex set of conditions and heuristics:
- there is one active tool per KoCanvasController (and there is one KoCanvasController
per view, because this is a class with scrollbars and a zoomlevel and so on)
- for every pointing device (determined by the unique id of tablet,
or 0 for mice -- you may have more than one mouse attached, but
Qt cannot distinguish between them, there is an associated tool.
- depending on things like tablet leave/enter proximity, incoming
mouse or tablet events and a little timer (that gets stopped when
we know what is what), the active pointing device is determined,
and the active tool is set accordingly.
Nota bene: if you use KoToolManager and register your canvases with
it you no longer have to manually implement methods to route mouse,
tablet, key or wheel events to the active tool. In fact, it's no
longer interesting to you which tool is active; you can safely
route the paint event through KoToolProxy::paint().
(The reason the input events are handled completely by the
toolmanager and the paint events not is that, generally speaking,
it's okay if the tools get the input events first, but you want to
paint your shapes or other canvas stuff first and only then paint
the tool stuff.)
*/
class KRITAFLAKE_EXPORT KoToolManager : public QObject
{
Q_OBJECT
public:
KoToolManager();
/// Return the toolmanager singleton
static KoToolManager* instance();
~KoToolManager() override;
/**
* Register actions for switching to tools at the actionCollection parameter.
* The actions will have the text / shortcut as stated by the toolFactory.
* If the application calls this in their KoView extending class they will have all the benefits
* from allowing this in the menus and to allow the use to configure the shortcuts used.
* @param ac the actionCollection that will be the parent of the actions.
* @param controller tools registered with this controller will have all their actions added as well.
*/
void registerToolActions(KActionCollection *ac, KoCanvasController *controller);
/**
* Register a new canvas controller
* @param controller the view controller that this toolmanager will manage the tools for
*/
void addController(KoCanvasController *controller);
/**
* Remove a set of controllers
* When the controller is no longer used it should be removed so all tools can be
* deleted and stop eating memory.
* @param controller the controller that is removed
*/
void removeCanvasController(KoCanvasController *controller);
/**
* Attempt to remove a controller.
* This is automatically called when a controller's proxy object is deleted, and
* it ensures that the controller is, in fact, removed, even if the creator forgot
* to do so.
* @param controller the proxy object of the controller to be removed
*/
Q_SLOT void attemptCanvasControllerRemoval(QObject *controller);
/// @return the active canvas controller
KoCanvasController *activeCanvasController() const;
/**
* Return the tool that is able to create shapes for this param canvas.
* This is typically used by the KoShapeSelector to set which shape to create next.
* @param canvas the canvas that is a child of a previously registered controller
* who's tool you want.
* @see addController()
*/
KoCreateShapesTool *shapeCreatorTool(KoCanvasBase *canvas) const;
/**
* Returns the tool for the given tool id. The tool may be 0
* @param canvas the canvas that is a child of a previously registered controller
* who's tool you want.
+ * @param id the tool identifier
* @see addController()
*/
KoToolBase *toolById(KoCanvasBase *canvas, const QString &id) const;
/// @return the currently active pointing device
KoInputDevice currentInputDevice() const;
/**
* For the list of shapes find out which tool is the highest priority tool that can handle it.
* @returns the toolId for the shapes.
* @param shapes a list of shapes, a selection for example, that is used to look for the tool.
*/
QString preferredToolForSelection(const QList<KoShape*> &shapes);
/**
* Returns the list of toolActions for the current tools.
* @returns lists of toolActions for the current tools.
*/
QList<KoToolAction*> toolActionList() const;
/// Request tool activation for the given canvas controller
void requestToolActivation(KoCanvasController *controller);
/// Returns the toolId of the currently active tool
QString activeToolId() const;
void initializeCurrentToolForCanvas();
class Private;
/**
* \internal return the private object for the toolmanager.
*/
KoToolManager::Private *priv();
public Q_SLOTS:
/**
* Request switching tool
* @param id the id of the tool
*/
void switchToolRequested(const QString &id);
/**
* Request change input device
* @param id the id of the input device
*/
void switchInputDeviceRequested(const KoInputDevice &id);
/**
* Request for temporary switching the tools.
* This switch can be later reverted with switchBackRequested().
* @param id the id of the tool
*
* @see switchBackRequested()
*/
void switchToolTemporaryRequested(const QString &id);
/**
* Switches back to the original tool after the temporary switch
* has been done. It the user changed the tool manually on the way,
* then it switches to the interaction tool
*/
void switchBackRequested();
Q_SIGNALS:
/**
* Emitted when a new tool is going to override the current tool
* @param canvas the currently active canvas.
*/
void aboutToChangeTool(KoCanvasController *canvas);
/**
* Emitted when a new tool was selected or became active.
* @param canvas the currently active canvas.
* @param uniqueToolId a random but unique code for the new tool.
*/
void changedTool(KoCanvasController *canvas, int uniqueToolId);
/**
* Emitted after the selection changed to state which unique shape-types are now
* in the selection.
- * @param canvas the currently active canvas.
* @param types a list of string that are the shape types of the selected objects.
*/
void toolCodesSelected(const QList<QString> &types);
/**
* Emitted after the current layer changed either its properties or to a new layer.
* @param canvas the currently active canvas.
* @param layer the layer that is selected.
*/
void currentLayerChanged(const KoCanvasController *canvas, const KoShapeLayer *layer);
/**
* Every time a new input device gets used by a tool, this event is emitted.
* @param device the new input device that the user picked up.
*/
void inputDeviceChanged(const KoInputDevice &device);
/**
* Emitted whenever the active canvas changed.
* @param canvas the new activated canvas (might be 0)
*/
void changedCanvas(const KoCanvasBase *canvas);
/**
* Emitted whenever the active tool changes the status text.
* @param statusText the new status text
*/
void changedStatusText(const QString &statusText);
/**
* emitted whenever a new tool is dynamically added for the given canvas
*/
void addedTool(KoToolAction *toolAction, KoCanvasController *canvas);
/**
* Emit the new tool option widgets to be used with this canvas.
*/
void toolOptionWidgetsChanged(KoCanvasController *controller, const QList<QPointer<QWidget> > &widgets);
private:
KoToolManager(const KoToolManager&);
KoToolManager operator=(const KoToolManager&);
Q_PRIVATE_SLOT(d, void toolActivated(ToolHelper *tool))
Q_PRIVATE_SLOT(d, void detachCanvas(KoCanvasController *controller))
Q_PRIVATE_SLOT(d, void attachCanvas(KoCanvasController *controller))
Q_PRIVATE_SLOT(d, void movedFocus(QWidget *from, QWidget *to))
Q_PRIVATE_SLOT(d, void updateCursor(const QCursor &cursor))
Q_PRIVATE_SLOT(d, void selectionChanged(const QList<KoShape*> &shapes))
Q_PRIVATE_SLOT(d, void currentLayerChanged(const KoShapeLayer *layer))
QPair<QString, KoToolBase*> createTools(KoCanvasController *controller, ToolHelper *tool);
Private *const d;
};
#endif
diff --git a/libs/flake/commands/KoPathBaseCommand.h b/libs/flake/commands/KoPathBaseCommand.h
index 25b23acc8d..ee40b5110d 100644
--- a/libs/flake/commands/KoPathBaseCommand.h
+++ b/libs/flake/commands/KoPathBaseCommand.h
@@ -1,53 +1,54 @@
/* This file is part of the KDE project
* Copyright (C) 2006 Jan Hambrecht <jaham@gmx.net>
* Copyright (C) 2006,2007 Thorsten Zachmann <zachmann@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOPATHBASECOMMAND_H
#define KOPATHBASECOMMAND_H
#include <kundo2command.h>
#include <QSet>
class KoPathShape;
/// the base command for commands altering a path shape
class KoPathBaseCommand : public KUndo2Command
{
public:
/**
* @param parent the parent command used for macro commands
*/
explicit KoPathBaseCommand(KUndo2Command *parent = 0);
/** initialize the base command with a single shape
+ * @param shape the shape
* @param parent the parent command used for macro commands
*/
explicit KoPathBaseCommand(KoPathShape *shape, KUndo2Command *parent = 0);
protected:
/**
* Schedules repainting of all shapes control point rects.
* @param normalizeShapes controls if paths are normalized before painting
*/
void repaint(bool normalizeShapes);
QSet<KoPathShape*> m_shapes; ///< the shapes the command operates on
};
#endif // KOPATHBASECOMMAND_H
diff --git a/libs/flake/commands/KoPathControlPointMoveCommand.h b/libs/flake/commands/KoPathControlPointMoveCommand.h
index e1d14381ba..12e8848e90 100644
--- a/libs/flake/commands/KoPathControlPointMoveCommand.h
+++ b/libs/flake/commands/KoPathControlPointMoveCommand.h
@@ -1,58 +1,59 @@
/* This file is part of the KDE project
* Copyright (C) 2006 Jan Hambrecht <jaham@gmx.net>
* Copyright (C) 2006,2007 Thorsten Zachmann <zachmann@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOPATHCONTROLPOINTMOVECOMMAND_H
#define KOPATHCONTROLPOINTMOVECOMMAND_H
#include <kundo2command.h>
#include <QPointF>
#include "KoPathPointData.h"
#include "KoPathPoint.h"
#include "kritaflake_export.h"
/// The undo / redo command for path point moving.
class KRITAFLAKE_EXPORT KoPathControlPointMoveCommand : public KUndo2Command
{
public:
/**
* Command to move one control path point.
+ * @param pointData the data of the point to move
* @param offset the offset by which the point is moved in document coordinates
* @param pointType the type of the point to move
* @param parent the parent command used for macro commands
*/
KoPathControlPointMoveCommand(const KoPathPointData &pointData, const QPointF &offset,
KoPathPoint::PointType pointType, KUndo2Command *parent = 0);
/// redo the command
void redo() override;
/// revert the actions done in redo
void undo() override;
int id() const override;
bool mergeWith(const KUndo2Command *command) override;
private:
KoPathPointData m_pointData;
// the offset in shape coordinates
QPointF m_offset;
KoPathPoint::PointType m_pointType;
};
#endif // KOPATHCONTROLPOINTMOVECOMMAND_H
diff --git a/libs/flake/commands/KoShapeGroupCommand.h b/libs/flake/commands/KoShapeGroupCommand.h
index 8d70cbc4e6..a18f8886a5 100644
--- a/libs/flake/commands/KoShapeGroupCommand.h
+++ b/libs/flake/commands/KoShapeGroupCommand.h
@@ -1,83 +1,80 @@
/* This file is part of the KDE project
* Copyright (C) 2006,2010 Thomas Zander <zander@kde.org>
* Copyright (C) 2007 Jan Hambrecht <jaham@gmx.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOSHAPEGROUPCOMMAND_H
#define KOSHAPEGROUPCOMMAND_H
#include "kritaflake_export.h"
#include <QList>
#include <QScopedPointer>
#include <kundo2command.h>
class KoShape;
class KoShapeGroup;
class KoShapeContainer;
class KoShapeGroupCommandPrivate;
/// The undo / redo command for grouping shapes
class KRITAFLAKE_EXPORT KoShapeGroupCommand : public KUndo2Command
{
public:
/**
* Create command to group a set of shapes into a predefined container.
* This uses the KoShapeGroupCommand(KoShapeGroup *container, const QList<KoShape *> &shapes, KUndo2Command *parent = 0);
* constructor.
* The createCommand will make sure that the group will have the z-index and the parent of the top most shape in the group.
*
* @param container the group to group the shapes under.
- * @param parent the parent command if the resulting command is a compound undo command.
* @param shapes a list of all the shapes that should be grouped.
+ * @param shouldNormalize whether the shapes should be normalized
*/
static KoShapeGroupCommand *createCommand(KoShapeContainer *container, const QList<KoShape *> &shapes, bool shouldNormalize = false);
/**
* Command to group a set of shapes into a predefined container.
* @param container the container to group the shapes under.
* @param shapes a list of all the shapes that should be grouped.
- * @param clipped shows whether the shapes should be clipped by the container
- * See KoShapeContainer::isClipped()
- * @param inheritTransform shows whether the shapes should inherit the parent transformation
- * See KoShapeContainer::inheritsTransform()
+ * @param shouldNormalize shows whether the shapes should be normalized by the container
* @param parent the parent command used for macro commands
*/
KoShapeGroupCommand(KoShapeContainer *container, const QList<KoShape *> &shapes, bool shouldNormalize, KUndo2Command *parent = 0);
/**
* Command to group a set of shapes into a predefined container.
* Convenience constructor since KoShapeGroup does not allow clipping.
* @param container the group to group the shapes under.
* @param parent the parent command if the resulting command is a compound undo command.
* @param shapes a list of all the shapes that should be grouped.
*/
KoShapeGroupCommand(KoShapeContainer *container, const QList<KoShape *> &shapes, KUndo2Command *parent = 0);
~KoShapeGroupCommand() override;
/// redo the command
void redo() override;
/// revert the actions done in redo
void undo() override;
protected:
const QScopedPointer<KoShapeGroupCommandPrivate> d;
};
#endif
diff --git a/libs/flake/commands/KoShapeReorderCommand.h b/libs/flake/commands/KoShapeReorderCommand.h
index ac640a31f2..1643052096 100644
--- a/libs/flake/commands/KoShapeReorderCommand.h
+++ b/libs/flake/commands/KoShapeReorderCommand.h
@@ -1,130 +1,132 @@
/* This file is part of the KDE project
* Copyright (C) 2006 Thomas Zander <zander@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOSHAPEREORDERCOMMAND_H
#define KOSHAPEREORDERCOMMAND_H
#include "kritaflake_export.h"
#include <boost/operators.hpp>
#include <kundo2command.h>
#include <QList>
class KoShape;
class KoShapeManager;
class KoShapeReorderCommandPrivate;
/// This command allows you to change the zIndex of a number of shapes.
class KRITAFLAKE_EXPORT KoShapeReorderCommand : public KUndo2Command
{
public:
struct KRITAFLAKE_EXPORT IndexedShape : boost::less_than_comparable<IndexedShape> {
IndexedShape();
IndexedShape(KoShape *_shape);
bool operator<(const IndexedShape &rhs) const;
int zIndex = 0;
KoShape *shape = 0;
};
public:
/**
* Constructor.
* @param shapes the set of objects that are moved.
* @param newIndexes the new indexes for the shapes.
* this list naturally must have the same amount of items as the shapes set.
* @param parent the parent command used for macro commands
*/
KoShapeReorderCommand(const QList<KoShape*> &shapes, QList<int> &newIndexes, KUndo2Command *parent = 0);
KoShapeReorderCommand(const QList<IndexedShape> &shapes, KUndo2Command *parent = 0);
~KoShapeReorderCommand() override;
/// An enum for defining what kind of reordering to use.
enum MoveShapeType {
RaiseShape, ///< raise the selected shape to the level that it is above the shape that is on top of it.
LowerShape, ///< Lower the selected shape to the level that it is below the shape that is below it.
BringToFront, ///< Raise the selected shape to be on top of all shapes.
SendToBack ///< Lower the selected shape to be below all other shapes.
};
/**
* Create a new KoShapeReorderCommand by calculating the new indexes required to move the shapes
* according to the move parameter.
* @param shapes all the shapes that should be moved.
* @param manager the shapeManager that contains all the shapes that could have their indexes changed.
* @param move the moving type.
* @param parent the parent command for grouping purposes.
* @return command for reordering the shapes or 0 if no reordering happened
*/
static KoShapeReorderCommand *createCommand(const QList<KoShape*> &shapes, KoShapeManager *manager,
MoveShapeType move, KUndo2Command *parent = 0);
/**
* @brief mergeInShape adjust zIndex of all the \p shapes and \p newShape to
* avoid collisions between \p shapes and \p newShape.
*
* Note1: \p newShape may or may not be contained in \p shapes, there
* is no difference.
* Note2: the collisions inside \p shapes are ignored. They are just
* adjusted to avoid collisions with \p newShape only
+ * @param shapes list of shapes
+ * @param newShape the new shape
* @param parent the parent command for grouping purposes.
* @return command for reordering the shapes or 0 if no reordering happened
*/
static KoShapeReorderCommand *mergeInShape(QList<KoShape*> shapes, KoShape *newShape,
KUndo2Command *parent = 0);
/**
* Recalculates the attached z-indexes of \p shapes so that all indexes go
* strictly in ascending order and no shapes have repetitive indexes. The
* physical order of the shapes in the array is not changed, on the indexes
* in IndexedShape are corrected.
*/
static
QList<KoShapeReorderCommand::IndexedShape>
homogenizeZIndexes(QList<IndexedShape> shapes);
/**
* Convenience version of homogenizeZIndexes() that removes all the IndexedShape
* objects, which z-index didn't change during homogenization. In a result
* you get a list that can be passed to KoShapeReorderCommand directly.
*/
static
QList<KoShapeReorderCommand::IndexedShape>
homogenizeZIndexesLazy(QList<IndexedShape> shapes);
/**
* Put all the shapes in \p shapesAbove above the shapes in \p shapesBelow, adjusting their
* z-index values.
*/
static QList<IndexedShape> mergeDownShapes(QList<KoShape*> shapesBelow, QList<KoShape*> shapesAbove);
/// redo the command
void redo() override;
/// revert the actions done in redo
void undo() override;
private:
KoShapeReorderCommandPrivate * const d;
};
KRITAFLAKE_EXPORT QDebug operator<<(QDebug dbg, const KoShapeReorderCommand::IndexedShape &indexedShape);
#endif
diff --git a/libs/flake/commands/KoShapeUngroupCommand.h b/libs/flake/commands/KoShapeUngroupCommand.h
index 098cbcd6b1..2dca24cf05 100644
--- a/libs/flake/commands/KoShapeUngroupCommand.h
+++ b/libs/flake/commands/KoShapeUngroupCommand.h
@@ -1,57 +1,58 @@
/* This file is part of the KDE project
* Copyright (C) 2006 Thomas Zander <zander@kde.org>
* Copyright (C) 2006 Jan Hambrecht <jaham@gmx.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOSHAPEUNGROUPCOMMAND_H
#define KOSHAPEUNGROUPCOMMAND_H
#include "kritaflake_export.h"
#include <kundo2command.h>
#include <QScopedPointer>
class KoShape;
class KoShapeGroup;
class KoShapeContainer;
/// The undo / redo command for ungrouping shapes
class KRITAFLAKE_EXPORT KoShapeUngroupCommand : public KUndo2Command
{
public:
/**
* Command to ungroup a set of shapes from one parent container.
* @param container the group to ungroup the shapes from.
* @param shapes a list of all the shapes that should be ungrouped.
+ * @param topLevelShapes a list of top level shapes.
* @param parent the parent command used for macro commands
*/
KoShapeUngroupCommand(KoShapeContainer *container, const QList<KoShape *> &shapes,
const QList<KoShape *> &topLevelShapes = QList<KoShape*>(), KUndo2Command *parent = 0);
~KoShapeUngroupCommand();
/// redo the command
void redo() override;
/// revert the actions done in redo
void undo() override;
private:
struct Private;
const QScopedPointer<Private> m_d;
};
#endif
diff --git a/libs/flake/svg/SvgGradientHelper.cpp b/libs/flake/svg/SvgGradientHelper.cpp
index c00dacec78..7845db079f 100644
--- a/libs/flake/svg/SvgGradientHelper.cpp
+++ b/libs/flake/svg/SvgGradientHelper.cpp
@@ -1,98 +1,98 @@
/* This file is part of the KDE project
* Copyright (C) 2007,2009 Jan Hambrecht <jaham@gmx.net>
* Copyright (C) 2010 Thorsten Zachmann <zachmann@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "SvgGradientHelper.h"
#include <QConicalGradient>
#include <QLinearGradient>
#include <QRadialGradient>
#include <cmath>
#include <KoFlake.h>
SvgGradientHelper::SvgGradientHelper()
- : m_gradient(new QGradient()), m_gradientUnits(KoFlake::ObjectBoundingBox)
+ : m_gradient(new QGradient()), m_gradientUnits(KoFlake::ObjectBoundingBox)
{
}
SvgGradientHelper::~SvgGradientHelper()
{
delete m_gradient;
}
SvgGradientHelper::SvgGradientHelper(const SvgGradientHelper &other)
- : m_gradient(KoFlake::cloneGradient(other.m_gradient))
- , m_gradientUnits(other.m_gradientUnits)
- , m_gradientTransform(other.m_gradientTransform)
+ : m_gradient(KoFlake::cloneGradient(other.m_gradient))
+ , m_gradientUnits(other.m_gradientUnits)
+ , m_gradientTransform(other.m_gradientTransform)
{
}
SvgGradientHelper & SvgGradientHelper::operator = (const SvgGradientHelper & rhs)
{
if (this == &rhs)
return *this;
m_gradientUnits = rhs.m_gradientUnits;
m_gradientTransform = rhs.m_gradientTransform;
m_gradient = KoFlake::cloneGradient(rhs.m_gradient);
return *this;
}
void SvgGradientHelper::setGradientUnits(KoFlake::CoordinateSystem units)
{
m_gradientUnits = units;
}
KoFlake::CoordinateSystem SvgGradientHelper::gradientUnits() const
{
return m_gradientUnits;
}
QGradient * SvgGradientHelper::gradient() const
{
return m_gradient;
}
void SvgGradientHelper::setGradient(QGradient * g)
{
delete m_gradient;
m_gradient = g;
}
QTransform SvgGradientHelper::transform() const
{
return m_gradientTransform;
}
void SvgGradientHelper::setTransform(const QTransform &transform)
{
m_gradientTransform = transform;
}
QGradient::Spread SvgGradientHelper::spreadMode() const
{
return m_spreadMode;
}
void SvgGradientHelper::setSpreadMode(const QGradient::Spread &spreadMode)
{
m_spreadMode = spreadMode;
}
diff --git a/libs/flake/svg/SvgUtil.h b/libs/flake/svg/SvgUtil.h
index 22493ca9c2..b72018261e 100644
--- a/libs/flake/svg/SvgUtil.h
+++ b/libs/flake/svg/SvgUtil.h
@@ -1,150 +1,150 @@
/* This file is part of the KDE project
* Copyright (C) 2009 Jan Hambrecht <jaham@gmx.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef SVGUTIL_H
#define SVGUTIL_H
#include "kritaflake_export.h"
#include <QRectF>
class QString;
class QTransform;
class QStringList;
class KoXmlWriter;
#include <KoXmlReaderForward.h>
class SvgGraphicsContext;
class KRITAFLAKE_EXPORT SvgUtil
{
public:
// remove later! pixels *are* user coordinates
static double fromUserSpace(double value);
static double toUserSpace(double value);
static double ptToPx(SvgGraphicsContext *gc, double value);
/// Converts given point from points to userspace units.
static QPointF toUserSpace(const QPointF &point);
/// Converts given rectangle from points to userspace units.
static QRectF toUserSpace(const QRectF &rect);
/// Converts given rectangle from points to userspace units.
static QSizeF toUserSpace(const QSizeF &size);
/**
* Parses the given string containing a percentage number.
- * @param s the input string containing the percentage
+ * @param value the input number containing the percentage
* @return the percentage number normalized to 0..100
*/
static QString toPercentage(qreal value);
/**
* Parses the given string containing a percentage number.
* @param s the input string containing the percentage
* @return the percentage number normalized to 0..1
*/
static double fromPercentage(QString s);
/**
* Converts position from objectBoundingBox units to userSpace units.
*/
static QPointF objectToUserSpace(const QPointF &position, const QRectF &objectBound);
/**
* Converts size from objectBoundingBox units to userSpace units.
*/
static QSizeF objectToUserSpace(const QSizeF &size, const QRectF &objectBound);
/**
* Converts position from userSpace units to objectBoundingBox units.
*/
static QPointF userSpaceToObject(const QPointF &position, const QRectF &objectBound);
/**
* Converts size from userSpace units to objectBoundingBox units.
*/
static QSizeF userSpaceToObject(const QSizeF &size, const QRectF &objectBound);
/// Converts specified transformation to a string
static QString transformToString(const QTransform &transform);
/// Writes a \p transform as an attribute \p name iff the transform is not empty
static void writeTransformAttributeLazy(const QString &name, const QTransform &transform, KoXmlWriter &shapeWriter);
/// Parses a viewbox attribute into an rectangle
static bool parseViewBox(SvgGraphicsContext *gc, const KoXmlElement &e, const QRectF &elementBounds, QRectF *_viewRect, QTransform *_viewTransform);
struct PreserveAspectRatioParser;
static void parseAspectRatio(const PreserveAspectRatioParser &p, const QRectF &elementBounds, const QRectF &viewRect, QTransform *_viewTransform);
/// Parses a length attribute
static qreal parseUnit(SvgGraphicsContext *gc, const QString &, bool horiz = false, bool vert = false, const QRectF &bbox = QRectF());
/// parses a length attribute in x-direction
static qreal parseUnitX(SvgGraphicsContext *gc, const QString &unit);
/// parses a length attribute in y-direction
static qreal parseUnitY(SvgGraphicsContext *gc, const QString &unit);
/// parses a length attribute in xy-direction
static qreal parseUnitXY(SvgGraphicsContext *gc, const QString &unit);
/// parses angle, result in *radians*!
static qreal parseUnitAngular(SvgGraphicsContext *gc, const QString &unit);
/// parses the number into parameter number
static const char * parseNumber(const char *ptr, qreal &number);
static qreal parseNumber(const QString &string);
static QString mapExtendedShapeTag(const QString &tagName, const KoXmlElement &element);
static QStringList simplifyList(const QString &str);
struct KRITAFLAKE_EXPORT PreserveAspectRatioParser
{
PreserveAspectRatioParser(const QString &str);
enum Alignment {
Min,
Middle,
Max
};
bool defer = false;
Qt::AspectRatioMode mode = Qt::IgnoreAspectRatio;
Alignment xAlignment = Min;
Alignment yAlignment = Min;
QPointF rectAnchorPoint(const QRectF &rc) const;
QString toString() const;
private:
Alignment alignmentFromString(const QString &str) const;
QString alignmentToString(Alignment alignment) const;
static qreal alignedValue(qreal min, qreal max, Alignment alignment);
};
};
#endif // SVGUTIL_H
diff --git a/libs/flake/text/KoSvgTextChunkShape.h b/libs/flake/text/KoSvgTextChunkShape.h
index fd790df9a6..df6c966f72 100644
--- a/libs/flake/text/KoSvgTextChunkShape.h
+++ b/libs/flake/text/KoSvgTextChunkShape.h
@@ -1,174 +1,174 @@
/*
* Copyright (c) 2017 Dmitry Kazakov <dimula73@gmail.com>
*
* 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 KOSVGTEXTCHUNKSHAPE_H
#define KOSVGTEXTCHUNKSHAPE_H
#include "kritaflake_export.h"
#include <KoShapeContainer.h>
#include <SvgShape.h>
class HtmlSavingContext;
class KoSvgTextProperties;
class KoSvgTextChunkShapePrivate;
class KoSvgTextChunkShapeLayoutInterface;
/**
* KoSvgTextChunkShape is an elementary block of SVG text object.
*
- * KoSvgTextChunkShape represents either a <tspan> or <text> element of SVG.
+ * KoSvgTextChunkShape represents either a \<tspan\> or \<text\> element of SVG.
* The chunk shape uses flake hierarchy to represent the DOM hierarchy of the
* supplied text. All the attributes of text blocks can be fetched using
* textProperties() method.
*
* KoSvgTextChunkShape uses special text properties object to overcome the
* flake's "property inheritance" limitation. Basically, flake doesn't support
* the inheritance of shape properties: every shape stores all the properties
* that were defined at the stage of loading/creation. KoSvgTextProperties is a
* wrapper that allows the user to compare the properties of the two shapes and
* return only the ones that are unique for a child shape. That allows us to
* generate a correct SVG/markup code that can be edited by the user easily.
*
* WARNING: beware the difference between "svg-text-chunk" and
* KoSvgTextChunkShape! The chunk shape is **not** a "text chunk" in SVG's
* definition. According to SVG, "text chunk" is a set of characters anchored to
* a specific absolute position on canvas. And KoSvgTextChunkShape is just one
- * <tspan> or <text> element. Obviously, one <tspan> can contain multiple "text
- * chunks" and, vice versa, a "text chunk" can spread onto multiple <span>'s.
+ * \<tspan\> or \<text\> element. Obviously, one \<tspan\> can contain multiple "text
+ * chunks" and, vice versa, a "text chunk" can spread onto multiple \<span\>'s.
*/
class KRITAFLAKE_EXPORT KoSvgTextChunkShape : public KoShapeContainer, public SvgShape
{
public:
KoSvgTextChunkShape();
KoSvgTextChunkShape(const KoSvgTextChunkShape &rhs);
~KoSvgTextChunkShape() override;
KoShape* cloneShape() const override;
QSizeF size() const override;
void setSize(const QSizeF &size) override;
QRectF outlineRect() const override;
QPainterPath outline() const override;
void paintComponent(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext) override;
void saveOdf(KoShapeSavingContext &Context) const override;
bool loadOdf(const KoXmlElement &element, KoShapeLoadingContext &Context) override;
bool saveHtml(HtmlSavingContext &context);
/**
* Reset the text shape into initial state, removing all the child shapes.
* This method is used by text-updating code to upload the updated text
* into shape. The uploading code first calls resetTextShape() and then adds
* new children.
*/
virtual void resetTextShape();
bool saveSvg(SvgSavingContext &context) override;
bool loadSvg(const KoXmlElement &element, SvgLoadingContext &context) override;
bool loadSvgTextNode(const KoXmlText &text, SvgLoadingContext &context);
/**
* Normalize the SVG character transformations
*
* In SVG x,y,dx,dy,rotate attributes are inherited from the parent shapes
* in a curious ways. We do not want to unwind the links on the fly, so we
* just parse and adjust them right when the loading completed. After
* normalizeCharTransformations() is finished, all the child shapes will
* have local transformations set according to the parent's lists.
*/
void normalizeCharTransformations();
/**
* Compress the inheritance of 'fill' and 'stroke'.
*
* The loading code makes no difference if the fill or stroke was inherited
* or not. That is not a problem for normal shapes, but it cannot work for
* text shapes. The text has different inheritance rules: if the parent
* text chunk has a gradient fill, its inheriting descendants will
* **smoothly continue** this gradient. They will not start a new gradient
* in their local coordinate system.
*
* Therefore, after loading, the loading code calls
* simplifyFillStrokeInheritance() which marks the coinciding strokes and
* fills so that the rendering code will be able to distinguish them in the
* future.
*
* Proper fill inheritance is also needed for the GUI. When the user
* changes the color of the parent text chunk, all the inheriting children
* should update its color automatically, without GUI recursively
* traversing the shapes.
*
*/
void simplifyFillStrokeInheritance();
/**
* SVG properties of the text chunk
* @return the properties object with fill and stroke included as a property
*/
KoSvgTextProperties textProperties() const;
/**
* Return the type of the chunk.
*
* The chunk can be either a "text chunk", that contains a text string
* itself, of an "intermediate chunk" that doesn't contain any text itself,
* but works as a group for a set of child chunks, which might be either
* text (leaf) or intermediate chunks. Such groups are needed to define a
- * common text style for a group of '<tspan>' objects.
+ * common text style for a group of '\<tspan\>' objects.
*
* @return true if the chunk is a "text chunk" false if it is "intermediate chunk"
*/
bool isTextNode() const;
/**
* A special interface for KoSvgTextShape's layout code. Don't use it
* unless you are KoSvgTextShape.
*/
KoSvgTextChunkShapeLayoutInterface* layoutInterface();
/**
* WARNING: this propperty is available only if isRootTextNode() is true
*
* @return true if the shape should be edited in a rich-text editor
*/
bool isRichTextPreferred() const;
/**
* WARNING: this propperty is available only if isRootTextNode() is true
*
* Sets whether the shape should be edited in rich-text editor
*/
void setRichTextPreferred(bool value);
protected:
/**
* Show if the shape is a root of the text hierarchy. Always true for
* KoSvgTextShape and always false for KoSvgTextChunkShape
*/
virtual bool isRootTextNode() const;
protected:
KoSvgTextChunkShape(KoSvgTextChunkShapePrivate *dd);
private:
Q_DECLARE_PRIVATE(KoSvgTextChunkShape)
};
#endif // KOSVGTEXTCHUNKSHAPE_H
diff --git a/libs/flake/text/KoSvgTextProperties.h b/libs/flake/text/KoSvgTextProperties.h
index 071cef7668..b0c51f5870 100644
--- a/libs/flake/text/KoSvgTextProperties.h
+++ b/libs/flake/text/KoSvgTextProperties.h
@@ -1,192 +1,192 @@
/*
* Copyright (c) 2017 Dmitry Kazakov <dimula73@gmail.com>
*
* 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 KOSVGTEXTPROPERTIES_H
#define KOSVGTEXTPROPERTIES_H
#include "kritaflake_export.h"
#include <QScopedPointer>
#include <QVariant>
#include <QList>
class SvgLoadingContext;
/**
* KoSvgTextProperties represents the text attributes defined in SVG DOM tree
*
* There is a limitation in flake: it doesn't support the inheritance of shape
* properties: every shape stores all the properties that were defined at the
* loading/creation stage. KoSvgTextProperties allows the user to compare
* the properties of the two shapes and distinguish, which properties were
* inherited by text shape, and which are its own. It is needed to generate a
* correct and clean SVG/markup code that can be edited by the user easily.
- * Otherwise, every <tspan> block will contain the full list of 20+ attributes,
+ * Otherwise, every \<tspan\> block will contain the full list of 20+ attributes,
* which are not interesting for the user, since they are inherited or default.
*
* To achieve the goal, KoSvgTextProperties wraps all the SVG attributes into a
* map of QVariants. When the user need to find a set of unique properties
* of the shape, it iterates through the map and compares values with standard
* QVariant-based comparison operator. If the property value in a child and a
* parent is not the same, then it is not inherited.
*/
class KRITAFLAKE_EXPORT KoSvgTextProperties
{
public:
/**
* Defines a set of supported properties. See SVG 1.1 for details.
*/
enum PropertyId {
WritingModeId,
DirectionId,
UnicodeBidiId,
TextAnchorId,
DominantBaselineId,
AlignmentBaselineId,
BaselineShiftModeId,
BaselineShiftValueId,
KerningId,
GlyphOrientationVerticalId,
GlyphOrientationHorizontalId,
LetterSpacingId,
WordSpacingId,
FontFamiliesId,
FontStyleId,
FontIsSmallCapsId,
FontStretchId,
FontWeightId,
FontSizeId,
FontSizeAdjustId,
TextDecorationId,
FillId,
StrokeId
};
public:
KoSvgTextProperties();
~KoSvgTextProperties();
KoSvgTextProperties(const KoSvgTextProperties &rhs);
KoSvgTextProperties& operator=(const KoSvgTextProperties &rhs);
/**
* Set the property \p id to \p value
*/
void setProperty(PropertyId id, const QVariant &value);
/**
* Check if property \p id is present in this properties set
*/
bool hasProperty(PropertyId id) const;
/**
* Return the value of property \p id. If the property doesn't exist in
* the shape, return \p defaultValue instead.
*/
QVariant property(PropertyId id, const QVariant &defaultValue = QVariant()) const;
/**
* Remove property \p id from the set
*/
void removeProperty(PropertyId id);
/**
* Return the value of property \p id. If the property doesn't exist in the
* shape, return the default value define in SVG 1.1.
*/
QVariant propertyOrDefault(PropertyId id) const;
/**
* Return a list of properties contained in this set
*/
QList<PropertyId> properties() const;
/**
* Return true if the set contains no properties
*/
bool isEmpty() const;
/**
* Reset all non-inheritable properties to default values. The set of
* non-inheritable properties is define by SVG 1.1. Used by the loading
- * code for resetting state automata's properties on entering a <tspan>.
+ * code for resetting state automata's properties on entering a \<tspan\>.
*/
void resetNonInheritableToDefault();
/**
* Apply properties from the parent shape. The property is set **iff** the
* property is inheritable according to SVG and this set does not define
* it.
*/
void inheritFrom(const KoSvgTextProperties &parentProperties);
/**
* Return true if the property \p id is inherited from \p parentProperties.
* The property is considered "inherited" **iff* it is inheritable
* according to SVG and the parent defined the same property with the same
* value.
*/
bool inheritsProperty(PropertyId id, const KoSvgTextProperties &parentProperties) const;
/**
* Return a set of properties that ar **not** inherited from \p
* parentProperties. The property is considered "inherited" **iff* it is
* inheritable according to SVG and the parent defined the same property
* with the same value.
*/
KoSvgTextProperties ownProperties(const KoSvgTextProperties &parentProperties) const;
/**
* @brief parseSvgTextAttribute add a property according to an XML attribute value.
* @param context shared loading context
* @param command XML attribute name
* @param value attribute value
*
* @see supportedXmlAttributes for a list of supported attributes
*/
void parseSvgTextAttribute(const SvgLoadingContext &context, const QString &command, const QString &value);
/**
* Convert all the properties of the set into a map of XML attribute/value
* pairs.
*/
QMap<QString,QString> convertToSvgTextAttributes() const;
/**
* Return a list of supported XML attribute names (defined in SVG)
*/
static QStringList supportedXmlAttributes();
/**
* Return a static object that defines default values for all the supported
* properties according to SVG
*/
static const KoSvgTextProperties& defaultProperties();
private:
struct Private;
const QScopedPointer<Private> m_d;
};
#endif // KOSVGTEXTPROPERTIES_H
diff --git a/libs/flake/text/KoSvgTextShape.h b/libs/flake/text/KoSvgTextShape.h
index 6e63952012..8a24d8596a 100644
--- a/libs/flake/text/KoSvgTextShape.h
+++ b/libs/flake/text/KoSvgTextShape.h
@@ -1,94 +1,94 @@
/*
* Copyright (c) 2017 Dmitry Kazakov <dimula73@gmail.com>
*
* 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 KOSVGTEXTSHAPE_H
#define KOSVGTEXTSHAPE_H
#include "kritaflake_export.h"
#include <KoShapeFactoryBase.h>
#include <KoSvgTextChunkShape.h>
#include <SvgShape.h>
class KoSvgTextProperties;
class KoSvgTextShapePrivate;
#define KoSvgTextShape_SHAPEID "KoSvgTextShapeID"
/**
- * KoSvgTextShape is a root chunk of the <text> element subtree.
+ * KoSvgTextShape is a root chunk of the \<text\> element subtree.
*/
class KRITAFLAKE_EXPORT KoSvgTextShape : public KoSvgTextChunkShape
{
public:
KoSvgTextShape();
KoSvgTextShape(const KoSvgTextShape &rhs);
~KoSvgTextShape() override;
KoShape* cloneShape() const override;
void paintComponent(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext) override;
void paintStroke(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext) override;
/**
* Reset the text shape into initial shape, removing all the child shapes
* and precalculated layouts. This method is used by text-updating code to
* upload the updated text into shape. The upload code first calls
* resetTextShape() and then adds new children.
*/
void resetTextShape() override;
/**
* Create a new text layout for the current content of the text shape
* chunks tree. The user should always call relayout() after every change
* in the text shapes hierarchy.
*/
void relayout();
QPainterPath textOutline();
protected:
/**
* Show if the shape is a root of the text hierarchy. Always true for
* KoSvgTextShape and always false for KoSvgTextChunkShape
*/
bool isRootTextNode() const override;
void shapeChanged(ChangeType type, KoShape *shape) override;
private:
Q_DECLARE_PRIVATE(KoSvgTextShape)
};
class KoSvgTextShapeFactory : public KoShapeFactoryBase
{
public:
/// constructor
KoSvgTextShapeFactory();
~KoSvgTextShapeFactory() {}
KoShape *createDefaultShape(KoDocumentResourceManager *documentResources = 0) const override;
KoShape *createShape(const KoProperties *params, KoDocumentResourceManager *documentResources = 0) const override;
/// Reimplemented
bool supports(const KoXmlElement &e, KoShapeLoadingContext &context) const override;
};
#endif // KOSVGTEXTSHAPE_H
diff --git a/libs/flake/text/KoSvgTextShapeMarkupConverter.h b/libs/flake/text/KoSvgTextShapeMarkupConverter.h
index e7f455c22a..9b58ce2468 100644
--- a/libs/flake/text/KoSvgTextShapeMarkupConverter.h
+++ b/libs/flake/text/KoSvgTextShapeMarkupConverter.h
@@ -1,144 +1,144 @@
/*
* Copyright (c) 2017 Dmitry Kazakov <dimula73@gmail.com>
*
* 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 KOSVGTEXTSHAPEMARKUPCONVERTER_H
#define KOSVGTEXTSHAPEMARKUPCONVERTER_H
#include "kritaflake_export.h"
#include <QScopedPointer>
#include <QList>
#include <QTextDocument>
#include <QTextCharFormat>
class QRectF;
class KoSvgTextShape;
/**
* KoSvgTextShapeMarkupConverter is a utility class for converting a
* KoSvgTextShape to/from user-editable markup/svg representation.
*
* Please note that the converted SVG is **not** the same as when saved into
* .kra! Some attributes are dropped to make the editing is easier for the
* user.
*/
class KRITAFLAKE_EXPORT KoSvgTextShapeMarkupConverter
{
public:
KoSvgTextShapeMarkupConverter(KoSvgTextShape *shape);
~KoSvgTextShapeMarkupConverter();
/**
* Convert the text shape into two strings: text and styles. Styles string
* is non-empty only when the text has some gradient/pattern attached. It is
* intended to be places into a separate tab in the GUI.
*
* @return true on success
*/
bool convertToSvg(QString *svgText, QString *stylesText);
/**
* @brief upload the svg representation of text into the shape
- * @param svgText <text> part of SVG
- * @param stylesText <defs> part of SVG (used only for gradients and patterns)
+ * @param svgText \<text\> part of SVG
+ * @param stylesText \<defs\> part of SVG (used only for gradients and patterns)
* @param boundsInPixels bounds of the entire image in pixel. Used for parsing percentage units.
* @param pixelsPerInch resolution of the image where we load the shape to
*
* @return true if the text was parsed successfully. Check `errors()` and `warnings()` for details.
*/
bool convertFromSvg(const QString &svgText, const QString &stylesText, const QRectF &boundsInPixels, qreal pixelsPerInch);
/**
* @brief convertToHtml convert the text in the text shape to html
* @param htmlText will be filled with correct html representing the text in the shape
- * @return true om success
+ * @return @c true on success
*/
bool convertToHtml(QString *htmlText);
/**
* @brief convertFromHtml converted Qt rich text html (and no other: http://doc.qt.io/qt-5/richtext-html-subset.html) to SVG
* @param htmlText the input html
* @param svgText the converted svg text element
* @param styles
- * @return true if the conversion was successful
+ * @return @c true if the conversion was successful
*/
bool convertFromHtml(const QString &htmlText, QString *svgText, QString *styles);
/**
* @brief convertDocumentToSvg
* @param doc the QTextDocument to convert.
* @param svgText the converted svg text element
- * @return true if the conversion was successful
+ * @return @c true if the conversion was successful
*/
bool convertDocumentToSvg(const QTextDocument *doc, QString *svgText);
/**
* @brief convertSvgToDocument
- * @param svgText the <text> element and it's children as a string.
+ * @param svgText the \<text\> element and it's children as a string.
* @param doc the QTextDocument that the conversion is written to.
- * @return true if the conversion was successful
+ * @return @c true if the conversion was successful
*/
bool convertSvgToDocument(const QString &svgText, QTextDocument *doc);
/**
* A list of errors happened during loading the user's text
*/
QStringList errors() const;
/**
* A list of warnings produced during loading the user's text
*/
QStringList warnings() const;
/**
* @brief style
* creates a style string based on the blockformat and the format.
* @param format the textCharFormat of the current text.
* @param blockFormat the block format of the current text.
* @param mostCommon the most common format to compare the format to.
* @return a string that can be written into a style element.
*/
QString style(QTextCharFormat format, QTextBlockFormat blockFormat, QTextCharFormat mostCommon = QTextCharFormat());
/**
* @brief stylesFromString
* returns a qvector with two textformats:
* at 0 is the QTextCharFormat
* at 1 is the QTextBlockFormat
* @param styles a style string split at ";"
* @param currentCharFormat the current charformat to compare against.
* @param currentBlockFormat the current blockformat to compare against.
* @return A QVector with at 0 a QTextCharFormat and at 1 a QBlockCharFormat.
*/
static QVector<QTextFormat> stylesFromString(QStringList styles, QTextCharFormat currentCharFormat, QTextBlockFormat currentBlockFormat);
/**
* @brief formatDifference
* A class to get the difference between two text-char formats.
* @param test the format to test
* @param reference the format to test against.
* @return the difference between the two.
*/
QTextFormat formatDifference(QTextFormat test, QTextFormat reference);
private:
struct Private;
const QScopedPointer<Private> d;
};
#endif // KOSVGTEXTSHAPEMARKUPCONVERTER_H
diff --git a/libs/flake/tools/KoPathTool.cpp b/libs/flake/tools/KoPathTool.cpp
index 20ba9d5955..dae2d081c9 100644
--- a/libs/flake/tools/KoPathTool.cpp
+++ b/libs/flake/tools/KoPathTool.cpp
@@ -1,1303 +1,1308 @@
/* This file is part of the KDE project
* Copyright (C) 2006-2012 Jan Hambrecht <jaham@gmx.net>
* Copyright (C) 2006,2007 Thorsten Zachmann <zachmann@kde.org>
* Copyright (C) 2007, 2010 Thomas Zander <zander@kde.org>
* Copyright (C) 2007 Boudewijn Rempt <boud@valdyas.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoPathTool.h"
#include "KoToolBase_p.h"
#include "KoPathShape_p.h"
#include "KoPathToolHandle.h"
#include "KoCanvasBase.h"
#include "KoShapeManager.h"
#include "KoSelectedShapesProxy.h"
#include "KoDocumentResourceManager.h"
#include "KoViewConverter.h"
#include "KoSelection.h"
#include "KoPointerEvent.h"
#include "commands/KoPathPointTypeCommand.h"
#include "commands/KoPathPointInsertCommand.h"
#include "commands/KoPathPointRemoveCommand.h"
#include "commands/KoPathSegmentTypeCommand.h"
#include "commands/KoPathBreakAtPointCommand.h"
#include "commands/KoPathSegmentBreakCommand.h"
#include "commands/KoParameterToPathCommand.h"
#include "commands/KoSubpathJoinCommand.h"
#include <commands/KoMultiPathPointMergeCommand.h>
#include <commands/KoMultiPathPointJoinCommand.h>
#include <commands/KoKeepShapesSelectedCommand.h>
#include "KoParameterShape.h"
#include <text/KoSvgTextShape.h>
#include "KoPathPoint.h"
#include "KoPathPointRubberSelectStrategy.h"
#include "KoPathSegmentChangeStrategy.h"
#include "KoPathConnectionPointStrategy.h"
#include "KoParameterChangeStrategy.h"
#include "PathToolOptionWidget.h"
#include "KoConnectionShape.h"
#include "KoSnapGuide.h"
#include "KoShapeController.h"
#include "kis_action_registry.h"
#include <KisHandlePainterHelper.h>
#include <KoShapeStrokeModel.h>
#include "kis_command_utils.h"
#include "kis_pointer_utils.h"
#include <KoIcon.h>
#include <QMenu>
#include <QAction>
#include <FlakeDebug.h>
#include <klocalizedstring.h>
#include <QPainter>
#include <QBitmap>
#include <QTabWidget>
#include <math.h>
static const unsigned char needle_bits[] = {
0x00, 0x00, 0x10, 0x00, 0x20, 0x00, 0x60, 0x00, 0xc0, 0x00, 0xc0, 0x01,
0x80, 0x03, 0x80, 0x07, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x3e, 0x00, 0x7e,
0x00, 0x7c, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x00
};
static const unsigned char needle_move_bits[] = {
0x00, 0x00, 0x10, 0x00, 0x20, 0x00, 0x60, 0x00, 0xc0, 0x00, 0xc0, 0x01,
0x80, 0x03, 0x80, 0x07, 0x10, 0x0f, 0x38, 0x1f, 0x54, 0x3e, 0xfe, 0x7e,
0x54, 0x7c, 0x38, 0x1c, 0x10, 0x18, 0x00, 0x00
};
// helper function to calculate the squared distance between two points
qreal squaredDistance(const QPointF& p1, const QPointF &p2)
{
qreal dx = p1.x()-p2.x();
qreal dy = p1.y()-p2.y();
return dx*dx + dy*dy;
}
struct KoPathTool::PathSegment {
PathSegment()
: path(0), segmentStart(0), positionOnSegment(0)
{
}
bool isValid() {
return path && segmentStart;
}
KoPathShape *path;
KoPathPoint *segmentStart;
qreal positionOnSegment;
};
KoPathTool::KoPathTool(KoCanvasBase *canvas)
- : KoToolBase(canvas)
- , m_pointSelection(this)
- , m_activeHandle(0)
- , m_handleRadius(3)
- , m_activeSegment(0)
- , m_currentStrategy(0)
- , m_activatedTemporarily(false)
+ : KoToolBase(canvas)
+ , m_pointSelection(this)
+ , m_activeHandle(0)
+ , m_handleRadius(3)
+ , m_activeSegment(0)
+ , m_currentStrategy(0)
+ , m_activatedTemporarily(false)
{
m_points = new QActionGroup(this);
// m_pointTypeGroup->setExclusive(true);
m_actionPathPointCorner = action("pathpoint-corner");
- m_actionPathPointCorner->setData(KoPathPointTypeCommand::Corner);
- m_points->addAction(m_actionPathPointCorner);
-
+ if (m_actionPathPointCorner) {
+ m_actionPathPointCorner->setData(KoPathPointTypeCommand::Corner);
+ m_points->addAction(m_actionPathPointCorner);
+ }
m_actionPathPointSmooth = action("pathpoint-smooth");
- m_actionPathPointSmooth->setData(KoPathPointTypeCommand::Smooth);
- m_points->addAction(m_actionPathPointSmooth);
+ if (m_actionPathPointSmooth) {
+ m_actionPathPointSmooth->setData(KoPathPointTypeCommand::Smooth);
+ m_points->addAction(m_actionPathPointSmooth);
+ }
m_actionPathPointSymmetric = action("pathpoint-symmetric");
- m_actionPathPointSymmetric->setData(KoPathPointTypeCommand::Symmetric);
- m_points->addAction(m_actionPathPointSymmetric);
+ if (m_actionPathPointSymmetric) {
+ m_actionPathPointSymmetric->setData(KoPathPointTypeCommand::Symmetric);
+ m_points->addAction(m_actionPathPointSymmetric);
+ }
m_actionCurvePoint = action("pathpoint-curve");
m_actionLinePoint = action("pathpoint-line");
m_actionLineSegment = action("pathsegment-line");
m_actionCurveSegment = action("pathsegment-curve");
m_actionAddPoint = action("pathpoint-insert");
m_actionRemovePoint = action("pathpoint-remove");
m_actionBreakPoint = action("path-break-point");
m_actionBreakSegment = action("path-break-segment");
m_actionJoinSegment = action("pathpoint-join");
m_actionMergePoints = action("pathpoint-merge");
m_actionConvertToPath = action("convert-to-path");
m_contextMenu.reset(new QMenu());
QBitmap b = QBitmap::fromData(QSize(16, 16), needle_bits);
QBitmap m = b.createHeuristicMask(false);
m_selectCursor = QCursor(b, m, 2, 0);
b = QBitmap::fromData(QSize(16, 16), needle_move_bits);
m = b.createHeuristicMask(false);
m_moveCursor = QCursor(b, m, 2, 0);
}
KoPathTool::~KoPathTool()
{
delete m_activeHandle;
delete m_activeSegment;
delete m_currentStrategy;
}
QList<QPointer<QWidget> > KoPathTool::createOptionWidgets()
{
QList<QPointer<QWidget> > list;
PathToolOptionWidget * toolOptions = new PathToolOptionWidget(this);
connect(this, SIGNAL(typeChanged(int)), toolOptions, SLOT(setSelectionType(int)));
connect(this, SIGNAL(singleShapeChanged(KoPathShape*)), toolOptions, SLOT(setCurrentShape(KoPathShape*)));
connect(toolOptions, SIGNAL(sigRequestUpdateActions()), this, SLOT(updateActions()));
updateOptionsWidget();
toolOptions->setWindowTitle(i18n("Edit Shape"));
list.append(toolOptions);
return list;
}
void KoPathTool::pointTypeChanged(QAction *type)
{
Q_D(KoToolBase);
if (m_pointSelection.hasSelection()) {
QList<KoPathPointData> selectedPoints = m_pointSelection.selectedPointsData();
KUndo2Command *initialConversionCommand = createPointToCurveCommand(selectedPoints);
// conversion should happen before the c-tor
// of KoPathPointTypeCommand is executed!
if (initialConversionCommand) {
initialConversionCommand->redo();
}
KUndo2Command *command =
- new KoPathPointTypeCommand(selectedPoints,
- static_cast<KoPathPointTypeCommand::PointType>(type->data().toInt()));
+ new KoPathPointTypeCommand(selectedPoints,
+ static_cast<KoPathPointTypeCommand::PointType>(type->data().toInt()));
if (initialConversionCommand) {
using namespace KisCommandUtils;
CompositeCommand *parent = new CompositeCommand();
parent->setText(command->text());
parent->addCommand(new SkipFirstRedoWrapper(initialConversionCommand));
parent->addCommand(command);
command = parent;
}
d->canvas->addCommand(command);
}
}
void KoPathTool::insertPoints()
{
Q_D(KoToolBase);
QList<KoPathPointData> segments(m_pointSelection.selectedSegmentsData());
if (segments.size() == 1) {
qreal positionInSegment = 0.5;
if (m_activeSegment && m_activeSegment->isValid()) {
positionInSegment = m_activeSegment->positionOnSegment;
}
KoPathPointInsertCommand *cmd = new KoPathPointInsertCommand(segments, positionInSegment);
d->canvas->addCommand(cmd);
// TODO: this construction is dangerous. The canvas can remove the command right after
// it has been added to it!
m_pointSelection.clear();
foreach (KoPathPoint * p, cmd->insertedPoints()) {
m_pointSelection.add(p, false);
}
}
}
void KoPathTool::removePoints()
{
Q_D(KoToolBase);
if (m_pointSelection.size() > 0) {
KUndo2Command *cmd = KoPathPointRemoveCommand::createCommand(m_pointSelection.selectedPointsData(), d->canvas->shapeController());
PointHandle *pointHandle = dynamic_cast<PointHandle*>(m_activeHandle);
if (pointHandle && m_pointSelection.contains(pointHandle->activePoint())) {
delete m_activeHandle;
m_activeHandle = 0;
}
clearActivePointSelectionReferences();
d->canvas->addCommand(cmd);
}
}
void KoPathTool::pointToLine()
{
Q_D(KoToolBase);
if (m_pointSelection.hasSelection()) {
QList<KoPathPointData> selectedPoints = m_pointSelection.selectedPointsData();
QList<KoPathPointData> pointToChange;
QList<KoPathPointData>::const_iterator it(selectedPoints.constBegin());
for (; it != selectedPoints.constEnd(); ++it) {
KoPathPoint *point = it->pathShape->pointByIndex(it->pointIndex);
if (point && (point->activeControlPoint1() || point->activeControlPoint2()))
pointToChange.append(*it);
}
if (! pointToChange.isEmpty()) {
d->canvas->addCommand(new KoPathPointTypeCommand(pointToChange, KoPathPointTypeCommand::Line));
}
}
}
void KoPathTool::pointToCurve()
{
Q_D(KoToolBase);
if (m_pointSelection.hasSelection()) {
QList<KoPathPointData> selectedPoints = m_pointSelection.selectedPointsData();
KUndo2Command *command = createPointToCurveCommand(selectedPoints);
if (command) {
d->canvas->addCommand(command);
}
}
}
KUndo2Command* KoPathTool::createPointToCurveCommand(const QList<KoPathPointData> &points)
{
KUndo2Command *command = 0;
QList<KoPathPointData> pointToChange;
QList<KoPathPointData>::const_iterator it(points.constBegin());
for (; it != points.constEnd(); ++it) {
KoPathPoint *point = it->pathShape->pointByIndex(it->pointIndex);
if (point && (! point->activeControlPoint1() || ! point->activeControlPoint2()))
pointToChange.append(*it);
}
if (!pointToChange.isEmpty()) {
command = new KoPathPointTypeCommand(pointToChange, KoPathPointTypeCommand::Curve);
}
return command;
}
void KoPathTool::segmentToLine()
{
Q_D(KoToolBase);
if (m_pointSelection.size() > 1) {
QList<KoPathPointData> segments(m_pointSelection.selectedSegmentsData());
if (segments.size() > 0) {
d->canvas->addCommand(new KoPathSegmentTypeCommand(segments, KoPathSegmentTypeCommand::Line));
}
}
}
void KoPathTool::segmentToCurve()
{
Q_D(KoToolBase);
if (m_pointSelection.size() > 1) {
QList<KoPathPointData> segments(m_pointSelection.selectedSegmentsData());
if (segments.size() > 0) {
d->canvas->addCommand(new KoPathSegmentTypeCommand(segments, KoPathSegmentTypeCommand::Curve));
}
}
}
void KoPathTool::convertToPath()
{
Q_D(KoToolBase);
KoSelection *selection = canvas()->selectedShapesProxy()->selection();
QList<KoParameterShape*> parameterShapes;
Q_FOREACH (KoShape *shape, m_pointSelection.selectedShapes()) {
KoParameterShape * parameteric = dynamic_cast<KoParameterShape*>(shape);
if (parameteric && parameteric->isParametricShape()) {
parameterShapes.append(parameteric);
}
}
if (!parameterShapes.isEmpty()) {
d->canvas->addCommand(new KoParameterToPathCommand(parameterShapes));
}
QList<KoSvgTextShape*> textShapes;
Q_FOREACH (KoShape *shape, selection->selectedEditableShapes()) {
if (KoSvgTextShape *text = dynamic_cast<KoSvgTextShape*>(shape)) {
textShapes.append(text);
}
}
if (!textShapes.isEmpty()) {
KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Convert to Path")); // TODO: reuse the text from KoParameterToPathCommand
const QList<KoShape*> oldSelectedShapes = implicitCastList<KoShape*>(textShapes);
new KoKeepShapesSelectedCommand(oldSelectedShapes, {}, canvas()->selectedShapesProxy(),
KisCommandUtils::FlipFlopCommand::State::INITIALIZING, cmd);
QList<KoShape*> newSelectedShapes;
Q_FOREACH (KoSvgTextShape *shape, textShapes) {
const QPainterPath path = shape->textOutline();
if (path.isEmpty()) continue;
KoPathShape *pathShape = KoPathShape::createShapeFromPainterPath(path);
pathShape->setBackground(shape->background());
pathShape->setStroke(shape->stroke());
pathShape->setZIndex(shape->zIndex());
pathShape->setTransformation(shape->transformation());
KoShapeContainer *parent = shape->parent();
canvas()->shapeController()->addShapeDirect(pathShape, parent, cmd);
newSelectedShapes << pathShape;
}
canvas()->shapeController()->removeShapes(oldSelectedShapes, cmd);
new KoKeepShapesSelectedCommand({}, newSelectedShapes, canvas()->selectedShapesProxy(),
KisCommandUtils::FlipFlopCommand::State::FINALIZING, cmd);
canvas()->addCommand(cmd);
}
updateOptionsWidget();
}
namespace {
bool checkCanJoinToPoints(const KoPathPointData & pd1, const KoPathPointData & pd2)
{
const KoPathPointIndex & index1 = pd1.pointIndex;
const KoPathPointIndex & index2 = pd2.pointIndex;
KoPathShape *path1 = pd1.pathShape;
KoPathShape *path2 = pd2.pathShape;
// check if subpaths are already closed
if (path1->isClosedSubpath(index1.first) || path2->isClosedSubpath(index2.first))
return false;
// check if first point is an endpoint
if (index1.second != 0 && index1.second != path1->subpathPointCount(index1.first)-1)
return false;
// check if second point is an endpoint
if (index2.second != 0 && index2.second != path2->subpathPointCount(index2.first)-1)
return false;
return true;
}
}
void KoPathTool::mergePointsImpl(bool doJoin)
{
Q_D(KoToolBase);
if (m_pointSelection.size() != 2)
return;
QList<KoPathPointData> pointData = m_pointSelection.selectedPointsData();
if (pointData.size() != 2) return;
const KoPathPointData & pd1 = pointData.at(0);
const KoPathPointData & pd2 = pointData.at(1);
if (!checkCanJoinToPoints(pd1, pd2)) {
return;
}
clearActivePointSelectionReferences();
KUndo2Command *cmd = 0;
if (doJoin) {
cmd = new KoMultiPathPointJoinCommand(pd1, pd2, d->canvas->shapeController()->documentBase(), d->canvas->shapeManager()->selection());
} else {
cmd = new KoMultiPathPointMergeCommand(pd1, pd2, d->canvas->shapeController()->documentBase(), d->canvas->shapeManager()->selection());
}
d->canvas->addCommand(cmd);
}
void KoPathTool::joinPoints()
{
mergePointsImpl(true);
}
void KoPathTool::mergePoints()
{
mergePointsImpl(false);
}
void KoPathTool::breakAtPoint()
{
Q_D(KoToolBase);
if (m_pointSelection.hasSelection()) {
d->canvas->addCommand(new KoPathBreakAtPointCommand(m_pointSelection.selectedPointsData()));
}
}
void KoPathTool::breakAtSegment()
{
Q_D(KoToolBase);
// only try to break a segment when 2 points of the same object are selected
if (m_pointSelection.objectCount() == 1 && m_pointSelection.size() == 2) {
QList<KoPathPointData> segments(m_pointSelection.selectedSegmentsData());
if (segments.size() == 1) {
d->canvas->addCommand(new KoPathSegmentBreakCommand(segments.at(0)));
}
}
}
void KoPathTool::paint(QPainter &painter, const KoViewConverter &converter)
{
Q_D(KoToolBase);
Q_FOREACH (KoPathShape *shape, m_pointSelection.selectedShapes()) {
KisHandlePainterHelper helper =
- KoShape::createHandlePainterHelper(&painter, shape, converter, m_handleRadius);
+ KoShape::createHandlePainterHelper(&painter, shape, converter, m_handleRadius);
helper.setHandleStyle(KisHandleStyle::primarySelection());
KoParameterShape * parameterShape = dynamic_cast<KoParameterShape*>(shape);
if (parameterShape && parameterShape->isParametricShape()) {
parameterShape->paintHandles(helper);
} else {
shape->paintPoints(helper);
}
if (!shape->stroke() || !shape->stroke()->isVisible()) {
helper.setHandleStyle(KisHandleStyle::secondarySelection());
helper.drawPath(shape->outline());
}
}
if (m_currentStrategy) {
painter.save();
m_currentStrategy->paint(painter, converter);
painter.restore();
}
m_pointSelection.paint(painter, converter, m_handleRadius);
if (m_activeHandle) {
if (m_activeHandle->check(m_pointSelection.selectedShapes())) {
m_activeHandle->paint(painter, converter, m_handleRadius);
} else {
delete m_activeHandle;
m_activeHandle = 0;
}
} else if (m_activeSegment && m_activeSegment->isValid()) {
KoPathShape *shape = m_activeSegment->path;
// if the stroke is invisible, then we already painted the outline of the shape!
if (shape->stroke() && shape->stroke()->isVisible()) {
KoPathPointIndex index = shape->pathPointIndex(m_activeSegment->segmentStart);
KoPathSegment segment = shape->segmentByIndex(index).toCubic();
KIS_SAFE_ASSERT_RECOVER_RETURN(segment.isValid());
KisHandlePainterHelper helper =
- KoShape::createHandlePainterHelper(&painter, shape, converter, m_handleRadius);
+ KoShape::createHandlePainterHelper(&painter, shape, converter, m_handleRadius);
helper.setHandleStyle(KisHandleStyle::secondarySelection());
QPainterPath path;
path.moveTo(segment.first()->point());
path.cubicTo(segment.first()->controlPoint2(),
segment.second()->controlPoint1(),
segment.second()->point());
helper.drawPath(path);
}
}
if (m_currentStrategy) {
painter.save();
KoShape::applyConversion(painter, converter);
d->canvas->snapGuide()->paint(painter, converter);
painter.restore();
}
}
void KoPathTool::repaintDecorations()
{
Q_FOREACH (KoShape *shape, m_pointSelection.selectedShapes()) {
repaint(shape->boundingRect());
}
m_pointSelection.repaint();
updateOptionsWidget();
}
void KoPathTool::mousePressEvent(KoPointerEvent *event)
{
// we are moving if we hit a point and use the left mouse button
event->ignore();
if (m_activeHandle) {
m_currentStrategy = m_activeHandle->handleMousePress(event);
event->accept();
} else {
if (event->button() & Qt::LeftButton) {
// check if we hit a path segment
if (m_activeSegment && m_activeSegment->isValid()) {
KoPathShape *shape = m_activeSegment->path;
KoPathPointIndex index = shape->pathPointIndex(m_activeSegment->segmentStart);
KoPathSegment segment = shape->segmentByIndex(index);
m_pointSelection.add(segment.first(), !(event->modifiers() & Qt::ShiftModifier));
m_pointSelection.add(segment.second(), false);
KoPathPointData data(shape, index);
m_currentStrategy = new KoPathSegmentChangeStrategy(this, event->point, data, m_activeSegment->positionOnSegment);
event->accept();
} else {
KoShapeManager *shapeManager = canvas()->shapeManager();
KoSelection *selection = shapeManager->selection();
KoShape *shape = shapeManager->shapeAt(event->point, KoFlake::ShapeOnTop);
if (shape && !selection->isSelected(shape)) {
if (!(event->modifiers() & Qt::ShiftModifier)) {
selection->deselectAll();
}
selection->select(shape);
} else {
KIS_ASSERT_RECOVER_RETURN(m_currentStrategy == 0);
m_currentStrategy = new KoPathPointRubberSelectStrategy(this, event->point);
event->accept();
}
}
}
}
}
void KoPathTool::mouseMoveEvent(KoPointerEvent *event)
{
if (event->button() & Qt::RightButton)
return;
if (m_currentStrategy) {
m_lastPoint = event->point;
m_currentStrategy->handleMouseMove(event->point, event->modifiers());
// repaint new handle positions
m_pointSelection.repaint();
if (m_activeHandle) {
m_activeHandle->repaint();
}
if (m_activeSegment) {
repaintSegment(m_activeSegment);
}
return;
}
if (m_activeSegment) {
KoPathPointIndex index = m_activeSegment->path->pathPointIndex(m_activeSegment->segmentStart);
KoPathSegment segment = m_activeSegment->path->segmentByIndex(index);
repaint(segment.boundingRect());
delete m_activeSegment;
m_activeSegment = 0;
}
Q_FOREACH (KoPathShape *shape, m_pointSelection.selectedShapes()) {
QRectF roi = handleGrabRect(shape->documentToShape(event->point));
KoParameterShape * parameterShape = dynamic_cast<KoParameterShape*>(shape);
if (parameterShape && parameterShape->isParametricShape()) {
int handleId = parameterShape->handleIdAt(roi);
if (handleId != -1) {
useCursor(m_moveCursor);
emit statusTextChanged(i18n("Drag to move handle."));
if (m_activeHandle)
m_activeHandle->repaint();
delete m_activeHandle;
if (KoConnectionShape * connectionShape = dynamic_cast<KoConnectionShape*>(parameterShape)) {
//debugFlake << "handleId" << handleId;
m_activeHandle = new ConnectionHandle(this, connectionShape, handleId);
m_activeHandle->repaint();
return;
} else {
//debugFlake << "handleId" << handleId;
m_activeHandle = new ParameterHandle(this, parameterShape, handleId);
m_activeHandle->repaint();
return;
}
}
} else {
QList<KoPathPoint*> points = shape->pointsAt(roi);
if (! points.empty()) {
// find the nearest control point from all points within the roi
KoPathPoint * bestPoint = 0;
KoPathPoint::PointType bestPointType = KoPathPoint::Node;
qreal minDistance = HUGE_VAL;
Q_FOREACH (KoPathPoint *p, points) {
// the node point must be hit if the point is not selected yet
if (! m_pointSelection.contains(p) && ! roi.contains(p->point()))
continue;
// check for the control points first as otherwise it is no longer
// possible to change the control points when they are the same as the point
if (p->activeControlPoint1() && roi.contains(p->controlPoint1())) {
qreal dist = squaredDistance(roi.center(), p->controlPoint1());
if (dist < minDistance) {
bestPoint = p;
bestPointType = KoPathPoint::ControlPoint1;
minDistance = dist;
}
}
if (p->activeControlPoint2() && roi.contains(p->controlPoint2())) {
qreal dist = squaredDistance(roi.center(), p->controlPoint2());
if (dist < minDistance) {
bestPoint = p;
bestPointType = KoPathPoint::ControlPoint2;
minDistance = dist;
}
}
// check the node point at last
qreal dist = squaredDistance(roi.center(), p->point());
if (dist < minDistance) {
bestPoint = p;
bestPointType = KoPathPoint::Node;
minDistance = dist;
}
}
if (! bestPoint)
return;
useCursor(m_moveCursor);
if (bestPointType == KoPathPoint::Node)
emit statusTextChanged(i18n("Drag to move point. Shift click to change point type."));
else
emit statusTextChanged(i18n("Drag to move control point."));
PointHandle *prev = dynamic_cast<PointHandle*>(m_activeHandle);
if (prev && prev->activePoint() == bestPoint && prev->activePointType() == bestPointType)
return; // no change;
if (m_activeHandle)
m_activeHandle->repaint();
delete m_activeHandle;
m_activeHandle = new PointHandle(this, bestPoint, bestPointType);
m_activeHandle->repaint();
return;
}
}
}
useCursor(m_selectCursor);
if (m_activeHandle) {
m_activeHandle->repaint();
}
delete m_activeHandle;
m_activeHandle = 0;
PathSegment *hoveredSegment = segmentAtPoint(event->point);
if(hoveredSegment) {
useCursor(Qt::PointingHandCursor);
emit statusTextChanged(i18n("Drag to change curve directly. Double click to insert new path point."));
m_activeSegment = hoveredSegment;
repaintSegment(m_activeSegment);
} else {
uint selectedPointCount = m_pointSelection.size();
if (selectedPointCount == 0)
emit statusTextChanged(QString());
else if (selectedPointCount == 1)
emit statusTextChanged(i18n("Press B to break path at selected point."));
else
emit statusTextChanged(i18n("Press B to break path at selected segments."));
}
}
void KoPathTool::repaintSegment(PathSegment *pathSegment)
{
if (!pathSegment || !pathSegment->isValid()) return;
KoPathPointIndex index = pathSegment->path->pathPointIndex(pathSegment->segmentStart);
KoPathSegment segment = pathSegment->path->segmentByIndex(index);
repaint(segment.boundingRect());
}
void KoPathTool::mouseReleaseEvent(KoPointerEvent *event)
{
Q_D(KoToolBase);
if (m_currentStrategy) {
const bool hadNoSelection = !m_pointSelection.hasSelection();
m_currentStrategy->finishInteraction(event->modifiers());
KUndo2Command *command = m_currentStrategy->createCommand();
if (command)
d->canvas->addCommand(command);
if (hadNoSelection && dynamic_cast<KoPathPointRubberSelectStrategy*>(m_currentStrategy)
&& !m_pointSelection.hasSelection()) {
// the click didn't do anything at all. Allow it to be used by others.
event->ignore();
}
delete m_currentStrategy;
m_currentStrategy = 0;
}
}
void KoPathTool::keyPressEvent(QKeyEvent *event)
{
if (m_currentStrategy) {
switch (event->key()) {
case Qt::Key_Control:
case Qt::Key_Alt:
case Qt::Key_Shift:
case Qt::Key_Meta:
if (! event->isAutoRepeat()) {
m_currentStrategy->handleMouseMove(m_lastPoint, event->modifiers());
}
break;
case Qt::Key_Escape:
m_currentStrategy->cancelInteraction();
delete m_currentStrategy;
m_currentStrategy = 0;
break;
default:
event->ignore();
return;
}
} else {
switch (event->key()) {
#ifndef NDEBUG
case Qt::Key_D:
if (m_pointSelection.objectCount() == 1) {
QList<KoPathPointData> selectedPoints = m_pointSelection.selectedPointsData();
KoPathShapePrivate *p = static_cast<KoPathShapePrivate*>(selectedPoints[0].pathShape->priv());
p->debugPath();
}
break;
#endif
case Qt::Key_B:
if (m_pointSelection.size() == 1)
breakAtPoint();
else if (m_pointSelection.size() >= 2)
breakAtSegment();
break;
default:
event->ignore();
return;
}
}
event->accept();
}
void KoPathTool::keyReleaseEvent(QKeyEvent *event)
{
if (m_currentStrategy) {
switch (event->key()) {
case Qt::Key_Control:
case Qt::Key_Alt:
case Qt::Key_Shift:
case Qt::Key_Meta:
if (! event->isAutoRepeat()) {
m_currentStrategy->handleMouseMove(m_lastPoint, Qt::NoModifier);
}
break;
default:
break;
}
}
event->accept();
}
void KoPathTool::mouseDoubleClickEvent(KoPointerEvent *event)
{
Q_D(KoToolBase);
event->ignore();
// check if we are doing something else at the moment
if (m_currentStrategy) return;
if (!m_activeHandle && m_activeSegment && m_activeSegment->isValid()) {
QList<KoPathPointData> segments;
segments.append(
- KoPathPointData(m_activeSegment->path,
- m_activeSegment->path->pathPointIndex(m_activeSegment->segmentStart)));
+ KoPathPointData(m_activeSegment->path,
+ m_activeSegment->path->pathPointIndex(m_activeSegment->segmentStart)));
KoPathPointInsertCommand *cmd = new KoPathPointInsertCommand(segments, m_activeSegment->positionOnSegment);
d->canvas->addCommand(cmd);
m_pointSelection.clear();
foreach (KoPathPoint * p, cmd->insertedPoints()) {
m_pointSelection.add(p, false);
}
updateActions();
event->accept();
} else if (!m_activeHandle && !m_activeSegment && m_activatedTemporarily) {
emit done();
event->accept();
} else if (!m_activeHandle && !m_activeSegment) {
KoShapeManager *shapeManager = canvas()->shapeManager();
KoSelection *selection = shapeManager->selection();
selection->deselectAll();
event->accept();
}
}
KoPathTool::PathSegment* KoPathTool::segmentAtPoint(const QPointF &point)
{
// the max allowed distance from a segment
const QRectF grabRoi = handleGrabRect(point);
const qreal distanceThreshold = 0.5 * KisAlgebra2D::maxDimension(grabRoi);
QScopedPointer<PathSegment> segment(new PathSegment);
Q_FOREACH (KoPathShape *shape, m_pointSelection.selectedShapes()) {
KoParameterShape * parameterShape = dynamic_cast<KoParameterShape*>(shape);
if (parameterShape && parameterShape->isParametricShape())
continue;
// convert document point to shape coordinates
const QPointF p = shape->documentToShape(point);
// our region of interest, i.e. a region around our mouse position
const QRectF roi = shape->documentToShape(grabRoi);
qreal minDistance = std::numeric_limits<qreal>::max();
// check all segments of this shape which intersect the region of interest
const QList<KoPathSegment> segments = shape->segmentsAt(roi);
foreach (const KoPathSegment &s, segments) {
const qreal nearestPointParam = s.nearestPoint(p);
const QPointF nearestPoint = s.pointAt(nearestPointParam);
const qreal distance = kisDistance(p, nearestPoint);
// are we within the allowed distance ?
if (distance > distanceThreshold)
continue;
// are we closer to the last closest point ?
if (distance < minDistance) {
segment->path = shape;
segment->segmentStart = s.first();
segment->positionOnSegment = nearestPointParam;
}
}
}
if (!segment->isValid()) {
segment.reset();
}
return segment.take();
}
void KoPathTool::activate(ToolActivation activation, const QSet<KoShape*> &shapes)
{
KoToolBase::activate(activation, shapes);
Q_D(KoToolBase);
m_activatedTemporarily = activation == TemporaryActivation;
// retrieve the actual global handle radius
m_handleRadius = handleRadius();
d->canvas->snapGuide()->reset();
useCursor(m_selectCursor);
m_canvasConnections.addConnection(d->canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged()));
m_canvasConnections.addConnection(d->canvas->selectedShapesProxy(), SIGNAL(selectionContentChanged()), this, SLOT(updateActions()));
m_shapeFillResourceConnector.connectToCanvas(d->canvas);
initializeWithShapes(shapes.toList());
connect(m_actionCurvePoint, SIGNAL(triggered()), this, SLOT(pointToCurve()), Qt::UniqueConnection);
connect(m_actionLinePoint, SIGNAL(triggered()), this, SLOT(pointToLine()), Qt::UniqueConnection);
connect(m_actionLineSegment, SIGNAL(triggered()), this, SLOT(segmentToLine()), Qt::UniqueConnection);
connect(m_actionCurveSegment, SIGNAL(triggered()), this, SLOT(segmentToCurve()), Qt::UniqueConnection);
connect(m_actionAddPoint, SIGNAL(triggered()), this, SLOT(insertPoints()), Qt::UniqueConnection);
connect(m_actionRemovePoint, SIGNAL(triggered()), this, SLOT(removePoints()), Qt::UniqueConnection);
connect(m_actionBreakPoint, SIGNAL(triggered()), this, SLOT(breakAtPoint()), Qt::UniqueConnection);
connect(m_actionBreakSegment, SIGNAL(triggered()), this, SLOT(breakAtSegment()), Qt::UniqueConnection);
connect(m_actionJoinSegment, SIGNAL(triggered()), this, SLOT(joinPoints()), Qt::UniqueConnection);
connect(m_actionMergePoints, SIGNAL(triggered()), this, SLOT(mergePoints()), Qt::UniqueConnection);
connect(m_actionConvertToPath, SIGNAL(triggered()), this, SLOT(convertToPath()), Qt::UniqueConnection);
connect(m_points, SIGNAL(triggered(QAction*)), this, SLOT(pointTypeChanged(QAction*)), Qt::UniqueConnection);
connect(&m_pointSelection, SIGNAL(selectionChanged()), this, SLOT(pointSelectionChanged()), Qt::UniqueConnection);
}
void KoPathTool::slotSelectionChanged()
{
Q_D(KoToolBase);
QList<KoShape*> shapes =
- d->canvas->selectedShapesProxy()->selection()->selectedEditableShapesAndDelegates();
+ d->canvas->selectedShapesProxy()->selection()->selectedEditableShapesAndDelegates();
initializeWithShapes(shapes);
}
void KoPathTool::notifyPathPointsChanged(KoPathShape *shape)
{
Q_UNUSED(shape);
// active handle and selection might have already become invalid, so just
// delete them without dereferencing anything...
delete m_activeHandle;
m_activeHandle = 0;
delete m_activeSegment;
m_activeSegment = 0;
}
void KoPathTool::clearActivePointSelectionReferences()
{
delete m_activeHandle;
m_activeHandle = 0;
delete m_activeSegment;
m_activeSegment = 0;
m_pointSelection.clear();
}
void KoPathTool::initializeWithShapes(const QList<KoShape*> shapes)
{
QList<KoPathShape*> selectedShapes;
Q_FOREACH (KoShape *shape, shapes) {
KoPathShape *pathShape = dynamic_cast<KoPathShape*>(shape);
if (pathShape && pathShape->isShapeEditable()) {
selectedShapes.append(pathShape);
}
}
const QRectF oldBoundingRect =
- KoShape::boundingRect(implicitCastList<KoShape*>(m_pointSelection.selectedShapes()));
+ KoShape::boundingRect(implicitCastList<KoShape*>(m_pointSelection.selectedShapes()));
if (selectedShapes != m_pointSelection.selectedShapes()) {
clearActivePointSelectionReferences();
m_pointSelection.setSelectedShapes(selectedShapes);
repaintDecorations();
}
Q_FOREACH (KoPathShape *shape, selectedShapes) {
// as the tool is just in activation repaintDecorations does not yet get called
// so we need to use repaint of the tool and it is only needed to repaint the
// current canvas
repaint(shape->boundingRect());
}
repaint(oldBoundingRect);
updateOptionsWidget();
updateActions();
}
void KoPathTool::updateOptionsWidget()
{
PathToolOptionWidget::Types type;
QList<KoPathShape*> selectedShapes = m_pointSelection.selectedShapes();
Q_FOREACH (KoPathShape *shape, selectedShapes) {
KoParameterShape * parameterShape = dynamic_cast<KoParameterShape*>(shape);
type |= parameterShape && parameterShape->isParametricShape() ?
- PathToolOptionWidget::ParametricShape : PathToolOptionWidget::PlainPath;
+ PathToolOptionWidget::ParametricShape : PathToolOptionWidget::PlainPath;
}
emit singleShapeChanged(selectedShapes.size() == 1 ? selectedShapes.first() : 0);
emit typeChanged(type);
}
void KoPathTool::updateActions()
{
QList<KoPathPointData> pointData = m_pointSelection.selectedPointsData();
bool canBreakAtPoint = false;
bool hasNonSmoothPoints = false;
bool hasNonSymmetricPoints = false;
bool hasNonSplitPoints = false;
bool hasNonLinePoints = false;
bool hasNonCurvePoints = false;
bool canJoinSubpaths = false;
if (!pointData.isEmpty()) {
Q_FOREACH (const KoPathPointData &pd, pointData) {
const int subpathIndex = pd.pointIndex.first;
const int pointIndex = pd.pointIndex.second;
canBreakAtPoint |= pd.pathShape->isClosedSubpath(subpathIndex) ||
- (pointIndex > 0 && pointIndex < pd.pathShape->subpathPointCount(subpathIndex) - 1);
+ (pointIndex > 0 && pointIndex < pd.pathShape->subpathPointCount(subpathIndex) - 1);
KoPathPoint *point = pd.pathShape->pointByIndex(pd.pointIndex);
hasNonSmoothPoints |= !(point->properties() & KoPathPoint::IsSmooth);
hasNonSymmetricPoints |= !(point->properties() & KoPathPoint::IsSymmetric);
hasNonSplitPoints |=
- point->properties() & KoPathPoint::IsSymmetric ||
- point->properties() & KoPathPoint::IsSmooth;
+ point->properties() & KoPathPoint::IsSymmetric ||
+ point->properties() & KoPathPoint::IsSmooth;
hasNonLinePoints |= point->activeControlPoint1() || point->activeControlPoint2();
hasNonCurvePoints |= !point->activeControlPoint1() && !point->activeControlPoint2();
}
if (pointData.size() == 2) {
const KoPathPointData & pd1 = pointData.at(0);
const KoPathPointData & pd2 = pointData.at(1);
canJoinSubpaths = checkCanJoinToPoints(pd1, pd2);
}
}
m_actionPathPointCorner->setEnabled(hasNonSplitPoints);
m_actionPathPointSmooth->setEnabled(hasNonSmoothPoints);
m_actionPathPointSymmetric->setEnabled(hasNonSymmetricPoints);
m_actionRemovePoint->setEnabled(!pointData.isEmpty());
m_actionBreakPoint->setEnabled(canBreakAtPoint);
m_actionCurvePoint->setEnabled(hasNonCurvePoints);
m_actionLinePoint->setEnabled(hasNonLinePoints);
m_actionJoinSegment->setEnabled(canJoinSubpaths);
m_actionMergePoints->setEnabled(canJoinSubpaths);
QList<KoPathPointData> segments(m_pointSelection.selectedSegmentsData());
bool canSplitAtSegment = false;
bool canConvertSegmentToLine = false;
bool canConvertSegmentToCurve= false;
if (!segments.isEmpty()) {
canSplitAtSegment = segments.size() == 1;
bool hasLines = false;
bool hasCurves = false;
Q_FOREACH (const KoPathPointData &pd, segments) {
KoPathSegment segment = pd.pathShape->segmentByIndex(pd.pointIndex);
hasLines |= segment.degree() == 1;
hasCurves |= segment.degree() > 1;
}
canConvertSegmentToLine = !segments.isEmpty() && hasCurves;
canConvertSegmentToCurve= !segments.isEmpty() && hasLines;
}
m_actionAddPoint->setEnabled(canSplitAtSegment);
m_actionLineSegment->setEnabled(canConvertSegmentToLine);
m_actionCurveSegment->setEnabled(canConvertSegmentToCurve);
m_actionBreakSegment->setEnabled(canSplitAtSegment);
KoSelection *selection = canvas()->selectedShapesProxy()->selection();
bool haveConvertibleShapes = false;
Q_FOREACH (KoShape *shape, selection->selectedEditableShapes()) {
KoParameterShape * parameterShape = dynamic_cast<KoParameterShape*>(shape);
KoSvgTextShape *textShape = dynamic_cast<KoSvgTextShape*>(shape);
if (textShape ||
- (parameterShape && parameterShape->isParametricShape())) {
+ (parameterShape && parameterShape->isParametricShape())) {
haveConvertibleShapes = true;
break;
}
}
m_actionConvertToPath->setEnabled(haveConvertibleShapes);
}
void KoPathTool::deactivate()
{
Q_D(KoToolBase);
m_shapeFillResourceConnector.disconnect();
m_canvasConnections.clear();
m_pointSelection.clear();
m_pointSelection.setSelectedShapes(QList<KoPathShape*>());
delete m_activeHandle;
m_activeHandle = 0;
delete m_activeSegment;
m_activeSegment = 0;
delete m_currentStrategy;
m_currentStrategy = 0;
d->canvas->snapGuide()->reset();
disconnect(m_actionCurvePoint, 0, this, 0);
disconnect(m_actionLinePoint, 0, this, 0);
disconnect(m_actionLineSegment, 0, this, 0);
disconnect(m_actionCurveSegment, 0, this, 0);
disconnect(m_actionAddPoint, 0, this, 0);
disconnect(m_actionRemovePoint, 0, this, 0);
disconnect(m_actionBreakPoint, 0, this, 0);
disconnect(m_actionBreakSegment, 0, this, 0);
disconnect(m_actionJoinSegment, 0, this, 0);
disconnect(m_actionMergePoints, 0, this, 0);
disconnect(m_actionConvertToPath, 0, this, 0);
disconnect(m_points, 0, this, 0);
disconnect(&m_pointSelection, 0, this, 0);
KoToolBase::deactivate();
}
void KoPathTool::documentResourceChanged(int key, const QVariant & res)
{
if (key == KoDocumentResourceManager::HandleRadius) {
int oldHandleRadius = m_handleRadius;
m_handleRadius = res.toUInt();
// repaint with the bigger of old and new handle radius
int maxRadius = qMax(m_handleRadius, oldHandleRadius);
Q_FOREACH (KoPathShape *shape, m_pointSelection.selectedShapes()) {
QRectF controlPointRect = shape->absoluteTransformation(0).map(shape->outline()).controlPointRect();
repaint(controlPointRect.adjusted(-maxRadius, -maxRadius, maxRadius, maxRadius));
}
}
}
void KoPathTool::pointSelectionChanged()
{
Q_D(KoToolBase);
updateActions();
d->canvas->snapGuide()->setIgnoredPathPoints(m_pointSelection.selectedPoints().toList());
emit selectionChanged(m_pointSelection.hasSelection());
}
void KoPathTool::repaint(const QRectF &repaintRect)
{
Q_D(KoToolBase);
//debugFlake <<"KoPathTool::repaint(" << repaintRect <<")" << m_handleRadius;
// widen border to take antialiasing into account
qreal radius = m_handleRadius + 1;
d->canvas->updateCanvas(repaintRect.adjusted(-radius, -radius, radius, radius));
}
namespace {
void addActionsGroupIfEnabled(QMenu *menu, QAction *a1, QAction *a2)
{
if (a1->isEnabled() || a2->isEnabled()) {
menu->addAction(a1);
menu->addAction(a2);
menu->addSeparator();
}
}
void addActionsGroupIfEnabled(QMenu *menu, QAction *a1, QAction *a2, QAction *a3)
{
if (a1->isEnabled() || a2->isEnabled()) {
menu->addAction(a1);
menu->addAction(a2);
menu->addAction(a3);
menu->addSeparator();
}
}
}
QMenu *KoPathTool::popupActionsMenu()
{
if (m_activeHandle) {
m_activeHandle->trySelectHandle();
}
if (m_activeSegment && m_activeSegment->isValid()) {
KoPathShape *shape = m_activeSegment->path;
KoPathSegment segment = shape->segmentByIndex(shape->pathPointIndex(m_activeSegment->segmentStart));
m_pointSelection.add(segment.first(), true);
m_pointSelection.add(segment.second(), false);
}
if (m_contextMenu) {
m_contextMenu->clear();
addActionsGroupIfEnabled(m_contextMenu.data(),
m_actionPathPointCorner,
m_actionPathPointSmooth,
m_actionPathPointSymmetric);
addActionsGroupIfEnabled(m_contextMenu.data(),
m_actionCurvePoint,
m_actionLinePoint);
addActionsGroupIfEnabled(m_contextMenu.data(),
m_actionAddPoint,
m_actionRemovePoint);
addActionsGroupIfEnabled(m_contextMenu.data(),
m_actionLineSegment,
m_actionCurveSegment);
addActionsGroupIfEnabled(m_contextMenu.data(),
m_actionBreakPoint,
m_actionBreakSegment);
addActionsGroupIfEnabled(m_contextMenu.data(),
m_actionJoinSegment,
m_actionMergePoints);
m_contextMenu->addAction(m_actionConvertToPath);
m_contextMenu->addSeparator();
}
return m_contextMenu.data();
}
void KoPathTool::deleteSelection()
{
removePoints();
}
KoToolSelection * KoPathTool::selection()
{
return &m_pointSelection;
}
void KoPathTool::requestUndoDuringStroke()
{
// noop!
}
void KoPathTool::requestStrokeCancellation()
{
explicitUserStrokeEndRequest();
}
void KoPathTool::requestStrokeEnd()
{
// noop!
}
void KoPathTool::explicitUserStrokeEndRequest()
{
if (m_activatedTemporarily) {
emit done();
}
}
diff --git a/libs/global/CMakeLists.txt b/libs/global/CMakeLists.txt
index 74e53b8456..873eb80db6 100644
--- a/libs/global/CMakeLists.txt
+++ b/libs/global/CMakeLists.txt
@@ -1,53 +1,55 @@
add_subdirectory( tests )
include(CheckFunctionExists)
check_function_exists(backtrace HAVE_BACKTRACE)
configure_file(config-debug.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-debug.h)
option(HAVE_MEMORY_LEAK_TRACKER "Enable memory leak tracker (always disabled in release build)" OFF)
option(HAVE_BACKTRACE_SUPPORT "Enable recording of backtrace in memory leak tracker" OFF)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-memory-leak-tracker.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-memory-leak-tracker.h) ### WRONG PLACE???
set(kritaglobal_LIB_SRCS
kis_assert.cpp
kis_debug.cpp
kis_algebra_2d.cpp
kis_memory_leak_tracker.cpp
kis_shared.cpp
kis_dom_utils.cpp
kis_painting_tweaks.cpp
KisHandlePainterHelper.cpp
KisHandleStyle.cpp
kis_relaxed_timer.cpp
kis_signal_compressor.cpp
kis_signal_compressor_with_param.cpp
kis_thread_safe_signal_compressor.cpp
kis_acyclic_signal_connector.cpp
kis_latency_tracker.cpp
KisQPainterStateSaver.cpp
KisSharedThreadPoolAdapter.cpp
KisSharedRunnable.cpp
KisRollingMeanAccumulatorWrapper.cpp
kis_config_notifier.cpp
KisDeleteLaterWrapper.cpp
+ KisUsageLogger.cpp
)
add_library(kritaglobal SHARED ${kritaglobal_LIB_SRCS} )
generate_export_header(kritaglobal BASE_NAME kritaglobal)
target_link_libraries(kritaglobal
PUBLIC
+ kritaversion
Qt5::Concurrent
Qt5::Core
Qt5::Gui
Qt5::Widgets
Qt5::Xml
KF5::I18n
)
set_target_properties(kritaglobal PROPERTIES
VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION}
)
install(TARGETS kritaglobal ${INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/libs/global/KisUsageLogger.cpp b/libs/global/KisUsageLogger.cpp
new file mode 100644
index 0000000000..8b94443d5e
--- /dev/null
+++ b/libs/global/KisUsageLogger.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2019 Boudewijn Rempt <boud@valdyas.org>
+ *
+ * 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 "KisUsageLogger.h"
+
+#include <QGlobalStatic>
+#include <QDebug>
+#include <QDateTime>
+#include <QSysInfo>
+#include <QStandardPaths>
+#include <QFile>
+#include <QDesktopWidget>
+#include <QClipboard>
+#include <QThread>
+
+#include <klocalizedstring.h>
+#include <KritaVersionWrapper.h>
+
+
+Q_GLOBAL_STATIC(KisUsageLogger, s_instance)
+
+const QString KisUsageLogger::s_sectionHeader("================================================================================\n");
+
+struct KisUsageLogger::Private {
+ bool active {false};
+ QFile logFile;
+};
+
+KisUsageLogger::KisUsageLogger()
+ : d(new Private)
+{
+ d->logFile.setFileName(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/krita.log");
+
+ rotateLog();
+ d->logFile.open(QFile::Append);
+ writeHeader();
+}
+
+KisUsageLogger::~KisUsageLogger()
+{
+ if (d->active) {
+ close();
+ }
+}
+
+void KisUsageLogger::initialize()
+{
+ s_instance->d->active = true;
+}
+
+void KisUsageLogger::close()
+{
+ log("Closing.");
+ s_instance->d->active = false;
+ s_instance->d->logFile.flush();
+ s_instance->d->logFile.close();
+}
+
+void KisUsageLogger::log(const QString &message)
+{
+ if (!s_instance->d->active) return;
+ if (!s_instance->d->logFile.isOpen()) return;
+
+ s_instance->d->logFile.write(QDateTime::currentDateTime().toString(Qt::RFC2822Date).toUtf8());
+ s_instance->d->logFile.write(": ");
+ write(message);
+}
+
+void KisUsageLogger::write(const QString &message)
+{
+ if (!s_instance->d->active) return;
+ if (!s_instance->d->logFile.isOpen()) return;
+
+ s_instance->d->logFile.write(message.toUtf8());
+ s_instance->d->logFile.write("\n");
+
+ s_instance->d->logFile.flush();
+}
+
+void KisUsageLogger::writeHeader()
+{
+ Q_ASSERT(d->logFile.isOpen());
+
+ QString sessionHeader = QString("SESSION: %1\n\n").arg(QDateTime::currentDateTime().toString(Qt::RFC2822Date));
+ QString disclaimer = i18n("WARNING: This file contains information about your system and the\n"
+ "images you have been working with.\n"
+ "\n"
+ "If you have problems with Krita, the Krita developers might ask\n"
+ "you to share this file with them. The information in this file is\n"
+ "not shared automatically with the Krita developers in any way. You\n"
+ "can disable logging to this file in Krita's Configure Krita Dialog.\n"
+ "\n"
+ "Please review the contents of this file before sharing this file with\n"
+ "anyone.\n\n");
+
+ QString systemInfo;
+
+ // NOTE: This is intentionally not translated!
+
+ // Krita version info
+ systemInfo.append("Krita\n");
+ systemInfo.append("\n Version: ").append(KritaVersionWrapper::versionString(true));
+ systemInfo.append("\n\n");
+
+ systemInfo.append("Qt\n");
+ systemInfo.append("\n Version (compiled): ").append(QT_VERSION_STR);
+ systemInfo.append("\n Version (loaded): ").append(qVersion());
+ systemInfo.append("\n\n");
+
+ // OS information
+ systemInfo.append("OS Information\n");
+ systemInfo.append("\n Build ABI: ").append(QSysInfo::buildAbi());
+ systemInfo.append("\n Build CPU: ").append(QSysInfo::buildCpuArchitecture());
+ systemInfo.append("\n CPU: ").append(QSysInfo::currentCpuArchitecture());
+ systemInfo.append("\n Kernel Type: ").append(QSysInfo::kernelType());
+ systemInfo.append("\n Kernel Version: ").append(QSysInfo::kernelVersion());
+ systemInfo.append("\n Pretty Productname: ").append(QSysInfo::prettyProductName());
+ systemInfo.append("\n Product Type: ").append(QSysInfo::productType());
+ systemInfo.append("\n Product Version: ").append(QSysInfo::productVersion());
+ systemInfo.append("\n\n");
+
+ d->logFile.write(s_sectionHeader.toUtf8());
+ d->logFile.write(sessionHeader.toUtf8());
+ d->logFile.write(disclaimer.toUtf8());
+ d->logFile.write(systemInfo.toUtf8());
+
+
+}
+
+void KisUsageLogger::rotateLog()
+{
+ d->logFile.open(QFile::ReadOnly);
+ QString log = QString::fromUtf8(d->logFile.readAll());
+ int sectionCount = log.count(s_sectionHeader);
+ int nextSectionIndex = log.indexOf(s_sectionHeader, s_sectionHeader.length());
+ while(sectionCount >= s_maxLogs) {
+ log = log.remove(0, log.indexOf(s_sectionHeader, nextSectionIndex));
+ nextSectionIndex = log.indexOf(s_sectionHeader, s_sectionHeader.length());
+ sectionCount = log.count(s_sectionHeader);
+ }
+ d->logFile.close();
+ d->logFile.open(QFile::WriteOnly);
+ d->logFile.write(log.toUtf8());
+ d->logFile.close();
+}
+
diff --git a/libs/ui/kis_safe_document_loader.h b/libs/global/KisUsageLogger.h
similarity index 50%
copy from libs/ui/kis_safe_document_loader.h
copy to libs/global/KisUsageLogger.h
index eb202a6984..5f128259f6 100644
--- a/libs/ui/kis_safe_document_loader.h
+++ b/libs/global/KisUsageLogger.h
@@ -1,48 +1,61 @@
/*
- * Copyright (c) 2013 Dmitry Kazakov <dimula73@gmail.com>
+ * Copyright (c) 2019 Boudewijn Rempt <boud@valdyas.org>
*
* 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 KISUSAGELOGGER_H
+#define KISUSAGELOGGER_H
-#ifndef __KIS_SAFE_DOCUMENT_LOADER_H
-#define __KIS_SAFE_DOCUMENT_LOADER_H
+#include <QString>
+#include <QScopedPointer>
-#include <QObject>
-#include "kis_types.h"
+#include "kritaglobal_export.h"
-class KisSafeDocumentLoader : public QObject
+/**
+ * @brief The KisUsageLogger class logs messages to a logfile
+ */
+class KRITAGLOBAL_EXPORT KisUsageLogger
{
- Q_OBJECT
-public:
- KisSafeDocumentLoader(const QString &path = "", QObject *parent = 0);
- ~KisSafeDocumentLoader() override;
public:
- void setPath(const QString &path);
- void reloadImage();
-private Q_SLOTS:
- void fileChanged(QString);
- void fileChangedCompressed(bool sync = false);
- void delayedLoadStart();
-Q_SIGNALS:
- void loadingFinished(KisPaintDeviceSP paintDevice, int xRes, int yRes);
+ KisUsageLogger();
+ ~KisUsageLogger();
+
+ static void initialize();
+ static void close();
+
+ /// Logs with date/time
+ static void log(const QString &message);
+
+ /// Writes without date/time
+ static void write(const QString &message);
private:
+
+ void writeHeader();
+ void rotateLog();
+
+ Q_DISABLE_COPY(KisUsageLogger)
+
struct Private;
- Private * const m_d;
+ const QScopedPointer<Private> d;
+
+ static const QString s_sectionHeader;
+ static const int s_maxLogs {10};
+
};
-#endif /* __KIS_SAFE_DOCUMENT_LOADER_H */
+#endif // KISUSAGELOGGER_H
diff --git a/libs/global/kis_acyclic_signal_connector.cpp b/libs/global/kis_acyclic_signal_connector.cpp
index f33374ffd5..85b9afeae8 100644
--- a/libs/global/kis_acyclic_signal_connector.cpp
+++ b/libs/global/kis_acyclic_signal_connector.cpp
@@ -1,307 +1,307 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_acyclic_signal_connector.h"
#include "kis_debug.h"
KisAcyclicSignalConnector::KisAcyclicSignalConnector(QObject *parent)
: QObject(parent),
m_signalsBlocked(0)
{
}
KisAcyclicSignalConnector::~KisAcyclicSignalConnector()
{
}
void KisAcyclicSignalConnector::connectForwardDouble(QObject *sender, const char *signal,
QObject *receiver, const char *method)
{
- connect(sender, signal, this, SLOT(forwardSlotDouble(double)));
- connect(this, SIGNAL(forwardSignalDouble(double)), receiver, method);
+ connect(sender, signal, this, SLOT(forwardSlotDouble(double)), Qt::UniqueConnection);
+ connect(this, SIGNAL(forwardSignalDouble(double)), receiver, method, Qt::UniqueConnection);
}
void KisAcyclicSignalConnector::connectBackwardDouble(QObject *sender, const char *signal,
QObject *receiver, const char *method)
{
- connect(sender, signal, this, SLOT(backwardSlotDouble(double)));
- connect(this, SIGNAL(backwardSignalDouble(double)), receiver, method);
+ connect(sender, signal, this, SLOT(backwardSlotDouble(double)), Qt::UniqueConnection);
+ connect(this, SIGNAL(backwardSignalDouble(double)), receiver, method, Qt::UniqueConnection);
}
void KisAcyclicSignalConnector::connectForwardInt(QObject *sender, const char *signal,
QObject *receiver, const char *method)
{
- connect(sender, signal, this, SLOT(forwardSlotInt(int)));
- connect(this, SIGNAL(forwardSignalInt(int)), receiver, method);
+ connect(sender, signal, this, SLOT(forwardSlotInt(int)), Qt::UniqueConnection);
+ connect(this, SIGNAL(forwardSignalInt(int)), receiver, method, Qt::UniqueConnection);
}
void KisAcyclicSignalConnector::connectBackwardInt(QObject *sender, const char *signal,
QObject *receiver, const char *method)
{
- connect(sender, signal, this, SLOT(backwardSlotInt(int)));
- connect(this, SIGNAL(backwardSignalInt(int)), receiver, method);
+ connect(sender, signal, this, SLOT(backwardSlotInt(int)), Qt::UniqueConnection);
+ connect(this, SIGNAL(backwardSignalInt(int)), receiver, method, Qt::UniqueConnection);
}
void KisAcyclicSignalConnector::connectForwardBool(QObject *sender, const char *signal,
QObject *receiver, const char *method)
{
- connect(sender, signal, this, SLOT(forwardSlotBool(bool)));
- connect(this, SIGNAL(forwardSignalBool(bool)), receiver, method);
+ connect(sender, signal, this, SLOT(forwardSlotBool(bool)), Qt::UniqueConnection);
+ connect(this, SIGNAL(forwardSignalBool(bool)), receiver, method, Qt::UniqueConnection);
}
void KisAcyclicSignalConnector::connectBackwardBool(QObject *sender, const char *signal,
QObject *receiver, const char *method)
{
- connect(sender, signal, this, SLOT(backwardSlotBool(bool)));
- connect(this, SIGNAL(backwardSignalBool(bool)), receiver, method);
+ connect(sender, signal, this, SLOT(backwardSlotBool(bool)), Qt::UniqueConnection);
+ connect(this, SIGNAL(backwardSignalBool(bool)), receiver, method, Qt::UniqueConnection);
}
void KisAcyclicSignalConnector::connectForwardVoid(QObject *sender, const char *signal,
QObject *receiver, const char *method)
{
- connect(sender, signal, this, SLOT(forwardSlotVoid()));
- connect(this, SIGNAL(forwardSignalVoid()), receiver, method);
+ connect(sender, signal, this, SLOT(forwardSlotVoid()), Qt::UniqueConnection);
+ connect(this, SIGNAL(forwardSignalVoid()), receiver, method, Qt::UniqueConnection);
}
void KisAcyclicSignalConnector::connectBackwardVoid(QObject *sender, const char *signal,
QObject *receiver, const char *method)
{
- connect(sender, signal, this, SLOT(backwardSlotVoid()));
- connect(this, SIGNAL(backwardSignalVoid()), receiver, method);
+ connect(sender, signal, this, SLOT(backwardSlotVoid()), Qt::UniqueConnection);
+ connect(this, SIGNAL(backwardSignalVoid()), receiver, method, Qt::UniqueConnection);
}
void KisAcyclicSignalConnector::connectForwardVariant(QObject *sender, const char *signal,
QObject *receiver, const char *method)
{
- connect(sender, signal, this, SLOT(forwardSlotVariant(QVariant)));
- connect(this, SIGNAL(forwardSignalVariant(QVariant)), receiver, method);
+ connect(sender, signal, this, SLOT(forwardSlotVariant(QVariant)), Qt::UniqueConnection);
+ connect(this, SIGNAL(forwardSignalVariant(QVariant)), receiver, method, Qt::UniqueConnection);
}
void KisAcyclicSignalConnector::connectBackwardVariant(QObject *sender, const char *signal,
QObject *receiver, const char *method)
{
- connect(sender, signal, this, SLOT(backwardSlotVariant(QVariant)));
- connect(this, SIGNAL(backwardSignalVariant(QVariant)), receiver, method);
+ connect(sender, signal, this, SLOT(backwardSlotVariant(QVariant)), Qt::UniqueConnection);
+ connect(this, SIGNAL(backwardSignalVariant(QVariant)), receiver, method, Qt::UniqueConnection);
}
void KisAcyclicSignalConnector::connectForwardResourcePair(QObject *sender, const char *signal, QObject *receiver, const char *method)
{
- connect(sender, signal, this, SLOT(forwardSlotResourcePair(int,QVariant)));
- connect(this, SIGNAL(forwardSignalResourcePair(int,QVariant)), receiver, method);
+ connect(sender, signal, this, SLOT(forwardSlotResourcePair(int,QVariant)), Qt::UniqueConnection);
+ connect(this, SIGNAL(forwardSignalResourcePair(int,QVariant)), receiver, method, Qt::UniqueConnection);
}
void KisAcyclicSignalConnector::connectBackwardResourcePair(QObject *sender, const char *signal, QObject *receiver, const char *method)
{
- connect(sender, signal, this, SLOT(backwardSlotResourcePair(int,QVariant)));
- connect(this, SIGNAL(backwardSignalResourcePair(int,QVariant)), receiver, method);
+ connect(sender, signal, this, SLOT(backwardSlotResourcePair(int,QVariant)), Qt::UniqueConnection);
+ connect(this, SIGNAL(backwardSignalResourcePair(int,QVariant)), receiver, method, Qt::UniqueConnection);
}
void KisAcyclicSignalConnector::connectForwardKoColor(QObject *sender, const char *signal, QObject *receiver, const char *method)
{
- connect(sender, signal, this, SLOT(forwardSlotKoColor(KoColor)));
- connect(this, SIGNAL(forwardSignalKoColor(KoColor)), receiver, method);
+ connect(sender, signal, this, SLOT(forwardSlotKoColor(KoColor)), Qt::UniqueConnection);
+ connect(this, SIGNAL(forwardSignalKoColor(KoColor)), receiver, method, Qt::UniqueConnection);
}
void KisAcyclicSignalConnector::connectBackwardKoColor(QObject *sender, const char *signal, QObject *receiver, const char *method)
{
- connect(sender, signal, this, SLOT(backwardSlotKoColor(KoColor)));
- connect(this, SIGNAL(backwardSignalKoColor(KoColor)), receiver, method);
+ connect(sender, signal, this, SLOT(backwardSlotKoColor(KoColor)), Qt::UniqueConnection);
+ connect(this, SIGNAL(backwardSignalKoColor(KoColor)), receiver, method, Qt::UniqueConnection);
}
void KisAcyclicSignalConnector::lock()
{
if (m_parentConnector) {
m_parentConnector->lock();
} else {
coordinatedLock();
Q_FOREACH(QPointer<KisAcyclicSignalConnector> conn, m_coordinatedConnectors) {
if (!conn) continue;
conn->coordinatedLock();
}
}
}
void KisAcyclicSignalConnector::unlock()
{
if (m_parentConnector) {
m_parentConnector->unlock();
} else {
Q_FOREACH(QPointer<KisAcyclicSignalConnector> conn, m_coordinatedConnectors) {
if (!conn) continue;
conn->coordinatedUnlock();
}
coordinatedUnlock();
}
}
void KisAcyclicSignalConnector::coordinatedLock()
{
m_signalsBlocked++;
}
void KisAcyclicSignalConnector::coordinatedUnlock()
{
m_signalsBlocked--;
}
KisAcyclicSignalConnector *KisAcyclicSignalConnector::createCoordinatedConnector()
{
KisAcyclicSignalConnector *conn = new KisAcyclicSignalConnector(this);
conn->m_parentConnector = this;
m_coordinatedConnectors.append(conn);
return conn;
}
void KisAcyclicSignalConnector::forwardSlotDouble(double value)
{
if (m_signalsBlocked) return;
lock();
emit forwardSignalDouble(value);
unlock();
}
void KisAcyclicSignalConnector::backwardSlotDouble(double value)
{
if (m_signalsBlocked) return;
lock();
emit backwardSignalDouble(value);
unlock();
}
void KisAcyclicSignalConnector::forwardSlotInt(int value)
{
if (m_signalsBlocked) return;
lock();
emit forwardSignalInt(value);
unlock();
}
void KisAcyclicSignalConnector::backwardSlotInt(int value)
{
if (m_signalsBlocked) return;
lock();
emit backwardSignalInt(value);
unlock();
}
void KisAcyclicSignalConnector::forwardSlotBool(bool value)
{
if (m_signalsBlocked) return;
lock();
emit forwardSignalBool(value);
unlock();
}
void KisAcyclicSignalConnector::backwardSlotBool(bool value)
{
if (m_signalsBlocked) return;
lock();
emit backwardSignalBool(value);
unlock();
}
void KisAcyclicSignalConnector::forwardSlotVoid()
{
if (m_signalsBlocked) return;
lock();
emit forwardSignalVoid();
unlock();
}
void KisAcyclicSignalConnector::backwardSlotVoid()
{
if (m_signalsBlocked) return;
lock();
emit backwardSignalVoid();
unlock();
}
void KisAcyclicSignalConnector::forwardSlotVariant(const QVariant &value)
{
if (m_signalsBlocked) return;
lock();
emit forwardSignalVariant(value);
unlock();
}
void KisAcyclicSignalConnector::backwardSlotVariant(const QVariant &value)
{
if (m_signalsBlocked) return;
lock();
emit backwardSignalVariant(value);
unlock();
}
void KisAcyclicSignalConnector::forwardSlotResourcePair(int key, const QVariant &resource)
{
if (m_signalsBlocked) return;
lock();
emit forwardSignalResourcePair(key, resource);
unlock();
}
void KisAcyclicSignalConnector::backwardSlotResourcePair(int key, const QVariant &resource)
{
if (m_signalsBlocked) return;
lock();
emit backwardSignalResourcePair(key, resource);
unlock();
}
void KisAcyclicSignalConnector::forwardSlotKoColor(const KoColor &value)
{
if (m_signalsBlocked) return;
lock();
emit forwardSignalKoColor(value);
unlock();
}
void KisAcyclicSignalConnector::backwardSlotKoColor(const KoColor &value)
{
if (m_signalsBlocked) return;
lock();
emit backwardSignalKoColor(value);
unlock();
}
diff --git a/libs/image/CMakeLists.txt b/libs/image/CMakeLists.txt
index ec6b486960..cf25e7365c 100644
--- a/libs/image/CMakeLists.txt
+++ b/libs/image/CMakeLists.txt
@@ -1,368 +1,374 @@
add_subdirectory( tests )
add_subdirectory( tiles3 )
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty
${CMAKE_CURRENT_SOURCE_DIR}/brushengine
${CMAKE_CURRENT_SOURCE_DIR}/commands
${CMAKE_CURRENT_SOURCE_DIR}/commands_new
${CMAKE_CURRENT_SOURCE_DIR}/filter
${CMAKE_CURRENT_SOURCE_DIR}/floodfill
${CMAKE_CURRENT_SOURCE_DIR}/generator
${CMAKE_CURRENT_SOURCE_DIR}/layerstyles
${CMAKE_CURRENT_SOURCE_DIR}/processing
${CMAKE_SOURCE_DIR}/sdk/tests
)
include_directories(SYSTEM
${EIGEN3_INCLUDE_DIR}
)
if(FFTW3_FOUND)
include_directories(${FFTW3_INCLUDE_DIR})
endif()
if(HAVE_VC)
include_directories(SYSTEM ${Vc_INCLUDE_DIR} ${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS})
ko_compile_for_all_implementations(__per_arch_circle_mask_generator_objs kis_brush_mask_applicator_factories.cpp)
else()
set(__per_arch_circle_mask_generator_objs kis_brush_mask_applicator_factories.cpp)
endif()
set(kritaimage_LIB_SRCS
tiles3/kis_tile.cc
tiles3/kis_tile_data.cc
tiles3/kis_tile_data_store.cc
tiles3/kis_tile_data_pooler.cc
tiles3/kis_tiled_data_manager.cc
tiles3/KisTiledExtentManager.cpp
tiles3/kis_memento_manager.cc
tiles3/kis_hline_iterator.cpp
tiles3/kis_vline_iterator.cpp
tiles3/kis_random_accessor.cc
tiles3/swap/kis_abstract_compression.cpp
tiles3/swap/kis_lzf_compression.cpp
tiles3/swap/kis_abstract_tile_compressor.cpp
tiles3/swap/kis_legacy_tile_compressor.cpp
tiles3/swap/kis_tile_compressor_2.cpp
tiles3/swap/kis_chunk_allocator.cpp
tiles3/swap/kis_memory_window.cpp
tiles3/swap/kis_swapped_data_store.cpp
tiles3/swap/kis_tile_data_swapper.cpp
kis_distance_information.cpp
kis_painter.cc
kis_painter_blt_multi_fixed.cpp
kis_marker_painter.cpp
KisPrecisePaintDeviceWrapper.cpp
kis_progress_updater.cpp
brushengine/kis_paint_information.cc
brushengine/kis_random_source.cpp
brushengine/KisPerStrokeRandomSource.cpp
brushengine/kis_stroke_random_source.cpp
brushengine/kis_paintop.cc
brushengine/kis_paintop_factory.cpp
brushengine/kis_paintop_preset.cpp
brushengine/kis_paintop_registry.cc
brushengine/kis_paintop_settings.cpp
brushengine/kis_paintop_settings_update_proxy.cpp
brushengine/kis_paintop_utils.cpp
brushengine/kis_no_size_paintop_settings.cpp
brushengine/kis_locked_properties.cc
brushengine/kis_locked_properties_proxy.cpp
brushengine/kis_locked_properties_server.cpp
brushengine/kis_paintop_config_widget.cpp
brushengine/kis_uniform_paintop_property.cpp
brushengine/kis_combo_based_paintop_property.cpp
brushengine/kis_slider_based_paintop_property.cpp
brushengine/kis_standard_uniform_properties_factory.cpp
brushengine/KisStrokeSpeedMeasurer.cpp
brushengine/KisPaintopSettingsIds.cpp
commands/kis_deselect_global_selection_command.cpp
commands/KisDeselectActiveSelectionCommand.cpp
commands/kis_image_change_layers_command.cpp
commands/kis_image_change_visibility_command.cpp
commands/kis_image_command.cpp
commands/kis_image_set_projection_color_space_command.cpp
commands/kis_image_layer_add_command.cpp
commands/kis_image_layer_move_command.cpp
commands/kis_image_layer_remove_command.cpp
commands/kis_image_layer_remove_command_impl.cpp
commands/kis_image_lock_command.cpp
commands/kis_node_command.cpp
commands/kis_node_compositeop_command.cpp
commands/kis_node_opacity_command.cpp
commands/kis_node_property_list_command.cpp
commands/kis_reselect_global_selection_command.cpp
commands/KisReselectActiveSelectionCommand.cpp
commands/kis_set_global_selection_command.cpp
commands_new/kis_saved_commands.cpp
commands_new/kis_processing_command.cpp
commands_new/kis_image_resize_command.cpp
commands_new/kis_image_set_resolution_command.cpp
commands_new/kis_node_move_command2.cpp
commands_new/kis_set_layer_style_command.cpp
commands_new/kis_selection_move_command2.cpp
commands_new/kis_update_command.cpp
commands_new/kis_switch_current_time_command.cpp
commands_new/kis_change_projection_color_command.cpp
commands_new/kis_activate_selection_mask_command.cpp
commands_new/kis_transaction_based_command.cpp
processing/kis_do_nothing_processing_visitor.cpp
processing/kis_simple_processing_visitor.cpp
processing/kis_crop_processing_visitor.cpp
processing/kis_crop_selections_processing_visitor.cpp
processing/kis_transform_processing_visitor.cpp
processing/kis_mirror_processing_visitor.cpp
processing/KisSelectionBasedProcessingHelper.cpp
filter/kis_filter.cc
filter/kis_filter_category_ids.cpp
filter/kis_filter_configuration.cc
filter/kis_color_transformation_configuration.cc
filter/kis_filter_registry.cc
filter/kis_color_transformation_filter.cc
generator/kis_generator.cpp
generator/kis_generator_layer.cpp
generator/kis_generator_registry.cpp
floodfill/kis_fill_interval_map.cpp
floodfill/kis_scanline_fill.cpp
lazybrush/kis_min_cut_worker.cpp
lazybrush/kis_lazy_fill_tools.cpp
lazybrush/kis_multiway_cut.cpp
lazybrush/KisWatershedWorker.cpp
lazybrush/kis_colorize_mask.cpp
lazybrush/kis_colorize_stroke_strategy.cpp
KisDelayedUpdateNodeInterface.cpp
kis_adjustment_layer.cc
kis_selection_based_layer.cpp
kis_node_filter_interface.cpp
kis_base_accessor.cpp
kis_base_node.cpp
kis_base_processor.cpp
kis_bookmarked_configuration_manager.cc
kis_node_uuid_info.cpp
kis_clone_layer.cpp
kis_colorspace_convert_visitor.cpp
kis_config_widget.cpp
kis_convolution_kernel.cc
kis_convolution_painter.cc
kis_gaussian_kernel.cpp
kis_edge_detection_kernel.cpp
kis_cubic_curve.cpp
kis_default_bounds.cpp
kis_default_bounds_base.cpp
kis_effect_mask.cc
kis_fast_math.cpp
kis_fill_painter.cc
kis_filter_mask.cpp
kis_filter_strategy.cc
kis_transform_mask.cpp
kis_transform_mask_params_interface.cpp
kis_recalculate_transform_mask_job.cpp
kis_recalculate_generator_layer_job.cpp
kis_transform_mask_params_factory_registry.cpp
kis_safe_transform.cpp
kis_gradient_painter.cc
kis_gradient_shape_strategy.cpp
kis_cached_gradient_shape_strategy.cpp
kis_polygonal_gradient_shape_strategy.cpp
kis_iterator_ng.cpp
kis_async_merger.cpp
kis_merge_walker.cc
kis_updater_context.cpp
kis_update_job_item.cpp
kis_stroke_strategy_undo_command_based.cpp
kis_simple_stroke_strategy.cpp
KisRunnableBasedStrokeStrategy.cpp
KisRunnableStrokeJobDataBase.cpp
KisRunnableStrokeJobData.cpp
KisRunnableStrokeJobsInterface.cpp
KisFakeRunnableStrokeJobsExecutor.cpp
kis_stroke_job_strategy.cpp
kis_stroke_strategy.cpp
kis_stroke.cpp
kis_strokes_queue.cpp
KisStrokesQueueMutatedJobInterface.cpp
kis_simple_update_queue.cpp
kis_update_scheduler.cpp
kis_queues_progress_updater.cpp
kis_composite_progress_proxy.cpp
kis_sync_lod_cache_stroke_strategy.cpp
kis_lod_capable_layer_offset.cpp
kis_update_time_monitor.cpp
KisImageConfigNotifier.cpp
kis_group_layer.cc
kis_count_visitor.cpp
kis_histogram.cc
kis_image_interfaces.cpp
kis_image_animation_interface.cpp
kis_time_range.cpp
kis_node_graph_listener.cpp
kis_image.cc
kis_image_signal_router.cpp
KisImageSignals.cpp
kis_image_config.cpp
kis_projection_updates_filter.cpp
kis_suspend_projection_updates_stroke_strategy.cpp
kis_regenerate_frame_stroke_strategy.cpp
kis_switch_time_stroke_strategy.cpp
kis_crop_saved_extra_data.cpp
kis_timed_signal_threshold.cpp
kis_layer.cc
kis_indirect_painting_support.cpp
kis_abstract_projection_plane.cpp
kis_layer_projection_plane.cpp
kis_layer_utils.cpp
kis_mask_projection_plane.cpp
kis_projection_leaf.cpp
kis_mask.cc
kis_base_mask_generator.cpp
kis_rect_mask_generator.cpp
kis_circle_mask_generator.cpp
kis_gauss_circle_mask_generator.cpp
kis_gauss_rect_mask_generator.cpp
${__per_arch_circle_mask_generator_objs}
kis_curve_circle_mask_generator.cpp
kis_curve_rect_mask_generator.cpp
kis_math_toolbox.cpp
kis_memory_statistics_server.cpp
kis_name_server.cpp
kis_node.cpp
kis_node_facade.cpp
kis_node_progress_proxy.cpp
kis_busy_progress_indicator.cpp
kis_node_visitor.cpp
kis_paint_device.cc
kis_paint_device_debug_utils.cpp
kis_fixed_paint_device.cpp
KisOptimizedByteArray.cpp
kis_paint_layer.cc
kis_perspective_math.cpp
kis_pixel_selection.cpp
kis_processing_information.cpp
kis_properties_configuration.cc
kis_random_accessor_ng.cpp
kis_random_generator.cc
kis_random_sub_accessor.cpp
kis_wrapped_random_accessor.cpp
kis_selection.cc
KisSelectionUpdateCompressor.cpp
kis_selection_mask.cpp
kis_update_outline_job.cpp
kis_update_selection_job.cpp
kis_serializable_configuration.cc
kis_transaction_data.cpp
kis_transform_worker.cc
kis_perspectivetransform_worker.cpp
bsplines/kis_bspline_1d.cpp
bsplines/kis_bspline_2d.cpp
bsplines/kis_nu_bspline_2d.cpp
kis_warptransform_worker.cc
kis_cage_transform_worker.cpp
kis_liquify_transform_worker.cpp
kis_green_coordinates_math.cpp
kis_transparency_mask.cc
kis_undo_adapter.cpp
kis_macro_based_undo_store.cpp
kis_surrogate_undo_adapter.cpp
kis_legacy_undo_adapter.cpp
kis_post_execution_undo_adapter.cpp
kis_processing_visitor.cpp
kis_processing_applicator.cpp
krita_utils.cpp
kis_outline_generator.cpp
kis_layer_composition.cpp
kis_selection_filters.cpp
KisProofingConfiguration.h
kis_keyframe.cpp
kis_keyframe_channel.cpp
kis_keyframe_commands.cpp
kis_scalar_keyframe_channel.cpp
kis_raster_keyframe_channel.cpp
kis_onion_skin_compositor.cpp
kis_onion_skin_cache.cpp
kis_idle_watcher.cpp
kis_psd_layer_style.cpp
kis_layer_properties_icons.cpp
layerstyles/kis_multiple_projection.cpp
layerstyles/kis_layer_style_filter.cpp
layerstyles/kis_layer_style_filter_environment.cpp
layerstyles/kis_layer_style_filter_projection_plane.cpp
layerstyles/kis_layer_style_projection_plane.cpp
layerstyles/kis_ls_drop_shadow_filter.cpp
layerstyles/kis_ls_satin_filter.cpp
layerstyles/kis_ls_stroke_filter.cpp
layerstyles/kis_ls_bevel_emboss_filter.cpp
layerstyles/kis_ls_overlay_filter.cpp
layerstyles/kis_ls_utils.cpp
layerstyles/gimp_bump_map.cpp
KisProofingConfiguration.cpp
kis_node_query_path.cc
)
set(einspline_SRCS
3rdparty/einspline/bspline_create.cpp
3rdparty/einspline/bspline_data.cpp
3rdparty/einspline/multi_bspline_create.cpp
3rdparty/einspline/nubasis.cpp
3rdparty/einspline/nubspline_create.cpp
3rdparty/einspline/nugrid.cpp
)
add_library(kritaimage SHARED ${kritaimage_LIB_SRCS} ${einspline_SRCS})
generate_export_header(kritaimage BASE_NAME kritaimage)
target_link_libraries(kritaimage
PUBLIC
kritaversion
kritawidgets
kritaglobal
kritapsd
kritaodf
kritapigment
kritacommand
kritawidgetutils
kritametadata
Qt5::Concurrent
)
target_link_libraries(kritaimage PUBLIC ${Boost_SYSTEM_LIBRARY})
+if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB)
+ if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB)
+ target_link_libraries(kritaimage PUBLIC atomic)
+ endif()
+endif()
+
if(OPENEXR_FOUND)
target_link_libraries(kritaimage PUBLIC ${OPENEXR_LIBRARIES})
endif()
if(FFTW3_FOUND)
target_link_libraries(kritaimage PRIVATE ${FFTW3_LIBRARIES})
endif()
if(HAVE_VC)
target_link_libraries(kritaimage PUBLIC ${Vc_LIBRARIES})
endif()
if (NOT GSL_FOUND)
message (WARNING "KRITA WARNING! No GNU Scientific Library was found! Krita's Shaped Gradients might be non-normalized! Please install GSL library.")
else ()
target_link_libraries(kritaimage PRIVATE ${GSL_LIBRARIES} ${GSL_CBLAS_LIBRARIES})
endif ()
target_include_directories(kritaimage
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/brushengine>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/filter>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/generator>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/layerstyles>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/processing>
)
set_target_properties(kritaimage PROPERTIES
VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION}
)
install(TARGETS kritaimage ${INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/libs/image/brushengine/kis_paintop_factory.h b/libs/image/brushengine/kis_paintop_factory.h
index 98d8d8e13e..7c62545621 100644
--- a/libs/image/brushengine/kis_paintop_factory.h
+++ b/libs/image/brushengine/kis_paintop_factory.h
@@ -1,116 +1,118 @@
/*
* Copyright (c) 2008 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_PAINTOP_FACTORY_H_
#define KIS_PAINTOP_FACTORY_H_
#include "kis_types.h"
#include "kritaimage_export.h"
#include <QObject>
#include <QString>
#include <QIcon>
#include <QStringList>
#include <kis_threaded_text_rendering_workaround.h>
class KisPainter;
class KisPaintOp;
class QWidget;
class KisPaintOpConfigWidget;
/**
* The paintop factory is responsible for creating paintops of the specified class.
* If there is an optionWidget, the derived paintop itself must support settings,
* and it's up to the factory to do that.
*/
class KRITAIMAGE_EXPORT KisPaintOpFactory : public QObject
{
Q_OBJECT
public:
enum PaintopVisibility {
AUTO,
ALWAYS,
NEVER
};
/**
* @param whiteListedCompositeOps list of compositeops that don't work with this paintop
*/
KisPaintOpFactory(const QStringList & whiteListedCompositeOps = QStringList());
~KisPaintOpFactory() override {}
static QString categoryStable();
#ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND
virtual void preinitializePaintOpIfNeeded(const KisPaintOpSettingsSP settings);
#endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */
/**
* Create a KisPaintOp with the given settings and painter.
* @param settings the settings associated with the input device
* @param painter the painter used to draw
+ * @param node the node used to draw
+ * @param image the image used to draw
*/
virtual KisPaintOp * createOp(const KisPaintOpSettingsSP settings, KisPainter * painter, KisNodeSP node, KisImageSP image) = 0;
virtual QString id() const = 0;
virtual QString name() const = 0;
virtual QString category() const = 0;
/**
* List of usually hidden compositeops that are useful for this paintop.
*/
QStringList whiteListedCompositeOps() const;
/**
* @brief icon
* @return the icon to represent this paintop.
*/
virtual QIcon icon();
/**
* Create and return an settings object for this paintop.
*/
virtual KisPaintOpSettingsSP settings() = 0;
/**
* create a widget that can display paintop settings
*/
virtual KisPaintOpConfigWidget* createConfigWidget(QWidget* parent) = 0;
/**
* Set the priority of this paintop, as it is shown in the UI; lower number means
* it will be show more to the front of the list.
* @param newPriority the priority
*/
void setPriority(int newPriority);
int priority() const;
/**
* This method will be called by the registry after all paintops are loaded
* Overwrite to let the factory do something.
*/
virtual void processAfterLoading() {}
private:
QStringList m_whiteListedCompositeOps;
int m_priority;
PaintopVisibility m_visibility;
};
#endif
diff --git a/libs/image/commands/kis_image_command.h b/libs/image/commands/kis_image_command.h
index 0c8b0c04f1..e5f3779449 100644
--- a/libs/image/commands/kis_image_command.h
+++ b/libs/image/commands/kis_image_command.h
@@ -1,71 +1,72 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2007 Sven Langkamp <sven.langkamp@gmail.com>
*
* 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_IMAGE_COMMAND_H_
#define KIS_IMAGE_COMMAND_H_
#include <kritaimage_export.h>
#include <kundo2command.h>
#include <QSize>
#include <QRect>
#include "kis_types.h"
#include "kis_paint_device.h"
/// the base command for commands altering a KisImage
class KRITAIMAGE_EXPORT KisImageCommand : public KUndo2Command
{
public:
/**
* Constructor
* @param name The name that will be shown in the ui
* @param image The image the command will be working on.
+ * @param parent The parent command.
*/
KisImageCommand(const KUndo2MagicString& name, KisImageWSP image, KUndo2Command *parent = 0);
~KisImageCommand() override;
protected:
/**
* Used for performing the smallest update
* after a node has been removed from stack.
* First tries to setDirty() node's siblings.
* If it doesn't help, performs full refresh.
*/
class UpdateTarget
{
public:
UpdateTarget(KisImageWSP image, KisNodeSP removedNode, const QRect &updateRect);
void update();
private:
KisImageWSP m_image;
QRect m_updateRect;
int m_removedNodeIndex;
KisNodeSP m_removedNodeParent;
};
protected:
KisImageWSP m_image;
};
#endif // KIS_IMAGE_COMMAND_H_
diff --git a/libs/image/commands/kis_image_layer_add_command.h b/libs/image/commands/kis_image_layer_add_command.h
index aff0848dac..6444c33e8c 100644
--- a/libs/image/commands/kis_image_layer_add_command.h
+++ b/libs/image/commands/kis_image_layer_add_command.h
@@ -1,53 +1,57 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2007 Sven Langkamp <sven.langkamp@gmail.com>
*
* 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_IMAGE_LAYER_ADD_COMMAND_H_
#define KIS_IMAGE_LAYER_ADD_COMMAND_H_
#include <kritaimage_export.h>
#include "kis_types.h"
#include "kis_image_command.h"
/// The command for adding a layer
class KRITAIMAGE_EXPORT KisImageLayerAddCommand : public KisImageCommand
{
public:
/**
* Constructor
* @param image The image the command will be working on.
- * @param layer the layer to add
+ * @param layer The layer to add
+ * @param parent The parent node
+ * @param aboveThis The node above this
+ * @param doRedoUpdates Whether to make the redo updates
+ * @param doUndoUpdates Whether to make the undo updates
*/
KisImageLayerAddCommand(KisImageWSP image, KisNodeSP layer, KisNodeSP parent, KisNodeSP aboveThis, bool doRedoUpdates = true, bool doUndoUpdates = true);
KisImageLayerAddCommand(KisImageWSP image, KisNodeSP layer, KisNodeSP parent, quint32 index, bool doRedoUpdates = true, bool doUndoUpdates = true);
void redo() override;
void undo() override;
private:
KisNodeSP m_layer;
KisNodeSP m_parent;
KisNodeSP m_aboveThis;
quint32 m_index;
bool m_doRedoUpdates;
bool m_doUndoUpdates;
};
#endif
diff --git a/libs/image/commands/kis_image_layer_move_command.h b/libs/image/commands/kis_image_layer_move_command.h
index 4b98dd98e7..d89e572bd3 100644
--- a/libs/image/commands/kis_image_layer_move_command.h
+++ b/libs/image/commands/kis_image_layer_move_command.h
@@ -1,62 +1,63 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2007 Sven Langkamp <sven.langkamp@gmail.com>
*
* 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_IMAGE_LAYER_MOVE_COMMAND_H_
#define KIS_IMAGE_LAYER_MOVE_COMMAND_H_
#include <kritaimage_export.h>
#include <QSize>
#include <QBitArray>
#include "kis_types.h"
#include "kis_image_command.h"
/// The command for layer moves inside the layer stack
class KRITAIMAGE_EXPORT KisImageLayerMoveCommand : public KisImageCommand
{
public:
/**
* Command for layer moves inside the layer stack
*
* @param image the image
- * @param layer the moved layer
+ * @param node the moved node
* @param newParent the next parent of the layer
* @param newAbove the layer that will be below the layer after the move
+ * @param doUpdates whether to do updates
*/
KisImageLayerMoveCommand(KisImageWSP image, KisNodeSP node, KisNodeSP newParent, KisNodeSP newAbove, bool doUpdates = true);
KisImageLayerMoveCommand(KisImageWSP image, KisNodeSP node, KisNodeSP newParent, quint32 index);
void redo() override;
void undo() override;
private:
KisNodeSP m_layer;
KisNodeSP m_prevParent;
KisNodeSP m_prevAbove;
KisNodeSP m_newParent;
KisNodeSP m_newAbove;
quint32 m_index;
bool m_useIndex;
bool m_doUpdates;
};
#endif
diff --git a/libs/image/filter/kis_color_transformation_filter.h b/libs/image/filter/kis_color_transformation_filter.h
index 52b8eacbbb..d495c004cd 100644
--- a/libs/image/filter/kis_color_transformation_filter.h
+++ b/libs/image/filter/kis_color_transformation_filter.h
@@ -1,48 +1,48 @@
/*
* Copyright (c) 2009 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _KIS_COLOR_TRANSFORMATION_FILTER_H_
#define _KIS_COLOR_TRANSFORMATION_FILTER_H_
#include "kis_filter.h"
#include "kritaimage_export.h"
/**
* This is a base class for filters that implement a filter for
- * \ref KoColorTransformation based filters.
+ * KoColorTransformationFactory based filters.
*/
class KRITAIMAGE_EXPORT KisColorTransformationFilter : public KisFilter
{
public:
KisColorTransformationFilter(const KoID& id, const KoID & category, const QString & entry);
~KisColorTransformationFilter() override;
void processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const override;
/**
* Create the color transformation that will be applied on the device.
*/
virtual KoColorTransformation* createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const = 0;
KisFilterConfigurationSP factoryConfiguration() const override;
};
#endif
diff --git a/libs/image/filter/kis_filter.h b/libs/image/filter/kis_filter.h
index 10a6964064..3e8ed01dd6 100644
--- a/libs/image/filter/kis_filter.h
+++ b/libs/image/filter/kis_filter.h
@@ -1,126 +1,126 @@
/*
* Copyright (c) 2004,2006-2007 Cyrille Berger <cberger@cberger.net>
*
* 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_FILTER_H_
#define _KIS_FILTER_H_
#include <list>
#include <QString>
#include <klocalizedstring.h>
#include "KoID.h"
#include "KoColorSpace.h"
#include "kis_types.h"
#include "kis_base_processor.h"
#include "kritaimage_export.h"
/**
* Basic interface of a Krita filter.
*/
class KRITAIMAGE_EXPORT KisFilter : public KisBaseProcessor
{
public:
/**
* Construct a Krita filter
*/
KisFilter(const KoID& id, const KoID & category, const QString & entry);
~KisFilter() override;
/**
* Override this function with the implementation of your filter.
*
* This is a low level function that expects all the conditions
* for the @param device be met. Use usual process() methods
* instead.
*
* @param device the paint device to filter
* @param applyRect the rectangle where the filter is applied
* @param config the parameters of the filter
* @param progressUpdater to pass on the progress the filter is making
*/
virtual void processImpl(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater = 0 ) const = 0;
/**
* Filter \p src device and write the result into \p dst device.
* If \p dst is an alpha color space device, it will get special
* treatment.
*
* @param src the source paint device
* @param dst the destination paint device
* @param selection the selection
* @param applyRect the rectangle where the filter is applied
* @param config the parameters of the filter
* @param progressUpdater to pass on the progress the filter is making
*/
void process(const KisPaintDeviceSP src,
KisPaintDeviceSP dst,
KisSelectionSP selection,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater = 0 ) const;
/**
* A convenience method for a two-device process() function
*/
void process(KisPaintDeviceSP device,
const QRect& applyRect,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater = 0 ) const;
/**
* Some filters need pixels outside the current processing rect to compute the new
* value (for instance, convolution filters)
*/
virtual QRect neededRect(const QRect & rect, const KisFilterConfigurationSP config, int lod) const;
/**
- * Similar to \ref neededRect: some filters will alter a lot of pixels that are
+ * Similar to @ref neededRect : some filters will alter a lot of pixels that are
* near to each other at the same time. So when you changed a single rectangle
* in a device, the actual rectangle that will feel the influence of this change
* might be bigger. Use this function to determine that rect.
*/
virtual QRect changedRect(const QRect & rect, const KisFilterConfigurationSP config, int lod) const;
/**
* Returns true if the filter is capable of handling LoD scaled planes
* when generating preview.
*/
virtual bool supportsLevelOfDetail(const KisFilterConfigurationSP config, int lod) const;
virtual bool needsTransparentPixels(const KisFilterConfigurationSP config, const KoColorSpace *cs) const;
protected:
QString configEntryGroup() const;
void setSupportsLevelOfDetail(bool value);
private:
bool m_supportsLevelOfDetail;
};
#endif
diff --git a/libs/image/generator/kis_generator.h b/libs/image/generator/kis_generator.h
index 55c393abf5..7beda85a76 100644
--- a/libs/image/generator/kis_generator.h
+++ b/libs/image/generator/kis_generator.h
@@ -1,91 +1,92 @@
/*
* Copyright (c) 2008 Boudewijn Rempt <boud@valdyas.org>
*
* 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_GENERATOR_H_
#define _KIS_GENERATOR_H_
#include <QString>
#include <klocalizedstring.h>
#include "KoID.h"
#include "KoColorSpace.h"
#include "kis_types.h"
#include "kis_base_processor.h"
#include "kritaimage_export.h"
class KisProcessingInformation;
/**
* Basic interface of a Krita generator: a generator is a program
* that can fill a paint device with a color. A generator can have a
* preferred colorspace.
*
* Generators can have initial parameter settings that determine the
* way a particular generator works, but also state that allows the generator
* to continue from one invocation of generate to another (handy for, e.g.,
* painting)
*/
class KRITAIMAGE_EXPORT KisGenerator : public KisBaseProcessor
{
friend class KisGeneratorConfigurationFactory;
public:
KisGenerator(const KoID& id, const KoID & category, const QString & entry);
~KisGenerator() override;
public:
/**
* Override this function with the implementation of your generator.
*
* @param dst the destination paint device
* @param size the size of the area that is to be filled
* @param config the parameters of the filter
+ * @param progressUpdater the progress updater
*/
virtual void generate(KisProcessingInformation dst,
const QSize& size,
const KisFilterConfigurationSP config,
KoUpdater* progressUpdater
) const = 0;
/**
* Provided for convenience when no progress reporting is needed.
*/
virtual void generate(KisProcessingInformation dst,
const QSize& size,
const KisFilterConfigurationSP config
) const;
/**
* @param _imageArea the rectangle of the image
* @return the rectangle that is affected by this generator, if the generator
* is supposed to affect all pixels, then the function should return
* @p _imageArea
*/
virtual QRect generatedRect(QRect _imageArea, const KisFilterConfigurationSP = 0) const;
protected:
/// @return the name of config group in KConfig
QString configEntryGroup() const;
};
#endif
diff --git a/libs/image/kis_adjustment_layer.h b/libs/image/kis_adjustment_layer.h
index 10c2dd64ab..447f0cf422 100644
--- a/libs/image/kis_adjustment_layer.h
+++ b/libs/image/kis_adjustment_layer.h
@@ -1,123 +1,125 @@
/*
* Copyright (c) 2006 Boudewijn Rempt <boud@valdyas.org>
*
* 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.
*/
/**
- * @file
+ * @file kis_adjustment_layer.h
* This file is part of the Krita calligra application. It handles
* a contains a KisFilter OR a KisLayer, and this class is created
* to influence the rendering of layers below this one. Can also
* function as a fixating layer.
*
* @author Boudewijn Rempt
* @author comments by hscott
* @since 1.5
*/
#ifndef KIS_ADJUSTMENT_LAYER_H_
#define KIS_ADJUSTMENT_LAYER_H_
#include <QObject>
#include <kritaimage_export.h>
#include "kis_selection_based_layer.h"
class KisFilterConfiguration;
/**
- * @class KisAdjustmentLayer Contains a KisFilter and a KisSelection.
+ * @class KisAdjustmentLayer
+ * @brief Contains a KisFilter and a KisSelection.
+ *
* If the selection is present, it is a mask used by the adjustment layer
* to know where to apply the filter, thus the combination is used
* to influence the rendering of the layers under this layer
* in the layerstack. AdjustmentLayers also function as a kind
* of "fixating layers".
*/
class KRITAIMAGE_EXPORT KisAdjustmentLayer : public KisSelectionBasedLayer
{
Q_OBJECT
public:
/**
* creates a new adjustment layer with the given
* configuration and selection. Note that the selection
* will be _copied_ (with COW, though).
* @param image the image to set this AdjustmentLayer to
* @param name name of the adjustment layer
* @param kfc the configuration for the adjustment layer filter
* @param selection is a mask used by the adjustment layer to
* know where to apply the filter.
*/
KisAdjustmentLayer(KisImageWSP image, const QString &name, KisFilterConfigurationSP kfc, KisSelectionSP selection);
KisAdjustmentLayer(const KisAdjustmentLayer& rhs);
~KisAdjustmentLayer() override;
bool accept(KisNodeVisitor &) override;
void accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) override;
/**
* clones this AdjustmentLayer into a KisNodeSP type.
* @return the KisNodeSP returned
*/
KisNodeSP clone() const override {
return KisNodeSP(new KisAdjustmentLayer(*this));
}
/**
* gets the adjustmentLayer's tool filter
* @return QIcon returns the QIcon tool filter
*/
QIcon icon() const override;
/**
* gets the AdjustmentLayer properties describing whether
* or not the node is locked, visible, and the filter
* name is it is a filter. Overrides sectionModelProperties
* in KisLayer, and KisLayer overrides
* sectionModelProperties in KisBaseNode.
* @return KisBaseNode::PropertyList returns a list
* of the properties
*/
KisBaseNode::PropertyList sectionModelProperties() const override;
public:
/**
* \see KisNodeFilterInterface::setFilter()
*/
void setFilter(KisFilterConfigurationSP filterConfig) override;
void setChannelFlags(const QBitArray & channelFlags) override;
protected:
// override from KisLayer
QRect incomingChangeRect(const QRect &rect) const override;
// override from KisNode
QRect needRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const override;
public Q_SLOTS:
/**
* gets this AdjustmentLayer. Overrides function in
* KisIndirectPaintingSupport
* @return this AdjustmentLayer
*/
KisLayer* layer() {
return this;
}
};
#endif // KIS_ADJUSTMENT_LAYER_H_
diff --git a/libs/image/kis_annotation.h b/libs/image/kis_annotation.h
index e106124211..b091236190 100644
--- a/libs/image/kis_annotation.h
+++ b/libs/image/kis_annotation.h
@@ -1,126 +1,128 @@
/*
* This file is part of the KDE project
*
* Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
*
* 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.
*/
/**
- * @file this file is part of the Krita application in calligra
+ * @file kis_annotation.h
+ * @brief This file is part of the Krita application in calligra
* @author Boudewijn Rempt
* @author comments by hscott
* @since 1.4 or 2005
*/
#ifndef _KIS_ANNOTATION_H_
#define _KIS_ANNOTATION_H_
#include <kis_shared.h>
#include <QByteArray>
#include <QString>
/**
- * @class KisAnnotation A data extension mechanism for Krita.
+ * @class KisAnnotation
+ * @brief A data extension mechanism for Krita.
*
* An annotation can be of something like a QByteArray or a QString or
* a more specific datatype that can be attached to an image (or maybe
* later, if needed, to a layer) and contains data that must be
* associated with an image for purposes of import/export.
*
* Annotations will be saved to krita images and may be exported in
* filetypes that support them.
*
* Examples of annotations are EXIF data and ICC profiles.
*/
class KisAnnotation : public KisShared
{
public:
/**
* creates a new annotation object. The annotation object cannot
* be changed later.
*
* @param type a non-localized string identifying the type of the
* annotation
* @param description a localized string describing the annotation
* @param data a binary blob containing the annotation data
*/
KisAnnotation(const QString & type, const QString & description, const QByteArray & data)
: m_type(type),
m_description(description),
m_annotation(data) {}
virtual ~KisAnnotation() {}
virtual KisAnnotation* clone() const {
return new KisAnnotation(*this);
}
/**
* gets a non-localized string identifying the type of the
* annotation.
* @return a non-localized string identifiying the type of the
* annotation
*/
const QString & type() const {
return m_type;
}
/**
* gets a localized string describing the type of annotations for
* used interface purposes.
* @return a localized string describing the type of the
* annotations for user interface purposes.
*/
const QString & description() const {
return m_description;
}
/**
* gets a binary blob representation of this annotation
* @return a binary blob representation of this annotation
*/
const QByteArray & annotation() const {
return m_annotation;
}
/**
* @brief displayText: override this to return an interpreted version of the annotation
*/
virtual QString displayText() const {
return QString::fromUtf8(m_annotation);
}
protected:
KisAnnotation(const KisAnnotation &rhs)
: KisShared(),
m_type(rhs.m_type),
m_description(rhs.m_description),
m_annotation(rhs.m_annotation)
{
}
protected:
QString m_type;
QString m_description;
QByteArray m_annotation;
};
#endif // _KIS_ANNOTATION_H_
diff --git a/libs/image/kis_base_mask_generator.h b/libs/image/kis_base_mask_generator.h
index 8df0dfe275..c79aca2d42 100644
--- a/libs/image/kis_base_mask_generator.h
+++ b/libs/image/kis_base_mask_generator.h
@@ -1,130 +1,134 @@
/*
* Copyright (c) 2008-2009 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
*
* 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_MASK_GENERATOR_H_
#define _KIS_MASK_GENERATOR_H_
#include <QScopedPointer>
#include <compositeops/KoVcMultiArchBuildSupport.h> //MSVC requires that Vc come first
#include <KoID.h>
#include <klocalizedstring.h>
#include "kritaimage_export.h"
class QDomElement;
class QDomDocument;
class KisBrushMaskApplicatorBase;
const KoID DefaultId("default", ki18n("Default")); ///< generate Krita default mask generator
const KoID SoftId("soft", ki18n("Soft")); ///< generate brush mask from former softbrush paintop, where softness is based on curve
const KoID GaussId("gauss", ki18n("Gaussian")); ///< generate brush mask with a Gaussian-blurred edge
static const int OVERSAMPLING = 4;
/**
* This is the base class for mask shapes
* You should subclass it if you want to create a new
* shape.
*/
class KRITAIMAGE_EXPORT KisMaskGenerator
{
public:
enum Type {
CIRCLE, RECTANGLE
};
public:
/**
- * This function creates an auto brush shape with the following value :
- * @param w width
- * @param h height
- * @param fh horizontal fade (fh \< w / 2 )
- * @param fv vertical fade (fv \< h / 2 )
+ * This function creates an auto brush shape with the following values:
+ * @param radius radius
+ * @param ratio aspect ratio
+ * @param fh horizontal fade
+ * @param fv vertical fade
+ * @param spikes number of spikes
+ * @param antialiasEdges whether to antialias edges
+ * @param type type
+ * @param id the brush identifier
*/
KisMaskGenerator(qreal radius, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges, Type type, const KoID& id = DefaultId);
KisMaskGenerator(const KisMaskGenerator &rhs);
virtual ~KisMaskGenerator();
virtual KisMaskGenerator* clone() const = 0;
private:
void init();
public:
/**
* @return the alpha value at the position (x,y)
*/
virtual quint8 valueAt(qreal x, qreal y) const = 0;
virtual bool shouldSupersample() const;
virtual bool shouldVectorize() const;
virtual KisBrushMaskApplicatorBase* applicator();
virtual void toXML(QDomDocument& , QDomElement&) const;
/**
* Unserialise a \ref KisMaskGenerator
*/
static KisMaskGenerator* fromXML(const QDomElement&);
qreal width() const;
qreal height() const;
qreal diameter() const;
void setDiameter(qreal value);
qreal ratio() const;
qreal horizontalFade() const;
qreal verticalFade() const;
int spikes() const;
Type type() const;
bool isEmpty() const;
void fixRotation(qreal &xr, qreal &yr) const;
inline QString id() const { return m_id.id(); }
inline QString name() const { return m_id.name(); }
static QList<KoID> maskGeneratorIds();
qreal softness() const;
virtual void setSoftness(qreal softness);
QString curveString() const;
void setCurveString(const QString& curveString);
bool antialiasEdges() const;
virtual void setScale(qreal scaleX, qreal scaleY);
protected:
qreal effectiveSrcWidth() const;
qreal effectiveSrcHeight() const;
private:
struct Private;
const QScopedPointer<Private> d;
const KoID& m_id;
};
#endif
diff --git a/libs/image/kis_cage_transform_worker.cpp b/libs/image/kis_cage_transform_worker.cpp
index 97b1209738..4cabe71a71 100644
--- a/libs/image/kis_cage_transform_worker.cpp
+++ b/libs/image/kis_cage_transform_worker.cpp
@@ -1,449 +1,447 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_cage_transform_worker.h"
#include "kis_grid_interpolation_tools.h"
#include "kis_green_coordinates_math.h"
#include <QPainter>
#include "KoColor.h"
#include "kis_selection.h"
#include "kis_painter.h"
#include "kis_image.h"
#include "krita_utils.h"
#include <qnumeric.h>
struct Q_DECL_HIDDEN KisCageTransformWorker::Private
{
Private(KisPaintDeviceSP _dev,
const QVector<QPointF> &_origCage,
KoUpdater *_progress,
int _pixelPrecision)
: dev(_dev),
origCage(_origCage),
progress(_progress),
pixelPrecision(_pixelPrecision)
{
}
KisPaintDeviceSP dev;
QImage srcImage;
QPointF srcImageOffset;
QVector<QPointF> origCage;
QVector<QPointF> transfCage;
KoUpdater *progress;
int pixelPrecision;
QVector<int> allToValidPointsMap;
QVector<QPointF> validPoints;
/**
* Contains all points fo the grid including non-defined
* points (the ones which are placed outside the cage).
*/
QVector<QPointF> allSrcPoints;
KisGreenCoordinatesMath cage;
QSize gridSize;
bool isGridEmpty() const {
return allSrcPoints.isEmpty();
}
QVector<QPointF> calculateTransformedPoints();
inline QVector<int> calculateMappedIndexes(int col, int row,
int *numExistingPoints);
int tryGetValidIndex(const QPoint &cellPt);
struct MapIndexesOp;
};
KisCageTransformWorker::KisCageTransformWorker(KisPaintDeviceSP dev,
const QVector<QPointF> &origCage,
KoUpdater *progress,
int pixelPrecision)
: m_d(new Private(dev, origCage, progress, pixelPrecision))
{
}
KisCageTransformWorker::KisCageTransformWorker(const QImage &srcImage,
const QPointF &srcImageOffset,
const QVector<QPointF> &origCage,
KoUpdater *progress,
int pixelPrecision)
: m_d(new Private(0, origCage, progress, pixelPrecision))
{
m_d->srcImage = srcImage;
m_d->srcImageOffset = srcImageOffset;
}
KisCageTransformWorker::~KisCageTransformWorker()
{
}
void KisCageTransformWorker::setTransformedCage(const QVector<QPointF> &transformedCage)
{
m_d->transfCage = transformedCage;
}
struct PointsFetcherOp
{
PointsFetcherOp(const QPolygonF &cagePolygon)
: m_cagePolygon(cagePolygon),
m_numValidPoints(0)
{
m_polygonDirection = KisAlgebra2D::polygonDirection(cagePolygon);
}
inline void processPoint(int col, int row,
int prevCol, int prevRow,
int colIndex, int rowIndex) {
Q_UNUSED(prevCol);
Q_UNUSED(prevRow);
Q_UNUSED(colIndex);
Q_UNUSED(rowIndex);
QPointF pt(col, row);
if (m_cagePolygon.containsPoint(pt, Qt::OddEvenFill)) {
KisAlgebra2D::adjustIfOnPolygonBoundary(m_cagePolygon, m_polygonDirection, &pt);
m_points << pt;
m_pointValid << true;
m_numValidPoints++;
} else {
m_points << pt;
m_pointValid << false;
}
}
inline void nextLine() {
}
QVector<bool> m_pointValid;
QVector<QPointF> m_points;
QPolygonF m_cagePolygon;
int m_polygonDirection;
int m_numValidPoints;
};
void KisCageTransformWorker::prepareTransform()
{
if (m_d->origCage.size() < 3) return;
const QPolygonF srcPolygon(m_d->origCage);
QRect srcBounds = m_d->dev ? m_d->dev->region().boundingRect() :
QRectF(m_d->srcImageOffset, m_d->srcImage.size()).toAlignedRect();
srcBounds &= srcPolygon.boundingRect().toAlignedRect();
// no need to process empty devices
if (srcBounds.isEmpty()) return;
-
m_d->gridSize =
GridIterationTools::calcGridSize(srcBounds, m_d->pixelPrecision);
PointsFetcherOp pointsOp(srcPolygon);
GridIterationTools::processGrid(pointsOp, srcBounds, m_d->pixelPrecision);
const int numPoints = pointsOp.m_points.size();
-
KIS_ASSERT_RECOVER_RETURN(numPoints == m_d->gridSize.width() * m_d->gridSize.height());
m_d->allSrcPoints = pointsOp.m_points;
m_d->allToValidPointsMap.resize(pointsOp.m_points.size());
m_d->validPoints.resize(pointsOp.m_numValidPoints);
{
int validIdx = 0;
for (int i = 0; i < numPoints; i++) {
const QPointF &pt = pointsOp.m_points[i];
const bool pointValid = pointsOp.m_pointValid[i];
if (pointValid) {
m_d->validPoints[validIdx] = pt;
m_d->allToValidPointsMap[i] = validIdx;
validIdx++;
} else {
m_d->allToValidPointsMap[i] = -1;
}
}
KIS_ASSERT_RECOVER_NOOP(validIdx == m_d->validPoints.size());
}
m_d->cage.precalculateGreenCoordinates(m_d->origCage, m_d->validPoints);
}
QVector<QPointF> KisCageTransformWorker::Private::calculateTransformedPoints()
{
cage.generateTransformedCageNormals(transfCage);
const int numValidPoints = validPoints.size();
QVector<QPointF> transformedPoints(numValidPoints);
for (int i = 0; i < numValidPoints; i++) {
transformedPoints[i] = cage.transformedPoint(i, transfCage);
if (qIsNaN(transformedPoints[i].x()) ||
qIsNaN(transformedPoints[i].y())) {
warnKrita << "WARNING: One grid point has been removed from consideration" << validPoints[i];
transformedPoints[i] = validPoints[i];
}
}
return transformedPoints;
}
inline QVector<int> KisCageTransformWorker::Private::
calculateMappedIndexes(int col, int row,
int *numExistingPoints)
{
*numExistingPoints = 0;
QVector<int> cellIndexes =
GridIterationTools::calculateCellIndexes(col, row, gridSize);
for (int i = 0; i < 4; i++) {
cellIndexes[i] = allToValidPointsMap[cellIndexes[i]];
*numExistingPoints += cellIndexes[i] >= 0;
}
return cellIndexes;
}
int KisCageTransformWorker::Private::
tryGetValidIndex(const QPoint &cellPt)
{
int index = -1;
if (cellPt.x() >= 0 &&
cellPt.y() >= 0 &&
cellPt.x() < gridSize.width() - 1 &&
cellPt.y() < gridSize.height() - 1) {
index = allToValidPointsMap[GridIterationTools::pointToIndex(cellPt, gridSize)];
}
return index;
}
struct KisCageTransformWorker::Private::MapIndexesOp {
MapIndexesOp(KisCageTransformWorker::Private *d)
: m_d(d),
m_srcCagePolygon(QPolygonF(m_d->origCage))
{
}
inline QVector<int> calculateMappedIndexes(int col, int row,
int *numExistingPoints) const {
return m_d->calculateMappedIndexes(col, row, numExistingPoints);
}
inline int tryGetValidIndex(const QPoint &cellPt) const {
return m_d->tryGetValidIndex(cellPt);
}
inline QPointF getSrcPointForce(const QPoint &cellPt) const {
return m_d->allSrcPoints[GridIterationTools::pointToIndex(cellPt, m_d->gridSize)];
}
inline const QPolygonF srcCropPolygon() const {
return m_srcCagePolygon;
}
KisCageTransformWorker::Private *m_d;
QPolygonF m_srcCagePolygon;
};
QRect KisCageTransformWorker::approxChangeRect(const QRect &rc)
{
const qreal margin = 0.30;
QVector<QPointF> cageSamplePoints;
const int minStep = 3;
const int maxSamples = 200;
const int totalPixels = rc.width() * rc.height();
const int realStep = qMax(minStep, totalPixels / maxSamples);
const QPolygonF cagePolygon(m_d->origCage);
for (int i = 0; i < totalPixels; i += realStep) {
const int x = rc.x() + i % rc.width();
const int y = rc.y() + i / rc.width();
const QPointF pt(x, y);
if (cagePolygon.containsPoint(pt, Qt::OddEvenFill)) {
cageSamplePoints << pt;
}
}
if (cageSamplePoints.isEmpty()) {
return rc;
}
KisGreenCoordinatesMath cage;
cage.precalculateGreenCoordinates(m_d->origCage, cageSamplePoints);
cage.generateTransformedCageNormals(m_d->transfCage);
const int numValidPoints = cageSamplePoints.size();
QVector<QPointF> transformedPoints(numValidPoints);
int failedPoints = 0;
for (int i = 0; i < numValidPoints; i++) {
transformedPoints[i] = cage.transformedPoint(i, m_d->transfCage);
if (qIsNaN(transformedPoints[i].x()) ||
qIsNaN(transformedPoints[i].y())) {
transformedPoints[i] = cageSamplePoints[i];
failedPoints++;
}
}
QRect resultRect =
KisAlgebra2D::approximateRectFromPoints(transformedPoints).toAlignedRect();
return KisAlgebra2D::blowRect(resultRect | rc, margin);
}
QRect KisCageTransformWorker::approxNeedRect(const QRect &rc, const QRect &fullBounds)
{
Q_UNUSED(rc);
return fullBounds;
}
void KisCageTransformWorker::run()
{
if (m_d->isGridEmpty()) return;
KIS_ASSERT_RECOVER_RETURN(m_d->origCage.size() >= 3);
KIS_ASSERT_RECOVER_RETURN(m_d->origCage.size() == m_d->transfCage.size());
QVector<QPointF> transformedPoints = m_d->calculateTransformedPoints();
KisPaintDeviceSP srcDev = new KisPaintDevice(*m_d->dev.data());
KisPaintDeviceSP tempDevice = new KisPaintDevice(m_d->dev->colorSpace());
{
KisSelectionSP selection = new KisSelection();
KisPainter painter(selection->pixelSelection());
painter.setPaintColor(KoColor(Qt::black, selection->pixelSelection()->colorSpace()));
painter.setAntiAliasPolygonFill(true);
painter.setFillStyle(KisPainter::FillStyleForegroundColor);
painter.setStrokeStyle(KisPainter::StrokeStyleNone);
painter.paintPolygon(m_d->origCage);
m_d->dev->clearSelection(selection);
}
GridIterationTools::PaintDevicePolygonOp polygonOp(srcDev, tempDevice);
Private::MapIndexesOp indexesOp(m_d.data());
GridIterationTools::iterateThroughGrid
<GridIterationTools::IncompletePolygonPolicy>(polygonOp, indexesOp,
m_d->gridSize,
m_d->validPoints,
transformedPoints);
QRect rect = tempDevice->extent();
KisPainter gc(m_d->dev);
gc.bitBlt(rect.topLeft(), tempDevice, rect);
}
QImage KisCageTransformWorker::runOnQImage(QPointF *newOffset)
{
if (m_d->isGridEmpty()) return QImage();
KIS_ASSERT_RECOVER(m_d->origCage.size() >= 3 &&
m_d->origCage.size() == m_d->transfCage.size()) {
return QImage();
}
KIS_ASSERT_RECOVER(!m_d->srcImage.isNull()) {
return QImage();
}
KIS_ASSERT_RECOVER(m_d->srcImage.format() == QImage::Format_ARGB32) {
return QImage();
}
QVector<QPointF> transformedPoints = m_d->calculateTransformedPoints();
QRectF dstBounds;
Q_FOREACH (const QPointF &pt, transformedPoints) {
KisAlgebra2D::accumulateBounds(pt, &dstBounds);
}
const QRectF srcBounds(m_d->srcImageOffset, m_d->srcImage.size());
dstBounds |= srcBounds;
QPointF dstQImageOffset = dstBounds.topLeft();
*newOffset = dstQImageOffset;
QRect dstBoundsI = dstBounds.toAlignedRect();
QImage dstImage(dstBoundsI.size(), m_d->srcImage.format());
dstImage.fill(0);
QImage tempImage(dstImage);
{
// we shouldn't create too many painters
QPainter gc(&dstImage);
gc.drawImage(-dstQImageOffset + m_d->srcImageOffset, m_d->srcImage);
gc.setBrush(Qt::black);
gc.setPen(Qt::black);
gc.setCompositionMode(QPainter::CompositionMode_Clear);
gc.drawPolygon(QPolygonF(m_d->origCage).translated(-dstQImageOffset));
gc.end();
}
GridIterationTools::QImagePolygonOp polygonOp(m_d->srcImage, tempImage, m_d->srcImageOffset, dstQImageOffset);
Private::MapIndexesOp indexesOp(m_d.data());
GridIterationTools::iterateThroughGrid
<GridIterationTools::IncompletePolygonPolicy>(polygonOp, indexesOp,
m_d->gridSize,
m_d->validPoints,
transformedPoints);
{
QPainter gc(&dstImage);
gc.drawImage(QPoint(), tempImage);
}
return dstImage;
}
diff --git a/libs/image/kis_datamanager.h b/libs/image/kis_datamanager.h
index 3b45d115e8..dcf74d37ba 100644
--- a/libs/image/kis_datamanager.h
+++ b/libs/image/kis_datamanager.h
@@ -1,349 +1,357 @@
/*
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
*
* 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_DATAMANAGER_H_
#define KIS_DATAMANAGER_H_
#include <QtGlobal>
class QRect;
class KisPaintDeviceWriter;
class QIODevice;
#include <tiles3/kis_tiled_data_manager.h>
#include <tiles3/kis_memento.h>
#define ACTUAL_DATAMGR KisTiledDataManager
/**
* KisDataManager defines the interface that modules responsible for
* storing and retrieving data must inmplement. Data modules, like
* the tile manager, are responsible for:
*
* * Storing undo/redo data
* * Offering ordered and unordered iterators over rects of pixels
* * (eventually) efficiently loading and saving data in a format
* that may allow deferred loading.
*
* A datamanager knows nothing about the type of pixel data except
* how many quint8's a single pixel takes.
*/
class KisDataManager : public ACTUAL_DATAMGR
{
public:
/**
* Create a new datamanger where every pixel will take pixelSize bytes and will be initialized
* by default with defPixel. The value of defPixel is copied, the caller still owns the pointer.
*
* Note that if pixelSize > size of the defPixel array, we will happily read beyond the
* defPixel array.
*/
KisDataManager(quint32 pixelSize, const quint8 *defPixel) : ACTUAL_DATAMGR(pixelSize, defPixel) {}
KisDataManager(const KisDataManager& dm) : ACTUAL_DATAMGR(dm) { }
~KisDataManager() override {
}
public:
/**
* Sets the default pixel. New data will be initialised with this pixel. The pixel is copied: the
* caller still owns the pointer.
*/
inline void setDefaultPixel(const quint8 *defPixel) {
return ACTUAL_DATAMGR::setDefaultPixel(defPixel);
}
/**
* Get a pointer to the default pixel.
*/
inline const quint8 *defaultPixel() const {
return ACTUAL_DATAMGR::defaultPixel();
}
/**
* Reguests a memento from the data manager. There is only one memento active
* at any given moment for a given paint device and all and any
* write actions on the datamanger builds undo data into this memento
* necessary to rollback the transaction.
*/
inline KisMementoSP getMemento() {
return ACTUAL_DATAMGR::getMemento();
}
/**
* Restores the image data to the state at the time of the getMemento() call.
*
* Note that rollback should be performed with mementos in the reverse order of
* their creation, as mementos only store incremental changes
*/
inline void rollback(KisMementoSP memento) {
ACTUAL_DATAMGR::rollback(memento);
}
/**
* Restores the image data to the state at the time of the rollback call of the memento.
*
* Note that rollforward must only be called when an rollback have previously been performed, and
* no intermittent actions have been performed (though it's ok to rollback other mementos and
* roll them forward again)
*/
inline void rollforward(KisMementoSP memento) {
ACTUAL_DATAMGR::rollforward(memento);
}
/**
* @returns true if there is a memento active. This means that
* iterators can rely on the oldData() function.
*/
inline bool hasCurrentMemento() const {
return ACTUAL_DATAMGR::hasCurrentMemento();
}
public:
/**
* Reads and writes the tiles
*
*/
inline bool write(KisPaintDeviceWriter &writer) {
return ACTUAL_DATAMGR::write(writer);
}
inline bool read(QIODevice *io) {
return ACTUAL_DATAMGR::read(io);
}
inline void purge(const QRect& area) {
ACTUAL_DATAMGR::purge(area);
}
/**
* The tiles may be not allocated directly from the glibc, but
* instead can be allocated in bigger blobs. After you freed quite
* a lot of data and are sure you won't need it anymore, you can
* release these pools to save the memory.
*/
static inline void releaseInternalPools() {
ACTUAL_DATAMGR::releaseInternalPools();
}
public:
/**
* Returns the number of bytes a pixel takes
*/
inline quint32 pixelSize() const {
return ACTUAL_DATAMGR::pixelSize();
}
/**
* Return the extent of the data in x,y,w,h.
*/
inline void extent(qint32 &x, qint32 &y, qint32 &w, qint32 &h) const {
return ACTUAL_DATAMGR::extent(x, y, w, h);
}
QRect extent() const {
return ACTUAL_DATAMGR::extent();
}
QRegion region() const {
return ACTUAL_DATAMGR::region();
}
public:
/**
* Crop or extend the data to x, y, w, h.
*/
inline void setExtent(qint32 x, qint32 y, qint32 w, qint32 h) {
return ACTUAL_DATAMGR::setExtent(x, y, w, h);
}
inline void setExtent(const QRect & rect) {
setExtent(rect.x(), rect.y(), rect.width(), rect.height());
}
public:
/**
* Clear the specified rect to the specified value.
*/
inline void clear(qint32 x, qint32 y,
qint32 w, qint32 h,
quint8 def) {
ACTUAL_DATAMGR::clear(x, y, w, h, def);
}
/**
* Clear the specified rect to the specified pixel value.
*/
inline void clear(qint32 x, qint32 y,
qint32 w, qint32 h,
const quint8 * def) {
ACTUAL_DATAMGR::clear(x, y, w, h, def);
}
/**
* Clear all back to default values.
*/
inline void clear() {
ACTUAL_DATAMGR::clear();
}
public:
/**
* Clones rect from another datamanager. The cloned area will be
* shared between both datamanagers as much as possible using
* copy-on-write. Parts of the rect that cannot be shared
* (cross tiles) are deep-copied,
*/
inline void bitBlt(KisTiledDataManagerSP srcDM, const QRect &rect) {
ACTUAL_DATAMGR::bitBlt(const_cast<KisTiledDataManager*>(srcDM.data()), rect);
}
/**
* The same as \ref bitBlt() but reads old data
*/
inline void bitBltOldData(KisTiledDataManagerSP srcDM, const QRect &rect) {
ACTUAL_DATAMGR::bitBltOldData(const_cast<KisTiledDataManager*>(srcDM.data()), rect);
}
/**
* Clones rect from another datamanager in a rough and fast way.
* All the tiles touched by rect will be shared, between both
* devices, that means it will copy a bigger area than was
* requested. This method is supposed to be used for bitBlt'ing
* into temporary paint devices.
*/
inline void bitBltRough(KisTiledDataManagerSP srcDM, const QRect &rect) {
ACTUAL_DATAMGR::bitBltRough(const_cast<KisTiledDataManager*>(srcDM.data()), rect);
}
/**
* The same as \ref bitBltRough() but reads old data
*/
inline void bitBltRoughOldData(KisTiledDataManagerSP srcDM, const QRect &rect) {
ACTUAL_DATAMGR::bitBltRoughOldData(const_cast<KisTiledDataManager*>(srcDM.data()), rect);
}
public:
/**
* Write the specified data to x, y. There is no checking on pixelSize!
*/
inline void setPixel(qint32 x, qint32 y, const quint8 * data) {
ACTUAL_DATAMGR::setPixel(x, y, data);
}
/**
* Copy the bytes in the specified rect to a chunk of memory.
* The pixelSize in bytes is w * h * pixelSize
*/
inline void readBytes(quint8 * data,
qint32 x, qint32 y,
qint32 w, qint32 h,
qint32 dataRowStride = -1) const {
ACTUAL_DATAMGR::readBytes(data, x, y, w, h, dataRowStride);
}
/**
* Copy the bytes to the specified rect. w * h * pixelSize bytes
* will be read, whether the caller prepared them or not.
*/
inline void writeBytes(const quint8 * data,
qint32 x, qint32 y,
qint32 w, qint32 h,
qint32 dataRowStride = -1) {
ACTUAL_DATAMGR::writeBytes(data, x, y, w, h, dataRowStride);
}
/**
* Copy the bytes in the paint device into a vector of arrays of bytes,
* where the number of arrays is the number of channels in the
* paint device. If the specified area is larger than the paint
* device's extent, the default pixel will be read.
*
- * @param channelsize a vector with for every channel its size in bytes
+ * @param channelsizes a vector with for every channel its size in bytes
+ * @param x x coordinate of the top left corner
+ * @param y y coordinate of the top left corner
+ * @param w width
+ * @param h height
*/
QVector<quint8*> readPlanarBytes(QVector<qint32> channelsizes, qint32 x, qint32 y, qint32 w, qint32 h) const {
return ACTUAL_DATAMGR::readPlanarBytes(channelsizes, x, y, w, h);
}
/**
* Write the data in the separate arrays to the channels. If there
* are less vectors than channels, the remaining channels will not
* be copied. If any of the arrays points to 0, the channel in
* that location will not be touched. If the specified area is
* larger than the paint device, the paint device will be
* extended. There are no guards: if the area covers more pixels
* than there are bytes in the arrays, krita will happily fill
* your paint device with areas of memory you never wanted to be
* read. Krita may also crash.
*
* @param planes a vector with a byte array for every plane
- * @param channelsize a vector with for every channel its size in
+ * @param channelsizes a vector with for every channel its size in
* bytes
+ * @param x x coordinate of the top left corner
+ * @param y y coordinate of the top left corner
+ * @param w width
+ * @param h height
*
* XXX: what about undo?
*/
void writePlanarBytes(QVector<quint8*> planes, QVector<qint32> channelsizes, qint32 x, qint32 y, qint32 w, qint32 h) {
ACTUAL_DATAMGR::writePlanarBytes(planes, channelsizes, x, y, w, h);
}
/**
* Get the number of contiguous columns starting at x, valid for all values
* of y between minY and maxY.
*/
inline qint32 numContiguousColumns(qint32 x, qint32 minY, qint32 maxY) const {
return ACTUAL_DATAMGR::numContiguousColumns(x, minY, maxY);
}
/**
* Get the number of contiguous rows starting at y, valid for all
* values of x between minX and maxX.
*/
inline qint32 numContiguousRows(qint32 y, qint32 minX, qint32 maxX) const {
return ACTUAL_DATAMGR::numContiguousRows(y, minX, maxX);
}
/**
* Get the row stride at pixel (x, y). This is the number of bytes
* to add to a pointer to pixel (x, y) to access (x, y + 1).
*/
inline qint32 rowStride(qint32 x, qint32 y) const {
return ACTUAL_DATAMGR::rowStride(x, y);
}
protected:
friend class KisRectIterator;
friend class KisHLineIterator;
friend class KisVLineIterator;
};
#endif // KIS_DATAMANAGER_H_
diff --git a/libs/image/kis_edge_detection_kernel.h b/libs/image/kis_edge_detection_kernel.h
index 2d8b5d67f0..f5c4d6b7d2 100644
--- a/libs/image/kis_edge_detection_kernel.h
+++ b/libs/image/kis_edge_detection_kernel.h
@@ -1,129 +1,131 @@
/*
* Copyright (c) 2017 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
*
* 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_EDGE_DETECTION_KERNEL_H
#define KIS_EDGE_DETECTION_KERNEL_H
#include "kritaimage_export.h"
#include "kis_types.h"
#include <Eigen/Core>
class QRect;
class KRITAIMAGE_EXPORT KisEdgeDetectionKernel
{
public:
KisEdgeDetectionKernel();
enum FilterType {
Simple, //A weird simple method used in our old sobel filter
Prewit, //The simpler prewitt detection, which doesn't smooth.
SobelVector //Sobel does smooth. The creation of bigger kernels is based on an approach regarding vectors.
};
enum FilterOutput {
pythagorean,
xGrowth,
xFall,
yGrowth,
yFall,
radian
};
/**
* @brief createHorizontalMatrix
* @param radius the radius. 1 makes a 3x3 kernel.
* @param type One of the entries in the enum Filtertype
* @param reverse which direction the gradient goes.
* The horizontal gradient by default detects the rightmost edges.
* Reversed it selects the leftmost edges.
* @return
*/
static Eigen::Matrix<qreal, Eigen::Dynamic, Eigen::Dynamic>
createHorizontalMatrix(qreal radius, FilterType type, bool reverse = false);
/**
* @brief createVerticalMatrix
* @param radius the radius. 1 makes a 3x3 kernel.
* @param type One of the entries in the enum Filtertype
* @param reverse which direction the gradient goes.
* The vertical gradient by default detects the topmost edges.
* Reversed it selects the bottommost edges.
* @return
*/
static Eigen::Matrix<qreal, Eigen::Dynamic, Eigen::Dynamic>
createVerticalMatrix(qreal radius, FilterType type, bool reverse = false);
static KisConvolutionKernelSP
createHorizontalKernel(qreal radius, FilterType type, bool denormalize = true, bool reverse = false);
static KisConvolutionKernelSP
createVerticalKernel(qreal radius, FilterType type, bool denormalize = true, bool reverse = false);
static int kernelSizeFromRadius(qreal radius);
static qreal sigmaFromRadius(qreal radius);
/**
* @brief applyEdgeDetection
* This applies the edge detection filter to the device.
* @param device the device to apply to.
* @param rect the affected rect.
* @param xRadius the radius of the horizontal sampling, radius of 0 is effectively disabling it.
* @param yRadius the radius of the vertical sampling, refius of 0 is effectively disabling it.
* @param type the type can be prewitt, sobel or simple, each of which
* have a different sampling for the eventual edge detection.
* @param channelFlags the affected channels.
* @param progressUpdater the progress updater if it exists.
+ * @param output the output mode.
* @param writeToAlpha whether or not to have the result applied to the transparency than the color channels,
* this is useful for fringe effects.
*/
static void applyEdgeDetection(KisPaintDeviceSP device,
const QRect& rect,
qreal xRadius, qreal yRadius,
FilterType type,
const QBitArray &channelFlags,
KoUpdater *progressUpdater,
FilterOutput output = pythagorean,
bool writeToAlpha = false);
/**
* @brief converToNormalMap
* Convert a channel of the device to a normal map. The channel will be interpreted as a heightmap.
* @param device the device
* @param rect the rectangle to apply this to.
* @param xRadius the xradius
* @param yRadius the yradius
* @param type the edge detection filter.
* @param channelToConvert the channel to use as a grayscale.
* @param channelOrder the order in which the xyz coordinates ought to be written to the pixels.
- * @param channelFlags
+ * @param channelFlip whether to flip the channels
+ * @param channelFlags the channel flags
* @param progressUpdater
*/
static void convertToNormalMap(KisPaintDeviceSP device,
const QRect & rect,
qreal xRadius,
qreal yRadius,
FilterType type,
int channelToConvert,
QVector<int> channelOrder,
QVector<bool> channelFlip,
const QBitArray &channelFlags,
KoUpdater *progressUpdater);
};
#endif // KIS_EDGE_DETECTION_KERNEL_H
diff --git a/libs/image/kis_fill_painter.h b/libs/image/kis_fill_painter.h
index 4f1648a60e..79107704c3 100644
--- a/libs/image/kis_fill_painter.h
+++ b/libs/image/kis_fill_painter.h
@@ -1,281 +1,281 @@
/*
* Copyright (c) 2004 Adrian Page <adrian@pagenet.plus.com>
* Copyright (c) 2004 Bart Coppens <kde@bartcoppens.be>
*
* 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_FILL_PAINTER_H_
#define KIS_FILL_PAINTER_H_
#include <QRect>
#include "KoColor.h"
#include "KoColorSpaceRegistry.h"
#include "kis_painter.h"
#include "kis_types.h"
#include "kis_selection.h"
#include <kritaimage_export.h>
class KoPattern;
class KisFilterConfiguration;
// XXX: Filling should set dirty rect.
/**
* This painter can be used to fill paint devices in different ways. This can also be used
* for flood filling related operations.
*/
class KRITAIMAGE_EXPORT KisFillPainter : public KisPainter
{
public:
/**
* Construct an empty painter. Use the begin(KisPaintDeviceSP) method to attach
* to a paint device
*/
KisFillPainter();
/**
* Start painting on the specified paint device
*/
KisFillPainter(KisPaintDeviceSP device);
KisFillPainter(KisPaintDeviceSP device, KisSelectionSP selection);
private:
void initFillPainter();
public:
/**
* Fill a rectangle with black transparent pixels (0, 0, 0, 0 for RGBA).
*/
void eraseRect(qint32 x1, qint32 y1, qint32 w, qint32 h);
/**
* Overloaded version of the above function.
*/
void eraseRect(const QRect& rc);
/**
* Fill current selection of KisPainter with a specified \p color.
*
* The filling rect is limited by \p rc to allow multithreaded
* filling/processing.
*/
void fillSelection(const QRect &rc, const KoColor &color);
/**
* Fill a rectangle with a certain color.
*/
void fillRect(qint32 x, qint32 y, qint32 w, qint32 h, const KoColor& c);
/**
* Overloaded version of the above function.
*/
void fillRect(const QRect& rc, const KoColor& c);
/**
* Fill a rectangle with a certain color and opacity.
*/
void fillRect(qint32 x, qint32 y, qint32 w, qint32 h, const KoColor& c, quint8 opacity);
/**
* Overloaded version of the above function.
*/
void fillRect(const QRect& rc, const KoColor& c, quint8 opacity);
/**
* Fill a rectangle with a certain pattern. The pattern is repeated if it does not fit the
* entire rectangle.
*/
void fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KoPattern * pattern, const QPoint &offset = QPoint());
/**
* Fill a rectangle with a certain pattern. The pattern is repeated if it does not fit the
* entire rectangle.
*/
void fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KisPaintDeviceSP device, const QRect& deviceRect);
/**
* Overloaded version of the above function.
*/
void fillRect(const QRect& rc, const KoPattern * pattern, const QPoint &offset = QPoint());
/**
* Fill the specified area with the output of the generator plugin that is configured
* in the generator parameter
*/
void fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KisFilterConfigurationSP generator);
/**
* Fills the enclosed area around the point with the set color. If
* there is a selection, the whole selection is filled. Note that
* you must have set the width and height on the painter if you
* don't have a selection.
*
* @param startX the X position where the floodfill starts
* @param startY the Y position where the floodfill starts
* @param sourceDevice the sourceDevice that determines the area that
* is floodfilled if sampleMerged is on
*/
void fillColor(int startX, int startY, KisPaintDeviceSP sourceDevice);
/**
* Fills the enclosed area around the point with the set pattern.
* If there is a selection, the whole selection is filled. Note
* that you must have set the width and height on the painter if
* you don't have a selection.
*
* @param startX the X position where the floodfill starts
* @param startY the Y position where the floodfill starts
* @param sourceDevice the sourceDevice that determines the area that
* is floodfilled if sampleMerged is on
*/
void fillPattern(int startX, int startY, KisPaintDeviceSP sourceDevice);
/**
* Returns a selection mask for the floodfill starting at the specified position.
*
* @param startX the X position where the floodfill starts
* @param startY the Y position where the floodfill starts
* @param sourceDevice the sourceDevice that determines the area that
* is floodfilled if sampleMerged is on
*/
KisSelectionSP createFloodSelection(int startX, int startY, KisPaintDeviceSP sourceDevice);
/**
* Set the threshold for floodfill. The range is 0-255: 0 means the fill will only
* fill parts that are the exact same color, 255 means anything will be filled
*/
void setFillThreshold(int threshold);
/** Returns the fill threshold, see setFillThreshold for details */
int fillThreshold() const {
return m_threshold;
}
bool useCompositioning() const {
return m_useCompositioning;
}
void setUseCompositioning(bool useCompositioning) {
m_useCompositioning = useCompositioning;
}
/** Sets the width of the paint device */
void setWidth(int w) {
m_width = w;
}
/** Sets the height of the paint device */
void setHeight(int h) {
m_height = h;
}
/** If true, floodfill doesn't fill outside the selected area of a layer */
bool careForSelection() const {
return m_careForSelection;
}
/** Set caring for selection. See careForSelection for details */
void setCareForSelection(bool set) {
m_careForSelection = set;
}
/** Sets the auto growth/shrinking radius */
void setSizemod(int sizemod) {
m_sizemod = sizemod;
}
- /** Sets how much to auto-grow or shrink (if @param sizemod is negative) the selection
+ /** Sets how much to auto-grow or shrink (if @p sizemod is negative) the selection
flood before painting, this affects every fill operation except fillRect */
int sizemod() {
return m_sizemod;
}
/** Sets feathering radius */
void setFeather(int feather) {
m_feather = feather;
}
/** defines the feathering radius for selection flood operations, this affects every
fill operation except fillRect */
uint feather() {
return m_feather;
}
private:
// for floodfill
void genericFillStart(int startX, int startY, KisPaintDeviceSP sourceDevice);
void genericFillEnd(KisPaintDeviceSP filled);
KisSelectionSP m_fillSelection;
int m_feather;
int m_sizemod;
int m_threshold;
int m_width, m_height;
QRect m_rect;
bool m_careForSelection;
bool m_useCompositioning;
};
inline
void KisFillPainter::fillRect(qint32 x, qint32 y, qint32 w, qint32 h, const KoColor& c)
{
fillRect(x, y, w, h, c, OPACITY_OPAQUE_U8);
}
inline
void KisFillPainter::fillRect(const QRect& rc, const KoColor& c)
{
fillRect(rc.x(), rc.y(), rc.width(), rc.height(), c, OPACITY_OPAQUE_U8);
}
inline
void KisFillPainter::eraseRect(qint32 x1, qint32 y1, qint32 w, qint32 h)
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KoColor c(Qt::black, cs);
fillRect(x1, y1, w, h, c, OPACITY_TRANSPARENT_U8);
}
inline
void KisFillPainter::eraseRect(const QRect& rc)
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KoColor c(Qt::black, cs);
fillRect(rc.x(), rc.y(), rc.width(), rc.height(), c, OPACITY_TRANSPARENT_U8);
}
inline
void KisFillPainter::fillRect(const QRect& rc, const KoColor& c, quint8 opacity)
{
fillRect(rc.x(), rc.y(), rc.width(), rc.height(), c, opacity);
}
inline
void KisFillPainter::setFillThreshold(int threshold)
{
m_threshold = threshold;
}
#endif //KIS_FILL_PAINTER_H_
diff --git a/libs/image/kis_filter_strategy.h b/libs/image/kis_filter_strategy.h
index 8c09af6797..1a037e2d50 100644
--- a/libs/image/kis_filter_strategy.h
+++ b/libs/image/kis_filter_strategy.h
@@ -1,206 +1,200 @@
/*
* Copyright (c) 2004 Michael Thaler <michael.thaler@physik.tu-muenchen.de>
* Copyright (c) 2005 C. Boemann <cbo@boemann.dk>
* Copyright (c) 2013 Juan Palacios <jpalaciosdev@gmail.com>
*
* 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_FILTER_STRATEGY_H_
#define KIS_FILTER_STRATEGY_H_
#include <klocalizedstring.h>
#include "KoGenericRegistry.h"
#include "KoID.h"
#include "kritaimage_export.h"
class KRITAIMAGE_EXPORT KisFilterStrategy
{
public:
KisFilterStrategy(KoID id) : m_id(id) {}
virtual ~KisFilterStrategy() { }
QString id() {
return m_id.id();
}
QString name() {
return m_id.name();
}
virtual qreal valueAt(qreal /*t*/) const {
return 0;
}
virtual qint32 intValueAt(qint32 t) const {
return qint32(255*valueAt(t / 256.0));
}
qreal support() {
return supportVal;
}
qint32 intSupport() {
return intSupportVal;
}
- virtual bool boxSpecial() {
- return false;
- }
virtual QString description() {
return QString();
}
protected:
qreal supportVal;
qint32 intSupportVal;
KoID m_id;
};
class KRITAIMAGE_EXPORT KisHermiteFilterStrategy : public KisFilterStrategy
{
public:
KisHermiteFilterStrategy() : KisFilterStrategy(KoID("Hermite", i18n("Hermite"))) {
supportVal = 1.0; intSupportVal = 256;
}
~KisHermiteFilterStrategy() override {}
qint32 intValueAt(qint32 t) const override;
qreal valueAt(qreal t) const override;
};
class KRITAIMAGE_EXPORT KisBicubicFilterStrategy : public KisFilterStrategy
{
public:
KisBicubicFilterStrategy() : KisFilterStrategy(KoID("Bicubic", i18n("Bicubic"))) {
supportVal = 2.0; intSupportVal = 512;
}
~KisBicubicFilterStrategy() override {}
QString description() override {
return i18n("Adds pixels using the color of surrounding pixels. Produces smoother tonal gradations than Bilinear.");
}
qint32 intValueAt(qint32 t) const override;
};
class KRITAIMAGE_EXPORT KisBoxFilterStrategy : public KisFilterStrategy
{
public:
KisBoxFilterStrategy() : KisFilterStrategy(KoID("NearestNeighbor", i18n("Nearest Neighbor"))) {
supportVal = 0.5; intSupportVal = 128;
}
~KisBoxFilterStrategy() override {}
QString description() override {
return i18n("Replicate pixels in the image. Preserves all the original detail, but can produce jagged effects.");
}
qint32 intValueAt(qint32 t) const override;
qreal valueAt(qreal t) const override;
- bool boxSpecial() override {
- return true;
- }
};
class KRITAIMAGE_EXPORT KisBilinearFilterStrategy : public KisFilterStrategy
{
public:
KisBilinearFilterStrategy() : KisFilterStrategy(KoID("Bilinear", i18n("Bilinear"))) {
supportVal = 1.0; intSupportVal = 256;
}
~KisBilinearFilterStrategy() override {}
QString description() override {
return i18n("Adds pixels averaging the color values of surrounding pixels. Produces medium quality results when the image is scaled from half to two times the original size.");
}
qint32 intValueAt(qint32 t) const override;
qreal valueAt(qreal t) const override;
};
class KRITAIMAGE_EXPORT KisBellFilterStrategy : public KisFilterStrategy
{
public:
KisBellFilterStrategy() : KisFilterStrategy(KoID("Bell", i18n("Bell"))) {
supportVal = 1.5; intSupportVal = 128 + 256;
}
~KisBellFilterStrategy() override {}
qreal valueAt(qreal t) const override;
};
class KRITAIMAGE_EXPORT KisBSplineFilterStrategy : public KisFilterStrategy
{
public:
KisBSplineFilterStrategy() : KisFilterStrategy(KoID("BSpline", i18n("BSpline"))) {
supportVal = 2.0; intSupportVal = 512;
}
~KisBSplineFilterStrategy() override {}
qreal valueAt(qreal t) const override;
};
class KRITAIMAGE_EXPORT KisLanczos3FilterStrategy : public KisFilterStrategy
{
public:
KisLanczos3FilterStrategy() : KisFilterStrategy(KoID("Lanczos3", i18n("Lanczos3"))) {
supportVal = 3.0; intSupportVal = 768;
}
~KisLanczos3FilterStrategy() override {}
QString description() override {
return i18n("Offers similar results than Bicubic, but maybe a little bit sharper. Can produce light and dark halos along strong edges.");
}
qreal valueAt(qreal t) const override;
private:
qreal sinc(qreal x) const;
};
class KRITAIMAGE_EXPORT KisMitchellFilterStrategy : public KisFilterStrategy
{
public:
KisMitchellFilterStrategy() : KisFilterStrategy(KoID("Mitchell", i18n("Mitchell"))) {
supportVal = 2.0; intSupportVal = 256;
}
~KisMitchellFilterStrategy() override {}
qreal valueAt(qreal t) const override;
};
class KRITAIMAGE_EXPORT KisFilterStrategyRegistry : public KoGenericRegistry<KisFilterStrategy *>
{
public:
KisFilterStrategyRegistry();
~KisFilterStrategyRegistry() override;
static KisFilterStrategyRegistry* instance();
/**
* This function return a list of all the keys in KoID format by using the name() method
* on the objects stored in the registry.
*/
QList<KoID> listKeys() const;
/**
* This function return a string formatted in HTML that contains the descriptions of all objects
* (with a non empty description) stored in the registry.
*/
QString formattedDescriptions() const;
private:
KisFilterStrategyRegistry(const KisFilterStrategyRegistry&);
KisFilterStrategyRegistry operator=(const KisFilterStrategyRegistry&);
};
#endif // KIS_FILTER_STRATEGY_H_
diff --git a/libs/image/kis_filter_weights_applicator.h b/libs/image/kis_filter_weights_applicator.h
index 1e8d8c9a18..772d770279 100644
--- a/libs/image/kis_filter_weights_applicator.h
+++ b/libs/image/kis_filter_weights_applicator.h
@@ -1,332 +1,332 @@
/*
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_FILTER_WEIGHTS_APPLICATOR_H
#define __KIS_FILTER_WEIGHTS_APPLICATOR_H
#include "kis_fixed_point_maths.h"
#include "kis_filter_weights_buffer.h"
#include "kis_iterator_ng.h"
#include <KoColorSpace.h>
#include <KoMixColorsOp.h>
namespace tmp {
template <class iter> iter createIterator(KisPaintDeviceSP dev, qint32 start, qint32 lineNum, qint32 len);
template <> KisHLineIteratorSP createIterator <KisHLineIteratorSP>
(KisPaintDeviceSP dev, qint32 start, qint32 lineNum, qint32 len)
{
return dev->createHLineIteratorNG(start, lineNum, len);
}
template <> KisVLineIteratorSP createIterator <KisVLineIteratorSP>
(KisPaintDeviceSP dev, qint32 start, qint32 lineNum, qint32 len)
{
return dev->createVLineIteratorNG(lineNum, start, len);
}
}
/**
* \class KisFilterWeightsApplicator
*
* This is a main class for transforming a line of pixel data. It
* transforms lines from \p src into \p dst using \p scale, \p shear
* and offset (\p dx) parameters.
*
* Notation:
- * <pixel_name>_l -- leftmost border of the pixel
- * <pixel_name>_c -- center of the pixel
+ * \<pixel_name\>_l -- leftmost border of the pixel
+ * \<pixel_name\>_c -- center of the pixel
*
*
* Example calculation of an offset (see calculateBlendSpan()):
* scale = 0.5;
- * offset = <very small value>
+ * offset = \<very small value\>
*
* +------ dst_l
* |
* | +-- dst_c
* | |
*
* dst: | * | * | * |
*
* src: | * | * | * | * | * | * | * | * |
*
* |||
* ||+--- next_c_in_src
* |||
* |+---- dst_c_in_src
* |||
* |++--- offset (near zero, measured in dst coordinates)
* |
* +-- _l position of the pixel, which is considered
- * cetral in the weights buffer
+ * central in the weights buffer
*
* Another example calculation of an offset (see calculateBlendSpan()):
* scale = 0.5;
- * offset = <high value near 0.5>
+ * offset = \<high value near 0.5\>
*
* +------ dst_l
* |
* | +-- dst_c
* | |
*
* dst: | * | * | * |
*
* src: | * | * | * | * | * | * | * | * |
*
* || |
* || +--- next_c_in_src
* || |
* +------ dst_c_in_src
* || |
* +|-+--- offset (near 0.5, measured in dst coordinates)
* |
* +-- _l position of the pixel, which is considered
- * cetral in the weights buffer
+ * central in the weights buffer
*/
class KisFilterWeightsApplicator
{
public:
KisFilterWeightsApplicator(KisPaintDeviceSP src,
KisPaintDeviceSP dst,
qreal realScale, qreal shear,
qreal dx,
bool clampToEdge)
: m_src(src),
m_dst(dst),
m_realScale(realScale),
m_shear(shear),
m_dx(dx),
m_clampToEdge(clampToEdge)
{
}
struct BlendSpan {
KisFilterWeightsBuffer::FilterWeights *weights;
int firstBlendPixel; // in src coords
KisFixedPoint offset;
KisFixedPoint offsetInc;
};
inline BlendSpan calculateBlendSpan(int dst_l, int line, KisFilterWeightsBuffer *buffer) const {
KisFixedPoint dst_c = l_to_c(dst_l);
KisFixedPoint dst_c_in_src = dstToSrc(dst_c.toFloat(), line);
KisFixedPoint next_c_in_src = (dst_c_in_src - qreal(0.5)).toIntCeil() + qreal(0.5);
BlendSpan span;
span.offset = (next_c_in_src - dst_c_in_src) * buffer->weightsPositionScale();
span.offsetInc = buffer->weightsPositionScale();
Q_ASSERT(span.offset <= span.offsetInc);
span.weights = buffer->weights(span.offset);
span.firstBlendPixel = next_c_in_src.toIntFloor() - span.weights->centerIndex;
return span;
}
class LinePos {
public:
LinePos()
: m_start(0), m_size(0)
{
}
LinePos(int start, int size)
: m_start(start), m_size(size)
{
}
inline int start() const {
return m_start;
}
/**
* WARNING: be careful! This is not the same as
* QRect::right()! This is an equivalent of (QRect::right() +
* QRect::width()) or QRectF::right(), that is it points to
* the pixel after(!) the actual last pixel. See Qt docs for
* more info about this historical difference.
*/
inline int end() const {
return m_start + m_size;
}
inline int size() const {
return m_size;
}
inline void unite(const LinePos &rhs) {
if (m_size > 0) {
int newStart = qMin(start(), rhs.start());
int newEnd = qMax(end(), rhs.end());
m_start = newStart;
m_size = newEnd - newStart;
} else {
m_start = rhs.start();
m_size = rhs.size();
}
}
private:
int m_start;
int m_size;
};
template <class T>
LinePos processLine(LinePos srcLine, int line, KisFilterWeightsBuffer *buffer, qreal filterSupport) {
int dstStart;
int dstEnd;
int leftSrcBorder;
int rightSrcBorder;
if (m_realScale >= 0) {
dstStart = findAntialiasedDstStart(srcLine.start(), filterSupport, line);
dstEnd = findAntialiasedDstEnd(srcLine.end(), filterSupport, line);
leftSrcBorder = getLeftSrcNeedBorder(dstStart, line, buffer);
rightSrcBorder = getRightSrcNeedBorder(dstEnd - 1, line, buffer);
}
else {
dstStart = findAntialiasedDstStart(srcLine.end(), filterSupport, line);
dstEnd = findAntialiasedDstEnd(srcLine.start(), filterSupport, line);
leftSrcBorder = getLeftSrcNeedBorder(dstEnd - 1, line, buffer);
rightSrcBorder = getRightSrcNeedBorder(dstStart, line, buffer);
}
if (dstStart >= dstEnd) return LinePos(dstStart, 0);
if (leftSrcBorder >= rightSrcBorder) return LinePos(dstStart, 0);
if (leftSrcBorder > srcLine.start()) return LinePos(dstStart, 0);
if (srcLine.end() > rightSrcBorder) return LinePos(dstStart, 9);
int pixelSize = m_src->pixelSize();
KoMixColorsOp *mixOp = m_src->colorSpace()->mixColorsOp();
const KoColor defaultPixelObject = m_src->defaultPixel();
const quint8 *defaultPixel = defaultPixelObject.data();
const quint8 *borderPixel = defaultPixel;
quint8 *srcLineBuf = new quint8[pixelSize * (rightSrcBorder - leftSrcBorder)];
int i = leftSrcBorder;
quint8 *bufPtr = srcLineBuf;
T srcIt = tmp::createIterator<T>(m_src, srcLine.start(), line, srcLine.size());
if (m_clampToEdge) {
borderPixel = srcIt->rawData();
}
for (; i < srcLine.start(); i++, bufPtr+=pixelSize) {
memcpy(bufPtr, borderPixel, pixelSize);
}
for (; i < srcLine.end(); i++, bufPtr+=pixelSize) {
quint8 *data = srcIt->rawData();
memcpy(bufPtr, data, pixelSize);
memcpy(data, defaultPixel, pixelSize);
srcIt->nextPixel();
}
if (m_clampToEdge) {
borderPixel = bufPtr - pixelSize;
}
for (; i < rightSrcBorder; i++, bufPtr+=pixelSize) {
memcpy(bufPtr, borderPixel, pixelSize);
}
const quint8 **colors = new const quint8* [buffer->maxSpan()];
T dstIt = tmp::createIterator<T>(m_dst, dstStart, line, dstEnd - dstStart);
for (int i = dstStart; i < dstEnd; i++) {
BlendSpan span = calculateBlendSpan(i, line, buffer);
int bufIndexStart = span.firstBlendPixel - leftSrcBorder;
int bufIndexEnd = bufIndexStart + span.weights->span;
const quint8 **colorsPtr = colors;
for (int j = bufIndexStart; j < bufIndexEnd; j++) {
*(colorsPtr++) = srcLineBuf + j * pixelSize;
}
mixOp->mixColors(colors, span.weights->weight, span.weights->span, dstIt->rawData());
dstIt->nextPixel();
}
delete[] colors;
delete[] srcLineBuf;
return LinePos(dstStart, qMax(0, dstEnd - dstStart));
}
private:
int findAntialiasedDstStart(int src_l, qreal support, int line) {
qreal dst = srcToDst(src_l, line);
return !m_clampToEdge ? floor(dst - support) : floor(dst);
}
int findAntialiasedDstEnd(int src_l, qreal support, int line) {
qreal dst = srcToDst(src_l, line);
return !m_clampToEdge ? ceil(dst + support) : ceil(dst);
}
int getLeftSrcNeedBorder(int dst_l, int line, KisFilterWeightsBuffer *buffer) {
BlendSpan span = calculateBlendSpan(dst_l, line, buffer);
return span.firstBlendPixel;
}
int getRightSrcNeedBorder(int dst_l, int line, KisFilterWeightsBuffer *buffer) {
BlendSpan span = calculateBlendSpan(dst_l, line, buffer);
return span.firstBlendPixel + span.weights->span;
}
inline KisFixedPoint l_to_c(KisFixedPoint pixel_l) const {
return pixel_l + KisFixedPoint(qreal(0.5));
}
inline KisFixedPoint c_to_l(KisFixedPoint pixel_c) const {
return pixel_c - KisFixedPoint(qreal(0.5));
}
inline qreal srcToDst(qreal src, int line) const {
return src * m_realScale + m_dx + line * m_shear;
}
inline qreal dstToSrc(qreal dst, int line) const {
return (dst - m_dx - line * m_shear) / m_realScale;
}
private:
KisPaintDeviceSP m_src;
KisPaintDeviceSP m_dst;
qreal m_realScale;
qreal m_shear;
qreal m_dx;
bool m_clampToEdge;
};
#endif /* __KIS_FILTER_WEIGHTS_APPLICATOR_H */
diff --git a/libs/image/kis_fixed_paint_device.h b/libs/image/kis_fixed_paint_device.h
index 392cbb5a1d..c77dbc5ae2 100644
--- a/libs/image/kis_fixed_paint_device.h
+++ b/libs/image/kis_fixed_paint_device.h
@@ -1,208 +1,213 @@
/*
* Copyright (c) 2009 Boudewijn Rempt <boud@valdyas.org>
*
* 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_FIXED_PAINT_DEVICE_H
#define KIS_FIXED_PAINT_DEVICE_H
#include <kritaimage_export.h>
#include <KoColorSpace.h>
#include "kis_shared.h"
#include <kis_shared_ptr.h>
#include <QRect>
#include <QImage>
#include "KisOptimizedByteArray.h"
class KoColor;
/**
* A fixed paint device is a simple paint device that consists of an array
* of bytes and a rectangle. It cannot grow, it cannot shrink, all you can
* do is fill the paint device with the right bytes and use it as an argument
* to KisPainter or use the bytes as an argument to KoColorSpace functions.
*/
class KRITAIMAGE_EXPORT KisFixedPaintDevice : public KisShared
{
public:
KisFixedPaintDevice(const KoColorSpace* colorSpace,
KisOptimizedByteArray::MemoryAllocatorSP allocator = KisOptimizedByteArray::MemoryAllocatorSP());
virtual ~KisFixedPaintDevice();
/**
* Deep copy the fixed paint device, including the data.
*/
KisFixedPaintDevice(const KisFixedPaintDevice& rhs);
/**
* Deep copy the fixed paint device, including the data.
*/
KisFixedPaintDevice& operator=(const KisFixedPaintDevice& rhs);
/**
* setRect sets the rect of the fixed paint device to rect.
* This will _not_ create the associated data area.
*
- * @rect the bounds in pixels. The x,y of the rect represent the origin
+ * @param rc the bounds in pixels. The x,y of the rect represent the origin
* of the fixed paint device.
*/
void setRect(const QRect& rc);
/**
* @return the rect that the data represents
*/
QRect bounds() const;
/**
* @return the amount of allocated pixels (you can fake the size with setRect/bounds)
* It is useful to know the accumulated memory size in pixels (not in bytes) for optimizations to avoid re-allocation.
*/
int allocatedPixels() const;
/**
* @return the pixelSize associated with this fixed paint device.
*/
quint32 pixelSize() const;
const KoColorSpace* colorSpace() const {
return m_colorSpace;
}
/**
* initializes the paint device.
*
* @param defaultValue the default byte with which all pixels will be filled.
* @return false if the allocation failed.
*/
bool initialize(quint8 defaultValue = 0);
/**
* Changed the size of the internal buffer to accommodate the exact number of bytes
* needed to store area bounds(). The allocated data is *not* initialized!
*/
void reallocateBufferWithoutInitialization();
/**
* If the size of the internal buffer is smaller than the one needed to accommodate
* bounds(), resize the buffer. Otherwise, do nothing. The allocated data is neither
* copying or initialized!
*/
void lazyGrowBufferWithoutInitialization();
/**
* @return a pointer to the beginning of the data associated with this fixed paint device.
*/
quint8* data();
const quint8* constData() const;
quint8* data() const;
/**
* Read the bytes representing the rectangle described by x, y, w, h into
* data. If data is not big enough, Krita will gladly overwrite the rest
* of your precious memory.
*
* Since this is a copy, you need to make sure you have enough memory.
*
* The reading is done only if the rectangular area x,y,w,h is inside the bounds of the device
* and the device is not empty
*/
void readBytes(quint8 * dstData, qint32 x, qint32 y, qint32 w, qint32 h) const;
/**
* Converts the paint device to a different colorspace
*/
void convertTo(const KoColorSpace * dstColorSpace = 0,
KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::internalConversionFlags());
/**
* Set color profile for the device without converting actual pixel data
*/
void setProfile(const KoColorProfile *profile);
/**
* Fill this paint device with the data from image
*
+ * @param image the image
* @param srcProfileName name of the RGB profile to interpret the image as. 0 is interpreted as sRGB
*/
virtual void convertFromQImage(const QImage& image, const QString &srcProfileName);
/**
* Create an RGBA QImage from a rectangle in the paint device.
*
+ * @param dstProfile RGB profile to use in conversion. May be 0, in which
+ * case it's up to the color strategy to choose a profile (most
+ * like sRGB).
* @param x Left coordinate of the rectangle
* @param y Top coordinate of the rectangle
* @param w Width of the rectangle in pixels
* @param h Height of the rectangle in pixels
- * @param dstProfile RGB profile to use in conversion. May be 0, in which
- * case it's up to the color strategy to choose a profile (most
- * like sRGB).
+ * @param renderingIntent Rendering intent
+ * @param conversionFlags Conversion flags
*/
virtual QImage convertToQImage(const KoColorProfile *dstProfile, qint32 x, qint32 y, qint32 w, qint32 h,
KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::internalConversionFlags()) const;
/**
* Create an RGBA QImage from a rectangle in the paint device. The
* rectangle is defined by the parent image's bounds.
*
* @param dstProfile RGB profile to use in conversion. May be 0, in which
* case it's up to the color strategy to choose a profile (most
* like sRGB).
+ * @param renderingIntent The rendering intent of conversion.
+ * @param conversionFlags The conversion flags.
*/
virtual QImage convertToQImage(const KoColorProfile *dstProfile,
KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::internalConversionFlags()) const;
/**
* Clear the given rectangle to transparent black.
*
* XXX: this will not (yet) expand the paint device to contain the specified rect
* but if the paintdevice has not been initialized, it will be.
*/
void clear(const QRect & rc);
/**
* Fill the given rectangle with the given pixel. This does not take the
* selection into account.
*
* XXX: this will not (yet) expand the paint device to contain the specified rect
* but if the paintdevice has not been initialized, it will be.
*/
void fill(qint32 x, qint32 y, qint32 w, qint32 h, const quint8 *fillPixel);
void fill(const QRect &rc, const KoColor &color);
/**
* Mirrors the device.
*/
void mirror(bool horizontal, bool vertical);
private:
const KoColorSpace* m_colorSpace;
QRect m_bounds;
KisOptimizedByteArray m_data;
};
#endif
diff --git a/libs/image/kis_image.h b/libs/image/kis_image.h
index 20225814b5..e9055ef6e3 100644
--- a/libs/image/kis_image.h
+++ b/libs/image/kis_image.h
@@ -1,1123 +1,1127 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2007 Boudewijn Rempt <boud@valdyas.org>
*
* 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_IMAGE_H_
#define KIS_IMAGE_H_
#include <QObject>
#include <QString>
#include <QPainter>
#include <QRect>
#include <QRegion>
#include <QBitArray>
#include <KoColorConversionTransformation.h>
#include "kis_paint_device.h" // msvc cannot handle forward declarations, so include kis_paint_device here
#include "kis_types.h"
#include "kis_shared.h"
#include "kis_node_graph_listener.h"
#include "kis_node_facade.h"
#include "kis_image_interfaces.h"
#include "kis_strokes_queue_undo_result.h"
#include <kritaimage_export.h>
-class KisDocument;
class KoColorSpace;
class KoColor;
class KisCompositeProgressProxy;
class KisUndoStore;
class KisUndoAdapter;
class KisImageSignalRouter;
class KisPostExecutionUndoAdapter;
class KisFilterStrategy;
class KoColorProfile;
class KisLayerComposition;
class KisSpontaneousJob;
class KisImageAnimationInterface;
class KUndo2MagicString;
class KisProofingConfiguration;
namespace KisMetaData
{
class MergeStrategy;
}
/**
* This is the image class, it contains a tree of KisLayer stack and
* meta information about the image. And it also provides some
* functions to manipulate the whole image.
*/
class KRITAIMAGE_EXPORT KisImage : public QObject,
public KisStrokesFacade,
public KisStrokeUndoFacade,
public KisUpdatesFacade,
public KisProjectionUpdateListener,
public KisNodeFacade,
public KisNodeGraphListener,
public KisShared
{
Q_OBJECT
public:
- /// @param colorSpace can be null. in that case it will be initialised to a default color space.
+ /// @p colorSpace can be null. In that case, it will be initialised to a default color space.
KisImage(KisUndoStore *undoStore, qint32 width, qint32 height, const KoColorSpace *colorSpace, const QString& name);
~KisImage() override;
public: // KisNodeGraphListener implementation
void aboutToAddANode(KisNode *parent, int index) override;
void nodeHasBeenAdded(KisNode *parent, int index) override;
void aboutToRemoveANode(KisNode *parent, int index) override;
void nodeChanged(KisNode * node) override;
void invalidateAllFrames() override;
void notifySelectionChanged() override;
void requestProjectionUpdate(KisNode *node, const QVector<QRect> &rects, bool resetAnimationCache) override;
void invalidateFrames(const KisTimeRange &range, const QRect &rect) override;
void requestTimeSwitch(int time) override;
KisNode* graphOverlayNode() const override;
public: // KisProjectionUpdateListener implementation
void notifyProjectionUpdated(const QRect &rc) override;
public:
/**
* Set the number of threads used by the image's working threads
*/
void setWorkingThreadsLimit(int value);
/**
* Return the number of threads available to the image's working threads
*/
int workingThreadsLimit() const;
/**
* Makes a copy of the image with all the layers. If possible, shallow
* copies of the layers are made.
*
* \p exactCopy shows if the copied image should look *exactly* the same as
* the other one (according to it's .kra xml representation). It means that
* the layers will have the same UUID keys and, therefore, you are not
* expected to use the copied image anywhere except for saving. Don't use
* this option if you plan to work with the copied image later.
*/
KisImage *clone(bool exactCopy = false);
/**
* Render the projection onto a QImage.
*/
QImage convertToQImage(qint32 x1,
qint32 y1,
qint32 width,
qint32 height,
const KoColorProfile * profile);
/**
* Render the projection onto a QImage.
* (this is an overloaded function)
*/
QImage convertToQImage(QRect imageRect,
const KoColorProfile * profile);
/**
* Render a thumbnail of the projection onto a QImage.
*/
QImage convertToQImage(const QSize& scaledImageSize, const KoColorProfile *profile);
/**
* [low-level] Lock the image without waiting for all the internal job queues are processed
*
* WARNING: Don't use it unless you really know what you are doing! Use barrierLock() instead!
*
* Waits for all the **currently running** internal jobs to complete and locks the image
* for writing. Please note that this function does **not** wait for all the internal
* queues to process, so there might be some non-finished actions pending. It means that
* you just postpone these actions until you unlock() the image back. Until then, then image
* might easily be frozen in some inconsistent state.
*
* The only sane usage for this function is to lock the image for **emergency**
* processing, when some internal action or scheduler got hung up, and you just want
* to fetch some data from the image without races.
*
* In all other cases, please use barrierLock() instead!
*/
void lock();
/**
* Unlocks the image and starts/resumes all the pending internal jobs. If the image
* has been locked for a non-readOnly access, then all the internal caches of the image
* (e.g. lod-planes) are reset and regeneration jobs are scheduled.
*/
void unlock();
/**
* @return return true if the image is in a locked state, i.e. all the internal
* jobs are blocked from execution by calling wither lock() or barrierLock().
*
* When the image is locked, the user can do some modifications to the image
* contents safely without a perspective having race conditions with internal
* image jobs.
*/
bool locked() const;
/**
* Sets the mask (it must be a part of the node hierarchy already) to be paited on
* the top of all layers. This method does all the locking and syncing for you. It
* is executed asynchronously.
*/
void setOverlaySelectionMask(KisSelectionMaskSP mask);
/**
* \see setOverlaySelectionMask
*/
KisSelectionMaskSP overlaySelectionMask() const;
/**
* \see setOverlaySelectionMask
*/
bool hasOverlaySelectionMask() const;
/**
* @return the global selection object or 0 if there is none. The
* global selection is always read-write.
*/
KisSelectionSP globalSelection() const;
/**
* Retrieve the next automatic layername (XXX: fix to add option to return Mask X)
*/
QString nextLayerName(const QString &baseName = "") const;
/**
* Set the automatic layer name counter one back.
*/
void rollBackLayerName();
/**
* @brief start asynchronous operation on resizing the image
*
* The method will resize the image to fit the new size without
* dropping any pixel data. The GUI will get correct
* notification with old and new sizes, so it adjust canvas origin
* accordingly and avoid jumping of the canvas on screen
*
* @param newRect the rectangle of the image which will be visible
* after operation is completed
*
* Please note that the actual operation starts asynchronously in
* a background, so you cannot expect the image having new size
* right after this call.
*/
void resizeImage(const QRect& newRect);
/**
* @brief start asynchronous operation on cropping the image
*
* The method will **drop** all the image data outside \p newRect
* and resize the image to fit the new size. The GUI will get correct
* notification with old and new sizes, so it adjust canvas origin
* accordingly and avoid jumping of the canvas on screen
*
* @param newRect the rectangle of the image which will be cut-out
*
* Please note that the actual operation starts asynchronously in
* a background, so you cannot expect the image having new size
* right after this call.
*/
void cropImage(const QRect& newRect);
/**
* @brief start asynchronous operation on cropping a subtree of nodes starting at \p node
*
* The method will **drop** all the layer data outside \p newRect. Neither
* image nor a layer will be moved anywhere
*
+ * @param node node to crop
* @param newRect the rectangle of the layer which will be cut-out
*
* Please note that the actual operation starts asynchronously in
* a background, so you cannot expect the image having new size
* right after this call.
*/
void cropNode(KisNodeSP node, const QRect& newRect);
/**
* @brief start asynchronous operation on scaling the image
* @param size new image size in pixels
* @param xres new image x-resolution pixels-per-pt
* @param yres new image y-resolution pixels-per-pt
* @param filterStrategy filtering strategy
*
* Please note that the actual operation starts asynchronously in
* a background, so you cannot expect the image having new size
* right after this call.
*/
void scaleImage(const QSize &size, qreal xres, qreal yres, KisFilterStrategy *filterStrategy);
/**
* @brief start asynchronous operation on scaling a subtree of nodes starting at \p node
* @param node node to scale
- * @param scaleX, @param scaleY scale coefficient to be applied to the node
+ * @param center the center of the scaling
+ * @param scaleX x-scale coefficient to be applied to the node
+ * @param scaleY y-scale coefficient to be applied to the node
* @param filterStrategy filtering strategy
+ * @param selection the selection we based on
*
* Please note that the actual operation starts asynchronously in
* a background, so you cannot expect the image having new size
* right after this call.
*/
void scaleNode(KisNodeSP node, const QPointF &center, qreal scaleX, qreal scaleY, KisFilterStrategy *filterStrategy, KisSelectionSP selection);
/**
* @brief start asynchronous operation on rotating the image
*
* The image is resized to fit the rotated rectangle
*
* @param radians rotation angle in radians
*
* Please note that the actual operation starts asynchronously in
* a background, so you cannot expect the operation being completed
* right after the call
*/
void rotateImage(double radians);
/**
* @brief start asynchronous operation on rotating a subtree of nodes starting at \p node
*
* The image is not resized!
*
* @param node the root of the subtree to rotate
* @param radians rotation angle in radians
+ * @param selection the selection we based on
*
* Please note that the actual operation starts asynchronously in
* a background, so you cannot expect the operation being completed
* right after the call
*/
void rotateNode(KisNodeSP node, double radians, KisSelectionSP selection);
/**
* @brief start asynchronous operation on shearing the image
*
* The image is resized to fit the sheared polygon
*
- * @param angleX, @param angleY are given in degrees.
+ * @p angleX, @p angleY are given in degrees.
*
* Please note that the actual operation starts asynchronously in
* a background, so you cannot expect the operation being completed
* right after the call
*/
void shear(double angleX, double angleY);
/**
* @brief start asynchronous operation on shearing a subtree of nodes starting at \p node
*
* The image is not resized!
*
* @param node the root of the subtree to rotate
- * @param angleX, @param angleY are given in degrees.
+ * @param angleX x-shear given in degrees.
+ * @param angleY y-shear given in degrees.
+ * @param selection the selection we based on
*
* Please note that the actual operation starts asynchronously in
* a background, so you cannot expect the operation being completed
* right after the call
*/
void shearNode(KisNodeSP node, double angleX, double angleY, KisSelectionSP selection);
/**
* Convert the image and all its layers to the dstColorSpace
*/
void convertImageColorSpace(const KoColorSpace *dstColorSpace,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags);
/**
* Set the color space of the projection (and the root layer)
* to dstColorSpace. No conversion is done for other layers,
* their colorspace can differ.
- * NOTE: Note conversion is done, only regeneration, so no rendering
+ * @note No conversion is done, only regeneration, so no rendering
* intent needed
*/
void convertProjectionColorSpace(const KoColorSpace *dstColorSpace);
// Get the profile associated with this image
const KoColorProfile * profile() const;
/**
* Set the profile of the image to the new profile and do the same for
* all layers that have the same colorspace and profile of the image.
* It doesn't do any pixel conversion.
*
* This is essential if you have loaded an image that didn't
* have an embedded profile to which you want to attach the right profile.
*
* This does not create an undo action; only call it when creating or
* loading an image.
*
* @returns false if the profile could not be assigned
*/
bool assignImageProfile(const KoColorProfile *profile);
/**
* Returns the current undo adapter. You can add new commands to the
* undo stack using the adapter. This adapter is used for a backward
* compatibility for old commands created before strokes. It blocks
* all the porcessing at the scheduler, waits until it's finished
* and executes commands exclusively.
*/
KisUndoAdapter* undoAdapter() const;
/**
* This adapter is used by the strokes system. The commands are added
* to it *after* redo() is done (in the scheduler context). They are
* wrapped into a special command and added to the undo stack. redo()
* in not called.
*/
KisPostExecutionUndoAdapter* postExecutionUndoAdapter() const override;
/**
* Replace current undo store with the new one. The old store
* will be deleted.
* This method is used by KisDocument for dropping all the commands
* during file loading.
*/
void setUndoStore(KisUndoStore *undoStore);
/**
* Return current undo store of the image
*/
KisUndoStore* undoStore();
/**
* Tell the image it's modified; this emits the sigImageModified
* signal. This happens when the image needs to be saved
*/
void setModified();
/**
* The default colorspace of this image: new layers will have this
* colorspace and the projection will have this colorspace.
*/
const KoColorSpace * colorSpace() const;
/**
* X resolution in pixels per pt
*/
double xRes() const;
/**
* Y resolution in pixels per pt
*/
double yRes() const;
/**
* Set the resolution in pixels per pt.
*/
void setResolution(double xres, double yres);
/**
* Convert a document coordinate to a pixel coordinate.
*
* @param documentCoord PostScript Pt coordinate to convert.
*/
QPointF documentToPixel(const QPointF &documentCoord) const;
/**
* Convert a document coordinate to an integer pixel coordinate rounded down.
*
* @param documentCoord PostScript Pt coordinate to convert.
*/
QPoint documentToImagePixelFloored(const QPointF &documentCoord) const;
/**
* Convert a document rectangle to a pixel rectangle.
*
* @param documentRect PostScript Pt rectangle to convert.
*/
QRectF documentToPixel(const QRectF &documentRect) const;
/**
* Convert a pixel coordinate to a document coordinate.
*
* @param pixelCoord pixel coordinate to convert.
*/
QPointF pixelToDocument(const QPointF &pixelCoord) const;
/**
* Convert an integer pixel coordinate to a document coordinate.
* The document coordinate is at the centre of the pixel.
*
* @param pixelCoord pixel coordinate to convert.
*/
QPointF pixelToDocument(const QPoint &pixelCoord) const;
/**
* Convert a document rectangle to an integer pixel rectangle.
*
* @param pixelCoord pixel coordinate to convert.
*/
QRectF pixelToDocument(const QRectF &pixelCoord) const;
/**
* Return the width of the image
*/
qint32 width() const;
/**
* Return the height of the image
*/
qint32 height() const;
/**
* Return the size of the image
*/
QSize size() const {
return QSize(width(), height());
}
/**
* @return the root node of the image node graph
*/
KisGroupLayerSP rootLayer() const;
/**
* Return the projection; that is, the complete, composited
* representation of this image.
*/
KisPaintDeviceSP projection() const;
/**
* Return the number of layers (not other nodes) that are in this
* image.
*/
qint32 nlayers() const;
/**
* Return the number of layers (not other node types) that are in
* this image and that are hidden.
*/
qint32 nHiddenLayers() const;
/**
* Merge all visible layers and discard hidden ones.
*/
void flatten(KisNodeSP activeNode);
/**
* Merge the specified layer with the layer
* below this layer, remove the specified layer.
*/
void mergeDown(KisLayerSP l, const KisMetaData::MergeStrategy* strategy);
/**
* flatten the layer: that is, the projection becomes the layer
* and all subnodes are removed. If this is not a paint layer, it will morph
* into a paint layer.
*/
void flattenLayer(KisLayerSP layer);
/**
* Merges layers in \p mergedLayers and creates a new layer above
* \p putAfter
*/
void mergeMultipleLayers(QList<KisNodeSP> mergedLayers, KisNodeSP putAfter);
/// @return the exact bounds of the image in pixel coordinates.
QRect bounds() const;
/**
* Returns the actual bounds of the image, taking LevelOfDetail
* into account. This value is used as a bounds() value of
* KisDefaultBounds object.
*/
QRect effectiveLodBounds() const;
/// use if the layers have changed _completely_ (eg. when flattening)
void notifyLayersChanged();
/**
* Sets the default color of the root layer projection. All the layers
* will be merged on top of this very color
*/
void setDefaultProjectionColor(const KoColor &color);
/**
* \see setDefaultProjectionColor()
*/
KoColor defaultProjectionColor() const;
void setRootLayer(KisGroupLayerSP rootLayer);
/**
* Add an annotation for this image. This can be anything: Gamma, EXIF, etc.
* Note that the "icc" annotation is reserved for the color strategies.
* If the annotation already exists, overwrite it with this one.
*/
void addAnnotation(KisAnnotationSP annotation);
/** get the annotation with the given type, can return 0 */
KisAnnotationSP annotation(const QString& type);
/** delete the annotation, if the image contains it */
void removeAnnotation(const QString& type);
/**
* Start of an iteration over the annotations of this image (including the ICC Profile)
*/
vKisAnnotationSP_it beginAnnotations();
/** end of an iteration over the annotations of this image */
vKisAnnotationSP_it endAnnotations();
/**
* Called before the image is deleted and sends the sigAboutToBeDeleted signal
*/
void notifyAboutToBeDeleted();
KisImageSignalRouter* signalRouter();
/**
* Returns whether we can reselect current global selection
*
* \see reselectGlobalSelection()
*/
bool canReselectGlobalSelection();
/**
* Returns the layer compositions for the image
*/
QList<KisLayerCompositionSP> compositions();
/**
* Adds a new layer composition, will be saved with the image
*/
void addComposition(KisLayerCompositionSP composition);
/**
* Remove the layer compostion
*/
void removeComposition(KisLayerCompositionSP composition);
/**
* Permit or deny the wrap-around mode for all the paint devices
* of the image. Note that permitting the wraparound mode will not
* necessarily activate it right now. To be activated the wrap
* around mode should be 1) permitted; 2) supported by the
* currently running stroke.
*/
void setWrapAroundModePermitted(bool value);
/**
* \return whether the wrap-around mode is permitted for this
* image. If the wrap around mode is permitted and the
* currently running stroke supports it, the mode will be
* activated for all paint devices of the image.
*
* \see setWrapAroundMode
*/
bool wrapAroundModePermitted() const;
/**
* \return whether the wraparound mode is activated for all the
* devices of the image. The mode is activated when both
* factors are true: the user permitted it and the stroke
* supports it
*/
bool wrapAroundModeActive() const;
/**
* \return current level of detail which is used when processing the image.
* Current working zoom = 2 ^ (- currentLevelOfDetail()). Default value is
* null, which means we work on the original image.
*/
int currentLevelOfDetail() const;
/**
* Notify KisImage which level of detail should be used in the
* lod-mode. Setting the mode does not guarantee the LOD to be
* used. It will be activated only when the stokes supports it.
*/
void setDesiredLevelOfDetail(int lod);
/**
* Relative position of the mirror axis center
* 0,0 - topleft corner of the image
* 1,1 - bottomright corner of the image
*/
QPointF mirrorAxesCenter() const;
/**
* Sets the relative position of the axes center
* \see mirrorAxesCenter() for details
*/
void setMirrorAxesCenter(const QPointF &value) const;
public Q_SLOTS:
/**
* Explicitly start regeneration of LoD planes of all the devices
* in the image. This call should be performed when the user is idle,
* just to make the quality of image updates better.
*/
void explicitRegenerateLevelOfDetail();
public:
/**
* Blocks usage of level of detail functionality. After this method
* has been called, no new strokes will use LoD.
*/
void setLevelOfDetailBlocked(bool value);
/**
* \see setLevelOfDetailBlocked()
*/
bool levelOfDetailBlocked() const;
/**
* Notifies that the node collapsed state has changed
*/
void notifyNodeCollpasedChanged();
KisImageAnimationInterface *animationInterface() const;
/**
* @brief setProofingConfiguration, this sets the image's proofing configuration, and signals
* the proofingConfiguration has changed.
* @param proofingConfig - the kis proofing config that will be used instead.
*/
void setProofingConfiguration(KisProofingConfigurationSP proofingConfig);
/**
* @brief proofingConfiguration
* @return the proofing configuration of the image.
*/
KisProofingConfigurationSP proofingConfiguration() const;
public Q_SLOTS:
bool startIsolatedMode(KisNodeSP node);
void stopIsolatedMode();
public:
KisNodeSP isolatedModeRoot() const;
Q_SIGNALS:
/**
* Emitted whenever an action has caused the image to be
- * recomposited.
- *
- * @param rc The rect that has been recomposited.
+ * recomposited. Parameter is the rect that has been recomposited.
*/
void sigImageUpdated(const QRect &);
/**
Emitted whenever the image has been modified, so that it
doesn't match with the version saved on disk.
*/
void sigImageModified();
/**
* The signal is emitted when the size of the image is changed.
* \p oldStillPoint and \p newStillPoint give the receiver the
* hint about how the new and old rect of the image correspond to
* each other. They specify the point of the image around which
* the conversion was done. This point will stay still on the
* user's screen. That is the \p newStillPoint of the new image
* will be painted at the same screen position, where \p
* oldStillPoint of the old image was painted.
*
* \param oldStillPoint is a still point represented in *old*
* image coordinates
*
* \param newStillPoint is a still point represented in *new*
* image coordinates
*/
void sigSizeChanged(const QPointF &oldStillPoint, const QPointF &newStillPoint);
void sigProfileChanged(const KoColorProfile * profile);
void sigColorSpaceChanged(const KoColorSpace* cs);
void sigResolutionChanged(double xRes, double yRes);
void sigRequestNodeReselection(KisNodeSP activeNode, const KisNodeList &selectedNodes);
/**
* Inform the model that a node was changed
*/
void sigNodeChanged(KisNodeSP node);
/**
* Inform that the image is going to be deleted
*/
void sigAboutToBeDeleted();
/**
* The signal is emitted right after a node has been connected
* to the graph of the nodes.
*
* WARNING: you must not request any graph-related information
* about the node being run in a not-scheduler thread. If you need
* information about the parent/siblings of the node connect
* with Qt::DirectConnection, get needed information and then
* emit another Qt::AutoConnection signal to pass this information
* to your thread. See details of the implementation
* in KisDummiesfacadeBase.
*/
void sigNodeAddedAsync(KisNodeSP node);
/**
* This signal is emitted right before a node is going to removed
* from the graph of the nodes.
*
* WARNING: you must not request any graph-related information
* about the node being run in a not-scheduler thread.
*
* \see comment in sigNodeAddedAsync()
*/
void sigRemoveNodeAsync(KisNodeSP node);
/**
* Emitted when the root node of the image has changed.
* It happens, e.g. when we flatten the image. When
* this happens the receiver should reload information
* about the image
*/
void sigLayersChangedAsync();
/**
* Emitted when the UI has requested the undo of the last stroke's
* operation. The point is, we cannot deal with the internals of
* the stroke without its creator knowing about it (which most
* probably cause a crash), so we just forward this request from
* the UI to the creator of the stroke.
*
* If your tool supports undoing part of its work, just listen to
* this signal and undo when it comes
*/
void sigUndoDuringStrokeRequested();
/**
* Emitted when the UI has requested the cancellation of
* the stroke. The point is, we cannot cancel the stroke
* without its creator knowing about it (which most probably
* cause a crash), so we just forward this request from the UI
* to the creator of the stroke.
*
* If your tool supports cancelling of its work in the middle
* of operation, just listen to this signal and cancel
* the stroke when it comes
*/
void sigStrokeCancellationRequested();
/**
* Emitted when the image decides that the stroke should better
* be ended. The point is, we cannot just end the stroke
* without its creator knowing about it (which most probably
* cause a crash), so we just forward this request from the UI
* to the creator of the stroke.
*
* If your tool supports long strokes that may involve multiple
* mouse actions in one stroke, just listen to this signal and
* end the stroke when it comes.
*/
void sigStrokeEndRequested();
/**
* Same as sigStrokeEndRequested() but is not emitted when the active node
* is changed.
*/
void sigStrokeEndRequestedActiveNodeFiltered();
/**
* Emitted when the isolated mode status has changed.
*
* Can be used by the receivers to catch a fact of forcefully
* stopping the isolated mode by the image when some complex
* action was requested
*/
void sigIsolatedModeChanged();
/**
* Emitted when one or more nodes changed the collapsed state
*
*/
void sigNodeCollapsedChanged();
/**
* Emitted when the proofing configuration of the image is being changed.
*
*/
void sigProofingConfigChanged();
/**
* Internal signal for asynchronously requesting isolated mode to stop. Don't use it
* outside KisImage, use sigIsolatedModeChanged() instead.
*/
void sigInternalStopIsolatedModeRequested();
public Q_SLOTS:
KisCompositeProgressProxy* compositeProgressProxy();
bool isIdle(bool allowLocked = false);
/**
* @brief Wait until all the queued background jobs are completed and lock the image.
*
* KisImage object has a local scheduler that executes long-running image
* rendering/modifying jobs (we call them "strokes") in a background. Basically,
* one should either access the image from the scope of such jobs (strokes) or
* just lock the image before using.
*
* Calling barrierLock() will wait until all the queued operations are finished
* and lock the image, so you can start accessing it in a safe way.
*
* @p readOnly tells the image if the caller is going to modify the image during
* holding the lock. Locking with non-readOnly access will reset all
* the internal caches of the image (lod-planes) when the lock status
* will be lifted.
*/
void barrierLock(bool readOnly = false);
/**
* @brief Tries to lock the image without waiting for the jobs to finish
*
* Same as barrierLock(), but doesn't block execution of the calling thread
* until all the background jobs are finished. Instead, in case of presence of
* unfinished jobs in the queue, it just returns false
*
* @return whether the lock has been acquired
* @see barrierLock
*/
bool tryBarrierLock(bool readOnly = false);
/**
* Wait for all the internal image jobs to complete and return without locking
* the image. This function is handly for tests or other synchronous actions,
* when one needs to wait for the result of his actions.
*/
void waitForDone();
KisStrokeId startStroke(KisStrokeStrategy *strokeStrategy) override;
void addJob(KisStrokeId id, KisStrokeJobData *data) override;
void endStroke(KisStrokeId id) override;
bool cancelStroke(KisStrokeId id) override;
/**
* @brief blockUpdates block updating the image projection
*/
void blockUpdates() override;
/**
* @brief unblockUpdates unblock updating the image project. This
* only restarts the scheduler and does not schedule a full refresh.
*/
void unblockUpdates() override;
/**
* Disables notification of the UI about the changes in the image.
* This feature is used by KisProcessingApplicator. It is needed
* when we change the size of the image. In this case, the whole
* image will be reloaded into UI by sigSizeChanged(), so there is
* no need to inform the UI about individual dirty rects.
*
* The last call to enableUIUpdates() will return the list of udpates
* that were requested while they were blocked.
*/
void disableUIUpdates() override;
/**
* \see disableUIUpdates
*/
QVector<QRect> enableUIUpdates() override;
/**
* Disables the processing of all the setDirty() requests that
* come to the image. The incoming requests are effectively
* *dropped*.
*
* This feature is used by KisProcessingApplicator. For many cases
* it provides its own updates interface, which recalculates the
* whole subtree of nodes. But while we change any particular
* node, it can ask for an update itself. This method is a way of
* blocking such intermediate (and excessive) requests.
*
* NOTE: this is a convenience function for setProjectionUpdatesFilter()
* that installs a predefined filter that eats everything. Please
* note that these calls are *not* recursive
*/
void disableDirtyRequests() override;
/**
* \see disableDirtyRequests()
*/
void enableDirtyRequests() override;
/**
* Installs a filter object that will filter all the incoming projection update
* requests. If the filter return true, the incoming update is dropped.
*
* NOTE: you cannot set filters recursively!
*/
void setProjectionUpdatesFilter(KisProjectionUpdatesFilterSP filter) override;
/**
* \see setProjectionUpdatesFilter()
*/
KisProjectionUpdatesFilterSP projectionUpdatesFilter() const override;
void refreshGraphAsync(KisNodeSP root = KisNodeSP()) override;
void refreshGraphAsync(KisNodeSP root, const QRect &rc) override;
void refreshGraphAsync(KisNodeSP root, const QRect &rc, const QRect &cropRect) override;
/**
* Triggers synchronous recomposition of the projection
*/
void refreshGraph(KisNodeSP root = KisNodeSP());
void refreshGraph(KisNodeSP root, const QRect& rc, const QRect &cropRect);
void initialRefreshGraph();
/**
* Initiate a stack regeneration skipping the recalculation of the
* filthy node's projection.
*
* Works exactly as pseudoFilthy->setDirty() with the only
* exception that pseudoFilthy::updateProjection() will not be
* called. That is used by KisRecalculateTransformMaskJob to avoid
* cyclic dependencies.
*/
void requestProjectionUpdateNoFilthy(KisNodeSP pseudoFilthy, const QRect &rc, const QRect &cropRect);
/**
* Adds a spontaneous job to the updates queue.
*
* A spontaneous job may do some trivial tasks in the background,
* like updating the outline of selection or purging unused tiles
* from the existing paint devices.
*/
void addSpontaneousJob(KisSpontaneousJob *spontaneousJob);
/**
* This method is called by the UI (*not* by the creator of the
* stroke) when it thinks the current stroke should undo its last
* action, for example, when the user presses Ctrl+Z while some
* stroke is active.
*
* If the creator of the stroke supports undoing of intermediate
* actions, it will be notified about this request and can undo
* its last action.
*/
void requestUndoDuringStroke();
/**
* This method is called by the UI (*not* by the creator of the
* stroke) when it thinks current stroke should be cancelled. If
* there is a running stroke that has already been detached from
* its creator (ended or cancelled), it will be forcefully
* cancelled and reverted. If there is an open stroke present, and
* if its creator supports cancelling, it will be notified about
* the request and the stroke will be cancelled
*/
void requestStrokeCancellation();
/**
* This method requests the last stroke executed on the image to become undone.
* If the stroke is not ended, or if all the Lod0 strokes are completed, the method
* returns UNDO_FAIL. If the last Lod0 is going to be finished soon, then UNDO_WAIT
* is returned and the caller should just wait for its completion and call global undo
* instead. UNDO_OK means one unfinished stroke has been undone.
*/
UndoResult tryUndoUnfinishedLod0Stroke();
/**
* This method is called when image or some other part of Krita
* (*not* the creator of the stroke) decides that the stroke
* should be ended. If the creator of the stroke supports it, it
* will be notified and the stroke will be cancelled
*/
void requestStrokeEnd();
/**
* Same as requestStrokeEnd() but is called by view manager when
* the current node is changed. Use to distinguish
* sigStrokeEndRequested() and
* sigStrokeEndRequestedActiveNodeFiltered() which are used by
* KisNodeJugglerCompressed
*/
void requestStrokeEndActiveNode();
private:
KisImage(const KisImage& rhs, KisUndoStore *undoStore, bool exactCopy);
KisImage& operator=(const KisImage& rhs);
void emitSizeChanged();
void resizeImageImpl(const QRect& newRect, bool cropLayers);
void rotateImpl(const KUndo2MagicString &actionName, KisNodeSP rootNode, double radians,
bool resizeImage, KisSelectionSP selection);
void shearImpl(const KUndo2MagicString &actionName, KisNodeSP rootNode,
bool resizeImage, double angleX, double angleY, KisSelectionSP selection);
void safeRemoveTwoNodes(KisNodeSP node1, KisNodeSP node2);
void refreshHiddenArea(KisNodeSP rootNode, const QRect &preparedArea);
void requestProjectionUpdateImpl(KisNode *node,
const QVector<QRect> &rects,
const QRect &cropRect);
friend class KisImageResizeCommand;
void setSize(const QSize& size);
friend class KisImageSetProjectionColorSpaceCommand;
void setProjectionColorSpace(const KoColorSpace * colorSpace);
friend class KisDeselectGlobalSelectionCommand;
friend class KisReselectGlobalSelectionCommand;
friend class KisSetGlobalSelectionCommand;
friend class KisImageTest;
friend class Document; // For libkis
/**
* Replaces the current global selection with globalSelection. If
* \p globalSelection is empty, removes the selection object, so that
* \ref globalSelection() will return 0 after that.
*/
void setGlobalSelection(KisSelectionSP globalSelection);
/**
* Deselects current global selection.
* \ref globalSelection() will return 0 after that.
*/
void deselectGlobalSelection();
/**
* Reselects current deselected selection
*
* \see deselectGlobalSelection()
*/
void reselectGlobalSelection();
private:
class KisImagePrivate;
KisImagePrivate * m_d;
};
#endif // KIS_IMAGE_H_
diff --git a/libs/image/kis_layer_utils.cpp b/libs/image/kis_layer_utils.cpp
index ddd0f966de..add2ecbe78 100644
--- a/libs/image/kis_layer_utils.cpp
+++ b/libs/image/kis_layer_utils.cpp
@@ -1,1509 +1,1505 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_utils.h"
#include <algorithm>
#include <QUuid>
#include <KoColorSpaceConstants.h>
#include <KoProperties.h>
#include "kis_painter.h"
#include "kis_image.h"
#include "kis_node.h"
#include "kis_layer.h"
#include "kis_paint_layer.h"
#include "kis_clone_layer.h"
#include "kis_group_layer.h"
#include "kis_selection.h"
#include "kis_selection_mask.h"
#include "kis_meta_data_merge_strategy.h"
#include <kundo2command.h>
#include "commands/kis_image_layer_add_command.h"
#include "commands/kis_image_layer_remove_command.h"
#include "commands/kis_image_layer_move_command.h"
#include "commands/kis_image_change_layers_command.h"
#include "commands_new/kis_activate_selection_mask_command.h"
#include "commands/kis_image_change_visibility_command.h"
#include "kis_abstract_projection_plane.h"
#include "kis_processing_applicator.h"
#include "kis_image_animation_interface.h"
#include "kis_keyframe_channel.h"
#include "kis_command_utils.h"
#include "commands_new/kis_change_projection_color_command.h"
#include "kis_layer_properties_icons.h"
#include "lazybrush/kis_colorize_mask.h"
#include "commands/kis_node_property_list_command.h"
#include "commands/kis_node_compositeop_command.h"
#include <KisDelayedUpdateNodeInterface.h>
#include "krita_utils.h"
#include "kis_image_signal_router.h"
namespace KisLayerUtils {
void fetchSelectionMasks(KisNodeList mergedNodes, QVector<KisSelectionMaskSP> &selectionMasks)
{
foreach (KisNodeSP node, mergedNodes) {
KisLayerSP layer = qobject_cast<KisLayer*>(node.data());
KisSelectionMaskSP mask;
if (layer && (mask = layer->selectionMask())) {
selectionMasks.append(mask);
}
}
}
struct MergeDownInfoBase {
MergeDownInfoBase(KisImageSP _image)
: image(_image),
storage(new SwitchFrameCommand::SharedStorage())
{
}
virtual ~MergeDownInfoBase() {}
KisImageWSP image;
QVector<KisSelectionMaskSP> selectionMasks;
KisNodeSP dstNode;
SwitchFrameCommand::SharedStorageSP storage;
QSet<int> frames;
bool useInTimeline = false;
bool enableOnionSkins = false;
virtual KisNodeList allSrcNodes() = 0;
KisLayerSP dstLayer() {
return qobject_cast<KisLayer*>(dstNode.data());
}
};
struct MergeDownInfo : public MergeDownInfoBase {
MergeDownInfo(KisImageSP _image,
KisLayerSP _prevLayer,
KisLayerSP _currLayer)
: MergeDownInfoBase(_image),
prevLayer(_prevLayer),
currLayer(_currLayer)
{
frames =
fetchLayerFramesRecursive(prevLayer) |
fetchLayerFramesRecursive(currLayer);
useInTimeline = prevLayer->useInTimeline() || currLayer->useInTimeline();
const KisPaintLayer *paintLayer = qobject_cast<KisPaintLayer*>(currLayer.data());
if (paintLayer) enableOnionSkins |= paintLayer->onionSkinEnabled();
paintLayer = qobject_cast<KisPaintLayer*>(prevLayer.data());
if (paintLayer) enableOnionSkins |= paintLayer->onionSkinEnabled();
}
KisLayerSP prevLayer;
KisLayerSP currLayer;
KisNodeList allSrcNodes() override {
KisNodeList mergedNodes;
mergedNodes << currLayer;
mergedNodes << prevLayer;
return mergedNodes;
}
};
struct MergeMultipleInfo : public MergeDownInfoBase {
MergeMultipleInfo(KisImageSP _image,
KisNodeList _mergedNodes)
: MergeDownInfoBase(_image),
mergedNodes(_mergedNodes)
{
foreach (KisNodeSP node, mergedNodes) {
frames |= fetchLayerFramesRecursive(node);
useInTimeline |= node->useInTimeline();
const KisPaintLayer *paintLayer = qobject_cast<KisPaintLayer*>(node.data());
if (paintLayer) {
enableOnionSkins |= paintLayer->onionSkinEnabled();
}
}
}
KisNodeList mergedNodes;
bool nodesCompositingVaries = false;
KisNodeList allSrcNodes() override {
return mergedNodes;
}
};
typedef QSharedPointer<MergeDownInfoBase> MergeDownInfoBaseSP;
typedef QSharedPointer<MergeDownInfo> MergeDownInfoSP;
typedef QSharedPointer<MergeMultipleInfo> MergeMultipleInfoSP;
struct FillSelectionMasks : public KUndo2Command {
FillSelectionMasks(MergeDownInfoBaseSP info) : m_info(info) {}
void redo() override {
fetchSelectionMasks(m_info->allSrcNodes(), m_info->selectionMasks);
}
private:
MergeDownInfoBaseSP m_info;
};
struct DisableColorizeKeyStrokes : public KisCommandUtils::AggregateCommand {
DisableColorizeKeyStrokes(MergeDownInfoBaseSP info) : m_info(info) {}
void populateChildCommands() override {
Q_FOREACH (KisNodeSP node, m_info->allSrcNodes()) {
recursiveApplyNodes(node,
[this] (KisNodeSP node) {
if (dynamic_cast<KisColorizeMask*>(node.data()) &&
KisLayerPropertiesIcons::nodeProperty(node, KisLayerPropertiesIcons::colorizeEditKeyStrokes, true).toBool()) {
KisBaseNode::PropertyList props = node->sectionModelProperties();
KisLayerPropertiesIcons::setNodeProperty(&props,
KisLayerPropertiesIcons::colorizeEditKeyStrokes,
false);
addCommand(new KisNodePropertyListCommand(node, props));
}
});
}
}
private:
MergeDownInfoBaseSP m_info;
};
struct DisableOnionSkins : public KisCommandUtils::AggregateCommand {
DisableOnionSkins(MergeDownInfoBaseSP info) : m_info(info) {}
void populateChildCommands() override {
Q_FOREACH (KisNodeSP node, m_info->allSrcNodes()) {
recursiveApplyNodes(node,
[this] (KisNodeSP node) {
if (KisLayerPropertiesIcons::nodeProperty(node, KisLayerPropertiesIcons::onionSkins, false).toBool()) {
KisBaseNode::PropertyList props = node->sectionModelProperties();
KisLayerPropertiesIcons::setNodeProperty(&props,
KisLayerPropertiesIcons::onionSkins,
false);
addCommand(new KisNodePropertyListCommand(node, props));
}
});
}
}
private:
MergeDownInfoBaseSP m_info;
};
struct DisableExtraCompositing : public KisCommandUtils::AggregateCommand {
DisableExtraCompositing(MergeMultipleInfoSP info) : m_info(info) {}
void populateChildCommands() override {
/**
* We disable extra compositing only in case all the layers have
* the same compositing properties, therefore, we can just sum them using
* Normal blend mode
*/
if (m_info->nodesCompositingVaries) return;
// we should disable dirty requests on **redo only**, otherwise
// the state of the layers will not be recovered on undo
m_info->image->disableDirtyRequests();
Q_FOREACH (KisNodeSP node, m_info->allSrcNodes()) {
if (node->compositeOpId() != COMPOSITE_OVER) {
addCommand(new KisNodeCompositeOpCommand(node, node->compositeOpId(), COMPOSITE_OVER));
}
if (KisLayerPropertiesIcons::nodeProperty(node, KisLayerPropertiesIcons::inheritAlpha, false).toBool()) {
KisBaseNode::PropertyList props = node->sectionModelProperties();
KisLayerPropertiesIcons::setNodeProperty(&props,
KisLayerPropertiesIcons::inheritAlpha,
false);
addCommand(new KisNodePropertyListCommand(node, props));
}
}
m_info->image->enableDirtyRequests();
}
private:
MergeMultipleInfoSP m_info;
};
struct DisablePassThroughForHeadsOnly : public KisCommandUtils::AggregateCommand {
DisablePassThroughForHeadsOnly(MergeDownInfoBaseSP info, bool skipIfDstIsGroup = false)
: m_info(info),
m_skipIfDstIsGroup(skipIfDstIsGroup)
{
}
void populateChildCommands() override {
if (m_skipIfDstIsGroup &&
m_info->dstLayer() &&
m_info->dstLayer()->inherits("KisGroupLayer")) {
return;
}
Q_FOREACH (KisNodeSP node, m_info->allSrcNodes()) {
if (KisLayerPropertiesIcons::nodeProperty(node, KisLayerPropertiesIcons::passThrough, false).toBool()) {
KisBaseNode::PropertyList props = node->sectionModelProperties();
KisLayerPropertiesIcons::setNodeProperty(&props,
KisLayerPropertiesIcons::passThrough,
false);
addCommand(new KisNodePropertyListCommand(node, props));
}
}
}
private:
MergeDownInfoBaseSP m_info;
bool m_skipIfDstIsGroup;
};
struct RefreshHiddenAreas : public KUndo2Command {
RefreshHiddenAreas(MergeDownInfoBaseSP info) : m_info(info) {}
void redo() override {
KisImageAnimationInterface *interface = m_info->image->animationInterface();
const QRect preparedRect = !interface->externalFrameActive() ?
m_info->image->bounds() : QRect();
foreach (KisNodeSP node, m_info->allSrcNodes()) {
refreshHiddenAreaAsync(node, preparedRect);
}
}
private:
QRect realNodeExactBounds(KisNodeSP rootNode, QRect currentRect = QRect()) {
KisNodeSP node = rootNode->firstChild();
while(node) {
currentRect |= realNodeExactBounds(node, currentRect);
node = node->nextSibling();
}
// TODO: it would be better to count up changeRect inside
// node's extent() method
currentRect |= rootNode->projectionPlane()->changeRect(rootNode->exactBounds());
return currentRect;
}
void refreshHiddenAreaAsync(KisNodeSP rootNode, const QRect &preparedArea) {
QRect realNodeRect = realNodeExactBounds(rootNode);
if (!preparedArea.contains(realNodeRect)) {
QRegion dirtyRegion = realNodeRect;
dirtyRegion -= preparedArea;
foreach(const QRect &rc, dirtyRegion.rects()) {
m_info->image->refreshGraphAsync(rootNode, rc, realNodeRect);
}
}
}
private:
MergeDownInfoBaseSP m_info;
};
struct RefreshDelayedUpdateLayers : public KUndo2Command {
RefreshDelayedUpdateLayers(MergeDownInfoBaseSP info) : m_info(info) {}
void redo() override {
foreach (KisNodeSP node, m_info->allSrcNodes()) {
forceAllDelayedNodesUpdate(node);
}
}
private:
MergeDownInfoBaseSP m_info;
};
struct KeepMergedNodesSelected : public KisCommandUtils::AggregateCommand {
KeepMergedNodesSelected(MergeDownInfoSP info, bool finalizing)
: m_singleInfo(info),
m_finalizing(finalizing) {}
KeepMergedNodesSelected(MergeMultipleInfoSP info, KisNodeSP putAfter, bool finalizing)
: m_multipleInfo(info),
m_finalizing(finalizing),
m_putAfter(putAfter) {}
void populateChildCommands() override {
KisNodeSP prevNode;
KisNodeSP nextNode;
KisNodeList prevSelection;
KisNodeList nextSelection;
KisImageSP image;
if (m_singleInfo) {
prevNode = m_singleInfo->currLayer;
nextNode = m_singleInfo->dstNode;
image = m_singleInfo->image;
} else if (m_multipleInfo) {
prevNode = m_putAfter;
nextNode = m_multipleInfo->dstNode;
prevSelection = m_multipleInfo->allSrcNodes();
image = m_multipleInfo->image;
}
if (!m_finalizing) {
addCommand(new KeepNodesSelectedCommand(prevSelection, KisNodeList(),
prevNode, KisNodeSP(),
image, false));
} else {
addCommand(new KeepNodesSelectedCommand(KisNodeList(), nextSelection,
KisNodeSP(), nextNode,
image, true));
}
}
private:
MergeDownInfoSP m_singleInfo;
MergeMultipleInfoSP m_multipleInfo;
bool m_finalizing;
KisNodeSP m_putAfter;
};
struct CreateMergedLayer : public KisCommandUtils::AggregateCommand {
CreateMergedLayer(MergeDownInfoSP info) : m_info(info) {}
void populateChildCommands() override {
// actual merging done by KisLayer::createMergedLayer (or specialized descendant)
m_info->dstNode = m_info->currLayer->createMergedLayerTemplate(m_info->prevLayer);
if (m_info->frames.size() > 0) {
m_info->dstNode->enableAnimation();
m_info->dstNode->getKeyframeChannel(KisKeyframeChannel::Content.id(), true);
}
m_info->dstNode->setUseInTimeline(m_info->useInTimeline);
KisPaintLayer *dstPaintLayer = qobject_cast<KisPaintLayer*>(m_info->dstNode.data());
if (dstPaintLayer) {
dstPaintLayer->setOnionSkinEnabled(m_info->enableOnionSkins);
}
}
private:
MergeDownInfoSP m_info;
};
struct CreateMergedLayerMultiple : public KisCommandUtils::AggregateCommand {
CreateMergedLayerMultiple(MergeMultipleInfoSP info, const QString name = QString() )
: m_info(info),
m_name(name) {}
void populateChildCommands() override {
QString mergedLayerName;
if (m_name.isEmpty()){
const QString mergedLayerSuffix = i18n("Merged");
mergedLayerName = m_info->mergedNodes.first()->name();
if (!mergedLayerName.endsWith(mergedLayerSuffix)) {
mergedLayerName = QString("%1 %2")
.arg(mergedLayerName).arg(mergedLayerSuffix);
}
} else {
mergedLayerName = m_name;
}
KisPaintLayer *dstPaintLayer = new KisPaintLayer(m_info->image, mergedLayerName, OPACITY_OPAQUE_U8);
m_info->dstNode = dstPaintLayer;
if (m_info->frames.size() > 0) {
m_info->dstNode->enableAnimation();
m_info->dstNode->getKeyframeChannel(KisKeyframeChannel::Content.id(), true);
}
auto channelFlagsLazy = [](KisNodeSP node) {
KisLayer *layer = dynamic_cast<KisLayer*>(node.data());
return layer ? layer->channelFlags() : QBitArray();
};
QString compositeOpId;
QBitArray channelFlags;
bool compositionVaries = false;
bool isFirstCycle = true;
foreach (KisNodeSP node, m_info->allSrcNodes()) {
if (isFirstCycle) {
compositeOpId = node->compositeOpId();
channelFlags = channelFlagsLazy(node);
isFirstCycle = false;
} else if (compositeOpId != node->compositeOpId() ||
channelFlags != channelFlagsLazy(node)) {
compositionVaries = true;
break;
}
KisLayerSP layer = qobject_cast<KisLayer*>(node.data());
if (layer && layer->layerStyle()) {
compositionVaries = true;
break;
}
}
if (!compositionVaries) {
if (!compositeOpId.isEmpty()) {
m_info->dstNode->setCompositeOpId(compositeOpId);
}
if (m_info->dstLayer() && !channelFlags.isEmpty()) {
m_info->dstLayer()->setChannelFlags(channelFlags);
}
}
m_info->nodesCompositingVaries = compositionVaries;
m_info->dstNode->setUseInTimeline(m_info->useInTimeline);
dstPaintLayer->setOnionSkinEnabled(m_info->enableOnionSkins);
}
private:
MergeMultipleInfoSP m_info;
QString m_name;
};
struct MergeLayers : public KisCommandUtils::AggregateCommand {
MergeLayers(MergeDownInfoSP info) : m_info(info) {}
void populateChildCommands() override {
// actual merging done by KisLayer::createMergedLayer (or specialized descendant)
m_info->currLayer->fillMergedLayerTemplate(m_info->dstLayer(), m_info->prevLayer);
}
private:
MergeDownInfoSP m_info;
};
struct MergeLayersMultiple : public KisCommandUtils::AggregateCommand {
MergeLayersMultiple(MergeMultipleInfoSP info) : m_info(info) {}
void populateChildCommands() override {
KisPainter gc(m_info->dstNode->paintDevice());
foreach (KisNodeSP node, m_info->allSrcNodes()) {
QRect rc = node->exactBounds() | m_info->image->bounds();
node->projectionPlane()->apply(&gc, rc);
}
}
private:
MergeMultipleInfoSP m_info;
};
struct MergeMetaData : public KUndo2Command {
MergeMetaData(MergeDownInfoSP info, const KisMetaData::MergeStrategy* strategy)
: m_info(info),
m_strategy(strategy) {}
void redo() override {
QRect layerProjectionExtent = m_info->currLayer->projection()->extent();
QRect prevLayerProjectionExtent = m_info->prevLayer->projection()->extent();
int prevLayerArea = prevLayerProjectionExtent.width() * prevLayerProjectionExtent.height();
int layerArea = layerProjectionExtent.width() * layerProjectionExtent.height();
QList<double> scores;
double norm = qMax(prevLayerArea, layerArea);
scores.append(prevLayerArea / norm);
scores.append(layerArea / norm);
QList<const KisMetaData::Store*> srcs;
srcs.append(m_info->prevLayer->metaData());
srcs.append(m_info->currLayer->metaData());
m_strategy->merge(m_info->dstLayer()->metaData(), srcs, scores);
}
private:
MergeDownInfoSP m_info;
const KisMetaData::MergeStrategy *m_strategy;
};
KeepNodesSelectedCommand::KeepNodesSelectedCommand(const KisNodeList &selectedBefore,
const KisNodeList &selectedAfter,
KisNodeSP activeBefore,
KisNodeSP activeAfter,
KisImageSP image,
bool finalize, KUndo2Command *parent)
: FlipFlopCommand(finalize, parent),
m_selectedBefore(selectedBefore),
m_selectedAfter(selectedAfter),
m_activeBefore(activeBefore),
m_activeAfter(activeAfter),
m_image(image)
{
}
void KeepNodesSelectedCommand::partB() {
KisImageSignalType type;
if (getState() == State::FINALIZING) {
type = ComplexNodeReselectionSignal(m_activeAfter, m_selectedAfter);
} else {
type = ComplexNodeReselectionSignal(m_activeBefore, m_selectedBefore);
}
m_image->signalRouter()->emitNotification(type);
}
SelectGlobalSelectionMask::SelectGlobalSelectionMask(KisImageSP image)
: m_image(image)
{
}
void SelectGlobalSelectionMask::redo() {
KisImageSignalType type =
ComplexNodeReselectionSignal(m_image->rootLayer()->selectionMask(), KisNodeList());
m_image->signalRouter()->emitNotification(type);
}
- KisLayerSP constructDefaultLayer(KisImageSP image) {
- return new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, image->colorSpace());
- }
-
RemoveNodeHelper::~RemoveNodeHelper()
{
}
/**
* The removal of two nodes in one go may be a bit tricky, because one
* of them may be the clone of another. If we remove the source of a
* clone layer, it will reincarnate into a paint layer. In this case
* the pointer to the second layer will be lost.
*
* That's why we need to care about the order of the nodes removal:
* the clone --- first, the source --- last.
*/
void RemoveNodeHelper::safeRemoveMultipleNodes(KisNodeList nodes, KisImageSP image) {
const bool lastLayer = scanForLastLayer(image, nodes);
auto isNodeWeird = [] (KisNodeSP node) {
const bool normalCompositeMode = node->compositeOpId() == COMPOSITE_OVER;
KisLayer *layer = dynamic_cast<KisLayer*>(node.data());
const bool hasInheritAlpha = layer && layer->alphaChannelDisabled();
return !normalCompositeMode && !hasInheritAlpha;
};
while (!nodes.isEmpty()) {
KisNodeList::iterator it = nodes.begin();
while (it != nodes.end()) {
if (!checkIsSourceForClone(*it, nodes)) {
KisNodeSP node = *it;
addCommandImpl(new KisImageLayerRemoveCommand(image, node, !isNodeWeird(node), true));
it = nodes.erase(it);
} else {
++it;
}
}
}
if (lastLayer) {
- KisLayerSP newLayer = constructDefaultLayer(image);
+ KisLayerSP newLayer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, image->colorSpace());
addCommandImpl(new KisImageLayerAddCommand(image, newLayer,
image->root(),
KisNodeSP(),
false, false));
}
}
bool RemoveNodeHelper::checkIsSourceForClone(KisNodeSP src, const KisNodeList &nodes) {
foreach (KisNodeSP node, nodes) {
if (node == src) continue;
KisCloneLayer *clone = dynamic_cast<KisCloneLayer*>(node.data());
if (clone && KisNodeSP(clone->copyFrom()) == src) {
return true;
}
}
return false;
}
bool RemoveNodeHelper::scanForLastLayer(KisImageWSP image, KisNodeList nodesToRemove) {
bool removeLayers = false;
Q_FOREACH(KisNodeSP nodeToRemove, nodesToRemove) {
if (qobject_cast<KisLayer*>(nodeToRemove.data())) {
removeLayers = true;
break;
}
}
if (!removeLayers) return false;
bool lastLayer = true;
KisNodeSP node = image->root()->firstChild();
while (node) {
if (!nodesToRemove.contains(node) &&
qobject_cast<KisLayer*>(node.data())) {
lastLayer = false;
break;
}
node = node->nextSibling();
}
return lastLayer;
}
SimpleRemoveLayers::SimpleRemoveLayers(const KisNodeList &nodes,
KisImageSP image)
: m_nodes(nodes),
m_image(image)
{
}
void SimpleRemoveLayers::populateChildCommands() {
if (m_nodes.isEmpty()) return;
safeRemoveMultipleNodes(m_nodes, m_image);
}
void SimpleRemoveLayers::addCommandImpl(KUndo2Command *cmd) {
addCommand(cmd);
}
struct InsertNode : public KisCommandUtils::AggregateCommand {
InsertNode(MergeDownInfoBaseSP info, KisNodeSP putAfter)
: m_info(info), m_putAfter(putAfter) {}
void populateChildCommands() override {
addCommand(new KisImageLayerAddCommand(m_info->image,
m_info->dstNode,
m_putAfter->parent(),
m_putAfter,
true, false));
}
private:
virtual void addCommandImpl(KUndo2Command *cmd) {
addCommand(cmd);
}
private:
MergeDownInfoBaseSP m_info;
KisNodeSP m_putAfter;
};
struct CleanUpNodes : private RemoveNodeHelper, public KisCommandUtils::AggregateCommand {
CleanUpNodes(MergeDownInfoBaseSP info, KisNodeSP putAfter)
: m_info(info), m_putAfter(putAfter) {}
static void findPerfectParent(KisNodeList nodesToDelete, KisNodeSP &putAfter, KisNodeSP &parent) {
if (!putAfter) {
putAfter = nodesToDelete.last();
}
// Add the new merged node on top of the active node -- checking
// whether the parent is going to be deleted
parent = putAfter->parent();
while (parent && nodesToDelete.contains(parent)) {
parent = parent->parent();
}
}
void populateChildCommands() override {
KisNodeList nodesToDelete = m_info->allSrcNodes();
KisNodeSP parent;
findPerfectParent(nodesToDelete, m_putAfter, parent);
if (!parent) {
KisNodeSP oldRoot = m_info->image->root();
KisNodeSP newRoot(new KisGroupLayer(m_info->image, "root", OPACITY_OPAQUE_U8));
addCommand(new KisImageLayerAddCommand(m_info->image,
m_info->dstNode,
newRoot,
KisNodeSP(),
true, false));
addCommand(new KisImageChangeLayersCommand(m_info->image, oldRoot, newRoot));
}
else {
if (parent == m_putAfter->parent()) {
addCommand(new KisImageLayerAddCommand(m_info->image,
m_info->dstNode,
parent,
m_putAfter,
true, false));
}
else {
addCommand(new KisImageLayerAddCommand(m_info->image,
m_info->dstNode,
parent,
parent->lastChild(),
true, false));
}
/**
* We can merge selection masks, in this case dstLayer is not defined!
*/
if (m_info->dstLayer()) {
reparentSelectionMasks(m_info->image,
m_info->dstLayer(),
m_info->selectionMasks);
}
KisNodeList safeNodesToDelete = m_info->allSrcNodes();
for (KisNodeList::iterator it = safeNodesToDelete.begin(); it != safeNodesToDelete.end(); ++it) {
KisNodeSP node = *it;
if (node->userLocked() && node->visible()) {
addCommand(new KisImageChangeVisibilityCommand(false, node));
}
}
KritaUtils::filterContainer<KisNodeList>(safeNodesToDelete, [](KisNodeSP node) {
return !node->userLocked();
});
safeRemoveMultipleNodes(safeNodesToDelete, m_info->image);
}
}
private:
void addCommandImpl(KUndo2Command *cmd) override {
addCommand(cmd);
}
void reparentSelectionMasks(KisImageSP image,
KisLayerSP newLayer,
const QVector<KisSelectionMaskSP> &selectionMasks) {
KIS_SAFE_ASSERT_RECOVER_RETURN(newLayer);
foreach (KisSelectionMaskSP mask, selectionMasks) {
addCommand(new KisImageLayerMoveCommand(image, mask, newLayer, newLayer->lastChild()));
addCommand(new KisActivateSelectionMaskCommand(mask, false));
}
}
private:
MergeDownInfoBaseSP m_info;
KisNodeSP m_putAfter;
};
SwitchFrameCommand::SharedStorage::~SharedStorage() {
}
SwitchFrameCommand::SwitchFrameCommand(KisImageSP image, int time, bool finalize, SharedStorageSP storage)
: FlipFlopCommand(finalize),
m_image(image),
m_newTime(time),
m_storage(storage) {}
SwitchFrameCommand::~SwitchFrameCommand() {}
void SwitchFrameCommand::partA() {
KisImageAnimationInterface *interface = m_image->animationInterface();
const int currentTime = interface->currentTime();
if (currentTime == m_newTime) {
m_storage->value = m_newTime;
return;
}
interface->image()->disableUIUpdates();
interface->saveAndResetCurrentTime(m_newTime, &m_storage->value);
}
void SwitchFrameCommand::partB() {
KisImageAnimationInterface *interface = m_image->animationInterface();
const int currentTime = interface->currentTime();
if (currentTime == m_storage->value) {
return;
}
interface->restoreCurrentTime(&m_storage->value);
interface->image()->enableUIUpdates();
}
struct AddNewFrame : public KisCommandUtils::AggregateCommand {
AddNewFrame(MergeDownInfoBaseSP info, int frame) : m_info(info), m_frame(frame) {}
void populateChildCommands() override {
KUndo2Command *cmd = new KisCommandUtils::SkipFirstRedoWrapper();
KisKeyframeChannel *channel = m_info->dstNode->getKeyframeChannel(KisKeyframeChannel::Content.id());
KisKeyframeSP keyframe = channel->addKeyframe(m_frame, cmd);
applyKeyframeColorLabel(keyframe);
addCommand(cmd);
}
void applyKeyframeColorLabel(KisKeyframeSP dstKeyframe) {
Q_FOREACH(KisNodeSP srcNode, m_info->allSrcNodes()) {
Q_FOREACH(KisKeyframeChannel *channel, srcNode->keyframeChannels().values()) {
KisKeyframeSP keyframe = channel->keyframeAt(m_frame);
if (!keyframe.isNull() && keyframe->colorLabel() != 0) {
dstKeyframe->setColorLabel(keyframe->colorLabel());
return;
}
}
}
dstKeyframe->setColorLabel(0);
}
private:
MergeDownInfoBaseSP m_info;
int m_frame;
};
QSet<int> fetchLayerFrames(KisNodeSP node) {
KisKeyframeChannel *channel = node->getKeyframeChannel(KisKeyframeChannel::Content.id());
if (!channel) return QSet<int>();
return channel->allKeyframeIds();
}
QSet<int> fetchLayerFramesRecursive(KisNodeSP rootNode) {
QSet<int> frames = fetchLayerFrames(rootNode);
KisNodeSP node = rootNode->firstChild();
while(node) {
frames |= fetchLayerFramesRecursive(node);
node = node->nextSibling();
}
return frames;
}
void updateFrameJobs(FrameJobs *jobs, KisNodeSP node) {
QSet<int> frames = fetchLayerFrames(node);
if (frames.isEmpty()) {
(*jobs)[0].insert(node);
} else {
foreach (int frame, frames) {
(*jobs)[frame].insert(node);
}
}
}
void updateFrameJobsRecursive(FrameJobs *jobs, KisNodeSP rootNode) {
updateFrameJobs(jobs, rootNode);
KisNodeSP node = rootNode->firstChild();
while(node) {
updateFrameJobsRecursive(jobs, node);
node = node->nextSibling();
}
}
void mergeDown(KisImageSP image, KisLayerSP layer, const KisMetaData::MergeStrategy* strategy)
{
if (!layer->prevSibling()) return;
// XXX: this breaks if we allow free mixing of masks and layers
KisLayerSP prevLayer = qobject_cast<KisLayer*>(layer->prevSibling().data());
if (!prevLayer) return;
if (!layer->visible() && !prevLayer->visible()) {
return;
}
KisImageSignalVector emitSignals;
emitSignals << ModifiedSignal;
KisProcessingApplicator applicator(image, 0,
KisProcessingApplicator::NONE,
emitSignals,
kundo2_i18n("Merge Down"));
if (layer->visible() && prevLayer->visible()) {
MergeDownInfoSP info(new MergeDownInfo(image, prevLayer, layer));
// disable key strokes on all colorize masks, all onion skins on
// paint layers and wait until update is finished with a barrier
applicator.applyCommand(new DisableColorizeKeyStrokes(info));
applicator.applyCommand(new DisableOnionSkins(info));
applicator.applyCommand(new KUndo2Command(), KisStrokeJobData::BARRIER);
applicator.applyCommand(new KeepMergedNodesSelected(info, false));
applicator.applyCommand(new FillSelectionMasks(info));
applicator.applyCommand(new CreateMergedLayer(info), KisStrokeJobData::BARRIER);
// NOTE: shape layer may have emitted spontaneous jobs during layer creation,
// wait for them to complete!
applicator.applyCommand(new RefreshDelayedUpdateLayers(info), KisStrokeJobData::BARRIER);
applicator.applyCommand(new KUndo2Command(), KisStrokeJobData::BARRIER);
// in two-layer mode we disable pass through only when the destination layer
// is not a group layer
applicator.applyCommand(new DisablePassThroughForHeadsOnly(info, true));
applicator.applyCommand(new KUndo2Command(), KisStrokeJobData::BARRIER);
if (info->frames.size() > 0) {
foreach (int frame, info->frames) {
applicator.applyCommand(new SwitchFrameCommand(info->image, frame, false, info->storage));
applicator.applyCommand(new AddNewFrame(info, frame));
applicator.applyCommand(new RefreshHiddenAreas(info));
applicator.applyCommand(new RefreshDelayedUpdateLayers(info), KisStrokeJobData::BARRIER);
applicator.applyCommand(new MergeLayers(info), KisStrokeJobData::BARRIER);
applicator.applyCommand(new SwitchFrameCommand(info->image, frame, true, info->storage));
}
} else {
applicator.applyCommand(new RefreshHiddenAreas(info));
applicator.applyCommand(new RefreshDelayedUpdateLayers(info), KisStrokeJobData::BARRIER);
applicator.applyCommand(new MergeLayers(info), KisStrokeJobData::BARRIER);
}
applicator.applyCommand(new MergeMetaData(info, strategy), KisStrokeJobData::BARRIER);
applicator.applyCommand(new CleanUpNodes(info, layer),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
applicator.applyCommand(new KeepMergedNodesSelected(info, true));
} else if (layer->visible()) {
applicator.applyCommand(new KeepNodesSelectedCommand(KisNodeList(), KisNodeList(),
layer, KisNodeSP(),
image, false));
applicator.applyCommand(
new SimpleRemoveLayers(KisNodeList() << prevLayer,
image),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
applicator.applyCommand(new KeepNodesSelectedCommand(KisNodeList(), KisNodeList(),
KisNodeSP(), layer,
image, true));
} else if (prevLayer->visible()) {
applicator.applyCommand(new KeepNodesSelectedCommand(KisNodeList(), KisNodeList(),
layer, KisNodeSP(),
image, false));
applicator.applyCommand(
new SimpleRemoveLayers(KisNodeList() << layer,
image),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
applicator.applyCommand(new KeepNodesSelectedCommand(KisNodeList(), KisNodeList(),
KisNodeSP(), prevLayer,
image, true));
}
applicator.end();
}
bool checkIsChildOf(KisNodeSP node, const KisNodeList &parents)
{
KisNodeList nodeParents;
KisNodeSP parent = node->parent();
while (parent) {
nodeParents << parent;
parent = parent->parent();
}
foreach(KisNodeSP perspectiveParent, parents) {
if (nodeParents.contains(perspectiveParent)) {
return true;
}
}
return false;
}
bool checkIsCloneOf(KisNodeSP node, const KisNodeList &nodes)
{
bool result = false;
KisCloneLayer *clone = dynamic_cast<KisCloneLayer*>(node.data());
if (clone) {
KisNodeSP cloneSource = KisNodeSP(clone->copyFrom());
Q_FOREACH(KisNodeSP subtree, nodes) {
result =
recursiveFindNode(subtree,
[cloneSource](KisNodeSP node) -> bool
{
return node == cloneSource;
});
if (!result) {
result = checkIsCloneOf(cloneSource, nodes);
}
if (result) {
break;
}
}
}
return result;
}
void filterMergableNodes(KisNodeList &nodes, bool allowMasks)
{
KisNodeList::iterator it = nodes.begin();
while (it != nodes.end()) {
if ((!allowMasks && !qobject_cast<KisLayer*>(it->data())) ||
checkIsChildOf(*it, nodes)) {
//qDebug() << "Skipping node" << ppVar((*it)->name());
it = nodes.erase(it);
} else {
++it;
}
}
}
void sortMergableNodes(KisNodeSP root, KisNodeList &inputNodes, KisNodeList &outputNodes)
{
KisNodeList::iterator it = std::find(inputNodes.begin(), inputNodes.end(), root);
if (it != inputNodes.end()) {
outputNodes << *it;
inputNodes.erase(it);
}
if (inputNodes.isEmpty()) {
return;
}
KisNodeSP child = root->firstChild();
while (child) {
sortMergableNodes(child, inputNodes, outputNodes);
child = child->nextSibling();
}
/**
* By the end of recursion \p inputNodes must be empty
*/
KIS_ASSERT_RECOVER_NOOP(root->parent() || inputNodes.isEmpty());
}
KisNodeList sortMergableNodes(KisNodeSP root, KisNodeList nodes)
{
KisNodeList result;
sortMergableNodes(root, nodes, result);
return result;
}
KisNodeList sortAndFilterMergableInternalNodes(KisNodeList nodes, bool allowMasks)
{
KIS_ASSERT_RECOVER(!nodes.isEmpty()) { return nodes; }
KisNodeSP root;
Q_FOREACH(KisNodeSP node, nodes) {
KisNodeSP localRoot = node;
while (localRoot->parent()) {
localRoot = localRoot->parent();
}
if (!root) {
root = localRoot;
}
KIS_ASSERT_RECOVER(root == localRoot) { return nodes; }
}
KisNodeList result;
sortMergableNodes(root, nodes, result);
filterMergableNodes(result, allowMasks);
return result;
}
void addCopyOfNameTag(KisNodeSP node)
{
const QString prefix = i18n("Copy of");
QString newName = node->name();
if (!newName.startsWith(prefix)) {
newName = QString("%1 %2").arg(prefix).arg(newName);
node->setName(newName);
}
}
KisNodeList findNodesWithProps(KisNodeSP root, const KoProperties &props, bool excludeRoot)
{
KisNodeList nodes;
if ((!excludeRoot || root->parent()) && root->check(props)) {
nodes << root;
}
KisNodeSP node = root->firstChild();
while (node) {
nodes += findNodesWithProps(node, props, excludeRoot);
node = node->nextSibling();
}
return nodes;
}
KisNodeList filterInvisibleNodes(const KisNodeList &nodes, KisNodeList *invisibleNodes, KisNodeSP *putAfter)
{
KIS_ASSERT_RECOVER(invisibleNodes) { return nodes; }
KIS_ASSERT_RECOVER(putAfter) { return nodes; }
KisNodeList visibleNodes;
int putAfterIndex = -1;
Q_FOREACH(KisNodeSP node, nodes) {
if (node->visible() || node->userLocked()) {
visibleNodes << node;
} else {
*invisibleNodes << node;
if (node == *putAfter) {
putAfterIndex = visibleNodes.size() - 1;
}
}
}
if (!visibleNodes.isEmpty() && putAfterIndex >= 0) {
putAfterIndex = qBound(0, putAfterIndex, visibleNodes.size() - 1);
*putAfter = visibleNodes[putAfterIndex];
}
return visibleNodes;
}
void changeImageDefaultProjectionColor(KisImageSP image, const KoColor &color)
{
KisImageSignalVector emitSignals;
emitSignals << ModifiedSignal;
KisProcessingApplicator applicator(image,
image->root(),
KisProcessingApplicator::RECURSIVE,
emitSignals,
kundo2_i18n("Change projection color"),
0,
142857 + 1);
applicator.applyCommand(new KisChangeProjectionColorCommand(image, color), KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE);
applicator.end();
}
void mergeMultipleLayersImpl(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter,
bool flattenSingleLayer, const KUndo2MagicString &actionName,
bool cleanupNodes = true, const QString layerName = QString())
{
if (!putAfter) {
putAfter = mergedNodes.first();
}
filterMergableNodes(mergedNodes);
{
KisNodeList tempNodes;
std::swap(mergedNodes, tempNodes);
sortMergableNodes(image->root(), tempNodes, mergedNodes);
}
if (mergedNodes.size() <= 1 &&
(!flattenSingleLayer && mergedNodes.size() == 1)) return;
KisImageSignalVector emitSignals;
emitSignals << ModifiedSignal;
emitSignals << ComplexNodeReselectionSignal(KisNodeSP(), KisNodeList(), KisNodeSP(), mergedNodes);
KisProcessingApplicator applicator(image, 0,
KisProcessingApplicator::NONE,
emitSignals,
actionName);
KisNodeList originalNodes = mergedNodes;
KisNodeList invisibleNodes;
mergedNodes = filterInvisibleNodes(originalNodes, &invisibleNodes, &putAfter);
if (!invisibleNodes.isEmpty()) {
/* If the putAfter node is invisible,
* we should instead pick one of the nodes
* to be merged to avoid a null putAfter.
*/
if (!putAfter->visible()){
putAfter = mergedNodes.first();
}
applicator.applyCommand(
new SimpleRemoveLayers(invisibleNodes,
image),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
}
if (mergedNodes.size() > 1 || invisibleNodes.isEmpty()) {
MergeMultipleInfoSP info(new MergeMultipleInfo(image, mergedNodes));
// disable key strokes on all colorize masks, all onion skins on
// paint layers and wait until update is finished with a barrier
applicator.applyCommand(new DisableColorizeKeyStrokes(info));
applicator.applyCommand(new DisableOnionSkins(info));
applicator.applyCommand(new DisablePassThroughForHeadsOnly(info));
applicator.applyCommand(new KUndo2Command(), KisStrokeJobData::BARRIER);
applicator.applyCommand(new KeepMergedNodesSelected(info, putAfter, false));
applicator.applyCommand(new FillSelectionMasks(info));
applicator.applyCommand(new CreateMergedLayerMultiple(info, layerName), KisStrokeJobData::BARRIER);
applicator.applyCommand(new DisableExtraCompositing(info));
applicator.applyCommand(new KUndo2Command(), KisStrokeJobData::BARRIER);
if (info->frames.size() > 0) {
foreach (int frame, info->frames) {
applicator.applyCommand(new SwitchFrameCommand(info->image, frame, false, info->storage));
applicator.applyCommand(new AddNewFrame(info, frame));
applicator.applyCommand(new RefreshHiddenAreas(info));
applicator.applyCommand(new RefreshDelayedUpdateLayers(info), KisStrokeJobData::BARRIER);
applicator.applyCommand(new MergeLayersMultiple(info), KisStrokeJobData::BARRIER);
applicator.applyCommand(new SwitchFrameCommand(info->image, frame, true, info->storage));
}
} else {
applicator.applyCommand(new RefreshHiddenAreas(info));
applicator.applyCommand(new RefreshDelayedUpdateLayers(info), KisStrokeJobData::BARRIER);
applicator.applyCommand(new MergeLayersMultiple(info), KisStrokeJobData::BARRIER);
}
//applicator.applyCommand(new MergeMetaData(info, strategy), KisStrokeJobData::BARRIER);
if (cleanupNodes){
applicator.applyCommand(new CleanUpNodes(info, putAfter),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
} else {
applicator.applyCommand(new InsertNode(info, putAfter),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
}
applicator.applyCommand(new KeepMergedNodesSelected(info, putAfter, true));
}
applicator.end();
}
void mergeMultipleLayers(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter)
{
mergeMultipleLayersImpl(image, mergedNodes, putAfter, false, kundo2_i18n("Merge Selected Nodes"));
}
void newLayerFromVisible(KisImageSP image, KisNodeSP putAfter)
{
KisNodeList mergedNodes;
mergedNodes << image->root();
mergeMultipleLayersImpl(image, mergedNodes, putAfter, true, kundo2_i18n("New From Visible"), false, i18nc("New layer created from all the visible layers", "Visible"));
}
struct MergeSelectionMasks : public KisCommandUtils::AggregateCommand {
MergeSelectionMasks(MergeDownInfoBaseSP info, KisNodeSP putAfter)
: m_info(info),
m_putAfter(putAfter){}
void populateChildCommands() override {
KisNodeSP parent;
CleanUpNodes::findPerfectParent(m_info->allSrcNodes(), m_putAfter, parent);
KisLayerSP parentLayer;
do {
parentLayer = qobject_cast<KisLayer*>(parent.data());
parent = parent->parent();
} while(!parentLayer && parent);
KisSelectionSP selection = new KisSelection();
foreach (KisNodeSP node, m_info->allSrcNodes()) {
KisMaskSP mask = dynamic_cast<KisMask*>(node.data());
if (!mask) continue;
selection->pixelSelection()->applySelection(
mask->selection()->pixelSelection(), SELECTION_ADD);
}
KisSelectionMaskSP mergedMask = new KisSelectionMask(m_info->image);
mergedMask->initSelection(parentLayer);
mergedMask->setSelection(selection);
m_info->dstNode = mergedMask;
}
private:
MergeDownInfoBaseSP m_info;
KisNodeSP m_putAfter;
};
struct ActivateSelectionMask : public KisCommandUtils::AggregateCommand {
ActivateSelectionMask(MergeDownInfoBaseSP info)
: m_info(info) {}
void populateChildCommands() override {
KisSelectionMaskSP mergedMask = dynamic_cast<KisSelectionMask*>(m_info->dstNode.data());
addCommand(new KisActivateSelectionMaskCommand(mergedMask, true));
}
private:
MergeDownInfoBaseSP m_info;
};
bool tryMergeSelectionMasks(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter)
{
QList<KisSelectionMaskSP> selectionMasks;
for (auto it = mergedNodes.begin(); it != mergedNodes.end(); /*noop*/) {
KisSelectionMaskSP mask = dynamic_cast<KisSelectionMask*>(it->data());
if (!mask) {
it = mergedNodes.erase(it);
} else {
selectionMasks.append(mask);
++it;
}
}
if (mergedNodes.isEmpty()) return false;
KisLayerSP parentLayer = qobject_cast<KisLayer*>(selectionMasks.first()->parent().data());
KIS_ASSERT_RECOVER(parentLayer) { return 0; }
KisImageSignalVector emitSignals;
emitSignals << ModifiedSignal;
KisProcessingApplicator applicator(image, 0,
KisProcessingApplicator::NONE,
emitSignals,
kundo2_i18n("Merge Selection Masks"));
MergeMultipleInfoSP info(new MergeMultipleInfo(image, mergedNodes));
applicator.applyCommand(new MergeSelectionMasks(info, putAfter));
applicator.applyCommand(new CleanUpNodes(info, putAfter),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::EXCLUSIVE);
applicator.applyCommand(new ActivateSelectionMask(info));
applicator.end();
return true;
}
void flattenLayer(KisImageSP image, KisLayerSP layer)
{
if (!layer->childCount() && !layer->layerStyle())
return;
KisNodeList mergedNodes;
mergedNodes << layer;
mergeMultipleLayersImpl(image, mergedNodes, layer, true, kundo2_i18n("Flatten Layer"));
}
void flattenImage(KisImageSP image, KisNodeSP activeNode)
{
if (!activeNode) {
activeNode = image->root()->lastChild();
}
KisNodeList mergedNodes;
mergedNodes << image->root();
mergeMultipleLayersImpl(image, mergedNodes, activeNode, true, kundo2_i18n("Flatten Image"));
}
KisSimpleUpdateCommand::KisSimpleUpdateCommand(KisNodeList nodes, bool finalize, KUndo2Command *parent)
: FlipFlopCommand(finalize, parent),
m_nodes(nodes)
{
}
void KisSimpleUpdateCommand::partB()
{
updateNodes(m_nodes);
}
void KisSimpleUpdateCommand::updateNodes(const KisNodeList &nodes)
{
Q_FOREACH(KisNodeSP node, nodes) {
node->setDirty(node->extent());
}
}
KisNodeSP recursiveFindNode(KisNodeSP node, std::function<bool(KisNodeSP)> func)
{
if (func(node)) {
return node;
}
node = node->firstChild();
while (node) {
KisNodeSP resultNode = recursiveFindNode(node, func);
if (resultNode) {
return resultNode;
}
node = node->nextSibling();
}
return 0;
}
KisNodeSP findNodeByUuid(KisNodeSP root, const QUuid &uuid)
{
return recursiveFindNode(root,
[uuid] (KisNodeSP node) {
return node->uuid() == uuid;
});
}
void forceAllDelayedNodesUpdate(KisNodeSP root)
{
KisLayerUtils::recursiveApplyNodes(root,
[] (KisNodeSP node) {
KisDelayedUpdateNodeInterface *delayedUpdate =
dynamic_cast<KisDelayedUpdateNodeInterface*>(node.data());
if (delayedUpdate) {
delayedUpdate->forceUpdateTimedNode();
}
});
}
KisImageSP findImageByHierarchy(KisNodeSP node)
{
while (node) {
const KisLayer *layer = dynamic_cast<const KisLayer*>(node.data());
if (layer) {
return layer->image();
}
node = node->parent();
}
return 0;
}
}
diff --git a/libs/image/kis_layer_utils.h b/libs/image/kis_layer_utils.h
index f6d1e725b7..821fcc0745 100644
--- a/libs/image/kis_layer_utils.h
+++ b/libs/image/kis_layer_utils.h
@@ -1,226 +1,224 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_UTILS_H
#define __KIS_LAYER_UTILS_H
#include <functional>
#include "kundo2command.h"
#include "kis_types.h"
#include "kritaimage_export.h"
#include "kis_command_utils.h"
class KoProperties;
class KoColor;
class QUuid;
namespace KisMetaData
{
class MergeStrategy;
}
namespace KisLayerUtils
{
KRITAIMAGE_EXPORT void sortMergableNodes(KisNodeSP root, QList<KisNodeSP> &inputNodes, QList<KisNodeSP> &outputNodes);
KRITAIMAGE_EXPORT KisNodeList sortMergableNodes(KisNodeSP root, KisNodeList nodes);
KRITAIMAGE_EXPORT void filterMergableNodes(KisNodeList &nodes, bool allowMasks = false);
KRITAIMAGE_EXPORT bool checkIsChildOf(KisNodeSP node, const KisNodeList &parents);
/**
* Returns true if:
* o \p node is a clone of some layer in \p nodes
* o \p node is a clone any child layer of any layer in \p nodes
* o \p node is a clone of a clone of a ..., that in the end points
* to any layer in \p nodes of their children.
*/
KRITAIMAGE_EXPORT bool checkIsCloneOf(KisNodeSP node, const KisNodeList &nodes);
KRITAIMAGE_EXPORT void forceAllDelayedNodesUpdate(KisNodeSP root);
KRITAIMAGE_EXPORT KisNodeList sortAndFilterMergableInternalNodes(KisNodeList nodes, bool allowMasks = false);
KRITAIMAGE_EXPORT void mergeDown(KisImageSP image, KisLayerSP layer, const KisMetaData::MergeStrategy* strategy);
KRITAIMAGE_EXPORT QSet<int> fetchLayerFrames(KisNodeSP node);
KRITAIMAGE_EXPORT QSet<int> fetchLayerFramesRecursive(KisNodeSP rootNode);
KRITAIMAGE_EXPORT void mergeMultipleLayers(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter);
KRITAIMAGE_EXPORT void newLayerFromVisible(KisImageSP image, KisNodeSP putAfter);
KRITAIMAGE_EXPORT bool tryMergeSelectionMasks(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter);
KRITAIMAGE_EXPORT void flattenLayer(KisImageSP image, KisLayerSP layer);
KRITAIMAGE_EXPORT void flattenImage(KisImageSP image, KisNodeSP activeNode);
KRITAIMAGE_EXPORT void addCopyOfNameTag(KisNodeSP node);
KRITAIMAGE_EXPORT KisNodeList findNodesWithProps(KisNodeSP root, const KoProperties &props, bool excludeRoot);
KRITAIMAGE_EXPORT void changeImageDefaultProjectionColor(KisImageSP image, const KoColor &color);
typedef QMap<int, QSet<KisNodeSP> > FrameJobs;
void updateFrameJobs(FrameJobs *jobs, KisNodeSP node);
void updateFrameJobsRecursive(FrameJobs *jobs, KisNodeSP rootNode);
struct SwitchFrameCommand : public KisCommandUtils::FlipFlopCommand {
struct SharedStorage {
/**
* For some reason the absence of a destructor in the SharedStorage
* makes Krita crash on exit. Seems like some compiler weirdness... (DK)
*/
~SharedStorage();
int value;
};
typedef QSharedPointer<SharedStorage> SharedStorageSP;
public:
SwitchFrameCommand(KisImageSP image, int time, bool finalize, SharedStorageSP storage);
~SwitchFrameCommand() override;
private:
void partA() override;
void partB() override;
private:
KisImageWSP m_image;
int m_newTime;
SharedStorageSP m_storage;
};
/**
* A command to keep correct set of selected/active nodes thoroughout
* the action.
*/
class KRITAIMAGE_EXPORT KeepNodesSelectedCommand : public KisCommandUtils::FlipFlopCommand
{
public:
KeepNodesSelectedCommand(const KisNodeList &selectedBefore,
const KisNodeList &selectedAfter,
KisNodeSP activeBefore,
KisNodeSP activeAfter,
KisImageSP image,
bool finalize, KUndo2Command *parent = 0);
void partB() override;
private:
KisNodeList m_selectedBefore;
KisNodeList m_selectedAfter;
KisNodeSP m_activeBefore;
KisNodeSP m_activeAfter;
KisImageWSP m_image;
};
struct KRITAIMAGE_EXPORT SelectGlobalSelectionMask : public KUndo2Command
{
SelectGlobalSelectionMask(KisImageSP image);
void redo() override;
KisImageSP m_image;
};
- KRITAIMAGE_EXPORT KisLayerSP constructDefaultLayer(KisImageSP image);
-
class KRITAIMAGE_EXPORT RemoveNodeHelper {
public:
virtual ~RemoveNodeHelper();
protected:
virtual void addCommandImpl(KUndo2Command *cmd) = 0;
void safeRemoveMultipleNodes(KisNodeList nodes, KisImageSP image);
private:
bool checkIsSourceForClone(KisNodeSP src, const KisNodeList &nodes);
static bool scanForLastLayer(KisImageWSP image, KisNodeList nodesToRemove);
};
struct SimpleRemoveLayers : private KisLayerUtils::RemoveNodeHelper, public KisCommandUtils::AggregateCommand {
SimpleRemoveLayers(const KisNodeList &nodes,
KisImageSP image);
void populateChildCommands() override;
protected:
void addCommandImpl(KUndo2Command *cmd) override;
private:
KisNodeList m_nodes;
KisImageSP m_image;
KisNodeList m_selectedNodes;
KisNodeSP m_activeNode;
};
class KRITAIMAGE_EXPORT KisSimpleUpdateCommand : public KisCommandUtils::FlipFlopCommand
{
public:
KisSimpleUpdateCommand(KisNodeList nodes, bool finalize, KUndo2Command *parent = 0);
void partB() override;
static void updateNodes(const KisNodeList &nodes);
private:
KisNodeList m_nodes;
};
template <typename T>
bool checkNodesDiffer(KisNodeList nodes, std::function<T(KisNodeSP)> checkerFunc)
{
bool valueDiffers = false;
bool initialized = false;
T currentValue = T();
Q_FOREACH (KisNodeSP node, nodes) {
if (!initialized) {
currentValue = checkerFunc(node);
initialized = true;
} else if (currentValue != checkerFunc(node)) {
valueDiffers = true;
break;
}
}
return valueDiffers;
}
/**
* Applies \p func to \p node and all its children recursively
*/
template <typename NodePointer, typename Functor>
void recursiveApplyNodes(NodePointer node, Functor func)
{
func(node);
node = node->firstChild();
while (node) {
recursiveApplyNodes(node, func);
node = node->nextSibling();
}
}
/**
* Walks through \p node and all its children recursively until
* \p func returns true. When \p func returns true, the node is
* considered to be found, the search is stopped and the found
* node is returned to the caller.
*/
KisNodeSP KRITAIMAGE_EXPORT recursiveFindNode(KisNodeSP node, std::function<bool(KisNodeSP)> func);
/**
* Recursively searches for a node with specified Uuid
*/
KisNodeSP KRITAIMAGE_EXPORT findNodeByUuid(KisNodeSP root, const QUuid &uuid);
KisImageSP KRITAIMAGE_EXPORT findImageByHierarchy(KisNodeSP node);
};
#endif /* __KIS_LAYER_UTILS_H */
diff --git a/libs/image/kis_math_toolbox.h b/libs/image/kis_math_toolbox.h
index 69fb436244..c9fd79257c 100644
--- a/libs/image/kis_math_toolbox.h
+++ b/libs/image/kis_math_toolbox.h
@@ -1,145 +1,148 @@
/*
* This file is part of the KDE project
*
* Copyright (c) 2005 Cyrille Berger <cberger@cberger.net>
*
* 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_MATH_TOOLBOX_H
#define KIS_MATH_TOOLBOX_H
#include <QObject>
#include <QRect>
#include <new>
#include <KoColorSpace.h>
#include "kis_types.h"
#include "kis_paint_device.h"
#ifdef _MSC_VER
#pragma warning(disable: 4290) // disable "C++ exception specification ignored" warning
#endif
#if !defined _MSC_VER
#pragma GCC diagnostic ignored "-Wcast-align"
#endif
typedef double(*PtrToDouble)(const quint8*, int);
typedef void (*PtrFromDouble)(quint8*, int, double);
class KRITAIMAGE_EXPORT KisMathToolbox
{
public:
struct KisFloatRepresentation {
KisFloatRepresentation(uint nsize, uint ndepth)
: coeffs(new float[nsize*nsize*ndepth])
, size(nsize)
, depth(ndepth) {
// XXX: Valgrind shows that these are being used without being initialised.
for (quint32 i = 0; i < nsize * nsize * ndepth; ++i) {
coeffs[i] = 0;
}
}
~KisFloatRepresentation() {
if (coeffs) delete[] coeffs;
}
float* coeffs;
uint size;
uint depth;
};
typedef KisFloatRepresentation KisWavelet;
/**
* This function initializes a wavelet structure
* @param lay the layer that will be used for the transformation
+ * @param rect the rectangular for transformation
*/
inline KisWavelet* initWavelet(KisPaintDeviceSP lay, const QRect&);
inline uint fastWaveletTotalSteps(const QRect&);
/**
* This function reconstruct the layer from the information of a wavelet
* @param src layer from which the wavelet will be computed
+ * @param rect the rectangular for reconstruction
* @param buff if set to 0, the buffer will be initialized by the function,
* you might want to give a buff to the function if you want to use the same buffer
* in transformToWavelet and in untransformToWavelet, use initWavelet to initialize
* the buffer
*/
KisWavelet* fastWaveletTransformation(KisPaintDeviceSP src, const QRect&, KisWavelet* buff = 0);
/**
* This function reconstruct the layer from the information of a wavelet
* @param dst layer on which the wavelet will be untransform
+ * @param rect the rectangular for reconstruction
* @param wav the wavelet
* @param buff if set to 0, the buffer will be initialized by the function,
* you might want to give a buff to the function if you want to use the same buffer
* in transformToWavelet and in untransformToWavelet, use initWavelet to initialize
* the buffer
*/
void fastWaveletUntransformation(KisPaintDeviceSP dst, const QRect&, KisWavelet* wav, KisWavelet* buff = 0);
bool getToDoubleChannelPtr(QList<KoChannelInfo *> cis, QVector<PtrToDouble>& f);
bool getFromDoubleChannelPtr(QList<KoChannelInfo *> cis, QVector<PtrFromDouble>& f);
double minChannelValue(KoChannelInfo *);
double maxChannelValue(KoChannelInfo *);
private:
void wavetrans(KisWavelet* wav, KisWavelet* buff, uint halfsize);
void waveuntrans(KisWavelet* wav, KisWavelet* buff, uint halfsize);
/**
* This function transform a paint device into a KisFloatRepresentation, this function is colorspace independent,
* for Wavelet, Pyramid and FFT the data is always the exact value of the channel stored in a float.
*/
void transformToFR(KisPaintDeviceSP src, KisFloatRepresentation*, const QRect&);
/**
* This function transform a KisFloatRepresentation into a paint device, this function is colorspace independent,
* for Wavelet, Pyramid and FFT the data is always the exact value of the channel stored in a float.
*/
void transformFromFR(KisPaintDeviceSP dst, KisFloatRepresentation*, const QRect&);
};
inline KisMathToolbox::KisWavelet* KisMathToolbox::initWavelet(KisPaintDeviceSP src, const QRect& rect)
{
int size;
int maxrectsize = (rect.height() < rect.width()) ? rect.width() : rect.height();
for (size = 2; size < maxrectsize; size *= 2) ;
qint32 depth = src->colorSpace()->colorChannelCount();
return new KisWavelet(size, depth);
}
inline uint KisMathToolbox::fastWaveletTotalSteps(const QRect& rect)
{
int size, steps;
int maxrectsize = (rect.height() < rect.width()) ? rect.width() : rect.height();
steps = 0;
for (size = 2; size < maxrectsize; size *= 2) steps += size / 2; ;
return steps;
}
#endif
diff --git a/libs/image/kis_merge_walker.h b/libs/image/kis_merge_walker.h
index 7a6cb842fc..e4b2af6117 100644
--- a/libs/image/kis_merge_walker.h
+++ b/libs/image/kis_merge_walker.h
@@ -1,92 +1,92 @@
/*
* Copyright (c) 2009 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_MERGE_WALKER_H
#define __KIS_MERGE_WALKER_H
#include "kis_types.h"
#include "kis_base_rects_walker.h"
class KisMergeWalker;
typedef KisSharedPtr<KisMergeWalker> KisMergeWalkerSP;
class KRITAIMAGE_EXPORT KisMergeWalker : public virtual KisBaseRectsWalker
{
public:
/**
* NO_FILTHY flag notifies the walker that there should be no (!)
* filthy node in the update. It means that the projection() of
* the node is already guaranteed to be ready, we just need to
* update all the higher-level nodes. Used by KisTransformMask
* regeneration code.
*/
enum Flags {
DEFAULT = 0,
NO_FILTHY
};
KisMergeWalker(QRect cropRect, Flags flags = DEFAULT);
~KisMergeWalker() override;
UpdateType type() const override;
protected:
KisMergeWalker() : m_flags(DEFAULT) {}
KisMergeWalker(Flags flags) : m_flags(flags) {}
/**
- * Begins visiting nodes starting with @startWith.
+ * Begins visiting nodes starting with @p startWith.
* First it climbs to the top of the graph, collecting
- * changeRects (it calls @registerChangeRect for every node).
+ * changeRects (it calls @ref registerChangeRect for every node).
* Then it goes down to the bottom collecting needRects
* for every branch.
*/
void startTrip(KisProjectionLeafSP startWith) override;
using KisBaseRectsWalker::startTrip;
void startTripWithMask(KisProjectionLeafSP filthyMask, KisMergeWalker::Flags flags);
private:
void startTripImpl(KisProjectionLeafSP startLeaf, Flags flags);
private:
/**
* Visits a node @leaf and goes on crowling
* towards the top of the graph, caling visitHigherNode() or
* startTrip() one more time. After the top is reached
* returns back to the @leaf.
*/
void visitHigherNode(KisProjectionLeafSP leaf, NodePosition positionToFilthy);
/**
* Visits a node @leaf and goes on crowling
* towards the bottom of the graph, caling visitLowerNode() or
* startTrip() one more time.
*/
void visitLowerNode(KisProjectionLeafSP leaf);
private:
const Flags m_flags;
};
#endif /* __KIS_MERGE_WALKER_H */
diff --git a/libs/image/kis_paint_device.h b/libs/image/kis_paint_device.h
index 99e91131a1..969a1181f4 100644
--- a/libs/image/kis_paint_device.h
+++ b/libs/image/kis_paint_device.h
@@ -1,880 +1,894 @@
/*
* Copyright (c) 2002 patrick julien <freak@codepimps.org>
* Copyright (c) 2006 Boudewijn Rempt <boud@valdyas.org>
*
* 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_PAINT_DEVICE_IMPL_H_
#define KIS_PAINT_DEVICE_IMPL_H_
#include <QObject>
#include <QRect>
#include <QVector>
#include "kis_debug.h"
#include <KoColorConversionTransformation.h>
#include "kis_types.h"
#include "kis_shared.h"
#include "kis_default_bounds_base.h"
#include <kritaimage_export.h>
class KUndo2Command;
class QRect;
class QImage;
class QPoint;
class QString;
class QColor;
class QIODevice;
class KoColor;
class KoColorSpace;
class KoColorProfile;
class KisDataManager;
class KisPaintDeviceWriter;
class KisKeyframe;
class KisRasterKeyframeChannel;
class KisPaintDeviceFramesInterface;
typedef KisSharedPtr<KisDataManager> KisDataManagerSP;
namespace KritaUtils {
enum DeviceCopyMode {
CopySnapshot = 0,
CopyAllFrames
};
}
/**
* A paint device contains the actual pixel data and offers methods
* to read and write pixels. A paint device has an integer x, y position
* (it is not positioned on the image with sub-pixel accuracy).
* A KisPaintDevice doesn't have any fixed size, the size changes dynamically
* when pixels are accessed by an iterator.
*/
class KRITAIMAGE_EXPORT KisPaintDevice
: public QObject
, public KisShared
{
Q_OBJECT
public:
/**
* Create a new paint device with the specified colorspace.
*
* @param colorSpace the colorspace of this paint device
* @param name for debugging purposes
*/
explicit KisPaintDevice(const KoColorSpace * colorSpace, const QString& name = QString());
/**
* Create a new paint device with the specified colorspace. The
* parent node will be notified of changes to this paint device.
*
* @param parent the node that contains this paint device
* @param colorSpace the colorspace of this paint device
* @param defaultBounds boundaries of the device in case it is empty
* @param name for debugging purposes
*/
KisPaintDevice(KisNodeWSP parent, const KoColorSpace * colorSpace, KisDefaultBoundsBaseSP defaultBounds = KisDefaultBoundsBaseSP(), const QString& name = QString());
/**
* Creates a copy of this device.
*
* If \p copyMode is CopySnapshot, the newly created device clones the
* current frame of \p rhs only (default and efficient
* behavior). If \p copyFrames is CopyAllFrames, the new device is a deep
* copy of the source with all the frames included.
*/
KisPaintDevice(const KisPaintDevice& rhs, KritaUtils::DeviceCopyMode copyMode = KritaUtils::CopySnapshot, KisNode *newParentNode = 0);
~KisPaintDevice() override;
protected:
/**
* A special constructor for usage in KisPixelSelection. It allows
* two paint devices to share a data manager.
*
* @param explicitDataManager data manager to use inside paint device
* @param src source paint device to copy parameters from
* @param name for debugging purposes
*/
KisPaintDevice(KisDataManagerSP explicitDataManager,
KisPaintDeviceSP src, const QString& name = QString());
public:
/**
* Write the pixels of this paint device into the specified file store.
*/
bool write(KisPaintDeviceWriter &store);
/**
* Fill this paint device with the pixels from the specified file store.
*/
bool read(QIODevice *stream);
public:
/**
* set the parent node of the paint device
*/
void setParentNode(KisNodeWSP parent);
/**
* set the default bounds for the paint device when
* the default pixel is not completely transparent
*/
void setDefaultBounds(KisDefaultBoundsBaseSP bounds);
/**
* the default bounds rect of the paint device
*/
KisDefaultBoundsBaseSP defaultBounds() const;
/**
* Moves the device to these new coordinates (no incremental move)
*/
void moveTo(qint32 x, qint32 y);
/**
* Convenience method for the above.
*/
virtual void moveTo(const QPoint& pt);
/**
* Return an X,Y offset of the device in a convenient form
*/
QPoint offset() const;
/**
* The X offset of the paint device
*/
qint32 x() const;
/**
* The Y offset of the paint device
*/
qint32 y() const;
/**
* set the X offset of the paint device
*/
void setX(qint32 x);
/**
* set the Y offset of the paint device
*/
void setY(qint32 y);
/**
* Retrieve the bounds of the paint device. The size is not exact,
* but may be larger if the underlying datamanager works that way.
* For instance, the tiled datamanager keeps the extent to the nearest
* multiple of 64.
*
* If default pixel is not transparent, then the actual extent
* rect is united with the defaultBounds()->bounds() value
* (the size of the image, usually).
*/
QRect extent() const;
/// Convenience method for the above
void extent(qint32 &x, qint32 &y, qint32 &w, qint32 &h) const;
/**
* Get the exact bounds of this paint device. The real solution is
* very slow because it does a linear scanline search, but it
* uses caching, so calling to this function without changing
* the device is quite cheap.
*
* Exactbounds follows these rules:
*
* <ul>
* <li>if default pixel is transparent, then exact bounds
* of actual pixel data are returned
* <li>if default pixel is not transparent, then the union
* (defaultBounds()->bounds() | nonDefaultPixelArea()) is
* returned
* </ul>
* \see calculateExactBounds()
*/
QRect exactBounds() const;
/**
* Relaxed version of the exactBounds() that can be used in tight
* loops. If the exact bounds value is present in the paint
* device cache, returns this value. If the cache is invalidated,
* returns extent() and tries to recalculate the exact bounds not
* faster than once in 1000 ms.
*/
QRect exactBoundsAmortized() const;
/**
* Returns exact rectangle of the paint device that contains
* non-default pixels. For paint devices with fully transparent
* default pixel is equivalent to exactBounds().
*
* nonDefaultPixelArea() follows these rules:
*
* <ul>
* <li>if default pixel is transparent, then exact bounds
* of actual pixel data are returned. The same as exactBounds()
* <li>if default pixel is not transparent, then calculates the
* rectangle of non-default pixels. May be smaller or greater
* than image bounds
* </ul>
* \see calculateExactBounds()
*/
QRect nonDefaultPixelArea() const;
/**
* Returns a rough approximation of region covered by device.
* For tiled data manager, it region will consist of a number
* of rects each corresponding to a tile.
*/
QRegion region() const;
/**
* The slow version of region() that searches for exact bounds of
* each rectangle in the region
*/
QRegion regionExact() const;
/**
* Cut the paint device down to the specified rect. If the crop
* area is bigger than the paint device, nothing will happen.
*/
void crop(qint32 x, qint32 y, qint32 w, qint32 h);
/// Convenience method for the above
void crop(const QRect & r);
/**
* Complete erase the current paint device. Its size will become 0. This
* does not take the selection into account.
*/
virtual void clear();
/**
* Clear the given rectangle to transparent black. The paint device will expand to
* contain the given rect.
*/
void clear(const QRect & rc);
/**
* Frees the memory occupied by the pixels containing default
* values. The extents() and exactBounds() of the image will
* probably also shrink
*/
void purgeDefaultPixels();
/**
* Sets the default pixel. New data will be initialised with this pixel. The pixel is copied: the
* caller still owns the pointer and needs to delete it to avoid memory leaks.
* If frame ID is given, set default pixel for that frame. Otherwise use active frame.
*/
void setDefaultPixel(const KoColor &defPixel);
/**
* Get a pointer to the default pixel.
* If the frame parameter is given, get the default pixel of
* specified frame. Otherwise use currently active frame.
*/
KoColor defaultPixel() const;
/**
* Fill the given rectangle with the given pixel. The paint device will expand to
* contain the given rect.
*/
void fill(const QRect & rc, const KoColor &color);
/**
* Overloaded function. For legacy purposes only.
* Please use fill(const QRect & rc, const KoColor &color) instead
*/
void fill(qint32 x, qint32 y, qint32 w, qint32 h, const quint8 *fillPixel);
public:
/**
* Prepares the device for fastBitBlt operation. It clears
* the device, switches x,y shifts and colorspace if needed.
* After this call fastBitBltPossible will return true.
* May be used for initialization of temporary devices.
*/
void prepareClone(KisPaintDeviceSP src);
/**
* Make this device to become a clone of \a src. It will have the same
* x,y shifts, colorspace and will share pixels inside \a rect.
* After calling this function:
* (this->extent() >= this->exactBounds() == rect).
*
* Rule of thumb:
*
* "Use makeCloneFrom() or makeCloneFromRough() if and only if you
* are the only owner of the destination paint device and you are
* 100% sure no other thread has access to it"
*/
void makeCloneFrom(KisPaintDeviceSP src, const QRect &rect);
/**
* Make this device to become a clone of \a src. It will have the same
* x,y shifts, colorspace and will share pixels inside \a rect.
* Be careful, this function will copy *at least* \a rect
* of pixels. Actual copy area will be a bigger - it will
* be aligned by tiles borders. So after calling this function:
* (this->extent() == this->exactBounds() >= rect).
*
* Rule of thumb:
*
* "Use makeCloneFrom() or makeCloneFromRough() if and only if you
* are the only owner of the destination paint device and you are
* 100% sure no other thread has access to it"
*/
void makeCloneFromRough(KisPaintDeviceSP src, const QRect &minimalRect);
protected:
friend class KisPaintDeviceTest;
friend class DataReaderThread;
/**
* Checks whether a src paint device can be used as source
* of fast bitBlt operation. The result of the check may
* depend on whether color spaces coincide, whether there is
* any shift of tiles between the devices and etc.
*
* WARNING: This check must be done <i>before</i> performing any
* fast bitBlt operation!
*
* \see fastBitBlt
* \see fastBitBltRough
*/
bool fastBitBltPossible(KisPaintDeviceSP src);
/**
* Clones rect from another paint device. The cloned area will be
* shared between both paint devices as much as possible using
* copy-on-write. Parts of the rect that cannot be shared
* (cross tiles) are deep-copied,
*
* \see fastBitBltPossible
* \see fastBitBltRough
*/
void fastBitBlt(KisPaintDeviceSP src, const QRect &rect);
/**
* The same as \ref fastBitBlt() but reads old data
*/
void fastBitBltOldData(KisPaintDeviceSP src, const QRect &rect);
/**
* Clones rect from another paint device in a rough and fast way.
* All the tiles touched by rect will be shared, between both
* devices, that means it will copy a bigger area than was
* requested. This method is supposed to be used for bitBlt'ing
* into temporary paint devices.
*
* \see fastBitBltPossible
* \see fastBitBlt
*/
void fastBitBltRough(KisPaintDeviceSP src, const QRect &rect);
/**
* The same as \ref fastBitBltRough() but reads old data
*/
void fastBitBltRoughOldData(KisPaintDeviceSP src, const QRect &rect);
public:
/**
* Read the bytes representing the rectangle described by x, y, w, h into
* data. If data is not big enough, Krita will gladly overwrite the rest
* of your precious memory.
*
* Since this is a copy, you need to make sure you have enough memory.
*
* Reading from areas not previously initialized will read the default
* pixel value into data but not initialize that region.
*/
void readBytes(quint8 * data, qint32 x, qint32 y, qint32 w, qint32 h) const;
/**
* Read the bytes representing the rectangle rect into
* data. If data is not big enough, Krita will gladly overwrite the rest
* of your precious memory.
*
* Since this is a copy, you need to make sure you have enough memory.
*
* Reading from areas not previously initialized will read the default
* pixel value into data but not initialize that region.
* @param data The address of the memory to receive the bytes read
* @param rect The rectangle in the paint device to read from
*/
void readBytes(quint8 * data, const QRect &rect) const;
/**
* Copy the bytes in data into the rect specified by x, y, w, h. If the
* data is too small or uninitialized, Krita will happily read parts of
* memory you never wanted to be read.
*
* If the data is written to areas of the paint device not previously initialized,
* the paint device will grow.
*/
void writeBytes(const quint8 * data, qint32 x, qint32 y, qint32 w, qint32 h);
/**
* Copy the bytes in data into the rectangle rect. If the
* data is too small or uninitialized, Krita will happily read parts of
* memory you never wanted to be read.
*
* If the data is written to areas of the paint device not previously initialized,
* the paint device will grow.
* @param data The address of the memory to write bytes from
* @param rect The rectangle in the paint device to write to
*/
void writeBytes(const quint8 * data, const QRect &rect);
/**
* Copy the bytes in the paint device into a vector of arrays of bytes,
* where the number of arrays is the number of channels in the
* paint device. If the specified area is larger than the paint
* device's extent, the default pixel will be read.
*/
QVector<quint8*> readPlanarBytes(qint32 x, qint32 y, qint32 w, qint32 h) const;
/**
* Write the data in the separate arrays to the channes. If there
* are less vectors than channels, the remaining channels will not
* be copied. If any of the arrays points to 0, the channel in
* that location will not be touched. If the specified area is
* larger than the paint device, the paint device will be
* extended. There are no guards: if the area covers more pixels
* than there are bytes in the arrays, krita will happily fill
* your paint device with areas of memory you never wanted to be
* read. Krita may also crash.
*
* XXX: what about undo?
*/
void writePlanarBytes(QVector<quint8*> planes, qint32 x, qint32 y, qint32 w, qint32 h);
/**
* Converts the paint device to a different colorspace
*
* @return a command that can be used to undo the conversion.
*/
KUndo2Command* convertTo(const KoColorSpace * dstColorSpace,
KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::internalConversionFlags());
/**
* Changes the profile of the colorspace of this paint device to the given
* profile. If the given profile is 0, nothing happens.
*/
bool setProfile(const KoColorProfile * profile);
/**
* Fill this paint device with the data from image; starting at (offsetX, offsetY)
- * @param srcProfileName name of the RGB profile to interpret the image as. 0 is interpreted as sRGB
+ * @param image the image
+ * @param profile name of the RGB profile to interpret the image as. 0 is interpreted as sRGB
+ * @param offsetX x offset
+ * @param offsetY y offset
*/
void convertFromQImage(const QImage& image, const KoColorProfile *profile, qint32 offsetX = 0, qint32 offsetY = 0);
/**
* Create an RGBA QImage from a rectangle in the paint device.
*
+ * @param dstProfile RGB profile to use in conversion. May be 0, in which
+ * case it's up to the color strategy to choose a profile (most
+ * like sRGB).
* @param x Left coordinate of the rectangle
* @param y Top coordinate of the rectangle
* @param w Width of the rectangle in pixels
* @param h Height of the rectangle in pixels
- * @param dstProfile RGB profile to use in conversion. May be 0, in which
- * case it's up to the color strategy to choose a profile (most
- * like sRGB).
+ * @param renderingIntent Rendering intent
+ * @param conversionFlags Conversion flags
*/
QImage convertToQImage(const KoColorProfile *dstProfile, qint32 x, qint32 y, qint32 w, qint32 h,
KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::internalConversionFlags()) const;
/**
* Overridden method for convenience
*/
QImage convertToQImage(const KoColorProfile *dstProfile,
const QRect &rc,
KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::internalConversionFlags()) const;
/**
* Create an RGBA QImage from a rectangle in the paint device. The
* rectangle is defined by the parent image's bounds.
*
* @param dstProfile RGB profile to use in conversion. May be 0, in which
* case it's up to the color strategy to choose a profile (most
* like sRGB).
+ * @param renderingIntent Rendering intent
+ * @param conversionFlags Conversion flags
*/
QImage convertToQImage(const KoColorProfile * dstProfile,
KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::internalConversionFlags()) const;
/**
* Creates a paint device thumbnail of the paint device, retaining
* the aspect ratio. The width and height of the returned device
* won't exceed \p maxw and \p maxw, but they may be smaller.
*
- * @param maxw: maximum width
- * @param maxh: maximum height
- * @param rect: only this rect will be used for the thumbnail
+ * @param w maximum width
+ * @param h maximum height
+ * @param rect only this rect will be used for the thumbnail
+ * @param outputRect output rectangle
*
*/
KisPaintDeviceSP createThumbnailDevice(qint32 w, qint32 h, QRect rect = QRect(), QRect outputRect = QRect()) const;
KisPaintDeviceSP createThumbnailDeviceOversampled(qint32 w, qint32 h, qreal oversample, QRect rect = QRect(), QRect outputRect = QRect()) const;
/**
* Creates a thumbnail of the paint device, retaining the aspect ratio.
* The width and height of the returned QImage won't exceed \p maxw and \p maxw, but they may be smaller.
* The colors are not corrected for display!
*
* @param maxw: maximum width
* @param maxh: maximum height
* @param rect: only this rect will be used for the thumbnail
* @param oversample: ratio used for antialiasing
+ * @param renderingIntent Rendering intent
+ * @param conversionFlags Conversion flags
*/
QImage createThumbnail(qint32 maxw, qint32 maxh, QRect rect, qreal oversample = 1,
KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::internalConversionFlags());
/**
* Cached version of createThumbnail(qint32 maxw, qint32 maxh, const KisSelection *selection, QRect rect)
*/
QImage createThumbnail(qint32 maxw, qint32 maxh, qreal oversample = 1,
KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::internalConversionFlags());
/**
* Fill c and opacity with the values found at x and y.
*
* The color values will be transformed from the profile of
* this paint device to the display profile.
*
* @return true if the operation was successful.
*/
bool pixel(qint32 x, qint32 y, QColor *c) const;
/**
* Fill kc with the values found at x and y. This method differs
* from the above in using KoColor, which can be of any colorspace
*
* The color values will be transformed from the profile of
* this paint device to the display profile.
*
* @return true if the operation was successful.
*/
bool pixel(qint32 x, qint32 y, KoColor * kc) const;
/**
* Set the specified pixel to the specified color. Note that this
* bypasses KisPainter. the PaintDevice is here used as an equivalent
* to QImage, not QPixmap. This means that this is not undoable; also,
* there is no compositing with an existing value at this location.
*
* The color values will be transformed from the display profile to
* the paint device profile.
*
* Note that this will use 8-bit values and may cause a significant
* degradation when used on 16-bit or hdr quality images.
*
* @return true if the operation was successful
*/
bool setPixel(qint32 x, qint32 y, const QColor& c);
/// Convenience method for the above
bool setPixel(qint32 x, qint32 y, const KoColor& kc);
/**
* @return the colorspace of the pixels in this paint device
*/
const KoColorSpace* colorSpace() const;
/**
* There is quite a common technique in Krita. It is used in
* cases, when we want to paint something over a paint device
* using the composition, opacity or selection. E.g. painting a
* dab in a paint op, filling the selection in the Fill Tool.
* Such work is usually done in the following way:
*
* 1) Create a paint device
*
* 2) Fill it with the desired color or data
*
* 3) Create a KisPainter and set all the properties of the
* transaction: selection, compositeOp, opacity and etc.
*
* 4) Paint a newly created paint device over the destination
* device.
*
* The following two methods (createCompositionSourceDevice() or
* createCompositionSourceDeviceFixed())should be used for the
* accomplishing the step 1). The point is that the desired color
* space of the temporary device may not coincide with the color
* space of the destination. That is the case, for example, for
* the alpha8() colorspace used in the selections. So for such
* devices the temporary target would have a different (grayscale)
* color space.
*
* So there are two rules of thumb:
*
* 1) If you need a temporary device which you are going to fill
* with some data and then paint over the paint device, create
* it with either createCompositionSourceDevice() or
* createCompositionSourceDeviceFixed().
*
* 2) Do *not* expect that the color spaces of the destination and
* the temporary device would coincide. If you need to copy a
* single pixel from one device to another, you can use
* KisCrossDeviceColorPicker class, that will handle all the
* necessary conversions for you.
*
* \see createCompositionSourceDeviceFixed()
* \see compositionSourceColorSpace()
* \see KisCrossDeviceColorPicker
* \see KisCrossDeviceColorPickerInt
*/
KisPaintDeviceSP createCompositionSourceDevice() const;
/**
* The same as createCompositionSourceDevice(), but initializes the
* newly created device with the content of \p cloneSource
*
* \see createCompositionSourceDevice()
*/
KisPaintDeviceSP createCompositionSourceDevice(KisPaintDeviceSP cloneSource) const;
/**
* The same as createCompositionSourceDevice(), but initializes
* the newly created device with the *rough* \p roughRect of
* \p cloneSource.
*
* "Rough rect" means that it may copy a bit more than
* requested. It is expected that the caller will not use the area
* outside \p roughRect.
*
* \see createCompositionSourceDevice()
*/
KisPaintDeviceSP createCompositionSourceDevice(KisPaintDeviceSP cloneSource, const QRect roughRect) const;
/**
* This is a convenience method for createCompositionSourceDevice()
*
* \see createCompositionSourceDevice()
*/
KisFixedPaintDeviceSP createCompositionSourceDeviceFixed() const;
/**
* This is a lowlevel method for the principle used in
* createCompositionSourceDevice(). In most of the cases the paint
* device creation methods should be used instead of this function.
*
* \see createCompositionSourceDevice()
* \see createCompositionSourceDeviceFixed()
*/
virtual const KoColorSpace* compositionSourceColorSpace() const;
/**
* @return the internal datamanager that keeps the pixels.
*/
KisDataManagerSP dataManager() const;
/**
* Replace the pixel data, color strategy, and profile.
*/
void setDataManager(KisDataManagerSP data, const KoColorSpace * colorSpace = 0);
/**
* Return the number of bytes a pixel takes.
*/
quint32 pixelSize() const;
/**
* Return the number of channels a pixel takes
*/
quint32 channelCount() const;
/**
* Create a keyframe channel for the content on this device.
* @param id identifier for the channel
- * @param node the parent node for the channel
* @return keyframe channel or 0 if there is not one
*/
KisRasterKeyframeChannel *createKeyframeChannel(const KoID &id);
KisRasterKeyframeChannel* keyframeChannel() const;
/**
* An interface to modify/load/save frames stored inside this device
*/
KisPaintDeviceFramesInterface* framesInterface();
public:
/**
* Add the specified rect to the parent layer's set of dirty rects
* (if there is a parent layer)
*/
void setDirty(const QRect & rc);
/**
* Add the specified region to the parent layer's dirty region
* (if there is a parent layer)
*/
void setDirty(const QRegion & region);
/**
* Set the parent layer completely dirty, if this paint device has
* as parent layer.
*/
void setDirty();
void setDirty(const QVector<QRect> rects);
/**
* Called by KisTransactionData when it thinks current time should
* be changed. And the requests is forwarded to the image if
* needed.
*/
void requestTimeSwitch(int time);
/**
* \return a sequence number corresponding to the current paint
* device state. Every time the paint device is changed,
* the sequence number is increased
*/
int sequenceNumber() const;
void estimateMemoryStats(qint64 &imageData, qint64 &temporaryData, qint64 &lodData) const;
public:
KisHLineIteratorSP createHLineIteratorNG(qint32 x, qint32 y, qint32 w);
KisHLineConstIteratorSP createHLineConstIteratorNG(qint32 x, qint32 y, qint32 w) const;
KisVLineIteratorSP createVLineIteratorNG(qint32 x, qint32 y, qint32 h);
KisVLineConstIteratorSP createVLineConstIteratorNG(qint32 x, qint32 y, qint32 h) const;
KisRandomAccessorSP createRandomAccessorNG(qint32 x, qint32 y);
KisRandomConstAccessorSP createRandomConstAccessorNG(qint32 x, qint32 y) const;
/**
* Create an iterator that will "artificially" extend the paint device with the
* value of the border when trying to access values outside the range of data.
*
- * @param rc indicates the rectangle that truly contains data
+ * @param x x of top left corner
+ * @param y y of top left corner
+ * @param w width of the border
+ * @param _dataWidth indicates the rectangle that truly contains data
*/
KisRepeatHLineConstIteratorSP createRepeatHLineConstIterator(qint32 x, qint32 y, qint32 w, const QRect& _dataWidth) const;
/**
* Create an iterator that will "artificially" extend the paint device with the
* value of the border when trying to access values outside the range of data.
*
- * @param rc indicates the rectangle that truly contains data
+ * @param x x of top left corner
+ * @param y y of top left corner
+ * @param h height of the border
+ * @param _dataWidth indicates the rectangle that truly contains data
*/
KisRepeatVLineConstIteratorSP createRepeatVLineConstIterator(qint32 x, qint32 y, qint32 h, const QRect& _dataWidth) const;
/**
* This function create a random accessor which can easily access to sub pixel values.
- * @param selection an up-to-date selection that has the same origin as the paint device
*/
KisRandomSubAccessorSP createRandomSubAccessor() const;
/** Clear the selected pixels from the paint device */
void clearSelection(KisSelectionSP selection);
Q_SIGNALS:
void profileChanged(const KoColorProfile * profile);
void colorSpaceChanged(const KoColorSpace *colorspace);
public:
friend class PaintDeviceCache;
/**
* Caclculates exact bounds of the device. Used internally
* by a transparent caching system. The solution is very slow
* because it does a linear scanline search. So the complexity
* is n*n at worst.
*
* \see exactBounds(), nonDefaultPixelArea()
*/
QRect calculateExactBounds(bool nonDefaultOnly) const;
public:
struct MemoryReleaseObject : public QObject {
~MemoryReleaseObject() override;
};
static MemoryReleaseObject* createMemoryReleaseObject();
public:
struct LodDataStruct {
virtual ~LodDataStruct();
};
QRegion regionForLodSyncing() const;
LodDataStruct* createLodDataStruct(int lod);
void updateLodDataStruct(LodDataStruct *dst, const QRect &srcRect);
void uploadLodDataStruct(LodDataStruct *dst);
void generateLodCloneDevice(KisPaintDeviceSP dst, const QRect &originalRect, int lod);
void setProjectionDevice(bool value);
void tesingFetchLodDevice(KisPaintDeviceSP targetDevice);
private:
KisPaintDevice& operator=(const KisPaintDevice&);
void init(const KoColorSpace *colorSpace,
KisDefaultBoundsBaseSP defaultBounds,
KisNodeWSP parent, const QString& name);
// Only KisPainter is allowed to have access to these low-level methods
friend class KisPainter;
/**
* Return a vector with in order the size in bytes of the channels
* in the colorspace of this paint device.
*/
QVector<qint32> channelSizes() const;
void emitColorSpaceChanged();
void emitProfileChanged();
private:
friend class KisPaintDeviceFramesInterface;
protected:
friend class KisSelectionTest;
KisNodeWSP parentNode() const;
private:
struct Private;
Private * const m_d;
};
#endif // KIS_PAINT_DEVICE_IMPL_H_
diff --git a/libs/image/kis_paint_device_debug_utils.h b/libs/image/kis_paint_device_debug_utils.h
index defb556dd8..85c5f6582d 100644
--- a/libs/image/kis_paint_device_debug_utils.h
+++ b/libs/image/kis_paint_device_debug_utils.h
@@ -1,67 +1,67 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_PAINT_DEVICE_DEBUG_UTILS_H
#define __KIS_PAINT_DEVICE_DEBUG_UTILS_H
class QRect;
class QString;
#include <kis_types.h>
#include <kritaimage_export.h>
void KRITAIMAGE_EXPORT kis_debug_save_device_incremental(KisPaintDeviceSP device,
int i,
const QRect &rc,
const QString &suffix, const QString &prefix);
/**
* Saves the paint device incrementally. Put this macro into a
* function that is called several times and you'll have as many
* separate dump files as the number of times the function was
* called. That is very convenient for debugging canvas updates:
* adding this macro will let you track the whole history of updates.
*
- * The files are saved with pattern: <counter>_<suffix>.png
+ * The files are saved with pattern: \<counter\>_\<suffix\>.png
*/
#define KIS_DUMP_DEVICE_1(device, rc, suffix) \
do { \
static int i = -1; i++; \
kis_debug_save_device_incremental((device), i, (rc), (suffix), QString()); \
} while(0)
/**
* Saves the paint device incrementally. Put this macro into a
* function that is called several times and you'll have as many
* separate dump files as the number of times the function was
* called. That is very convenient for debugging canvas updates:
* adding this macro will let you track the whole history of updates.
*
* The \p prefix parameter makes it easy to sort out dumps from
* different functions.
*
- * The files are saved with pattern: <prefix>_<counter>_<suffix>.png
+ * The files are saved with pattern: \<prefix\>_\<counter\>_\<suffix\>.png
*/
#define KIS_DUMP_DEVICE_2(device, rc, suffix, prefix) \
do { \
static int i = -1; i++; \
kis_debug_save_device_incremental((device), i, (rc), (suffix), (prefix)); \
} while(0)
#endif /* __KIS_PAINT_DEVICE_DEBUG_UTILS_H */
diff --git a/libs/image/kis_paint_device_frames_interface.h b/libs/image/kis_paint_device_frames_interface.h
index bf5f6f33bb..66e9493512 100644
--- a/libs/image/kis_paint_device_frames_interface.h
+++ b/libs/image/kis_paint_device_frames_interface.h
@@ -1,154 +1,155 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_PAINT_DEVICE_FRAMES_INTERFACE_H
#define __KIS_PAINT_DEVICE_FRAMES_INTERFACE_H
#include "kis_types.h"
#include "kritaimage_export.h"
class KisPaintDeviceData;
class KisPaintDeviceWriter;
class KisDataManager;
typedef KisSharedPtr<KisDataManager> KisDataManagerSP;
class KRITAIMAGE_EXPORT KisPaintDeviceFramesInterface
{
public:
KisPaintDeviceFramesInterface(KisPaintDevice *parentDevice);
/**
* Return a list of IDs for the frames contained in this paint device
* @return list of frame IDs
*/
QList<int> frames();
/**
* Creates a new frame on the device and returns an identifier for it.
* @return frame id of the newly created frame
*/
int createFrame(bool copy, int copySrc, const QPoint &offset, KUndo2Command *parentCommand);
/**
* Delete the frame with given id
* @param frame frame ID
+ * @param parentCommand parent command
*/
void deleteFrame(int frame, KUndo2Command *parentCommand);
/**
* Copy the given frame into the target device
* @param frameId ID of the frame to be copied
* @param targetDevice paint device to copy to
*/
void fetchFrame(int frameId, KisPaintDeviceSP targetDevice);
/**
* Copy the given paint device contents into the specified frame
- * @param dstFrameId ID of the frame to be overwritten (must exist)
* @param srcFrameId ID of the frame to copy from (must exist)
- * @param sourceDevice paint device to copy from
+ * @param dstFrameId ID of the frame to be overwritten (must exist)
+ * @param srcDevice paint device to copy from
*/
void uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice);
/**
* Copy the given paint device contents into the specified frame
* @param dstFrameId ID of the frame to be overwritten (must exist)
- * @param sourceDevice paint device to copy from
+ * @param srcDevice paint device to copy from
*/
void uploadFrame(int dstFrameId, KisPaintDeviceSP srcDevice);
/**
* @return extent() of \p frameId
*/
QRect frameBounds(int frameId);
/**
* @return offset of a data on \p frameId
*/
QPoint frameOffset(int frameId) const;
/**
* Sets default pixel for \p frameId
*/
void setFrameDefaultPixel(const KoColor &defPixel, int frameId);
/**
* @return default pixel for \p frameId
*/
KoColor frameDefaultPixel(int frameId) const;
/**
* Write a \p frameId onto \p store
*/
bool writeFrame(KisPaintDeviceWriter &store, int frameId);
/**
* Loads content of a \p frameId from \p stream.
*
* NOTE: the frame must be created manually with createFrame()
* beforehand!
*/
bool readFrame(QIODevice *stream, int frameId);
/**
* Returns frameId of the currently active frame.
* Should be used by Undo framework only!
*/
int currentFrameId() const;
/**
* Returns the data manager of the specified frame.
* Should be used by Undo framework only!
*/
KisDataManagerSP frameDataManager(int frameId) const;
/**
* Resets the cache object associated with the frame.
* Should be used by Undo framework only!
*/
void invalidateFrameCache(int frameId);
/**
* Sets the offset for \p frameId.
* Should be used by Undo framework only!
*/
void setFrameOffset(int frameId, const QPoint &offset);
struct TestingDataObjects {
typedef KisPaintDeviceData Data;
typedef QHash<int, Data*> FramesHash;
Data *m_data;
Data *m_lodData;
Data *m_externalFrameData;
FramesHash m_frames;
Data *m_currentData;
};
TestingDataObjects testingGetDataObjects() const;
QList<KisPaintDeviceData*> testingGetDataObjectsList() const;
private:
KisPaintDevice *q;
};
#endif /* __KIS_PAINT_DEVICE_FRAMES_INTERFACE_H */
diff --git a/libs/image/kis_painter.cc b/libs/image/kis_painter.cc
index 7f8036058b..5b341d4084 100644
--- a/libs/image/kis_painter.cc
+++ b/libs/image/kis_painter.cc
@@ -1,2985 +1,2985 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2004 Clarence Dang <dang@kde.org>
* Copyright (c) 2004 Adrian Page <adrian@pagenet.plus.com>
* Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2008-2010 Lukáš Tvrdý <lukast.dev@gmail.com>
* Copyright (c) 2010 José Luis Vergara Toloza <pentalis@gmail.com>
* Copyright (c) 2011 Silvio Heinrich <plassy@web.de>
*
* 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_painter.h"
#include <stdlib.h>
#include <string.h>
#include <cfloat>
#include <cmath>
#include <climits>
#ifndef Q_OS_WIN
#include <strings.h>
#endif
#include <QImage>
#include <QRect>
#include <QString>
#include <QStringList>
#include <kundo2command.h>
#include <kis_debug.h>
#include <klocalizedstring.h>
#include "kis_image.h"
#include "filter/kis_filter.h"
#include "kis_layer.h"
#include "kis_paint_device.h"
#include "kis_fixed_paint_device.h"
#include "kis_transaction.h"
#include "kis_vec.h"
#include "kis_iterator_ng.h"
#include "kis_random_accessor_ng.h"
#include "filter/kis_filter_configuration.h"
#include "kis_pixel_selection.h"
#include <brushengine/kis_paint_information.h>
#include "kis_paintop_registry.h"
#include "kis_perspective_math.h"
#include "tiles3/kis_random_accessor.h"
#include <kis_distance_information.h>
#include <KoColorSpaceMaths.h>
#include "kis_lod_transform.h"
#include "kis_algebra_2d.h"
#include "krita_utils.h"
// Maximum distance from a Bezier control point to the line through the start
// and end points for the curve to be considered flat.
#define BEZIER_FLATNESS_THRESHOLD 0.5
#include "kis_painter_p.h"
KisPainter::KisPainter()
: d(new Private(this))
{
init();
}
KisPainter::KisPainter(KisPaintDeviceSP device)
: d(new Private(this, device->colorSpace()))
{
init();
Q_ASSERT(device);
begin(device);
}
KisPainter::KisPainter(KisPaintDeviceSP device, KisSelectionSP selection)
: d(new Private(this, device->colorSpace()))
{
init();
Q_ASSERT(device);
begin(device);
d->selection = selection;
}
void KisPainter::init()
{
d->selection = 0 ;
d->transaction = 0;
d->paintOp = 0;
d->pattern = 0;
d->sourceLayer = 0;
d->fillStyle = FillStyleNone;
d->strokeStyle = StrokeStyleBrush;
d->antiAliasPolygonFill = true;
d->progressUpdater = 0;
d->gradient = 0;
d->maskPainter = 0;
d->fillPainter = 0;
d->maskImageWidth = 255;
d->maskImageHeight = 255;
d->mirrorHorizontally = false;
d->mirrorVertically = false;
d->isOpacityUnit = true;
d->paramInfo = KoCompositeOp::ParameterInfo();
d->renderingIntent = KoColorConversionTransformation::internalRenderingIntent();
d->conversionFlags = KoColorConversionTransformation::internalConversionFlags();
}
KisPainter::~KisPainter()
{
// TODO: Maybe, don't be that strict?
// deleteTransaction();
end();
delete d->paintOp;
delete d->maskPainter;
delete d->fillPainter;
delete d;
}
template <bool useOldData>
void copyAreaOptimizedImpl(const QPoint &dstPt,
KisPaintDeviceSP src,
KisPaintDeviceSP dst,
const QRect &srcRect)
{
const QRect dstRect(dstPt, srcRect.size());
const QRect srcExtent = src->extent();
const QRect dstExtent = dst->extent();
const QRect srcSampleRect = srcExtent & srcRect;
const QRect dstSampleRect = dstExtent & dstRect;
const bool srcEmpty = srcSampleRect.isEmpty();
const bool dstEmpty = dstSampleRect.isEmpty();
if (!srcEmpty || !dstEmpty) {
if (srcEmpty) {
dst->clear(dstRect);
} else {
QRect srcCopyRect = srcRect;
QRect dstCopyRect = dstRect;
if (!srcExtent.contains(srcRect)) {
if (src->defaultPixel() == dst->defaultPixel()) {
const QRect dstSampleInSrcCoords = dstSampleRect.translated(srcRect.topLeft() - dstPt);
if (dstSampleInSrcCoords.isEmpty() || srcSampleRect.contains(dstSampleInSrcCoords)) {
srcCopyRect = srcSampleRect;
} else {
srcCopyRect = srcSampleRect | dstSampleInSrcCoords;
}
dstCopyRect = QRect(dstPt + srcCopyRect.topLeft() - srcRect.topLeft(), srcCopyRect.size());
}
}
KisPainter gc(dst);
gc.setCompositeOp(dst->colorSpace()->compositeOp(COMPOSITE_COPY));
if (useOldData) {
gc.bitBltOldData(dstCopyRect.topLeft(), src, srcCopyRect);
} else {
gc.bitBlt(dstCopyRect.topLeft(), src, srcCopyRect);
}
}
}
}
void KisPainter::copyAreaOptimized(const QPoint &dstPt,
KisPaintDeviceSP src,
KisPaintDeviceSP dst,
const QRect &srcRect)
{
copyAreaOptimizedImpl<false>(dstPt, src, dst, srcRect);
}
void KisPainter::copyAreaOptimizedOldData(const QPoint &dstPt,
KisPaintDeviceSP src,
KisPaintDeviceSP dst,
const QRect &srcRect)
{
copyAreaOptimizedImpl<true>(dstPt, src, dst, srcRect);
}
void KisPainter::copyAreaOptimized(const QPoint &dstPt,
KisPaintDeviceSP src,
KisPaintDeviceSP dst,
const QRect &originalSrcRect,
KisSelectionSP selection)
{
if (!selection) {
copyAreaOptimized(dstPt, src, dst, originalSrcRect);
return;
}
const QRect selectionRect = selection->selectedRect();
const QRect srcRect = originalSrcRect & selectionRect;
const QPoint dstOffset = srcRect.topLeft() - originalSrcRect.topLeft();
const QRect dstRect = QRect(dstPt + dstOffset, srcRect.size());
const bool srcEmpty = (src->extent() & srcRect).isEmpty();
const bool dstEmpty = (dst->extent() & dstRect).isEmpty();
if (!srcEmpty || !dstEmpty) {
//if (srcEmpty) {
// doesn't support dstRect
// dst->clearSelection(selection);
// } else */
{
KisPainter gc(dst);
gc.setSelection(selection);
gc.setCompositeOp(dst->colorSpace()->compositeOp(COMPOSITE_COPY));
gc.bitBlt(dstRect.topLeft(), src, srcRect);
}
}
}
KisPaintDeviceSP KisPainter::convertToAlphaAsAlpha(KisPaintDeviceSP src)
{
const KoColorSpace *srcCS = src->colorSpace();
const QRect processRect = src->extent();
KisPaintDeviceSP dst(new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()));
if (processRect.isEmpty()) return dst;
KisSequentialConstIterator srcIt(src, processRect);
KisSequentialIterator dstIt(dst, processRect);
while (srcIt.nextPixel() && dstIt.nextPixel()) {
const quint8 *srcPtr = srcIt.rawDataConst();
quint8 *alpha8Ptr = dstIt.rawData();
const quint8 white = srcCS->intensity8(srcPtr);
const quint8 alpha = srcCS->opacityU8(srcPtr);
*alpha8Ptr = KoColorSpaceMaths<quint8>::multiply(alpha, KoColorSpaceMathsTraits<quint8>::unitValue - white);
}
return dst;
}
KisPaintDeviceSP KisPainter::convertToAlphaAsGray(KisPaintDeviceSP src)
{
const KoColorSpace *srcCS = src->colorSpace();
const QRect processRect = src->extent();
KisPaintDeviceSP dst(new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()));
if (processRect.isEmpty()) return dst;
KisSequentialConstIterator srcIt(src, processRect);
KisSequentialIterator dstIt(dst, processRect);
while (srcIt.nextPixel() && dstIt.nextPixel()) {
const quint8 *srcPtr = srcIt.rawDataConst();
quint8 *alpha8Ptr = dstIt.rawData();
*alpha8Ptr = srcCS->intensity8(srcPtr);
}
return dst;
}
bool KisPainter::checkDeviceHasTransparency(KisPaintDeviceSP dev)
{
const QRect deviceBounds = dev->exactBounds();
const QRect imageBounds = dev->defaultBounds()->bounds();
if (deviceBounds.isEmpty() ||
(deviceBounds & imageBounds) != imageBounds) {
return true;
}
const KoColorSpace *cs = dev->colorSpace();
KisSequentialConstIterator it(dev, deviceBounds);
while(it.nextPixel()) {
if (cs->opacityU8(it.rawDataConst()) != OPACITY_OPAQUE_U8) {
return true;
}
}
return false;
}
void KisPainter::begin(KisPaintDeviceSP device)
{
begin(device, d->selection);
}
void KisPainter::begin(KisPaintDeviceSP device, KisSelectionSP selection)
{
if (!device) return;
d->selection = selection;
Q_ASSERT(device->colorSpace());
end();
d->device = device;
d->colorSpace = device->colorSpace();
d->compositeOp = d->colorSpace->compositeOp(COMPOSITE_OVER);
d->pixelSize = device->pixelSize();
}
void KisPainter::end()
{
Q_ASSERT_X(!d->transaction, "KisPainter::end()",
"end() was called for the painter having a transaction. "
"Please use end/deleteTransaction() instead");
}
void KisPainter::beginTransaction(const KUndo2MagicString& transactionName,int timedID)
{
Q_ASSERT_X(!d->transaction, "KisPainter::beginTransaction()",
"You asked for a new transaction while still having "
"another one. Please finish the first one with "
"end/deleteTransaction() first");
d->transaction = new KisTransaction(transactionName, d->device);
Q_CHECK_PTR(d->transaction);
d->transaction->undoCommand()->setTimedID(timedID);
}
void KisPainter::revertTransaction()
{
Q_ASSERT_X(d->transaction, "KisPainter::revertTransaction()",
"No transaction is in progress");
d->transaction->revert();
delete d->transaction;
d->transaction = 0;
}
void KisPainter::endTransaction(KisUndoAdapter *undoAdapter)
{
Q_ASSERT_X(d->transaction, "KisPainter::endTransaction()",
"No transaction is in progress");
d->transaction->commit(undoAdapter);
delete d->transaction;
d->transaction = 0;
}
void KisPainter::endTransaction(KisPostExecutionUndoAdapter *undoAdapter)
{
Q_ASSERT_X(d->transaction, "KisPainter::endTransaction()",
"No transaction is in progress");
d->transaction->commit(undoAdapter);
delete d->transaction;
d->transaction = 0;
}
KUndo2Command* KisPainter::endAndTakeTransaction()
{
Q_ASSERT_X(d->transaction, "KisPainter::endTransaction()",
"No transaction is in progress");
KUndo2Command *transactionData = d->transaction->endAndTake();
delete d->transaction;
d->transaction = 0;
return transactionData;
}
void KisPainter::deleteTransaction()
{
if (!d->transaction) return;
delete d->transaction;
d->transaction = 0;
}
void KisPainter::putTransaction(KisTransaction* transaction)
{
Q_ASSERT_X(!d->transaction, "KisPainter::putTransaction()",
"You asked for a new transaction while still having "
"another one. Please finish the first one with "
"end/deleteTransaction() first");
d->transaction = transaction;
}
KisTransaction* KisPainter::takeTransaction()
{
Q_ASSERT_X(d->transaction, "KisPainter::takeTransaction()",
"No transaction is in progress");
KisTransaction *temp = d->transaction;
d->transaction = 0;
return temp;
}
QVector<QRect> KisPainter::takeDirtyRegion()
{
QVector<QRect> vrect = d->dirtyRects;
d->dirtyRects.clear();
return vrect;
}
void KisPainter::addDirtyRect(const QRect & rc)
{
QRect r = rc.normalized();
if (r.isValid()) {
d->dirtyRects.append(rc);
}
}
void KisPainter::addDirtyRects(const QVector<QRect> &rects)
{
d->dirtyRects.reserve(d->dirtyRects.size() + rects.size());
Q_FOREACH (const QRect &rc, rects) {
const QRect r = rc.normalized();
if (r.isValid()) {
d->dirtyRects.append(rc);
}
}
}
inline bool KisPainter::Private::tryReduceSourceRect(const KisPaintDevice *srcDev,
QRect *srcRect,
qint32 *srcX,
qint32 *srcY,
qint32 *srcWidth,
qint32 *srcHeight,
qint32 *dstX,
qint32 *dstY)
{
/**
* In case of COMPOSITE_COPY and Wrap Around Mode even the pixels
* outside the device extent matter, because they will be either
* directly copied (former case) or cloned from another area of
* the image.
*/
if (compositeOp->id() != COMPOSITE_COPY &&
compositeOp->id() != COMPOSITE_DESTINATION_IN &&
compositeOp->id() != COMPOSITE_DESTINATION_ATOP &&
!srcDev->defaultBounds()->wrapAroundMode()) {
/**
* If srcDev->extent() (the area of the tiles containing
* srcDev) is smaller than srcRect, then shrink srcRect to
* that size. This is done as a speed optimization, useful for
* stack recomposition in KisImage. srcRect won't grow if
* srcDev->extent() is larger.
*/
*srcRect &= srcDev->extent();
if (srcRect->isEmpty()) return true;
// Readjust the function paramenters to the new dimensions.
*dstX += srcRect->x() - *srcX; // This will only add, not subtract
*dstY += srcRect->y() - *srcY; // Idem
srcRect->getRect(srcX, srcY, srcWidth, srcHeight);
}
return false;
}
void KisPainter::bitBltWithFixedSelection(qint32 dstX, qint32 dstY,
const KisPaintDeviceSP srcDev,
const KisFixedPaintDeviceSP selection,
qint32 selX, qint32 selY,
qint32 srcX, qint32 srcY,
qint32 srcWidth, qint32 srcHeight)
{
// TODO: get selX and selY working as intended
/* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just
initializing they perform some dummy passes with those parameters, and it must not crash */
if (srcWidth == 0 || srcHeight == 0) return;
if (srcDev.isNull()) return;
if (d->device.isNull()) return;
// Check that selection has an alpha colorspace, crash if false
Q_ASSERT(selection->colorSpace() == KoColorSpaceRegistry::instance()->alpha8());
QRect srcRect = QRect(srcX, srcY, srcWidth, srcHeight);
QRect selRect = QRect(selX, selY, srcWidth, srcHeight);
/* Trying to read outside a KisFixedPaintDevice is inherently wrong and shouldn't be done,
so crash if someone attempts to do this. Don't resize YET as it would obfuscate the mistake. */
Q_ASSERT(selection->bounds().contains(selRect));
Q_UNUSED(selRect); // only used by the above Q_ASSERT
/**
* An optimization, which crops the source rect by the bounds of
* the source device when it is possible
*/
if (d->tryReduceSourceRect(srcDev, &srcRect,
&srcX, &srcY,
&srcWidth, &srcHeight,
&dstX, &dstY)) return;
/* Create an intermediate byte array to hold information before it is written
to the current paint device (d->device) */
quint8* dstBytes = 0;
try {
dstBytes = new quint8[srcWidth * srcHeight * d->device->pixelSize()];
} catch (const std::bad_alloc&) {
warnKrita << "KisPainter::bitBltWithFixedSelection std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "dst bytes";
return;
}
d->device->readBytes(dstBytes, dstX, dstY, srcWidth, srcHeight);
// Copy the relevant bytes of raw data from srcDev
quint8* srcBytes = 0;
try {
srcBytes = new quint8[srcWidth * srcHeight * srcDev->pixelSize()];
} catch (const std::bad_alloc&) {
warnKrita << "KisPainter::bitBltWithFixedSelection std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "src bytes";
return;
}
srcDev->readBytes(srcBytes, srcX, srcY, srcWidth, srcHeight);
QRect selBounds = selection->bounds();
const quint8 *selRowStart = selection->data() +
(selBounds.width() * (selY - selBounds.top()) + (selX - selBounds.left())) * selection->pixelSize();
/*
* This checks whether there is nothing selected.
*/
if (!d->selection) {
/* As there's nothing selected, blit to dstBytes (intermediary bit array),
ignoring d->selection (the user selection)*/
d->paramInfo.dstRowStart = dstBytes;
d->paramInfo.dstRowStride = srcWidth * d->device->pixelSize();
d->paramInfo.srcRowStart = srcBytes;
d->paramInfo.srcRowStride = srcWidth * srcDev->pixelSize();
d->paramInfo.maskRowStart = selRowStart;
d->paramInfo.maskRowStride = selBounds.width() * selection->pixelSize();
d->paramInfo.rows = srcHeight;
d->paramInfo.cols = srcWidth;
d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
}
else {
/* Read the user selection (d->selection) bytes into an array, ready
to merge in the next block*/
quint32 totalBytes = srcWidth * srcHeight * selection->pixelSize();
quint8* mergedSelectionBytes = 0;
try {
mergedSelectionBytes = new quint8[ totalBytes ];
} catch (const std::bad_alloc&) {
warnKrita << "KisPainter::bitBltWithFixedSelection std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "total bytes";
return;
}
d->selection->projection()->readBytes(mergedSelectionBytes, dstX, dstY, srcWidth, srcHeight);
// Merge selections here by multiplying them - compositeOP(COMPOSITE_MULT)
d->paramInfo.dstRowStart = mergedSelectionBytes;
d->paramInfo.dstRowStride = srcWidth * selection->pixelSize();
d->paramInfo.srcRowStart = selRowStart;
d->paramInfo.srcRowStride = selBounds.width() * selection->pixelSize();
d->paramInfo.maskRowStart = 0;
d->paramInfo.maskRowStride = 0;
d->paramInfo.rows = srcHeight;
d->paramInfo.cols = srcWidth;
KoColorSpaceRegistry::instance()->alpha8()->compositeOp(COMPOSITE_MULT)->composite(d->paramInfo);
// Blit to dstBytes (intermediary bit array)
d->paramInfo.dstRowStart = dstBytes;
d->paramInfo.dstRowStride = srcWidth * d->device->pixelSize();
d->paramInfo.srcRowStart = srcBytes;
d->paramInfo.srcRowStride = srcWidth * srcDev->pixelSize();
d->paramInfo.maskRowStart = mergedSelectionBytes;
d->paramInfo.maskRowStride = srcWidth * selection->pixelSize();
d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
delete[] mergedSelectionBytes;
}
d->device->writeBytes(dstBytes, dstX, dstY, srcWidth, srcHeight);
delete[] dstBytes;
delete[] srcBytes;
addDirtyRect(QRect(dstX, dstY, srcWidth, srcHeight));
}
void KisPainter::bitBltWithFixedSelection(qint32 dstX, qint32 dstY,
const KisPaintDeviceSP srcDev,
const KisFixedPaintDeviceSP selection,
qint32 srcWidth, qint32 srcHeight)
{
bitBltWithFixedSelection(dstX, dstY, srcDev, selection, 0, 0, 0, 0, srcWidth, srcHeight);
}
template <bool useOldSrcData>
void KisPainter::bitBltImpl(qint32 dstX, qint32 dstY,
const KisPaintDeviceSP srcDev,
qint32 srcX, qint32 srcY,
qint32 srcWidth, qint32 srcHeight)
{
/* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just
initializing they perform some dummy passes with those parameters, and it must not crash */
if (srcWidth == 0 || srcHeight == 0) return;
if (srcDev.isNull()) return;
if (d->device.isNull()) return;
QRect srcRect = QRect(srcX, srcY, srcWidth, srcHeight);
if (d->compositeOp->id() == COMPOSITE_COPY) {
if(!d->selection && d->isOpacityUnit &&
srcX == dstX && srcY == dstY &&
d->device->fastBitBltPossible(srcDev)) {
if(useOldSrcData) {
d->device->fastBitBltOldData(srcDev, srcRect);
} else {
d->device->fastBitBlt(srcDev, srcRect);
}
addDirtyRect(srcRect);
return;
}
}
else {
/**
* An optimization, which crops the source rect by the bounds of
* the source device when it is possible
*/
if (d->tryReduceSourceRect(srcDev, &srcRect,
&srcX, &srcY,
&srcWidth, &srcHeight,
&dstX, &dstY)) return;
}
qint32 dstY_ = dstY;
qint32 srcY_ = srcY;
qint32 rowsRemaining = srcHeight;
// Read below
KisRandomConstAccessorSP srcIt = srcDev->createRandomConstAccessorNG(srcX, srcY);
KisRandomAccessorSP dstIt = d->device->createRandomAccessorNG(dstX, dstY);
/* Here be a huge block of verbose code that does roughly the same than
the other bit blit operations. This one is longer than the rest in an effort to
optimize speed and memory use */
if (d->selection) {
KisPaintDeviceSP selectionProjection(d->selection->projection());
KisRandomConstAccessorSP maskIt = selectionProjection->createRandomConstAccessorNG(dstX, dstY);
while (rowsRemaining > 0) {
qint32 dstX_ = dstX;
qint32 srcX_ = srcX;
qint32 columnsRemaining = srcWidth;
qint32 numContiguousDstRows = dstIt->numContiguousRows(dstY_);
qint32 numContiguousSrcRows = srcIt->numContiguousRows(srcY_);
qint32 numContiguousSelRows = maskIt->numContiguousRows(dstY_);
qint32 rows = qMin(numContiguousDstRows, numContiguousSrcRows);
rows = qMin(rows, numContiguousSelRows);
rows = qMin(rows, rowsRemaining);
while (columnsRemaining > 0) {
qint32 numContiguousDstColumns = dstIt->numContiguousColumns(dstX_);
qint32 numContiguousSrcColumns = srcIt->numContiguousColumns(srcX_);
qint32 numContiguousSelColumns = maskIt->numContiguousColumns(dstX_);
qint32 columns = qMin(numContiguousDstColumns, numContiguousSrcColumns);
columns = qMin(columns, numContiguousSelColumns);
columns = qMin(columns, columnsRemaining);
qint32 srcRowStride = srcIt->rowStride(srcX_, srcY_);
srcIt->moveTo(srcX_, srcY_);
qint32 dstRowStride = dstIt->rowStride(dstX_, dstY_);
dstIt->moveTo(dstX_, dstY_);
qint32 maskRowStride = maskIt->rowStride(dstX_, dstY_);
maskIt->moveTo(dstX_, dstY_);
d->paramInfo.dstRowStart = dstIt->rawData();
d->paramInfo.dstRowStride = dstRowStride;
// if we don't use the oldRawData, we need to access the rawData of the source device.
d->paramInfo.srcRowStart = useOldSrcData ? srcIt->oldRawData() : static_cast<KisRandomAccessor2*>(srcIt.data())->rawData();
d->paramInfo.srcRowStride = srcRowStride;
d->paramInfo.maskRowStart = static_cast<KisRandomAccessor2*>(maskIt.data())->rawData();
d->paramInfo.maskRowStride = maskRowStride;
d->paramInfo.rows = rows;
d->paramInfo.cols = columns;
d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
srcX_ += columns;
dstX_ += columns;
columnsRemaining -= columns;
}
srcY_ += rows;
dstY_ += rows;
rowsRemaining -= rows;
}
}
else {
while (rowsRemaining > 0) {
qint32 dstX_ = dstX;
qint32 srcX_ = srcX;
qint32 columnsRemaining = srcWidth;
qint32 numContiguousDstRows = dstIt->numContiguousRows(dstY_);
qint32 numContiguousSrcRows = srcIt->numContiguousRows(srcY_);
qint32 rows = qMin(numContiguousDstRows, numContiguousSrcRows);
rows = qMin(rows, rowsRemaining);
while (columnsRemaining > 0) {
qint32 numContiguousDstColumns = dstIt->numContiguousColumns(dstX_);
qint32 numContiguousSrcColumns = srcIt->numContiguousColumns(srcX_);
qint32 columns = qMin(numContiguousDstColumns, numContiguousSrcColumns);
columns = qMin(columns, columnsRemaining);
qint32 srcRowStride = srcIt->rowStride(srcX_, srcY_);
srcIt->moveTo(srcX_, srcY_);
qint32 dstRowStride = dstIt->rowStride(dstX_, dstY_);
dstIt->moveTo(dstX_, dstY_);
d->paramInfo.dstRowStart = dstIt->rawData();
d->paramInfo.dstRowStride = dstRowStride;
// if we don't use the oldRawData, we need to access the rawData of the source device.
d->paramInfo.srcRowStart = useOldSrcData ? srcIt->oldRawData() : static_cast<KisRandomAccessor2*>(srcIt.data())->rawData();
d->paramInfo.srcRowStride = srcRowStride;
d->paramInfo.maskRowStart = 0;
d->paramInfo.maskRowStride = 0;
d->paramInfo.rows = rows;
d->paramInfo.cols = columns;
d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
srcX_ += columns;
dstX_ += columns;
columnsRemaining -= columns;
}
srcY_ += rows;
dstY_ += rows;
rowsRemaining -= rows;
}
}
addDirtyRect(QRect(dstX, dstY, srcWidth, srcHeight));
}
void KisPainter::bitBlt(qint32 dstX, qint32 dstY,
const KisPaintDeviceSP srcDev,
qint32 srcX, qint32 srcY,
qint32 srcWidth, qint32 srcHeight)
{
bitBltImpl<false>(dstX, dstY, srcDev, srcX, srcY, srcWidth, srcHeight);
}
void KisPainter::bitBlt(const QPoint & pos, const KisPaintDeviceSP srcDev, const QRect & srcRect)
{
bitBlt(pos.x(), pos.y(), srcDev, srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height());
}
void KisPainter::bitBltOldData(qint32 dstX, qint32 dstY,
const KisPaintDeviceSP srcDev,
qint32 srcX, qint32 srcY,
qint32 srcWidth, qint32 srcHeight)
{
bitBltImpl<true>(dstX, dstY, srcDev, srcX, srcY, srcWidth, srcHeight);
}
void KisPainter::bitBltOldData(const QPoint & pos, const KisPaintDeviceSP srcDev, const QRect & srcRect)
{
bitBltOldData(pos.x(), pos.y(), srcDev, srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height());
}
void KisPainter::fill(qint32 x, qint32 y, qint32 width, qint32 height, const KoColor& color)
{
/* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just
* initializing they perform some dummy passes with those parameters, and it must not crash */
if(width == 0 || height == 0 || d->device.isNull())
return;
KoColor srcColor(color, d->device->compositionSourceColorSpace());
qint32 dstY = y;
qint32 rowsRemaining = height;
KisRandomAccessorSP dstIt = d->device->createRandomAccessorNG(x, y);
if(d->selection) {
KisPaintDeviceSP selectionProjection(d->selection->projection());
KisRandomConstAccessorSP maskIt = selectionProjection->createRandomConstAccessorNG(x, y);
while(rowsRemaining > 0) {
qint32 dstX = x;
qint32 columnsRemaining = width;
qint32 numContiguousDstRows = dstIt->numContiguousRows(dstY);
qint32 numContiguousSelRows = maskIt->numContiguousRows(dstY);
qint32 rows = qMin(numContiguousDstRows, numContiguousSelRows);
rows = qMin(rows, rowsRemaining);
while (columnsRemaining > 0) {
qint32 numContiguousDstColumns = dstIt->numContiguousColumns(dstX);
qint32 numContiguousSelColumns = maskIt->numContiguousColumns(dstX);
qint32 columns = qMin(numContiguousDstColumns, numContiguousSelColumns);
columns = qMin(columns, columnsRemaining);
qint32 dstRowStride = dstIt->rowStride(dstX, dstY);
dstIt->moveTo(dstX, dstY);
qint32 maskRowStride = maskIt->rowStride(dstX, dstY);
maskIt->moveTo(dstX, dstY);
d->paramInfo.dstRowStart = dstIt->rawData();
d->paramInfo.dstRowStride = dstRowStride;
d->paramInfo.srcRowStart = srcColor.data();
d->paramInfo.srcRowStride = 0; // srcRowStride is set to zero to use the compositeOp with only a single color pixel
d->paramInfo.maskRowStart = maskIt->oldRawData();
d->paramInfo.maskRowStride = maskRowStride;
d->paramInfo.rows = rows;
d->paramInfo.cols = columns;
d->colorSpace->bitBlt(srcColor.colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
dstX += columns;
columnsRemaining -= columns;
}
dstY += rows;
rowsRemaining -= rows;
}
}
else {
while(rowsRemaining > 0) {
qint32 dstX = x;
qint32 columnsRemaining = width;
qint32 numContiguousDstRows = dstIt->numContiguousRows(dstY);
qint32 rows = qMin(numContiguousDstRows, rowsRemaining);
while(columnsRemaining > 0) {
qint32 numContiguousDstColumns = dstIt->numContiguousColumns(dstX);
qint32 columns = qMin(numContiguousDstColumns, columnsRemaining);
qint32 dstRowStride = dstIt->rowStride(dstX, dstY);
dstIt->moveTo(dstX, dstY);
d->paramInfo.dstRowStart = dstIt->rawData();
d->paramInfo.dstRowStride = dstRowStride;
d->paramInfo.srcRowStart = srcColor.data();
d->paramInfo.srcRowStride = 0; // srcRowStride is set to zero to use the compositeOp with only a single color pixel
d->paramInfo.maskRowStart = 0;
d->paramInfo.maskRowStride = 0;
d->paramInfo.rows = rows;
d->paramInfo.cols = columns;
d->colorSpace->bitBlt(srcColor.colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
dstX += columns;
columnsRemaining -= columns;
}
dstY += rows;
rowsRemaining -= rows;
}
}
addDirtyRect(QRect(x, y, width, height));
}
void KisPainter::bltFixed(qint32 dstX, qint32 dstY,
const KisFixedPaintDeviceSP srcDev,
qint32 srcX, qint32 srcY,
qint32 srcWidth, qint32 srcHeight)
{
/* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just
initializing they perform some dummy passes with those parameters, and it must not crash */
if (srcWidth == 0 || srcHeight == 0) return;
if (srcDev.isNull()) return;
if (d->device.isNull()) return;
QRect srcRect = QRect(srcX, srcY, srcWidth, srcHeight);
QRect srcBounds = srcDev->bounds();
/* Trying to read outside a KisFixedPaintDevice is inherently wrong and shouldn't be done,
so crash if someone attempts to do this. Don't resize as it would obfuscate the mistake. */
KIS_SAFE_ASSERT_RECOVER_RETURN(srcBounds.contains(srcRect));
Q_UNUSED(srcRect); // only used in above assertion
/* Create an intermediate byte array to hold information before it is written
to the current paint device (aka: d->device) */
quint8* dstBytes = 0;
try {
dstBytes = new quint8[srcWidth * srcHeight * d->device->pixelSize()];
} catch (const std::bad_alloc&) {
warnKrita << "KisPainter::bltFixed std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "total bytes";
return;
}
d->device->readBytes(dstBytes, dstX, dstY, srcWidth, srcHeight);
const quint8 *srcRowStart = srcDev->data() +
(srcBounds.width() * (srcY - srcBounds.top()) + (srcX - srcBounds.left())) * srcDev->pixelSize();
d->paramInfo.dstRowStart = dstBytes;
d->paramInfo.dstRowStride = srcWidth * d->device->pixelSize();
d->paramInfo.srcRowStart = srcRowStart;
d->paramInfo.srcRowStride = srcBounds.width() * srcDev->pixelSize();
d->paramInfo.maskRowStart = 0;
d->paramInfo.maskRowStride = 0;
d->paramInfo.rows = srcHeight;
d->paramInfo.cols = srcWidth;
if (d->selection) {
/* d->selection is a KisPaintDevice, so first a readBytes is performed to
get the area of interest... */
KisPaintDeviceSP selectionProjection(d->selection->projection());
quint8* selBytes = 0;
try {
selBytes = new quint8[srcWidth * srcHeight * selectionProjection->pixelSize()];
}
catch (const std::bad_alloc&) {
delete[] dstBytes;
return;
}
selectionProjection->readBytes(selBytes, dstX, dstY, srcWidth, srcHeight);
d->paramInfo.maskRowStart = selBytes;
d->paramInfo.maskRowStride = srcWidth * selectionProjection->pixelSize();
}
// ...and then blit.
d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
d->device->writeBytes(dstBytes, dstX, dstY, srcWidth, srcHeight);
delete[] d->paramInfo.maskRowStart;
delete[] dstBytes;
addDirtyRect(QRect(dstX, dstY, srcWidth, srcHeight));
}
void KisPainter::bltFixed(const QPoint & pos, const KisFixedPaintDeviceSP srcDev, const QRect & srcRect)
{
bltFixed(pos.x(), pos.y(), srcDev, srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height());
}
void KisPainter::bltFixedWithFixedSelection(qint32 dstX, qint32 dstY,
const KisFixedPaintDeviceSP srcDev,
const KisFixedPaintDeviceSP selection,
qint32 selX, qint32 selY,
qint32 srcX, qint32 srcY,
quint32 srcWidth, quint32 srcHeight)
{
// TODO: get selX and selY working as intended
/* This check for nonsense ought to be a Q_ASSERT. However, when paintops are just
initializing they perform some dummy passes with those parameters, and it must not crash */
if (srcWidth == 0 || srcHeight == 0) return;
if (srcDev.isNull()) return;
if (d->device.isNull()) return;
// Check that selection has an alpha colorspace, crash if false
Q_ASSERT(selection->colorSpace() == KoColorSpaceRegistry::instance()->alpha8());
QRect srcRect = QRect(srcX, srcY, srcWidth, srcHeight);
QRect selRect = QRect(selX, selY, srcWidth, srcHeight);
QRect srcBounds = srcDev->bounds();
QRect selBounds = selection->bounds();
/* Trying to read outside a KisFixedPaintDevice is inherently wrong and shouldn't be done,
so crash if someone attempts to do this. Don't resize as it would obfuscate the mistake. */
Q_ASSERT(srcBounds.contains(srcRect));
Q_UNUSED(srcRect); // only used in above assertion
Q_ASSERT(selBounds.contains(selRect));
Q_UNUSED(selRect); // only used in above assertion
/* Create an intermediate byte array to hold information before it is written
to the current paint device (aka: d->device) */
quint8* dstBytes = 0;
try {
dstBytes = new quint8[srcWidth * srcHeight * d->device->pixelSize()];
} catch (const std::bad_alloc&) {
warnKrita << "KisPainter::bltFixedWithFixedSelection std::bad_alloc for " << srcWidth << " * " << srcHeight << " * " << d->device->pixelSize() << "total bytes";
return;
}
d->device->readBytes(dstBytes, dstX, dstY, srcWidth, srcHeight);
const quint8 *srcRowStart = srcDev->data() +
(srcBounds.width() * (srcY - srcBounds.top()) + (srcX - srcBounds.left())) * srcDev->pixelSize();
const quint8 *selRowStart = selection->data() +
(selBounds.width() * (selY - selBounds.top()) + (selX - selBounds.left())) * selection->pixelSize();
if (!d->selection) {
/* As there's nothing selected, blit to dstBytes (intermediary bit array),
ignoring d->selection (the user selection)*/
d->paramInfo.dstRowStart = dstBytes;
d->paramInfo.dstRowStride = srcWidth * d->device->pixelSize();
d->paramInfo.srcRowStart = srcRowStart;
d->paramInfo.srcRowStride = srcBounds.width() * srcDev->pixelSize();
d->paramInfo.maskRowStart = selRowStart;
d->paramInfo.maskRowStride = selBounds.width() * selection->pixelSize();
d->paramInfo.rows = srcHeight;
d->paramInfo.cols = srcWidth;
d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
}
else {
/* Read the user selection (d->selection) bytes into an array, ready
to merge in the next block*/
quint32 totalBytes = srcWidth * srcHeight * selection->pixelSize();
quint8 * mergedSelectionBytes = 0;
try {
mergedSelectionBytes = new quint8[ totalBytes ];
} catch (const std::bad_alloc&) {
warnKrita << "KisPainter::bltFixedWithFixedSelection std::bad_alloc for " << totalBytes << "total bytes";
delete[] dstBytes;
return;
}
d->selection->projection()->readBytes(mergedSelectionBytes, dstX, dstY, srcWidth, srcHeight);
// Merge selections here by multiplying them - compositeOp(COMPOSITE_MULT)
d->paramInfo.dstRowStart = mergedSelectionBytes;
d->paramInfo.dstRowStride = srcWidth * selection->pixelSize();
d->paramInfo.srcRowStart = selRowStart;
d->paramInfo.srcRowStride = selBounds.width() * selection->pixelSize();
d->paramInfo.maskRowStart = 0;
d->paramInfo.maskRowStride = 0;
d->paramInfo.rows = srcHeight;
d->paramInfo.cols = srcWidth;
KoColorSpaceRegistry::instance()->alpha8()->compositeOp(COMPOSITE_MULT)->composite(d->paramInfo);
// Blit to dstBytes (intermediary bit array)
d->paramInfo.dstRowStart = dstBytes;
d->paramInfo.dstRowStride = srcWidth * d->device->pixelSize();
d->paramInfo.srcRowStart = srcRowStart;
d->paramInfo.srcRowStride = srcBounds.width() * srcDev->pixelSize();
d->paramInfo.maskRowStart = mergedSelectionBytes;
d->paramInfo.maskRowStride = srcWidth * selection->pixelSize();
d->colorSpace->bitBlt(srcDev->colorSpace(), d->paramInfo, d->compositeOp, d->renderingIntent, d->conversionFlags);
delete[] mergedSelectionBytes;
}
d->device->writeBytes(dstBytes, dstX, dstY, srcWidth, srcHeight);
delete[] dstBytes;
addDirtyRect(QRect(dstX, dstY, srcWidth, srcHeight));
}
void KisPainter::bltFixedWithFixedSelection(qint32 dstX, qint32 dstY,
const KisFixedPaintDeviceSP srcDev,
const KisFixedPaintDeviceSP selection,
quint32 srcWidth, quint32 srcHeight)
{
bltFixedWithFixedSelection(dstX, dstY, srcDev, selection, 0, 0, 0, 0, srcWidth, srcHeight);
}
void KisPainter::paintLine(const KisPaintInformation &pi1,
const KisPaintInformation &pi2,
KisDistanceInformation *currentDistance)
{
if (d->device && d->paintOp && d->paintOp->canPaint()) {
d->paintOp->paintLine(pi1, pi2, currentDistance);
}
}
void KisPainter::paintPolyline(const vQPointF &points,
int index, int numPoints)
{
if (d->fillStyle != FillStyleNone) {
fillPolygon(points, d->fillStyle);
}
if (index >= points.count())
return;
if (numPoints < 0)
numPoints = points.count();
if (index + numPoints > points.count())
numPoints = points.count() - index;
if (numPoints > 1) {
KisDistanceInformation saveDist(points[0],
KisAlgebra2D::directionBetweenPoints(points[0], points[1], 0.0));
for (int i = index; i < index + numPoints - 1; i++) {
paintLine(points [i], points [i + 1], &saveDist);
}
}
}
static void getBezierCurvePoints(const KisVector2D &pos1,
const KisVector2D &control1,
const KisVector2D &control2,
const KisVector2D &pos2,
vQPointF& points)
{
LineEquation line = LineEquation::Through(pos1, pos2);
qreal d1 = line.absDistance(control1);
qreal d2 = line.absDistance(control2);
if (d1 < BEZIER_FLATNESS_THRESHOLD && d2 < BEZIER_FLATNESS_THRESHOLD) {
points.push_back(toQPointF(pos1));
} else {
// Midpoint subdivision. See Foley & Van Dam Computer Graphics P.508
KisVector2D l2 = (pos1 + control1) / 2;
KisVector2D h = (control1 + control2) / 2;
KisVector2D l3 = (l2 + h) / 2;
KisVector2D r3 = (control2 + pos2) / 2;
KisVector2D r2 = (h + r3) / 2;
KisVector2D l4 = (l3 + r2) / 2;
getBezierCurvePoints(pos1, l2, l3, l4, points);
getBezierCurvePoints(l4, r2, r3, pos2, points);
}
}
void KisPainter::getBezierCurvePoints(const QPointF &pos1,
const QPointF &control1,
const QPointF &control2,
const QPointF &pos2,
vQPointF& points) const
{
::getBezierCurvePoints(toKisVector2D(pos1), toKisVector2D(control1), toKisVector2D(control2), toKisVector2D(pos2), points);
}
void KisPainter::paintBezierCurve(const KisPaintInformation &pi1,
const QPointF &control1,
const QPointF &control2,
const KisPaintInformation &pi2,
KisDistanceInformation *currentDistance)
{
if (d->paintOp && d->paintOp->canPaint()) {
d->paintOp->paintBezierCurve(pi1, control1, control2, pi2, currentDistance);
}
}
void KisPainter::paintRect(const QRectF &rect)
{
QRectF normalizedRect = rect.normalized();
vQPointF points;
points.push_back(normalizedRect.topLeft());
points.push_back(normalizedRect.bottomLeft());
points.push_back(normalizedRect.bottomRight());
points.push_back(normalizedRect.topRight());
paintPolygon(points);
}
void KisPainter::paintRect(const qreal x,
const qreal y,
const qreal w,
const qreal h)
{
paintRect(QRectF(x, y, w, h));
}
void KisPainter::paintEllipse(const QRectF &rect)
{
QRectF r = rect.normalized(); // normalize before checking as negative width and height are empty too
if (r.isEmpty()) return;
// See http://www.whizkidtech.redprince.net/bezier/circle/ for explanation.
// kappa = (4/3*(sqrt(2)-1))
const qreal kappa = 0.5522847498;
const qreal lx = (r.width() / 2) * kappa;
const qreal ly = (r.height() / 2) * kappa;
QPointF center = r.center();
QPointF p0(r.left(), center.y());
QPointF p1(r.left(), center.y() - ly);
QPointF p2(center.x() - lx, r.top());
QPointF p3(center.x(), r.top());
vQPointF points;
getBezierCurvePoints(p0, p1, p2, p3, points);
QPointF p4(center.x() + lx, r.top());
QPointF p5(r.right(), center.y() - ly);
QPointF p6(r.right(), center.y());
getBezierCurvePoints(p3, p4, p5, p6, points);
QPointF p7(r.right(), center.y() + ly);
QPointF p8(center.x() + lx, r.bottom());
QPointF p9(center.x(), r.bottom());
getBezierCurvePoints(p6, p7, p8, p9, points);
QPointF p10(center.x() - lx, r.bottom());
QPointF p11(r.left(), center.y() + ly);
getBezierCurvePoints(p9, p10, p11, p0, points);
paintPolygon(points);
}
void KisPainter::paintEllipse(const qreal x,
const qreal y,
const qreal w,
const qreal h)
{
paintEllipse(QRectF(x, y, w, h));
}
void KisPainter::paintAt(const KisPaintInformation& pi,
KisDistanceInformation *savedDist)
{
if (d->paintOp && d->paintOp->canPaint()) {
d->paintOp->paintAt(pi, savedDist);
}
}
void KisPainter::fillPolygon(const vQPointF& points, FillStyle fillStyle)
{
if (points.count() < 3) {
return;
}
if (fillStyle == FillStyleNone) {
return;
}
QPainterPath polygonPath;
polygonPath.moveTo(points.at(0));
for (int pointIndex = 1; pointIndex < points.count(); pointIndex++) {
polygonPath.lineTo(points.at(pointIndex));
}
polygonPath.closeSubpath();
d->fillStyle = fillStyle;
fillPainterPath(polygonPath);
}
void KisPainter::paintPolygon(const vQPointF& points)
{
if (d->fillStyle != FillStyleNone) {
fillPolygon(points, d->fillStyle);
}
if (d->strokeStyle != StrokeStyleNone) {
if (points.count() > 1) {
KisDistanceInformation distance(points[0],
KisAlgebra2D::directionBetweenPoints(points[0], points[1], 0.0));
for (int i = 0; i < points.count() - 1; i++) {
paintLine(KisPaintInformation(points[i]), KisPaintInformation(points[i + 1]), &distance);
}
paintLine(points[points.count() - 1], points[0], &distance);
}
}
}
void KisPainter::paintPainterPath(const QPainterPath& path)
{
if (d->fillStyle != FillStyleNone) {
fillPainterPath(path);
}
if (d->strokeStyle == StrokeStyleNone) return;
QPointF lastPoint, nextPoint;
int elementCount = path.elementCount();
KisDistanceInformation saveDist;
for (int i = 0; i < elementCount; i++) {
QPainterPath::Element element = path.elementAt(i);
switch (element.type) {
case QPainterPath::MoveToElement:
lastPoint = QPointF(element.x, element.y);
break;
case QPainterPath::LineToElement:
nextPoint = QPointF(element.x, element.y);
paintLine(KisPaintInformation(lastPoint), KisPaintInformation(nextPoint), &saveDist);
lastPoint = nextPoint;
break;
case QPainterPath::CurveToElement:
nextPoint = QPointF(path.elementAt(i + 2).x, path.elementAt(i + 2).y);
paintBezierCurve(KisPaintInformation(lastPoint),
QPointF(path.elementAt(i).x, path.elementAt(i).y),
QPointF(path.elementAt(i + 1).x, path.elementAt(i + 1).y),
KisPaintInformation(nextPoint), &saveDist);
lastPoint = nextPoint;
break;
default:
continue;
}
}
}
void KisPainter::fillPainterPath(const QPainterPath& path)
{
fillPainterPath(path, QRect());
}
void KisPainter::fillPainterPath(const QPainterPath& path, const QRect &requestedRect)
{
if (d->mirrorHorizontally || d->mirrorVertically) {
KisLodTransform lod(d->device);
QPointF effectiveAxesCenter = lod.map(d->axesCenter);
QTransform C1 = QTransform::fromTranslate(-effectiveAxesCenter.x(), -effectiveAxesCenter.y());
QTransform C2 = QTransform::fromTranslate(effectiveAxesCenter.x(), effectiveAxesCenter.y());
QTransform t;
QPainterPath newPath;
QRect newRect;
if (d->mirrorHorizontally) {
t = C1 * QTransform::fromScale(-1,1) * C2;
newPath = t.map(path);
newRect = t.mapRect(requestedRect);
d->fillPainterPathImpl(newPath, newRect);
}
if (d->mirrorVertically) {
t = C1 * QTransform::fromScale(1,-1) * C2;
newPath = t.map(path);
newRect = t.mapRect(requestedRect);
d->fillPainterPathImpl(newPath, newRect);
}
if (d->mirrorHorizontally && d->mirrorVertically) {
t = C1 * QTransform::fromScale(-1,-1) * C2;
newPath = t.map(path);
newRect = t.mapRect(requestedRect);
d->fillPainterPathImpl(newPath, newRect);
}
}
d->fillPainterPathImpl(path, requestedRect);
}
void KisPainter::Private::fillPainterPathImpl(const QPainterPath& path, const QRect &requestedRect)
{
if (fillStyle == FillStyleNone) {
return;
}
// Fill the polygon bounding rectangle with the required contents then we'll
// create a mask for the actual polygon coverage.
if (!fillPainter) {
polygon = device->createCompositionSourceDevice();
fillPainter = new KisFillPainter(polygon);
} else {
polygon->clear();
}
Q_CHECK_PTR(polygon);
QRectF boundingRect = path.boundingRect();
QRect fillRect = boundingRect.toAlignedRect();
// Expand the rectangle to allow for anti-aliasing.
fillRect.adjust(-1, -1, 1, 1);
if (requestedRect.isValid()) {
fillRect &= requestedRect;
}
switch (fillStyle) {
default:
- /* Falls through. */
+ Q_FALLTHROUGH();
case FillStyleGradient:
// Currently unsupported
- /* Falls through. */
+ Q_FALLTHROUGH();
case FillStyleStrokes:
// Currently unsupported
warnImage << "Unknown or unsupported fill style in fillPolygon\n";
- /* Falls through. */
+ Q_FALLTHROUGH();
case FillStyleForegroundColor:
fillPainter->fillRect(fillRect, q->paintColor(), OPACITY_OPAQUE_U8);
break;
case FillStyleBackgroundColor:
fillPainter->fillRect(fillRect, q->backgroundColor(), OPACITY_OPAQUE_U8);
break;
case FillStylePattern:
if (pattern) { // if the user hasn't got any patterns installed, we shouldn't crash...
fillPainter->fillRect(fillRect, pattern);
}
break;
case FillStyleGenerator:
if (generator) { // if the user hasn't got any generators, we shouldn't crash...
fillPainter->fillRect(fillRect.x(), fillRect.y(), fillRect.width(), fillRect.height(), q->generator());
}
break;
}
if (polygonMaskImage.isNull() || (maskPainter == 0)) {
polygonMaskImage = QImage(maskImageWidth, maskImageHeight, QImage::Format_ARGB32_Premultiplied);
maskPainter = new QPainter(&polygonMaskImage);
maskPainter->setRenderHint(QPainter::Antialiasing, q->antiAliasPolygonFill());
}
// Break the mask up into chunks so we don't have to allocate a potentially very large QImage.
const QColor black(Qt::black);
const QBrush brush(Qt::white);
for (qint32 x = fillRect.x(); x < fillRect.x() + fillRect.width(); x += maskImageWidth) {
for (qint32 y = fillRect.y(); y < fillRect.y() + fillRect.height(); y += maskImageHeight) {
polygonMaskImage.fill(black.rgb());
maskPainter->translate(-x, -y);
maskPainter->fillPath(path, brush);
maskPainter->translate(x, y);
qint32 rectWidth = qMin(fillRect.x() + fillRect.width() - x, maskImageWidth);
qint32 rectHeight = qMin(fillRect.y() + fillRect.height() - y, maskImageHeight);
KisHLineIteratorSP lineIt = polygon->createHLineIteratorNG(x, y, rectWidth);
quint8 tmp;
for (int row = y; row < y + rectHeight; row++) {
QRgb* line = reinterpret_cast<QRgb*>(polygonMaskImage.scanLine(row - y));
do {
tmp = qRed(line[lineIt->x() - x]);
polygon->colorSpace()->applyAlphaU8Mask(lineIt->rawData(), &tmp, 1);
} while (lineIt->nextPixel());
lineIt->nextRow();
}
}
}
QRect bltRect = !requestedRect.isEmpty() ? requestedRect : fillRect;
q->bitBlt(bltRect.x(), bltRect.y(), polygon, bltRect.x(), bltRect.y(), bltRect.width(), bltRect.height());
}
void KisPainter::drawPainterPath(const QPainterPath& path, const QPen& pen)
{
drawPainterPath(path, pen, QRect());
}
void KisPainter::drawPainterPath(const QPainterPath& path, const QPen& pen, const QRect &requestedRect)
{
// we are drawing mask, it has to be white
// color of the path is given by paintColor()
Q_ASSERT(pen.color() == Qt::white);
if (!d->fillPainter) {
d->polygon = d->device->createCompositionSourceDevice();
d->fillPainter = new KisFillPainter(d->polygon);
} else {
d->polygon->clear();
}
Q_CHECK_PTR(d->polygon);
QRectF boundingRect = path.boundingRect();
QRect fillRect = boundingRect.toAlignedRect();
// take width of the pen into account
int penWidth = qRound(pen.widthF());
fillRect.adjust(-penWidth, -penWidth, penWidth, penWidth);
// Expand the rectangle to allow for anti-aliasing.
fillRect.adjust(-1, -1, 1, 1);
if (!requestedRect.isNull()) {
fillRect &= requestedRect;
}
d->fillPainter->fillRect(fillRect, paintColor(), OPACITY_OPAQUE_U8);
if (d->polygonMaskImage.isNull() || (d->maskPainter == 0)) {
d->polygonMaskImage = QImage(d->maskImageWidth, d->maskImageHeight, QImage::Format_ARGB32_Premultiplied);
d->maskPainter = new QPainter(&d->polygonMaskImage);
d->maskPainter->setRenderHint(QPainter::Antialiasing, antiAliasPolygonFill());
}
// Break the mask up into chunks so we don't have to allocate a potentially very large QImage.
const QColor black(Qt::black);
QPen oldPen = d->maskPainter->pen();
d->maskPainter->setPen(pen);
for (qint32 x = fillRect.x(); x < fillRect.x() + fillRect.width(); x += d->maskImageWidth) {
for (qint32 y = fillRect.y(); y < fillRect.y() + fillRect.height(); y += d->maskImageHeight) {
d->polygonMaskImage.fill(black.rgb());
d->maskPainter->translate(-x, -y);
d->maskPainter->drawPath(path);
d->maskPainter->translate(x, y);
qint32 rectWidth = qMin(fillRect.x() + fillRect.width() - x, d->maskImageWidth);
qint32 rectHeight = qMin(fillRect.y() + fillRect.height() - y, d->maskImageHeight);
KisHLineIteratorSP lineIt = d->polygon->createHLineIteratorNG(x, y, rectWidth);
quint8 tmp;
for (int row = y; row < y + rectHeight; row++) {
QRgb* line = reinterpret_cast<QRgb*>(d->polygonMaskImage.scanLine(row - y));
do {
tmp = qRed(line[lineIt->x() - x]);
d->polygon->colorSpace()->applyAlphaU8Mask(lineIt->rawData(), &tmp, 1);
} while (lineIt->nextPixel());
lineIt->nextRow();
}
}
}
d->maskPainter->setPen(oldPen);
QRect r = d->polygon->extent();
bitBlt(r.x(), r.y(), d->polygon, r.x(), r.y(), r.width(), r.height());
}
inline void KisPainter::compositeOnePixel(quint8 *dst, const KoColor &color)
{
d->paramInfo.dstRowStart = dst;
d->paramInfo.dstRowStride = 0;
d->paramInfo.srcRowStart = color.data();
d->paramInfo.srcRowStride = 0;
d->paramInfo.maskRowStart = 0;
d->paramInfo.maskRowStride = 0;
d->paramInfo.rows = 1;
d->paramInfo.cols = 1;
d->colorSpace->bitBlt(color.colorSpace(), d->paramInfo, d->compositeOp,
d->renderingIntent,
d->conversionFlags);
}
/**/
void KisPainter::drawLine(const QPointF& start, const QPointF& end, qreal width, bool antialias){
int x1 = qFloor(start.x());
int y1 = qFloor(start.y());
int x2 = qFloor(end.x());
int y2 = qFloor(end.y());
if ((x2 == x1 ) && (y2 == y1)) return;
int dstX = x2-x1;
int dstY = y2-y1;
qreal uniC = dstX*y1 - dstY*x1;
qreal projectionDenominator = 1.0 / (pow((double)dstX, 2) + pow((double)dstY, 2));
qreal subPixel;
if (qAbs(dstX) > qAbs(dstY)){
subPixel = start.x() - x1;
}else{
subPixel = start.y() - y1;
}
qreal halfWidth = width * 0.5 + subPixel;
int W_ = qRound(halfWidth) + 1;
// save the state
int X1_ = x1;
int Y1_ = y1;
int X2_ = x2;
int Y2_ = y2;
if (x2<x1) std::swap(x1,x2);
if (y2<y1) std::swap(y1,y2);
qreal denominator = sqrt(pow((double)dstY,2) + pow((double)dstX,2));
if (denominator == 0.0) {
denominator = 1.0;
}
denominator = 1.0/denominator;
qreal projection,scanX,scanY,AA_;
KisRandomAccessorSP accessor = d->device->createRandomAccessorNG(x1, y1);
KisRandomConstAccessorSP selectionAccessor;
if (d->selection) {
selectionAccessor = d->selection->projection()->createRandomConstAccessorNG(x1, y1);
}
for (int y = y1-W_; y < y2+W_ ; y++){
for (int x = x1-W_; x < x2+W_; x++){
projection = ( (x-X1_)* dstX + (y-Y1_)*dstY ) * projectionDenominator;
scanX = X1_ + projection * dstX;
scanY = Y1_ + projection * dstY;
if (((scanX < x1) || (scanX > x2)) || ((scanY < y1) || (scanY > y2))) {
AA_ = qMin( sqrt( pow((double)x - X1_, 2) + pow((double)y - Y1_, 2) ),
sqrt( pow((double)x - X2_, 2) + pow((double)y - Y2_, 2) ));
}else{
AA_ = qAbs(dstY*x - dstX*y + uniC) * denominator;
}
if (AA_>halfWidth) {
continue;
}
accessor->moveTo(x, y);
if (selectionAccessor) selectionAccessor->moveTo(x,y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
KoColor mycolor = d->paintColor;
if (antialias && AA_ > halfWidth-1.0) {
mycolor.colorSpace()->multiplyAlpha(mycolor.data(), 1.0 - (AA_-(halfWidth-1.0)), 1);
}
compositeOnePixel(accessor->rawData(), mycolor);
}
}
}
}
/**/
void KisPainter::drawLine(const QPointF & start, const QPointF & end)
{
drawThickLine(start, end, 1, 1);
}
void KisPainter::drawDDALine(const QPointF & start, const QPointF & end)
{
int x = qFloor(start.x());
int y = qFloor(start.y());
int x2 = qFloor(end.x());
int y2 = qFloor(end.y());
// Width and height of the line
int xd = x2 - x;
int yd = y2 - y;
float m = (float)yd / (float)xd;
float fx = x;
float fy = y;
int inc;
KisRandomAccessorSP accessor = d->device->createRandomAccessorNG(x, y);
KisRandomConstAccessorSP selectionAccessor;
if (d->selection) {
selectionAccessor = d->selection->projection()->createRandomConstAccessorNG(x, y);
}
accessor->moveTo(x, y);
if (selectionAccessor) selectionAccessor->moveTo(x,y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), d->paintColor);
}
if (fabs(m) > 1.0f) {
inc = (yd > 0) ? 1 : -1;
m = 1.0f / m;
m *= inc;
while (y != y2) {
y = y + inc;
fx = fx + m;
x = qRound(fx);
accessor->moveTo(x, y);
if (selectionAccessor) selectionAccessor->moveTo(x, y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), d->paintColor);
}
}
} else {
inc = (xd > 0) ? 1 : -1;
m *= inc;
while (x != x2) {
x = x + inc;
fy = fy + m;
y = qRound(fy);
accessor->moveTo(x, y);
if (selectionAccessor) selectionAccessor->moveTo(x, y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), d->paintColor);
}
}
}
}
void KisPainter::drawWobblyLine(const QPointF & start, const QPointF & end)
{
KoColor mycolor(d->paintColor);
int x1 = qFloor(start.x());
int y1 = qFloor(start.y());
int x2 = qFloor(end.x());
int y2 = qFloor(end.y());
KisRandomAccessorSP accessor = d->device->createRandomAccessorNG(x1, y1);
KisRandomConstAccessorSP selectionAccessor;
if (d->selection) {
selectionAccessor = d->selection->projection()->createRandomConstAccessorNG(x1, y1);
}
// Width and height of the line
int xd = (x2 - x1);
int yd = (y2 - y1);
int x;
int y;
float fx = (x = x1);
float fy = (y = y1);
float m = (float)yd / (float)xd;
int inc;
if (fabs(m) > 1) {
inc = (yd > 0) ? 1 : -1;
m = 1.0f / m;
m *= inc;
while (y != y2) {
fx = fx + m;
y = y + inc;
x = qRound(fx);
float br1 = qFloor(fx + 1) - fx;
float br2 = fx - qFloor(fx);
accessor->moveTo(x, y);
if (selectionAccessor) selectionAccessor->moveTo(x, y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
mycolor.setOpacity((quint8)(255*br1));
compositeOnePixel(accessor->rawData(), mycolor);
}
accessor->moveTo(x + 1, y);
if (selectionAccessor) selectionAccessor->moveTo(x + 1, y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
mycolor.setOpacity((quint8)(255*br2));
compositeOnePixel(accessor->rawData(), mycolor);
}
}
} else {
inc = (xd > 0) ? 1 : -1;
m *= inc;
while (x != x2) {
fy = fy + m;
x = x + inc;
y = qRound(fy);
float br1 = qFloor(fy + 1) - fy;
float br2 = fy - qFloor(fy);
accessor->moveTo(x, y);
if (selectionAccessor) selectionAccessor->moveTo(x, y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
mycolor.setOpacity((quint8)(255*br1));
compositeOnePixel(accessor->rawData(), mycolor);
}
accessor->moveTo(x, y + 1);
if (selectionAccessor) selectionAccessor->moveTo(x, y + 1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
mycolor.setOpacity((quint8)(255*br2));
compositeOnePixel(accessor->rawData(), mycolor);
}
}
}
}
void KisPainter::drawWuLine(const QPointF & start, const QPointF & end)
{
KoColor lineColor(d->paintColor);
int x1 = qFloor(start.x());
int y1 = qFloor(start.y());
int x2 = qFloor(end.x());
int y2 = qFloor(end.y());
KisRandomAccessorSP accessor = d->device->createRandomAccessorNG(x1, y1);
KisRandomConstAccessorSP selectionAccessor;
if (d->selection) {
selectionAccessor = d->selection->projection()->createRandomConstAccessorNG(x1, y1);
}
float grad, xd, yd;
float xgap, ygap, xend, yend, yf, xf;
float brightness1, brightness2;
int ix1, ix2, iy1, iy2;
quint8 c1, c2;
// gradient of line
xd = (x2 - x1);
yd = (y2 - y1);
if (yd == 0) {
/* Horizontal line */
int incr = (x1 < x2) ? 1 : -1;
ix1 = x1;
ix2 = x2;
iy1 = y1;
while (ix1 != ix2) {
ix1 = ix1 + incr;
accessor->moveTo(ix1, iy1);
if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), lineColor);
}
}
return;
}
if (xd == 0) {
/* Vertical line */
int incr = (y1 < y2) ? 1 : -1;
iy1 = y1;
iy2 = y2;
ix1 = x1;
while (iy1 != iy2) {
iy1 = iy1 + incr;
accessor->moveTo(ix1, iy1);
if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), lineColor);
}
}
return;
}
if (fabs(xd) > fabs(yd)) {
// horizontal line
// line have to be paint from left to right
if (x1 > x2) {
std::swap(x1, x2);
std::swap(y1, y2);
xd = (x2 - x1);
yd = (y2 - y1);
}
grad = yd / xd;
// nearest X,Y integer coordinates
xend = x1;
yend = y1 + grad * (xend - x1);
xgap = invertFrac(x1 + 0.5f);
ix1 = x1;
iy1 = qFloor(yend);
// calc the intensity of the other end point pixel pair.
brightness1 = invertFrac(yend) * xgap;
brightness2 = frac(yend) * xgap;
c1 = (int)(brightness1 * OPACITY_OPAQUE_U8);
c2 = (int)(brightness2 * OPACITY_OPAQUE_U8);
accessor->moveTo(ix1, iy1);
if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
lineColor.setOpacity(c1);
compositeOnePixel(accessor->rawData(), lineColor);
}
accessor->moveTo(ix1, iy1 + 1);
if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1 + 1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
lineColor.setOpacity(c2);
compositeOnePixel(accessor->rawData(), lineColor);
}
// calc first Y-intersection for main loop
yf = yend + grad;
xend = x2;
yend = y2 + grad * (xend - x2);
xgap = invertFrac(x2 - 0.5f);
ix2 = x2;
iy2 = qFloor(yend);
brightness1 = invertFrac(yend) * xgap;
brightness2 = frac(yend) * xgap;
c1 = (int)(brightness1 * OPACITY_OPAQUE_U8);
c2 = (int)(brightness2 * OPACITY_OPAQUE_U8);
accessor->moveTo(ix2, iy2);
if (selectionAccessor) selectionAccessor->moveTo(ix2, iy2);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
lineColor.setOpacity(c1);
compositeOnePixel(accessor->rawData(), lineColor);
}
accessor->moveTo(ix2, iy2 + 1);
if (selectionAccessor) selectionAccessor->moveTo(ix2, iy2 + 1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
lineColor.setOpacity(c2);
compositeOnePixel(accessor->rawData(), lineColor);
}
// main loop
for (int x = ix1 + 1; x <= ix2 - 1; x++) {
brightness1 = invertFrac(yf);
brightness2 = frac(yf);
c1 = (int)(brightness1 * OPACITY_OPAQUE_U8);
c2 = (int)(brightness2 * OPACITY_OPAQUE_U8);
accessor->moveTo(x, qFloor(yf));
if (selectionAccessor) selectionAccessor->moveTo(x, qFloor(yf));
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
lineColor.setOpacity(c1);
compositeOnePixel(accessor->rawData(), lineColor);
}
accessor->moveTo(x, qFloor(yf) + 1);
if (selectionAccessor) selectionAccessor->moveTo(x, qFloor(yf) + 1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
lineColor.setOpacity(c2);
compositeOnePixel(accessor->rawData(), lineColor);
}
yf = yf + grad;
}
} else {
//vertical
// line have to be painted from left to right
if (y1 > y2) {
std::swap(x1, x2);
std::swap(y1, y2);
xd = (x2 - x1);
yd = (y2 - y1);
}
grad = xd / yd;
// nearest X,Y integer coordinates
yend = y1;
xend = x1 + grad * (yend - y1);
ygap = y1;
ix1 = qFloor(xend);
iy1 = y1;
// calc the intensity of the other end point pixel pair.
brightness1 = invertFrac(xend) * ygap;
brightness2 = frac(xend) * ygap;
c1 = (int)(brightness1 * OPACITY_OPAQUE_U8);
c2 = (int)(brightness2 * OPACITY_OPAQUE_U8);
accessor->moveTo(ix1, iy1);
if (selectionAccessor) selectionAccessor->moveTo(ix1, iy1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
lineColor.setOpacity(c1);
compositeOnePixel(accessor->rawData(), lineColor);
}
accessor->moveTo(x1 + 1, y1);
if (selectionAccessor) selectionAccessor->moveTo(x1 + 1, y1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
lineColor.setOpacity(c2);
compositeOnePixel(accessor->rawData(), lineColor);
}
// calc first Y-intersection for main loop
xf = xend + grad;
yend = y2;
xend = x2 + grad * (yend - y2);
ygap = invertFrac(y2 - 0.5f);
ix2 = qFloor(xend);
iy2 = y2;
brightness1 = invertFrac(xend) * ygap;
brightness2 = frac(xend) * ygap;
c1 = (int)(brightness1 * OPACITY_OPAQUE_U8);
c2 = (int)(brightness2 * OPACITY_OPAQUE_U8);
accessor->moveTo(ix2, iy2);
if (selectionAccessor) selectionAccessor->moveTo(ix2, iy2);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
lineColor.setOpacity(c1);
compositeOnePixel(accessor->rawData(), lineColor);
}
accessor->moveTo(ix2 + 1, iy2);
if (selectionAccessor) selectionAccessor->moveTo(ix2 + 1, iy2);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
lineColor.setOpacity(c2);
compositeOnePixel(accessor->rawData(), lineColor);
}
// main loop
for (int y = iy1 + 1; y <= iy2 - 1; y++) {
brightness1 = invertFrac(xf);
brightness2 = frac(xf);
c1 = (int)(brightness1 * OPACITY_OPAQUE_U8);
c2 = (int)(brightness2 * OPACITY_OPAQUE_U8);
accessor->moveTo(qFloor(xf), y);
if (selectionAccessor) selectionAccessor->moveTo(qFloor(xf), y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
lineColor.setOpacity(c1);
compositeOnePixel(accessor->rawData(), lineColor);
}
accessor->moveTo(qFloor(xf) + 1, y);
if (selectionAccessor) selectionAccessor->moveTo(qFloor(xf) + 1, y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
lineColor.setOpacity(c2);
compositeOnePixel(accessor->rawData(), lineColor);
}
xf = xf + grad;
}
}//end-of-else
}
void KisPainter::drawThickLine(const QPointF & start, const QPointF & end, int startWidth, int endWidth)
{
KisRandomAccessorSP accessor = d->device->createRandomAccessorNG(start.x(), start.y());
KisRandomConstAccessorSP selectionAccessor;
if (d->selection) {
selectionAccessor = d->selection->projection()->createRandomConstAccessorNG(start.x(), start.y());
}
const KoColorSpace *cs = d->device->colorSpace();
KoColor c1(d->paintColor);
KoColor c2(d->paintColor);
KoColor c3(d->paintColor);
KoColor col1(c1);
KoColor col2(c1);
float grada, gradb, dxa, dxb, dya, dyb, fraca, fracb,
xfa, yfa, xfb, yfb, b1a, b2a, b1b, b2b, dstX, dstY;
int x, y, ix1, ix2, iy1, iy2;
int x0a, y0a, x1a, y1a, x0b, y0b, x1b, y1b;
int tp0, tn0, tp1, tn1;
int horizontal = 0;
float opacity = 1.0;
tp0 = startWidth / 2;
tn0 = startWidth / 2;
if (startWidth % 2 == 0) // even width startWidth
tn0--;
tp1 = endWidth / 2;
tn1 = endWidth / 2;
if (endWidth % 2 == 0) // even width endWidth
tn1--;
int x0 = qRound(start.x());
int y0 = qRound(start.y());
int x1 = qRound(end.x());
int y1 = qRound(end.y());
dstX = x1 - x0; // run of general line
dstY = y1 - y0; // rise of general line
if (dstY < 0) dstY = -dstY;
if (dstX < 0) dstX = -dstX;
if (dstX > dstY) { // horizontalish
horizontal = 1;
x0a = x0; y0a = y0 - tn0;
x0b = x0; y0b = y0 + tp0;
x1a = x1; y1a = y1 - tn1;
x1b = x1; y1b = y1 + tp1;
} else {
x0a = x0 - tn0; y0a = y0;
x0b = x0 + tp0; y0b = y0;
x1a = x1 - tn1; y1a = y1;
x1b = x1 + tp1; y1b = y1;
}
if (horizontal) { // draw endpoints
for (int i = y0a; i <= y0b; i++) {
accessor->moveTo(x0, i);
if (selectionAccessor) selectionAccessor->moveTo(x0, i);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), c1);
}
}
for (int i = y1a; i <= y1b; i++) {
accessor->moveTo(x1, i);
if (selectionAccessor) selectionAccessor->moveTo(x1, i);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), c1);
}
}
} else {
for (int i = x0a; i <= x0b; i++) {
accessor->moveTo(i, y0);
if (selectionAccessor) selectionAccessor->moveTo(i, y0);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), c1);
}
}
for (int i = x1a; i <= x1b; i++) {
accessor->moveTo(i, y1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), c1);
}
}
}
//antialias endpoints
if (x1 != x0 && y1 != y0) {
if (horizontal) {
accessor->moveTo(x0a, y0a - 1);
if (selectionAccessor) selectionAccessor->moveTo(x0a, y0a - 1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
qreal alpha = cs->opacityF(accessor->rawData());
opacity = .25 * c1.opacityF() + (1 - .25) * alpha;
col1.setOpacity(opacity);
compositeOnePixel(accessor->rawData(), col1);
}
accessor->moveTo(x1b, y1b + 1);
if (selectionAccessor) selectionAccessor->moveTo(x1b, y1b + 1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
qreal alpha = cs->opacityF(accessor->rawData());
opacity = .25 * c2.opacityF() + (1 - .25) * alpha;
col1.setOpacity(opacity);
compositeOnePixel(accessor->rawData(), col1);
}
} else {
accessor->moveTo(x0a - 1, y0a);
if (selectionAccessor) selectionAccessor->moveTo(x0a - 1, y0a);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
qreal alpha = cs->opacityF(accessor->rawData());
opacity = .25 * c1.opacityF() + (1 - .25) * alpha;
col1.setOpacity(opacity);
compositeOnePixel(accessor->rawData(), col1);
}
accessor->moveTo(x1b + 1, y1b);
if (selectionAccessor) selectionAccessor->moveTo(x1b + 1, y1b);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
qreal alpha = cs->opacityF(accessor->rawData());
opacity = .25 * c2.opacityF() + (1 - .25) * alpha;
col1.setOpacity(opacity);
compositeOnePixel(accessor->rawData(), col1);
}
}
}
dxa = x1a - x0a; // run of a
dya = y1a - y0a; // rise of a
dxb = x1b - x0b; // run of b
dyb = y1b - y0b; // rise of b
if (horizontal) { // horizontal-ish lines
if (x1 < x0) {
int xt, yt, wt;
KoColor tmp;
xt = x1a; x1a = x0a; x0a = xt;
yt = y1a; y1a = y0a; y0a = yt;
xt = x1b; x1b = x0b; x0b = xt;
yt = y1b; y1b = y0b; y0b = yt;
xt = x1; x1 = x0; x0 = xt;
yt = y1; y1 = y0; y0 = yt;
tmp = c1; c1 = c2; c2 = tmp;
wt = startWidth; startWidth = endWidth; endWidth = wt;
}
grada = dya / dxa;
gradb = dyb / dxb;
ix1 = x0; iy1 = y0;
ix2 = x1; iy2 = y1;
yfa = y0a + grada;
yfb = y0b + gradb;
for (x = ix1 + 1; x <= ix2 - 1; x++) {
fraca = yfa - qFloor(yfa);
b1a = 1 - fraca;
b2a = fraca;
fracb = yfb - qFloor(yfb);
b1b = 1 - fracb;
b2b = fracb;
// color first pixel of bottom line
opacity = ((x - ix1) / dstX) * c2.opacityF() + (1 - (x - ix1) / dstX) * c1.opacityF();
c3.setOpacity(opacity);
accessor->moveTo(x, qFloor(yfa));
if (selectionAccessor) selectionAccessor->moveTo(x, qFloor(yfa));
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
qreal alpha = cs->opacityF(accessor->rawData());
opacity = b1a * c3.opacityF() + (1 - b1a) * alpha;
col1.setOpacity(opacity);
compositeOnePixel(accessor->rawData(), col1);
}
// color first pixel of top line
if (!(startWidth == 1 && endWidth == 1)) {
accessor->moveTo(x, qFloor(yfb));
if (selectionAccessor) selectionAccessor->moveTo(x, qFloor(yfb));
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
qreal alpha = cs->opacityF(accessor->rawData());
opacity = b1b * c3.opacityF() + (1 - b1b) * alpha;
col1.setOpacity(opacity);
compositeOnePixel(accessor->rawData(), col1);
}
}
// color second pixel of bottom line
if (grada != 0 && grada != 1) { // if not flat or exact diagonal
accessor->moveTo(x, qFloor(yfa) + 1);
if (selectionAccessor) selectionAccessor->moveTo(x, qFloor(yfa) + 1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
qreal alpha = cs->opacityF(accessor->rawData());
opacity = b2a * c3.opacityF() + (1 - b2a) * alpha;
col2.setOpacity(opacity);
compositeOnePixel(accessor->rawData(), col2);
}
}
// color second pixel of top line
if (gradb != 0 && gradb != 1 && !(startWidth == 1 && endWidth == 1)) {
accessor->moveTo(x, qFloor(yfb) + 1);
if (selectionAccessor) selectionAccessor->moveTo(x, qFloor(yfb) + 1);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
qreal alpha = cs->opacityF(accessor->rawData());
opacity = b2b * c3.opacityF() + (1 - b2b) * alpha;
col2.setOpacity(opacity);
compositeOnePixel(accessor->rawData(), col2);
}
}
// fill remaining pixels
if (!(startWidth == 1 && endWidth == 1)) {
if (yfa < yfb)
for (int i = qFloor(yfa) + 1; i <= qFloor(yfb); i++) {
accessor->moveTo(x, i);
if (selectionAccessor) selectionAccessor->moveTo(x, i);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), c3);
}
}
else
for (int i = qFloor(yfa) + 1; i >= qFloor(yfb); i--) {
accessor->moveTo(x, i);
if (selectionAccessor) selectionAccessor->moveTo(x, i);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), c3);
}
}
}
yfa += grada;
yfb += gradb;
}
} else { // vertical-ish lines
if (y1 < y0) {
int xt, yt, wt;
xt = x1a; x1a = x0a; x0a = xt;
yt = y1a; y1a = y0a; y0a = yt;
xt = x1b; x1b = x0b; x0b = xt;
yt = y1b; y1b = y0b; y0b = yt;
xt = x1; x1 = x0; x0 = xt;
yt = y1; y1 = y0; y0 = yt;
KoColor tmp;
tmp = c1; c1 = c2; c2 = tmp;
wt = startWidth; startWidth = endWidth; endWidth = wt;
}
grada = dxa / dya;
gradb = dxb / dyb;
ix1 = x0; iy1 = y0;
ix2 = x1; iy2 = y1;
xfa = x0a + grada;
xfb = x0b + gradb;
for (y = iy1 + 1; y <= iy2 - 1; y++) {
fraca = xfa - qFloor(xfa);
b1a = 1 - fraca;
b2a = fraca;
fracb = xfb - qFloor(xfb);
b1b = 1 - fracb;
b2b = fracb;
// color first pixel of left line
opacity = ((y - iy1) / dstY) * c2.opacityF() + (1 - (y - iy1) / dstY) * c1.opacityF();
c3.setOpacity(opacity);
accessor->moveTo(qFloor(xfa), y);
if (selectionAccessor) selectionAccessor->moveTo(qFloor(xfa), y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
qreal alpha = cs->opacityF(accessor->rawData());
opacity = b1a * c3.opacityF() + (1 - b1a) * alpha;
col1.setOpacity(opacity);
compositeOnePixel(accessor->rawData(), col1);
}
// color first pixel of right line
if (!(startWidth == 1 && endWidth == 1)) {
accessor->moveTo(qFloor(xfb), y);
if (selectionAccessor) selectionAccessor->moveTo(qFloor(xfb), y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
qreal alpha = cs->opacityF(accessor->rawData());
opacity = b1b * c3.opacityF() + (1 - b1b) * alpha;
col1.setOpacity(opacity);
compositeOnePixel(accessor->rawData(), col1);
}
}
// color second pixel of left line
if (grada != 0 && grada != 1) { // if not flat or exact diagonal
accessor->moveTo(qFloor(xfa) + 1, y);
if (selectionAccessor) selectionAccessor->moveTo(qFloor(xfa) + 1, y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
qreal alpha = cs->opacityF(accessor->rawData());
opacity = b2a * c3.opacityF() + (1 - b2a) * alpha;
col2.setOpacity(opacity);
compositeOnePixel(accessor->rawData(), col2);
}
}
// color second pixel of right line
if (gradb != 0 && gradb != 1 && !(startWidth == 1 && endWidth == 1)) {
accessor->moveTo(qFloor(xfb) + 1, y);
if (selectionAccessor) selectionAccessor->moveTo(qFloor(xfb) + 1, y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
qreal alpha = cs->opacityF(accessor->rawData());
opacity = b2b * c3.opacityF() + (1 - b2b) * alpha;
col2.setOpacity(opacity);
compositeOnePixel(accessor->rawData(), col2);
}
}
// fill remaining pixels between current xfa,xfb
if (!(startWidth == 1 && endWidth == 1)) {
if (xfa < xfb)
for (int i = qFloor(xfa) + 1; i <= qFloor(xfb); i++) {
accessor->moveTo(i, y);
if (selectionAccessor) selectionAccessor->moveTo(i, y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), c3);
}
}
else
for (int i = qFloor(xfb); i <= qFloor(xfa) + 1; i++) {
accessor->moveTo(i, y);
if (selectionAccessor) selectionAccessor->moveTo(i, y);
if (!selectionAccessor || *selectionAccessor->oldRawData() > SELECTION_THRESHOLD) {
compositeOnePixel(accessor->rawData(), c3);
}
}
}
xfa += grada;
xfb += gradb;
}
}
}
void KisPainter::setProgress(KoUpdater * progressUpdater)
{
d->progressUpdater = progressUpdater;
}
const KisPaintDeviceSP KisPainter::device() const
{
return d->device;
}
KisPaintDeviceSP KisPainter::device()
{
return d->device;
}
void KisPainter::setChannelFlags(QBitArray channelFlags)
{
// Q_ASSERT(channelFlags.isEmpty() || quint32(channelFlags.size()) == d->colorSpace->channelCount());
// Now, if all bits in the channelflags are true, pass an empty channel flags bitarray
// because otherwise the compositeops cannot optimize.
d->paramInfo.channelFlags = channelFlags;
if (!channelFlags.isEmpty() && channelFlags == QBitArray(channelFlags.size(), true)) {
d->paramInfo.channelFlags = QBitArray();
}
}
QBitArray KisPainter::channelFlags()
{
return d->paramInfo.channelFlags;
}
void KisPainter::setPattern(const KoPattern * pattern)
{
d->pattern = pattern;
}
const KoPattern * KisPainter::pattern() const
{
return d->pattern;
}
void KisPainter::setPaintColor(const KoColor& color)
{
d->paintColor = color;
if (d->device) {
d->paintColor.convertTo(d->device->compositionSourceColorSpace());
}
}
const KoColor &KisPainter::paintColor() const
{
return d->paintColor;
}
void KisPainter::setBackgroundColor(const KoColor& color)
{
d->backgroundColor = color;
if (d->device) {
d->backgroundColor.convertTo(d->device->compositionSourceColorSpace());
}
}
const KoColor &KisPainter::backgroundColor() const
{
return d->backgroundColor;
}
void KisPainter::setGenerator(KisFilterConfigurationSP generator)
{
d->generator = generator;
}
const KisFilterConfigurationSP KisPainter::generator() const
{
return d->generator;
}
void KisPainter::setFillStyle(FillStyle fillStyle)
{
d->fillStyle = fillStyle;
}
KisPainter::FillStyle KisPainter::fillStyle() const
{
return d->fillStyle;
}
void KisPainter::setAntiAliasPolygonFill(bool antiAliasPolygonFill)
{
d->antiAliasPolygonFill = antiAliasPolygonFill;
}
bool KisPainter::antiAliasPolygonFill()
{
return d->antiAliasPolygonFill;
}
void KisPainter::setStrokeStyle(KisPainter::StrokeStyle strokeStyle)
{
d->strokeStyle = strokeStyle;
}
KisPainter::StrokeStyle KisPainter::strokeStyle() const
{
return d->strokeStyle;
}
void KisPainter::setFlow(quint8 flow)
{
d->paramInfo.flow = float(flow) / 255.0f;
}
quint8 KisPainter::flow() const
{
return quint8(d->paramInfo.flow * 255.0f);
}
void KisPainter::setOpacityUpdateAverage(quint8 opacity)
{
d->isOpacityUnit = opacity == OPACITY_OPAQUE_U8;
d->paramInfo.updateOpacityAndAverage(float(opacity) / 255.0f);
}
void KisPainter::setAverageOpacity(qreal averageOpacity)
{
d->paramInfo.setOpacityAndAverage(d->paramInfo.opacity, averageOpacity);
}
qreal KisPainter::blendAverageOpacity(qreal opacity, qreal averageOpacity)
{
const float exponent = 0.1;
return averageOpacity < opacity ?
opacity :
exponent * opacity + (1.0 - exponent) * (averageOpacity);
}
void KisPainter::setOpacity(quint8 opacity)
{
d->isOpacityUnit = opacity == OPACITY_OPAQUE_U8;
d->paramInfo.opacity = float(opacity) / 255.0f;
}
quint8 KisPainter::opacity() const
{
return quint8(d->paramInfo.opacity * 255.0f);
}
void KisPainter::setCompositeOp(const KoCompositeOp * op)
{
d->compositeOp = op;
}
const KoCompositeOp * KisPainter::compositeOp()
{
return d->compositeOp;
}
/**
* TODO: Rename this setCompositeOpId(). See KoCompositeOpRegistry.h
*/
void KisPainter::setCompositeOp(const QString& op)
{
d->compositeOp = d->colorSpace->compositeOp(op);
}
void KisPainter::setSelection(KisSelectionSP selection)
{
d->selection = selection;
}
KisSelectionSP KisPainter::selection()
{
return d->selection;
}
KoUpdater * KisPainter::progressUpdater()
{
return d->progressUpdater;
}
void KisPainter::setGradient(const KoAbstractGradient* gradient)
{
d->gradient = gradient;
}
const KoAbstractGradient* KisPainter::gradient() const
{
return d->gradient;
}
void KisPainter::setPaintOpPreset(KisPaintOpPresetSP preset, KisNodeSP node, KisImageSP image)
{
d->paintOpPreset = preset;
KisPaintOp *paintop = KisPaintOpRegistry::instance()->paintOp(preset, this, node, image);
Q_ASSERT(paintop);
if (paintop) {
delete d->paintOp;
d->paintOp = paintop;
}
else {
warnKrita << "Could not create paintop for preset " << preset->name();
}
}
KisPaintOpPresetSP KisPainter::preset() const
{
return d->paintOpPreset;
}
KisPaintOp* KisPainter::paintOp() const
{
return d->paintOp;
}
void KisPainter::setMirrorInformation(const QPointF& axesCenter, bool mirrorHorizontally, bool mirrorVertically)
{
d->axesCenter = axesCenter;
d->mirrorHorizontally = mirrorHorizontally;
d->mirrorVertically = mirrorVertically;
}
void KisPainter::copyMirrorInformationFrom(const KisPainter *other)
{
d->axesCenter = other->d->axesCenter;
d->mirrorHorizontally = other->d->mirrorHorizontally;
d->mirrorVertically = other->d->mirrorVertically;
}
bool KisPainter::hasMirroring() const
{
return d->mirrorHorizontally || d->mirrorVertically;
}
bool KisPainter::hasHorizontalMirroring() const
{
return d->mirrorHorizontally;
}
bool KisPainter::hasVerticalMirroring() const
{
return d->mirrorVertically;
}
void KisPainter::setMaskImageSize(qint32 width, qint32 height)
{
d->maskImageWidth = qBound(1, width, 256);
d->maskImageHeight = qBound(1, height, 256);
d->fillPainter = 0;
d->polygonMaskImage = QImage();
}
//void KisPainter::setLockAlpha(bool protect)
//{
// if(d->paramInfo.channelFlags.isEmpty()) {
// d->paramInfo.channelFlags = d->colorSpace->channelFlags(true, true);
// }
// QBitArray switcher =
// d->colorSpace->channelFlags(protect, !protect);
// if(protect) {
// d->paramInfo.channelFlags &= switcher;
// }
// else {
// d->paramInfo.channelFlags |= switcher;
// }
// Q_ASSERT(quint32(d->paramInfo.channelFlags.size()) == d->colorSpace->channelCount());
//}
//bool KisPainter::alphaLocked() const
//{
// QBitArray switcher = d->colorSpace->channelFlags(false, true);
// return !(d->paramInfo.channelFlags & switcher).count(true);
//}
void KisPainter::setRenderingIntent(KoColorConversionTransformation::Intent intent)
{
d->renderingIntent = intent;
}
void KisPainter::setColorConversionFlags(KoColorConversionTransformation::ConversionFlags conversionFlags)
{
d->conversionFlags = conversionFlags;
}
void KisPainter::setRunnableStrokeJobsInterface(KisRunnableStrokeJobsInterface *interface)
{
d->runnableStrokeJobsInterface = interface;
}
KisRunnableStrokeJobsInterface *KisPainter::runnableStrokeJobsInterface() const
{
if (!d->runnableStrokeJobsInterface) {
if (!d->fakeRunnableStrokeJobsInterface) {
d->fakeRunnableStrokeJobsInterface.reset(new KisFakeRunnableStrokeJobsExecutor());
}
return d->fakeRunnableStrokeJobsInterface.data();
}
return d->runnableStrokeJobsInterface;
}
void KisPainter::renderMirrorMaskSafe(QRect rc, KisFixedPaintDeviceSP dab, bool preserveDab)
{
if (!d->mirrorHorizontally && !d->mirrorVertically) return;
KisFixedPaintDeviceSP dabToProcess = dab;
if (preserveDab) {
dabToProcess = new KisFixedPaintDevice(*dab);
}
renderMirrorMask(rc, dabToProcess);
}
void KisPainter::renderMirrorMaskSafe(QRect rc, KisPaintDeviceSP dab, int sx, int sy, KisFixedPaintDeviceSP mask, bool preserveMask)
{
if (!d->mirrorHorizontally && !d->mirrorVertically) return;
KisFixedPaintDeviceSP maskToProcess = mask;
if (preserveMask) {
maskToProcess = new KisFixedPaintDevice(*mask);
}
renderMirrorMask(rc, dab, sx, sy, maskToProcess);
}
void KisPainter::renderMirrorMask(QRect rc, KisFixedPaintDeviceSP dab)
{
int x = rc.topLeft().x();
int y = rc.topLeft().y();
KisLodTransform t(d->device);
QPoint effectiveAxesCenter = t.map(d->axesCenter).toPoint();
int mirrorX = -((x+rc.width()) - effectiveAxesCenter.x()) + effectiveAxesCenter.x();
int mirrorY = -((y+rc.height()) - effectiveAxesCenter.y()) + effectiveAxesCenter.y();
if (d->mirrorHorizontally && d->mirrorVertically){
dab->mirror(true, false);
bltFixed(mirrorX, y, dab, 0,0,rc.width(),rc.height());
dab->mirror(false,true);
bltFixed(mirrorX, mirrorY, dab, 0,0,rc.width(),rc.height());
dab->mirror(true, false);
bltFixed(x, mirrorY, dab, 0,0,rc.width(),rc.height());
}
else if (d->mirrorHorizontally){
dab->mirror(true, false);
bltFixed(mirrorX, y, dab, 0,0,rc.width(),rc.height());
}
else if (d->mirrorVertically){
dab->mirror(false, true);
bltFixed(x, mirrorY, dab, 0,0,rc.width(),rc.height());
}
}
void KisPainter::renderMirrorMask(QRect rc, KisFixedPaintDeviceSP dab, KisFixedPaintDeviceSP mask)
{
int x = rc.topLeft().x();
int y = rc.topLeft().y();
KisLodTransform t(d->device);
QPoint effectiveAxesCenter = t.map(d->axesCenter).toPoint();
int mirrorX = -((x+rc.width()) - effectiveAxesCenter.x()) + effectiveAxesCenter.x();
int mirrorY = -((y+rc.height()) - effectiveAxesCenter.y()) + effectiveAxesCenter.y();
if (d->mirrorHorizontally && d->mirrorVertically){
dab->mirror(true, false);
mask->mirror(true, false);
bltFixedWithFixedSelection(mirrorX,y, dab, mask, rc.width() ,rc.height() );
dab->mirror(false,true);
mask->mirror(false, true);
bltFixedWithFixedSelection(mirrorX,mirrorY, dab, mask, rc.width() ,rc.height() );
dab->mirror(true, false);
mask->mirror(true, false);
bltFixedWithFixedSelection(x,mirrorY, dab, mask, rc.width() ,rc.height() );
}else if (d->mirrorHorizontally){
dab->mirror(true, false);
mask->mirror(true, false);
bltFixedWithFixedSelection(mirrorX,y, dab, mask, rc.width() ,rc.height() );
}else if (d->mirrorVertically){
dab->mirror(false, true);
mask->mirror(false, true);
bltFixedWithFixedSelection(x,mirrorY, dab, mask, rc.width() ,rc.height() );
}
}
void KisPainter::renderMirrorMask(QRect rc, KisPaintDeviceSP dab){
if (d->mirrorHorizontally || d->mirrorVertically){
KisFixedPaintDeviceSP mirrorDab(new KisFixedPaintDevice(dab->colorSpace()));
QRect dabRc( QPoint(0,0), QSize(rc.width(),rc.height()) );
mirrorDab->setRect(dabRc);
mirrorDab->lazyGrowBufferWithoutInitialization();
dab->readBytes(mirrorDab->data(),rc);
renderMirrorMask( QRect(rc.topLeft(),dabRc.size()), mirrorDab);
}
}
void KisPainter::renderMirrorMask(QRect rc, KisPaintDeviceSP dab, int sx, int sy, KisFixedPaintDeviceSP mask)
{
if (d->mirrorHorizontally || d->mirrorVertically){
KisFixedPaintDeviceSP mirrorDab(new KisFixedPaintDevice(dab->colorSpace()));
QRect dabRc( QPoint(0,0), QSize(rc.width(),rc.height()) );
mirrorDab->setRect(dabRc);
mirrorDab->lazyGrowBufferWithoutInitialization();
dab->readBytes(mirrorDab->data(),QRect(QPoint(sx,sy),rc.size()));
renderMirrorMask(rc, mirrorDab, mask);
}
}
void KisPainter::renderDabWithMirroringNonIncremental(QRect rc, KisPaintDeviceSP dab)
{
QVector<QRect> rects;
int x = rc.topLeft().x();
int y = rc.topLeft().y();
KisLodTransform t(d->device);
QPoint effectiveAxesCenter = t.map(d->axesCenter).toPoint();
int mirrorX = -((x+rc.width()) - effectiveAxesCenter.x()) + effectiveAxesCenter.x();
int mirrorY = -((y+rc.height()) - effectiveAxesCenter.y()) + effectiveAxesCenter.y();
rects << rc;
if (d->mirrorHorizontally && d->mirrorVertically){
rects << QRect(mirrorX, y, rc.width(), rc.height());
rects << QRect(mirrorX, mirrorY, rc.width(), rc.height());
rects << QRect(x, mirrorY, rc.width(), rc.height());
} else if (d->mirrorHorizontally) {
rects << QRect(mirrorX, y, rc.width(), rc.height());
} else if (d->mirrorVertically) {
rects << QRect(x, mirrorY, rc.width(), rc.height());
}
Q_FOREACH (const QRect &rc, rects) {
d->device->clear(rc);
}
QRect resultRect = dab->extent() | rc;
bool intersects = false;
for (int i = 1; i < rects.size(); i++) {
if (rects[i].intersects(resultRect)) {
intersects = true;
break;
}
}
/**
* If there are no cross-intersections, we can use a fast path
* and do no cycling recompositioning
*/
if (!intersects) {
rects.resize(1);
}
Q_FOREACH (const QRect &rc, rects) {
bitBlt(rc.topLeft(), dab, rc);
}
Q_FOREACH (const QRect &rc, rects) {
renderMirrorMask(rc, dab);
}
}
bool KisPainter::hasDirtyRegion() const
{
return !d->dirtyRects.isEmpty();
}
void KisPainter::mirrorRect(Qt::Orientation direction, QRect *rc) const
{
KisLodTransform t(d->device);
QPoint effectiveAxesCenter = t.map(d->axesCenter).toPoint();
KritaUtils::mirrorRect(direction, effectiveAxesCenter, rc);
}
void KisPainter::mirrorDab(Qt::Orientation direction, KisRenderedDab *dab) const
{
KisLodTransform t(d->device);
QPoint effectiveAxesCenter = t.map(d->axesCenter).toPoint();
KritaUtils::mirrorDab(direction, effectiveAxesCenter, dab);
}
const QVector<QRect> KisPainter::calculateAllMirroredRects(const QRect &rc)
{
QVector<QRect> rects;
KisLodTransform t(d->device);
QPoint effectiveAxesCenter = t.map(d->axesCenter).toPoint();
QRect baseRect = rc;
rects << baseRect;
if (d->mirrorHorizontally && d->mirrorVertically){
KritaUtils::mirrorRect(Qt::Horizontal, effectiveAxesCenter, &baseRect);
rects << baseRect;
KritaUtils::mirrorRect(Qt::Vertical, effectiveAxesCenter, &baseRect);
rects << baseRect;
KritaUtils::mirrorRect(Qt::Horizontal, effectiveAxesCenter, &baseRect);
rects << baseRect;
} else if (d->mirrorHorizontally) {
KritaUtils::mirrorRect(Qt::Horizontal, effectiveAxesCenter, &baseRect);
rects << baseRect;
} else if (d->mirrorVertically) {
KritaUtils::mirrorRect(Qt::Vertical, effectiveAxesCenter, &baseRect);
rects << baseRect;
}
return rects;
}
diff --git a/libs/image/kis_painter.h b/libs/image/kis_painter.h
index 46c02ccf7c..2775aa5919 100644
--- a/libs/image/kis_painter.h
+++ b/libs/image/kis_painter.h
@@ -1,879 +1,882 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004 Clarence Dang <dang@kde.org>
* Copyright (c) 2008-2010 Lukáš Tvrdý <lukast.dev@gmail.com>
* Copyright (c) 2010 José Luis Vergara Toloza <pentalis@gmail.com>
*
* 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_PAINTER_H_
#define KIS_PAINTER_H_
#include <math.h>
#include <QVector>
#include <KoColorSpaceConstants.h>
#include <KoColorConversionTransformation.h>
#include "kundo2magicstring.h"
#include "kis_types.h"
#include <kis_filter_configuration.h>
#include <kritaimage_export.h>
class QPen;
class KUndo2Command;
class QRect;
class QRectF;
class QBitArray;
class QPainterPath;
class KoAbstractGradient;
class KoUpdater;
class KoColor;
class KoCompositeOp;
class KisUndoAdapter;
class KisPostExecutionUndoAdapter;
class KisTransaction;
class KoPattern;
class KisPaintInformation;
class KisPaintOp;
class KisDistanceInformation;
struct KisRenderedDab;
class KisRunnableStrokeJobsInterface;
/**
* KisPainter contains the graphics primitives necessary to draw on a
* KisPaintDevice. This is the same kind of abstraction as used in Qt
* itself, where you have QPainter and QPaintDevice.
*
* However, KisPainter works on a tiled image and supports different
* color models, and that's a lot more complicated.
*
* KisPainter supports transactions that can group various paint operations
* in one undoable step.
*
* For more complex operations, you might want to have a look at the subclasses
* of KisPainter: KisConvolutionPainter, KisFillPainter and KisGradientPainter
*
* KisPainter sets a number of default values, like COMPOSITE_OVER for compositeop,
* OPACITY_OPAQUE for opacity and no selection for selection.
*/
class KRITAIMAGE_EXPORT KisPainter
{
public:
/// Construct painter without a device
KisPainter();
/// Construct a painter, and begin painting on the device
KisPainter(KisPaintDeviceSP device);
/// Construct a painter, and begin painting on the device. All actions will be masked by the given selection.
KisPainter(KisPaintDeviceSP device, KisSelectionSP selection);
virtual ~KisPainter();
public:
static void copyAreaOptimized(const QPoint &dstPt,
KisPaintDeviceSP src,
KisPaintDeviceSP dst,
const QRect &originalSrcRect);
static void copyAreaOptimizedOldData(const QPoint &dstPt,
KisPaintDeviceSP src,
KisPaintDeviceSP dst,
const QRect &originalSrcRect);
static void copyAreaOptimized(const QPoint &dstPt,
KisPaintDeviceSP src,
KisPaintDeviceSP dst,
const QRect &originalSrcRect,
KisSelectionSP selection);
static KisPaintDeviceSP convertToAlphaAsAlpha(KisPaintDeviceSP src);
static KisPaintDeviceSP convertToAlphaAsGray(KisPaintDeviceSP src);
static bool checkDeviceHasTransparency(KisPaintDeviceSP dev);
/**
* Start painting on the specified device. Not undoable.
*/
void begin(KisPaintDeviceSP device);
/**
* Start painting on the specified paint device. All actions will be masked by the given selection.
*/
void begin(KisPaintDeviceSP device, KisSelectionSP selection);
/**
* Finish painting on the current device
*/
void end();
/**
* If set, the painter action is cancelable, if the action supports that.
*/
void setProgress(KoUpdater * progressUpdater);
/// Begin an undoable paint operation
void beginTransaction(const KUndo2MagicString& transactionName = KUndo2MagicString(),int timedID = -1);
/// Cancel all the changes made by the painter
void revertTransaction();
/// Finish the undoable paint operation
void endTransaction(KisUndoAdapter *undoAdapter);
/**
* Finish transaction and load it to a special adapter for strokes
*/
void endTransaction(KisPostExecutionUndoAdapter *undoAdapter);
/**
* Finishes a transaction and returns a pointer to its undo command
*/
KUndo2Command* endAndTakeTransaction();
/**
* Finish the transaction and delete it's undo information.
* NOTE: Be careful, because all the previous transactions
* will become non-undoable after execution of this method.
*/
void deleteTransaction();
/// continue a transaction started somewhere else
void putTransaction(KisTransaction* transaction);
/// take transaction out of the reach of KisPainter
KisTransaction* takeTransaction();
/// Returns the current paint device.
const KisPaintDeviceSP device() const;
KisPaintDeviceSP device();
/**
* Blast a region of srcWidth @param srcWidth and srcHeight @param srcHeight from @param
* srcDev onto the current paint device. @param srcX and @param srcY set the x and y
* positions of the origin top-left corner, @param dstX and @param dstY those of
* the destination.
* Any pixel read outside the limits of @param srcDev will return the
* default pixel, this is a property of \ref KisPaintDevice.
*
* @param dstX the destination x-coordinate
* @param dstY the destination y-coordinate
* @param srcDev the source device
* @param srcX the source x-coordinate
* @param srcY the source y-coordinate
* @param srcWidth the width of the region to be manipulated
* @param srcHeight the height of the region to be manipulated
*/
void bitBlt(qint32 dstX, qint32 dstY,
const KisPaintDeviceSP srcDev,
qint32 srcX, qint32 srcY,
qint32 srcWidth, qint32 srcHeight);
/**
* Convenience method that uses QPoint and QRect.
*
- * @param pos the destination coordinate, it replaces @param dstX and @param dstY.
+ * @param pos the destination coordinate, it replaces @p dstX and @p dstY.
* @param srcDev the source device.
- * @param srcRect the rectangle describing the area to blast from @param srcDev into the current paint device.
- * @param srcRect replaces @param srcX, @param srcY, @param srcWidth and @param srcHeight.
+ * @param srcRect the rectangle describing the area to blast from @p srcDev into the current paint device.
+ * @p srcRect replaces @p srcX, @p srcY, @p srcWidth and @p srcHeight.
*
*/
void bitBlt(const QPoint & pos, const KisPaintDeviceSP srcDev, const QRect & srcRect);
/**
* The same as @ref bitBlt() but reads data from oldData() part of the device
*
* @param dstX the destination x-coordinate
* @param dstY the destination y-coordinate
* @param srcDev the source device
* @param srcX the source x-coordinate
* @param srcY the source y-coordinate
* @param srcWidth the width of the region to be manipulated
* @param srcHeight the height of the region to be manipulated
*/
void bitBltOldData(qint32 dstX, qint32 dstY,
const KisPaintDeviceSP srcDev,
qint32 srcX, qint32 srcY,
qint32 srcWidth, qint32 srcHeight);
/**
* Convenience method that uses QPoint and QRect.
*
- * @param pos the destination coordinate, it replaces @param dstX and @param dstY.
+ * @param pos the destination coordinate, it replaces @p dstX and @p dstY.
* @param srcDev the source device.
* @param srcRect the rectangle describing the area to blast from @param srcDev into the current paint device.
- * @param srcRect replaces @param srcX, @param srcY, @param srcWidth and @param srcHeight.
+ * @p srcRect replaces @p srcX, @p srcY, @p srcWidth and @p srcHeight.
*
*/
void bitBltOldData(const QPoint & pos, const KisPaintDeviceSP srcDev, const QRect & srcRect);
/**
* Blasts a @param selection of srcWidth @param srcWidth and srcHeight @param srcHeight
* of @param srcDev on the current paint device. There is parameters
* to control where the area begins in each distinct device, explained below.
* @param selection can be used as a mask to shape @param srcDev to
* something interesting in the same step it is rendered to the current
* paint device. @param selection 's colorspace must be alpha8 (the
* colorspace for selections/transparency), the rectangle formed by
* @param selX, @param selY, @param srcWidth and @param srcHeight must not go
* beyond its limits, and they must be different from zero.
* @param selection and KisPainter's selection (the user selection) are
* fused together through the composite operation COMPOSITE_MULT.
* Any pixel read outside the limits of @param srcDev will return the
* default pixel, this is a property of \ref KisPaintDevice.
*
* @param dstX the destination x-coordinate
* @param dstY the destination y-coordinate
* @param srcDev the source device
* @param selection the custom selection to apply on the source device
* @param selX the selection x-coordinate
* @param selY the selection y-coordinate
* @param srcX the source x-coordinate
* @param srcY the source y-coordinate
* @param srcWidth the width of the region to be manipulated
* @param srcHeight the height of the region to be manipulated
*
*/
void bitBltWithFixedSelection(qint32 dstX, qint32 dstY,
const KisPaintDeviceSP srcDev,
const KisFixedPaintDeviceSP selection,
qint32 selX, qint32 selY,
qint32 srcX, qint32 srcY,
qint32 srcWidth, qint32 srcHeight);
/**
- * Convenience method that assumes @param selX, @param selY, @param srcX and @param srcY are
- * equal to 0. Best used when @param selection and the desired area of @param srcDev have exactly
+ * Convenience method that assumes @p selX, @p selY, @p srcX and @p srcY are
+ * equal to 0. Best used when @p selection and the desired area of @p srcDev have exactly
* the same dimensions and are specially made for each other.
*
* @param dstX the destination x-coordinate
* @param dstY the destination y-coordinate
* @param srcDev the source device
* @param selection the custom selection to apply on the source device
* @param srcWidth the width of the region to be manipulated
* @param srcHeight the height of the region to be manipulated
*/
void bitBltWithFixedSelection(qint32 dstX, qint32 dstY,
const KisPaintDeviceSP srcDev,
const KisFixedPaintDeviceSP selection,
qint32 srcWidth, qint32 srcHeight);
/**
- * Blast a region of srcWidth @param srcWidth and srcHeight @param srcHeight from @param srcDev onto the current
- * paint device. @param srcX and @param srcY set the x and y positions of the
- * origin top-left corner, @param dstX and @param dstY those of the destination.
- * @param srcDev is a \ref KisFixedPaintDevice: this means that @param srcDev must have the same
+ * Blast a region of srcWidth @p srcWidth and srcHeight @p srcHeight from @p srcDev onto the current
+ * paint device. @p srcX and @p srcY set the x and y positions of the
+ * origin top-left corner, @p dstX and @p dstY those of the destination.
+ * @p srcDev is a @ref KisFixedPaintDevice : this means that @p srcDev must have the same
* colorspace as the destination device.
*
* @param dstX the destination x-coordinate
* @param dstY the destination y-coordinate
* @param srcDev the source device
* @param srcX the source x-coordinate
* @param srcY the source y-coordinate
* @param srcWidth the width of the region to be manipulated
* @param srcHeight the height of the region to be manipulated
*/
void bltFixed(qint32 dstX, qint32 dstY,
const KisFixedPaintDeviceSP srcDev,
qint32 srcX, qint32 srcY,
qint32 srcWidth, qint32 srcHeight);
/**
* Render the area \p rc from \p srcDevices on the destination device.
* If \p rc doesn't cross the device's rect, then the device is not
* rendered at all.
*/
void bltFixed(const QRect &rc, const QList<KisRenderedDab> allSrcDevices);
/**
* Convenience method that uses QPoint and QRect.
*
- * @param pos the destination coordinate, it replaces @param dstX and @param dstY.
+ * @param pos the destination coordinate, it replaces @p dstX and @p dstY.
* @param srcDev the source device.
- * @param srcRect the rectangle describing the area to blast from @param srcDev into the current paint device.
- * @param srcRect replaces @param srcX, @param srcY, @param srcWidth and @param srcHeight.
+ * @param srcRect the rectangle describing the area to blast from @p srcDev into the current paint device.
+ * @param srcRect replaces @p srcX, @p srcY, @p srcWidth and @p srcHeight.
*
*/
void bltFixed(const QPoint & pos, const KisFixedPaintDeviceSP srcDev, const QRect & srcRect);
/**
- * Blasts a @param selection of srcWidth @param srcWidth and srcHeight @param srcHeight
- * of @param srcDev on the current paint device. There is parameters to control
- * the top-left corner of the area in each respective paint device (@param dstX,
- * @param dstY, @param srcX, @param srcY).
- * @param selection can be used as a mask to shape @param srcDev to something
+ * Blasts a @p selection of srcWidth @p srcWidth and srcHeight @p srcHeight
+ * of @p srcDev on the current paint device. There is parameters to control
+ * the top-left corner of the area in each respective paint device (@p dstX,
+ * @p dstY, @p srcX, @p srcY).
+ * @p selection can be used as a mask to shape @p srcDev to something
* interesting in the same step it is rendered to the current paint device.
- * @param srcDev is a \ref KisFixedPaintDevice: this means that @param srcDev
+ * @p srcDev is a @ref KisFixedPaintDevice : this means that @p srcDev
* must have the same colorspace as the destination device.
- * @param selection 's colorspace must be alpha8 (the colorspace for
+ * @p selection 's colorspace must be alpha8 (the colorspace for
* selections/transparency).
* The rectangle formed by the respective top-left coordinates of each device
- * and @param srcWidth and @param srcHeight must not go beyond their limits, and
+ * and @p srcWidth and @p srcHeight must not go beyond their limits, and
* they must be different from zero.
- * @param selection and KisPainter's selection (the user selection) are
+ * @p selection and KisPainter's selection (the user selection) are
* fused together through the composite operation COMPOSITE_MULT.
*
* @param dstX the destination x-coordinate
* @param dstY the destination y-coordinate
* @param srcDev the source device
* @param selection the selection stored in fixed device
* @param selX the selection x-coordinate
* @param selY the selection y-coordinate
* @param srcX the source x-coordinate
* @param srcY the source y-coordinate
* @param srcWidth the width of the region to be manipulated
* @param srcHeight the height of the region to be manipulated
*/
void bltFixedWithFixedSelection(qint32 dstX, qint32 dstY,
const KisFixedPaintDeviceSP srcDev,
const KisFixedPaintDeviceSP selection,
qint32 selX, qint32 selY,
qint32 srcX, qint32 srcY,
quint32 srcWidth, quint32 srcHeight);
/**
- * Convenience method that assumes @param selX, @param selY, @param srcX and @param srcY are
- * equal to 0. Best used when @param selection and @param srcDev have exactly the same
+ * Convenience method that assumes @p selX, @p selY, @p srcX and @p srcY are
+ * equal to 0. Best used when @p selection and @p srcDev have exactly the same
* dimensions and are specially made for each other.
*
* @param dstX the destination x-coordinate
* @param dstY the destination y-coordinate
* @param srcDev the source device
* @param selection the custom selection to apply on the source device
* @param srcWidth the width of the region to be manipulated
* @param srcHeight the height of the region to be manipulated
*/
void bltFixedWithFixedSelection(qint32 dstX, qint32 dstY,
const KisFixedPaintDeviceSP srcDev,
const KisFixedPaintDeviceSP selection,
quint32 srcWidth, quint32 srcHeight);
/**
- * fills a region of width @param width and height @param height of the current
- * paint device with the color @param color. @param x and @param y set the x and y positions of the
+ * fills a region of width @p width and height @p height of the current
+ * paint device with the color @p color. @p x and @p y set the x and y positions of the
* origin top-left corner.
*
* @param x the destination x-coordinate
* @param y the destination y-coordinate
* @param width the width of the region to be manipulated
* @param height the height of the region to be manipulated
* @param color the color the area is filled with
*/
void fill(qint32 x, qint32 y, qint32 width, qint32 height, const KoColor& color);
/**
* First you need to setup the painter with setMirrorInformation,
* then these set of methods provide way to render the devices mirrored
* according the axesCenter vertically or horizontally or both.
*
* @param rc rectangle area covered by dab
* @param dab this device will be mirrored in-place, it means that it will be changed
*/
void renderMirrorMask(QRect rc, KisFixedPaintDeviceSP dab);
void renderMirrorMask(QRect rc, KisFixedPaintDeviceSP dab, KisFixedPaintDeviceSP mask);
void renderMirrorMask(QRect rc, KisPaintDeviceSP dab);
void renderMirrorMask(QRect rc, KisPaintDeviceSP dab, int sx, int sy, KisFixedPaintDeviceSP mask);
/**
* Convenience method for renderMirrorMask(), allows to choose whether
* we need to preserve out dab or do the transformations in-place.
*
* @param rc rectangle area covered by dab
* @param dab the device to render
* @param preserveDab states whether a temporary device should be
* created to do the transformations
*/
void renderMirrorMaskSafe(QRect rc, KisFixedPaintDeviceSP dab, bool preserveDab);
/**
* Convenience method for renderMirrorMask(), allows to choose whether
* we need to preserve our fixed mask or do the transformations in-place.
*
- * @param rc rectangle area covered by dab
+ * @param rc rectangular area covered by dab
* @param dab the device to render
+ * @param sx x coordinate of the top left corner of the area
+ * @param sy y coordinate of the top left corner of the area
* @param mask mask to use for rendering
* @param preserveMask states whether a temporary device should be
* created to do the transformations
*/
void renderMirrorMaskSafe(QRect rc, KisPaintDeviceSP dab, int sx, int sy, KisFixedPaintDeviceSP mask, bool preserveMask);
/**
* A complex method that re-renders a dab on an \p rc area.
* The \p rc area and all the dedicated mirroring areas are cleared
* before the painting, so this method should be used by paintops
* which do not update the canvas incrementally, but instead
* regenerate some internal cache \p dab with the COMPOSITE_COPY op.
*
* \see KisExperimentPaintOp
*/
void renderDabWithMirroringNonIncremental(QRect rc, KisPaintDeviceSP dab);
/**
* @return true if the painter has some rects marked as dirty
* @see takeDirtyRegion(), addDirtyRect()
*/
bool hasDirtyRegion() const;
/**
* The methods in this class do not tell the paintdevice to update, but they calculate the
* dirty area. This method returns this dirty area and resets it.
*/
QVector<QRect> takeDirtyRegion();
/**
* Paint a line that connects the dots in points
*/
void paintPolyline(const QVector <QPointF> &points,
int index = 0, int numPoints = -1);
/**
* Draw a line between pos1 and pos2 using the currently set brush and color.
* If savedDist is less than zero, the brush is painted at pos1 before being
* painted along the line using the spacing setting.
* @return the drag distance, that is the remains of the distance between p1 and p2 not covered
* because the currently set brush has a spacing greater than that distance.
*/
void paintLine(const KisPaintInformation &pi1,
const KisPaintInformation &pi2,
KisDistanceInformation *currentDistance);
/**
- * Draw a Bezier curve between pos1 and pos2 using control points 1 and 2.
+ * Draw a Bezier curve between @p pi1 and @p pi2 using control points @p control1 and @p control2.
* If savedDist is less than zero, the brush is painted at pos1 before being
* painted along the curve using the spacing setting.
- * @return the drag distance, that is the remains of the distance between p1 and p2 not covered
+ * @return the drag distance, that is the remains of the distance between @p pi1 and @p pi2 not covered
* because the currently set brush has a spacing greater than that distance.
*/
void paintBezierCurve(const KisPaintInformation &pi1,
const QPointF &control1,
const QPointF &control2,
const KisPaintInformation &pi2,
KisDistanceInformation *currentDistance);
/**
* Fill the given vector points with the points needed to draw the Bezier curve between
- * pos1 and pos2 using control points 1 and 2, excluding the final pos2.
+ * @p pos1 and @p pos2 using control points @p control1 and @p control2, excluding the final pos2.
*/
void getBezierCurvePoints(const QPointF &pos1,
const QPointF &control1,
const QPointF &control2,
const QPointF &pos2,
vQPointF& points) const;
/**
* Paint a rectangle.
* @param rect the rectangle to paint.
*/
void paintRect(const QRectF &rect);
/**
* Paint a rectangle.
*
* @param x x coordinate of the top-left corner
* @param y y coordinate of the top-left corner
* @param w the rectangle width
* @param h the rectangle height
*/
void paintRect(const qreal x,
const qreal y,
const qreal w,
const qreal h);
/**
* Paint the ellipse that fills the given rectangle.
*
* @param rect the rectangle containing the ellipse to paint.
*/
void paintEllipse(const QRectF &rect);
/**
* Paint the ellipse that fills the given rectangle.
*
* @param x x coordinate of the top-left corner
* @param y y coordinate of the top-left corner
* @param w the rectangle width
* @param h the rectangle height
*/
void paintEllipse(const qreal x,
const qreal y,
const qreal w,
const qreal h);
/**
* Paint the polygon with the points given in points. It automatically closes the polygon
* by drawing the line from the last point to the first.
*/
void paintPolygon(const vQPointF& points);
/** Draw a spot at pos using the currently set paint op, brush and color */
void paintAt(const KisPaintInformation &pos,
KisDistanceInformation *savedDist);
/**
* Stroke the given QPainterPath.
*/
void paintPainterPath(const QPainterPath& path);
/**
* Fills the area enclosed by the given QPainterPath
* Convenience method for fillPainterPath(path, rect)
*/
void fillPainterPath(const QPainterPath& path);
/**
* Fills the portion of an area enclosed by the given QPainterPath
*
- * \param rect the portion of the path to fill
+ * \param path the portion of the path to fill
+ * \param requestedRect the rectangle containing the area
*/
void fillPainterPath(const QPainterPath& path, const QRect &requestedRect);
/**
* Draw the path using the Pen
*
* if \p requestedRect is null, the entire path is painted
*/
void drawPainterPath(const QPainterPath& path, const QPen& pen, const QRect &requestedRect);
// convenience overload
void drawPainterPath(const QPainterPath& path, const QPen& pen);
/**
* paint an unstroked one-pixel wide line from specified start position to the
* specified end position.
*
*/
void drawLine(const QPointF & start, const QPointF & end);
/**
* paint an unstroked line with thickness from specified start position to the
* specified end position. Scanline algorithm is used.
*/
void drawLine(const QPointF &start, const QPointF &end, qreal width, bool antialias);
/**
* paints an unstroked, aliased one-pixel line using the DDA algorithm from specified start position to the
* specified end position.
*
*/
void drawDDALine(const QPointF & start, const QPointF & end);
/**
* Paint an unstroked, wobbly one-pixel wide line from the specified start to the specified
* end position.
*
*/
void drawWobblyLine(const QPointF & start, const QPointF & end);
/**
* Paint an unstroked, anti-aliased one-pixel wide line from the specified start to the specified
* end position using the Wu algorithm
*/
void drawWuLine(const QPointF & start, const QPointF & end);
/**
* Paint an unstroked wide line from the specified start to the specified
- * end position with width varying from @param w1 at the start to @param w2 at
+ * end position with width varying from @p start at the start to @p end at
* the end.
*
* XXX: the width should be set in doubles, not integers.
*/
void drawThickLine(const QPointF & start, const QPointF & end, int startWidth, int endWidth);
/**
* Set the channelflags: a bit array where true means that the
* channel corresponding in position with the bit will be read
* by the operation, and false means that it will not be affected.
*
* An empty channelFlags parameter means that all channels are
* affected.
*
- * @param the bit array that masks the source channels; only
+ * @param channelFlags the bit array that masks the source channels; only
* the channels where the corresponding bit is true will will be
* composited onto the destination device.
*/
void setChannelFlags(QBitArray channelFlags);
/// @return the channel flags
QBitArray channelFlags();
/**
- * Set the paintop preset to use. If @param image is given,
+ * Set the paintop preset to use. If @p image is given,
* the paintop will be created using this image as parameter.
* Some paintops really want to know about the image they work
* for, e.g. the clone paintop.
*/
void setPaintOpPreset(KisPaintOpPresetSP preset, KisNodeSP node, KisImageSP image);
/// Return the paintop preset
KisPaintOpPresetSP preset() const;
/**
* Return the active paintop (which is created based on the specified preset and
* will be deleted as soon as the KisPainter instance dies).
*/
KisPaintOp* paintOp() const;
void setMirrorInformation(const QPointF &axesCenter, bool mirrorHorizontally, bool mirrorVertically);
void copyMirrorInformationFrom(const KisPainter *other);
/**
* Returns whether the mirroring methods will do any
* work when called
*/
bool hasMirroring() const;
/**
* Indicates if horizontal mirroring mode is activated
*/
bool hasHorizontalMirroring() const;
/**
* Indicates if vertical mirroring mode is activated
*/
bool hasVerticalMirroring() const;
/**
* Mirror \p rc in the requested \p direction around the center point defined
* in the painter.
*/
void mirrorRect(Qt::Orientation direction, QRect *rc) const;
/**
* Mirror \p dab in the requested direction around the center point defined
* in the painter. The dab's offset is adjusted automatically.
*/
void mirrorDab(Qt::Orientation direction, KisRenderedDab *dab) const;
/**
* Calculate the list of the mirrored rects that will be painted on the
* the canvas when calling renderMirrorMask() at al
*/
const QVector<QRect> calculateAllMirroredRects(const QRect &rc);
/// Set the current pattern
void setPattern(const KoPattern * pattern);
/// Returns the currently set pattern
const KoPattern * pattern() const;
/**
* Set the color that will be used to paint with, and convert it
* to the color space of the current paint device.
*/
void setPaintColor(const KoColor& color);
/// Returns the color that will be used to paint with
const KoColor &paintColor() const;
/**
* Set the current background color, and convert it
* to the color space of the current paint device.
*/
void setBackgroundColor(const KoColor& color);
/// Returns the current background color
const KoColor &backgroundColor() const;
/// Set the current generator (a generator can be used to fill an area
void setGenerator(KisFilterConfigurationSP generator);
/// @return the current generator configuration
const KisFilterConfigurationSP generator() const;
/// This enum contains the styles with which we can fill things like polygons and ellipses
enum FillStyle {
FillStyleNone,
FillStyleForegroundColor,
FillStyleBackgroundColor,
FillStylePattern,
FillStyleGradient,
FillStyleStrokes,
FillStyleGenerator,
};
/// Set the current style with which to fill
void setFillStyle(FillStyle fillStyle);
/// Returns the current fill style
FillStyle fillStyle() const;
/// Set whether a polygon's filled area should be anti-aliased or not. The default is true.
void setAntiAliasPolygonFill(bool antiAliasPolygonFill);
/// Return whether a polygon's filled area should be anti-aliased or not
bool antiAliasPolygonFill();
/// The style of the brush stroke around polygons and so
enum StrokeStyle {
StrokeStyleNone,
StrokeStyleBrush
};
/// Set the current brush stroke style
void setStrokeStyle(StrokeStyle strokeStyle);
/// Returns the current brush stroke style
StrokeStyle strokeStyle() const;
void setFlow(quint8 flow);
quint8 flow() const;
/**
* Sets the opacity of the painting and recalculates the
* mean opacity of the stroke. This mean value is used to
* make ALPHA_DARKEN painting look correct
*/
void setOpacityUpdateAverage(quint8 opacity);
/**
* Sets average opacity, that is used to make ALPHA_DARKEN painting look correct
*/
void setAverageOpacity(qreal averageOpacity);
/**
* Calculate average opacity value after painting a single dab with \p opacity
*/
static qreal blendAverageOpacity(qreal opacity, qreal averageOpacity);
/// Set the opacity which is used in painting (like filling polygons)
void setOpacity(quint8 opacity);
/// Returns the opacity that is used in painting
quint8 opacity() const;
/// Set the composite op for this painter
void setCompositeOp(const KoCompositeOp * op);
const KoCompositeOp * compositeOp();
/// Set the composite op for this painter by string.
/// Note: the colorspace must be set previously!
void setCompositeOp(const QString& op);
/**
* Add \p r to the current set of dirty rects
*/
void addDirtyRect(const QRect &r);
/**
* Add \p rects to the current set of dirty rects
*/
void addDirtyRects(const QVector<QRect> &rects);
/**
* Reset the selection to the given selection. All painter actions will be
* masked by the specified selection.
*/
void setSelection(KisSelectionSP selection);
/**
* @return the selection set on this painter.
*/
KisSelectionSP selection();
void setGradient(const KoAbstractGradient* gradient);
const KoAbstractGradient* gradient() const;
/**
* Set the size of the tile in fillPainterPath, useful when optimizing the use of fillPainterPath
* e.g. Spray paintop uses more small tiles, although selections uses bigger tiles. QImage::fill
* is quite expensive so with smaller images you can save instructions
* Default and maximum size is 256x256 image
*/
void setMaskImageSize(qint32 width, qint32 height);
// /**
// * If the alpha channel is locked, the alpha values of the paint device we are painting on
// * will not change.
// */
// void setLockAlpha(bool protect);
// bool alphaLocked() const;
/**
* set the rendering intent in case pixels need to be converted before painting
*/
void setRenderingIntent(KoColorConversionTransformation::Intent intent);
/**
* set the conversion flags in case pixels need to be converted before painting
*/
void setColorConversionFlags(KoColorConversionTransformation::ConversionFlags conversionFlags);
/**
* Set interface for running asynchronous jobs by paintops.
*
* NOTE: the painter does *not* own the interface device. It is the responsibility
* of the caller to ensure that the interface object is alive during the lifetime
* of the painter.
*/
void setRunnableStrokeJobsInterface(KisRunnableStrokeJobsInterface *interface);
/**
* Get the interface for running asynchronous jobs. It is used by paintops mostly.
*/
KisRunnableStrokeJobsInterface* runnableStrokeJobsInterface() const;
protected:
/// Initialize, set everything to '0' or defaults
void init();
/// Fill the polygon defined by points with the fillStyle
void fillPolygon(const vQPointF& points, FillStyle fillStyle);
private:
KisPainter(const KisPainter&);
KisPainter& operator=(const KisPainter&);
float frac(float value) {
float tmp = 0;
return modff(value , &tmp);
}
float invertFrac(float value) {
float tmp = 0;
return 1.0f - modff(value , &tmp);
}
protected:
KoUpdater * progressUpdater();
private:
template <bool useOldSrcData>
void bitBltImpl(qint32 dstX, qint32 dstY,
const KisPaintDeviceSP srcDev,
qint32 srcX, qint32 srcY,
qint32 srcWidth, qint32 srcHeight);
inline void compositeOnePixel(quint8 *dst, const KoColor &color);
private:
struct Private;
Private* const d;
};
#endif // KIS_PAINTER_H_
diff --git a/libs/image/kis_raster_keyframe_channel.h b/libs/image/kis_raster_keyframe_channel.h
index 2ccec11eec..c2a791f21e 100644
--- a/libs/image/kis_raster_keyframe_channel.h
+++ b/libs/image/kis_raster_keyframe_channel.h
@@ -1,95 +1,96 @@
/*
* Copyright (c) 2015 Jouni Pentikäinen <joupent@gmail.com>
*
* 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_RASTER_KEYFRAME_CHANNEL_H
#define _KIS_RASTER_KEYFRAME_CHANNEL_H
#include "kis_keyframe_channel.h"
class KRITAIMAGE_EXPORT KisRasterKeyframeChannel : public KisKeyframeChannel
{
Q_OBJECT
public:
KisRasterKeyframeChannel(const KoID& id, const KisPaintDeviceWSP paintDevice, KisDefaultBoundsBaseSP defaultBounds);
KisRasterKeyframeChannel(const KisRasterKeyframeChannel &rhs, KisNode *newParentNode, const KisPaintDeviceWSP newPaintDevice);
~KisRasterKeyframeChannel() override;
public:
/**
* Return the ID of the active frame at a given time. The active frame is
* defined by the keyframe at the given time or the last keyframe before it.
* @param time
* @return active frame id
*/
int frameIdAt(int time) const;
/**
* Copy the active frame at given time to target device.
* @param keyframe keyframe to copy from
* @param targetDevice device to copy the frame to
*/
void fetchFrame(KisKeyframeSP keyframe, KisPaintDeviceSP targetDevice);
/**
* Copy the content of the sourceDevice into a new keyframe at given time
* @param time position of new keyframe
* @param sourceDevice source for content
+ * @param parentCommand parent command used for stacking
*/
void importFrame(int time, KisPaintDeviceSP sourceDevice, KUndo2Command *parentCommand);
QRect frameExtents(KisKeyframeSP keyframe);
QString frameFilename(int frameId) const;
/**
* When choosing filenames for frames, this will be appended to the node filename
*/
void setFilenameSuffix(const QString &suffix);
bool hasScalarValue() const override;
QDomElement toXML(QDomDocument doc, const QString &layerFilename) override;
void loadXML(const QDomElement &channelNode) override;
void setOnionSkinsEnabled(bool value);
bool onionSkinsEnabled() const;
protected:
KisKeyframeSP createKeyframe(int time, const KisKeyframeSP copySrc, KUndo2Command *parentCommand) override;
void destroyKeyframe(KisKeyframeSP key, KUndo2Command *parentCommand) override;
void uploadExternalKeyframe(KisKeyframeChannel *srcChannel, int srcTime, KisKeyframeSP dstFrame) override;
QRect affectedRect(KisKeyframeSP key) override;
void saveKeyframe(KisKeyframeSP keyframe, QDomElement keyframeElement, const QString &layerFilename) override;
KisKeyframeSP loadKeyframe(const QDomElement &keyframeNode) override;
friend class KisRasterKeyframe;
bool keyframeHasContent(const KisKeyframe *keyframe) const;
private:
void setFrameFilename(int frameId, const QString &filename);
QString chooseFrameFilename(int frameId, const QString &layerFilename);
int frameId(KisKeyframeSP keyframe) const;
int frameId(const KisKeyframe *keyframe) const;
struct Private;
QScopedPointer<Private> m_d;
};
#endif
diff --git a/libs/image/kis_repeat_iterators_pixel.h b/libs/image/kis_repeat_iterators_pixel.h
index 1c0d18bcc3..507e5cb52a 100644
--- a/libs/image/kis_repeat_iterators_pixel.h
+++ b/libs/image/kis_repeat_iterators_pixel.h
@@ -1,268 +1,288 @@
/*
* Copyright (c) 2008 Cyrille Berger <cberger@cberger.net>
*
* 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_REPEAT_ITERATORS_PIXEL_H_
#define _KIS_REPEAT_ITERATORS_PIXEL_H_
#include <QRect>
#include "kis_shared.h"
#include "tiles3/kis_hline_iterator.h"
#include "tiles3/kis_vline_iterator.h"
template<class T>
class KisRepeatHLineIteratorPixelBase;
template<class T>
class KisRepeatVLineIteratorPixelBase;
/**
* This iterator is an iterator that will "artificially" extend the paint device with the
* value of the border when trying to access values outside the range of data.
*/
template<class T>
class KisRepeatLineIteratorPixelBase : public KisShared
{
Q_DISABLE_COPY(KisRepeatLineIteratorPixelBase)
public:
friend class KisRepeatHLineIteratorPixelBase<T>;
friend class KisRepeatVLineIteratorPixelBase<T>;
/**
- * @param rc indicates the rectangle that truly contains data
+ * @param dm data manager
+ * @param x x of top left corner
+ * @param y y of top left corner
+ * @param offsetx x offset
+ * @param offsety y offset
+ * @param _rc indicates the rectangle that truly contains data
+ * @param completeListener completion listener
*/
inline KisRepeatLineIteratorPixelBase(KisDataManager *dm, qint32 x, qint32 y, qint32 offsetx, qint32 offsety, const QRect& _rc, KisIteratorCompleteListener *completeListener);
virtual inline ~KisRepeatLineIteratorPixelBase();
public:
inline qint32 x() const {
return m_realX;
}
inline qint32 y() const {
return m_realY;
}
inline const quint8 * oldRawData() const {
return m_iterator->oldRawData();
}
private:
KisDataManager* m_dm;
qint32 m_realX, m_realY;
qint32 m_offsetX, m_offsetY;
QRect m_dataRect;
T* m_iterator;
KisIteratorCompleteListener *m_completeListener;
};
/**
* This iterator is an iterator that will "artificially" extend the paint device with the
* value of the border when trying to access values outside the range of data.
*/
template<class T>
class KisRepeatHLineIteratorPixelBase : public KisRepeatLineIteratorPixelBase<T>
{
public:
/**
- * @param rc indicates the rectangle that truly contains data
+ * @param dm data manager
+ * @param x x of top left corner
+ * @param y y of top left corner
+ * @param w width
+ * @param offsetx x offset
+ * @param offsety y offset
+ * @param _rc indicates the rectangle that truly contains data
+ * @param completeListener completion listener
*/
inline KisRepeatHLineIteratorPixelBase(KisDataManager *dm, qint32 x, qint32 y, qint32 w, qint32 offsetx, qint32 offsety, const QRect& _rc, KisIteratorCompleteListener *completeListener);
inline ~KisRepeatHLineIteratorPixelBase() override;
inline bool nextPixel();
/**
* Reach next row.
*/
inline void nextRow();
private:
void createIterator();
private:
qint32 m_startX;
qint32 m_startIteratorX;
qint32 m_width;
};
/**
* This iterator is an iterator that will "artificially" extend the paint device with the
* value of the border when trying to access values outside the range of data.
*/
template<class T>
class KisRepeatVLineIteratorPixelBase : public KisRepeatLineIteratorPixelBase<T>
{
public:
/**
- * @param rc indicates the rectangle that truly contains data
+ * @param dm data manager
+ * @param x x of top left corner
+ * @param y y of top left corner
+ * @param h height
+ * @param offsetx x offset
+ * @param offsety y offset
+ * @param _rc indicates the rectangle that truly contains data
+ * @param completeListener completion listener
*/
inline KisRepeatVLineIteratorPixelBase(KisDataManager *dm, qint32 x, qint32 y, qint32 h, qint32 offsetx, qint32 offsety, const QRect& _rc, KisIteratorCompleteListener *completeListener);
inline ~KisRepeatVLineIteratorPixelBase() override;
inline KisRepeatVLineIteratorPixelBase<T> & operator ++();
inline bool nextPixel();
/**
* Reach next row.
*/
inline void nextColumn();
private:
void createIterator();
private:
qint32 m_startY;
qint32 m_startIteratorY;
qint32 m_height;
};
//------------------------ Implementations ------------------------//
//---------------- KisRepeatLineIteratorPixelBase -----------------//
template<class T>
KisRepeatLineIteratorPixelBase<T>::KisRepeatLineIteratorPixelBase(KisDataManager *dm, qint32 x, qint32 y, qint32 offsetx, qint32 offsety, const QRect& _rc, KisIteratorCompleteListener *completeListener) :
m_dm(dm),
m_realX(x), m_realY(y),
m_offsetX(offsetx), m_offsetY(offsety),
m_dataRect(_rc),
m_iterator(0),
m_completeListener(completeListener)
{
}
template<class T>
KisRepeatLineIteratorPixelBase<T>::~KisRepeatLineIteratorPixelBase()
{
delete m_iterator;
}
//---------------- KisRepeatHLineIteratorPixelBase ----------------//
template<class T>
KisRepeatHLineIteratorPixelBase<T>::KisRepeatHLineIteratorPixelBase(KisDataManager *dm, qint32 x, qint32 y, qint32 w, qint32 offsetx, qint32 offsety, const QRect& _rc, KisIteratorCompleteListener *completeListener)
: KisRepeatLineIteratorPixelBase<T>(dm, x, y, offsetx, offsety , _rc, completeListener),
m_startX(x), m_startIteratorX(x),
m_width(w)
{
// Compute the startx value of the iterator
if (m_startIteratorX < _rc.left()) {
m_startIteratorX = _rc.left();
}
createIterator();
}
template<class T>
KisRepeatHLineIteratorPixelBase<T>::~KisRepeatHLineIteratorPixelBase()
{
}
template<class T>
inline bool KisRepeatHLineIteratorPixelBase<T>::nextPixel()
{
Q_ASSERT(this->m_iterator);
if (this->m_realX >= this->m_dataRect.x() && this->m_realX < this->m_dataRect.x() + this->m_dataRect.width() - 1) {
this->m_iterator->nextPixel();
}
++this->m_realX;
return (this->m_realX < m_startX + m_width);
}
template<class T>
inline void KisRepeatHLineIteratorPixelBase<T>::nextRow()
{
if (this->m_realY >= this->m_dataRect.y() && this->m_realY < this->m_dataRect.y() + this->m_dataRect.height() - 1) {
this->m_iterator->nextRow();
} else {
createIterator();
}
this->m_realX = this->m_startX;
++this->m_realY;
}
template<class T>
void KisRepeatHLineIteratorPixelBase<T>::createIterator()
{
// Cleanup
delete this->m_iterator;
qint32 startY = this->m_realY;
if (startY < this->m_dataRect.y()) {
startY = this->m_dataRect.top();
}
if (startY > (this->m_dataRect.y() + this->m_dataRect.height() - 1)) {
startY = (this->m_dataRect.y() + this->m_dataRect.height() - 1);
}
int width = this->m_dataRect.x() + this->m_dataRect.width() - this->m_startIteratorX;
this->m_iterator = new T(this->m_dm, this->m_startIteratorX, startY, width, this->m_offsetX, this->m_offsetY, false, this->m_completeListener);
this->m_realX = this->m_startX;
}
//---------------- KisRepeatVLineIteratorPixelBase ----------------//
template<class T>
KisRepeatVLineIteratorPixelBase<T>::KisRepeatVLineIteratorPixelBase(KisDataManager *dm, qint32 x, qint32 y, qint32 h, qint32 offsetx, qint32 offsety, const QRect& _rc, KisIteratorCompleteListener *completeListener)
: KisRepeatLineIteratorPixelBase<T>(dm, x, y, offsetx, offsety , _rc, completeListener),
m_startY(y), m_startIteratorY(y),
m_height(h)
{
// Compute the startx value of the iterator
if (m_startIteratorY < _rc.top()) {
m_startIteratorY = _rc.top();
}
createIterator();
}
template<class T>
KisRepeatVLineIteratorPixelBase<T>::~KisRepeatVLineIteratorPixelBase()
{
}
template<class T>
inline bool KisRepeatVLineIteratorPixelBase<T>::nextPixel()
{
Q_ASSERT(this->m_iterator);
if (this->m_realY >= this->m_dataRect.y() && this->m_realY < this->m_dataRect.y() + this->m_dataRect.height() - 1) {
this->m_iterator->nextPixel();
}
++this->m_realY;
return (this->m_realY < m_startY + m_height);
}
template<class T>
inline void KisRepeatVLineIteratorPixelBase<T>::nextColumn()
{
if (this->m_realX >= this->m_dataRect.x() && this->m_realX < this->m_dataRect.x() + this->m_dataRect.width() - 1) {
this->m_iterator->nextColumn();
} else {
createIterator();
}
this->m_realY = this->m_startY;
++this->m_realX;
}
template<class T>
void KisRepeatVLineIteratorPixelBase<T>::createIterator()
{
// Cleanup
delete this->m_iterator;
qint32 startX = this->m_realX;
if (startX < this->m_dataRect.x()) {
startX = this->m_dataRect.x();
}
if (startX > (this->m_dataRect.x() + this->m_dataRect.width() - 1)) {
startX = (this->m_dataRect.x() + this->m_dataRect.width() - 1);
}
int height = this->m_dataRect.y() + this->m_dataRect.height() - this->m_startIteratorY;
this->m_iterator = new T(this->m_dm, startX, this->m_startIteratorY, height, this->m_offsetX, this->m_offsetY, false, this->m_completeListener);
this->m_realY = this->m_startY;
}
#endif
diff --git a/libs/image/kis_selection_based_layer.h b/libs/image/kis_selection_based_layer.h
index cc57a75074..08c1ba8304 100644
--- a/libs/image/kis_selection_based_layer.h
+++ b/libs/image/kis_selection_based_layer.h
@@ -1,210 +1,211 @@
/*
* Copyright (c) 2006 Boudewijn Rempt <boud@valdyas.org>
* (c) 2009 Dmitry Kazakov <dimula73@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#ifndef KIS_SELECTION_BASED_LAYER_H_
#define KIS_SELECTION_BASED_LAYER_H_
#include <QObject>
#include "kis_types.h"
#include "kis_layer.h"
#include "kis_indirect_painting_support.h"
#include <kritaimage_export.h>
#include "kis_node_filter_interface.h"
class KisFilterConfiguration;
/**
- * @class KisSelectionBasedLayer describes base behaviour for
+ * @class KisSelectionBasedLayer
+ * @brief Describes base behaviour for
* selection base classes like KisAdjustmentLayer and KisGeneratorLayer.
* These classes should have a persistent selection that controls
* the area where filter/generators are applied. The area outside
* this selection is not affected by the layer
*/
class KRITAIMAGE_EXPORT KisSelectionBasedLayer : public KisLayer, public KisIndirectPaintingSupport, public KisNodeFilterInterface
{
Q_OBJECT
public:
/**
* creates a new layer with the given selection.
* Note that the selection will be _copied_ (with COW, though).
* @param image the image to set this layer to
* @param name name of the layer
* @param selection is a mask used by the layer to know
* where to apply the filter/generator.
*/
KisSelectionBasedLayer(KisImageWSP image, const QString &name, KisSelectionSP selection, KisFilterConfigurationSP filterConfig, bool useGeneratorRegistry = false);
KisSelectionBasedLayer(const KisSelectionBasedLayer& rhs);
~KisSelectionBasedLayer() override;
/**
* tells whether the @node can be a child of this layer
* @param node to be connected node
* @return tells if to be connected is a child of KisMask
*/
bool allowAsChild(KisNodeSP node) const override;
void setImage(KisImageWSP image) override;
KisPaintDeviceSP original() const override;
KisPaintDeviceSP paintDevice() const override;
bool needProjection() const override;
/**
* resets cached projection of lower layer to a new device
* @return void
*/
virtual void resetCache();
/**
* for KisLayer::setDirty(const QRegion&)
*/
using KisLayer::setDirty;
/**
* Mark a layer as dirty. We can't use KisLayer's one
* as our extent() function doesn't fit for this
*/
void setDirty() override;
public:
/**
* Returns the selection of the layer
*
* Do not mix it with selection() which returns
* the currently active selection of the image
*/
KisSelectionSP internalSelection() const;
/**
* sets the selection of this layer to a copy of
* selection
* @param selection the selection to set
* @return void
*/
void setInternalSelection(KisSelectionSP selection);
/**
* When painted in indirect painting mode, the internal selection
* might not contain actual selection, because a part of it is
* stored on an indirect painting device. This method returns the
* merged copy of the real selection. The area in \p rect only is
* guaranteed to be prepared. The content of the rest of the
* selection is undefined.
*/
KisSelectionSP fetchComposedInternalSelection(const QRect &rect) const;
/**
* gets this layer's x coordinate, taking selection into account
* @return x-coordinate value
*/
qint32 x() const override;
/**
* gets this layer's y coordinate, taking selection into account
* @return y-coordinate value
*/
qint32 y() const override;
/**
* sets this layer's y coordinate, taking selection into account
* @param x x coordinate
*/
void setX(qint32 x) override;
/**
* sets this layer's y coordinate, taking selection into account
* @param y y coordinate
*/
void setY(qint32 y) override;
public:
/**
* gets an approximation of where the bounds on actual data
* are in this layer, taking selection into account
*/
QRect extent() const override;
/**
* returns the exact bounds of where the actual data resides
* in this layer, taking selection into account
*/
QRect exactBounds() const override;
/**
* copies the image and reformats it to thumbnail size
* and returns the new thumbnail image.
* @param w width of the thumbnail to create
* @param h height of the thumbnail to create
* @return the thumbnail image created.
*/
QImage createThumbnail(qint32 w, qint32 h) override;
protected:
// override from KisLayer
void copyOriginalToProjection(const KisPaintDeviceSP original,
KisPaintDeviceSP projection,
const QRect& rect) const override;
// override from KisNode
QRect needRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const override;
protected:
void initSelection();
QRect cropChangeRectBySelection(const QRect &rect) const;
/**
* Sets if the selection should be used in
* copyOriginalToProjection() method.
*
* Default value is 'true'. The descendants should override it to
* get desired behaviour.
*
* Must be called only once in the child's constructor
*/
void setUseSelectionInProjection(bool value) const;
KisKeyframeChannel *requestKeyframeChannel(const QString &id) override;
public Q_SLOTS:
void slotImageSizeChanged();
/**
* gets this layer. Overriddes function in
* KisIndirectPaintingSupport
* @return this AdjustmentLayer
*/
KisLayer* layer() {
return this;
}
private:
struct Private;
Private * const m_d;
};
#endif /* KIS_SELECTION_BASED_LAYER_H_ */
diff --git a/libs/image/kis_transform_worker.h b/libs/image/kis_transform_worker.h
index 4a9c6db520..df00969cdf 100644
--- a/libs/image/kis_transform_worker.h
+++ b/libs/image/kis_transform_worker.h
@@ -1,160 +1,160 @@
/*
* Copyright (c) 2004 Michael Thaler <michael.thaler@physik.tu-muenchen.de>
* Copyright (c) 2005 C. Boemann <cbo@boemann.dk>
* Copyright (c) 2010 Marc Pegon <pe.marc@free.fr>
*
* 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_TRANSFORM_WORKER_H_
#define KIS_TRANSFORM_WORKER_H_
#include "kis_types.h"
#include "kritaimage_export.h"
#include <QRect>
#include <KoUpdater.h>
class KisPaintDevice;
class KisFilterStrategy;
class QTransform;
class KRITAIMAGE_EXPORT KisTransformWorker
{
/* What are xshearOrigin, yshearOrigin :
*
* let's keep it simple and say we only have horizontal shearing (it's similar with vertical shearing)
* that means we will apply the transformation :
* x' = x + xshear * y and y' = y, where x,y are the old coordinates of the pixels, and x' y' the new coordinates
* that means, the more we go down in the image (y++), the more x' is different from x
* most of the times, we want to shear a part of the image centered at y = y0 != 0.
* i.e. we want x' = x at y = y0
* in that case, it's good to apply instead x' = x + xshear * (y - yshearOrigin), y' = y.
* please note that it's still possible to obtain the same result by copying the part you want to shear at
* in another paintDevice at y = -y0 and use the transformWorker with yshearOrigin = 0.
*/
public:
KisTransformWorker(KisPaintDeviceSP dev,
double xscale, double yscale,
double xshear, double yshear,
double xshearOrigin, double yshearOrigin,
double rotation,
qint32 xtranslate, qint32 ytranslate,
KoUpdaterPtr progress,
KisFilterStrategy *filter);
~KisTransformWorker();
/**
* Mirror the specified device along the X or Y axis at the
* coordinate \p axis.
*/
static void mirror(KisPaintDeviceSP dev, qreal axis, Qt::Orientation orientation);
/**
* Convenience methods for mirror(dev, axis, orientation)
*/
static void mirrorX(KisPaintDeviceSP dev, qreal axis);
static void mirrorY(KisPaintDeviceSP dev, qreal axis);
/**
* Mirror the device relative to the center of its exactBounds()
*/
static void mirrorX(KisPaintDeviceSP dev);
static void mirrorY(KisPaintDeviceSP dev);
/**
* Offset the specified device with wrapping around edges of rect specified as QRect(0,0,wrapSize.width, wrapSize.height)*
* @param device device to be offset
* @param offsetPosition position where the new origin will be
- * @param wrapSize width and height of the wrap edge, usual scenario is to use canvas width&height
+ * @param wrapRect width and height of the wrap edge, usual scenario is to use canvas width&height
*
**/
static void offset(KisPaintDeviceSP device, const QPoint &offsetPosition, const QRect &wrapRect);
public:
// returns false if interrupted
bool run();
bool runPartial(const QRect &processRect);
/**
* Returns a matrix of the transformation executed by the worker.
* Resulting transformation has the following form (in Qt's matrix
* notation (all the matrices are transposed)):
*
* transform = TS.inverted() * S * TS * SC * R * T
*
* ,where:
* TS - shear origin transpose
* S - shear itself (shearX * shearY)
* SC - scale
- * R - rotation (@rotation parameter)
- * T - transpose (@xtranslate, @ytranslate)
+ * R - rotation (@p rotation parameter)
+ * T - transpose (@p xtranslate, @p ytranslate)
*
* WARNING: due to some rounding problems in the worker
* the work it does does not correspond to the matrix exactly!
* The result always differs 1-3 pixel. So be careful with it
* (or fix it)
*/
QTransform transform() const;
/**
* Transforms the outline of the pixel selection (if it is valid)
*/
void transformPixelSelectionOutline(KisPixelSelectionSP pixelSelection) const;
private:
// XXX (BSAR): Why didn't we use the shared-pointer versions of the paint device classes?
// CBR: because the template functions used within don't work if it's not true pointers
template <class T> void transformPass(KisPaintDevice* src,
KisPaintDevice* dst,
double xscale,
double shear,
double dx,
KisFilterStrategy *filterStrategy,
int portion);
friend class KisTransformWorkerTest;
static QRect rotateRight90(KisPaintDeviceSP dev,
QRect boundRect,
KoUpdaterPtr progressUpdater,
int portion);
static QRect rotateLeft90(KisPaintDeviceSP dev,
QRect boundRect,
KoUpdaterPtr progressUpdater,
int portion);
static QRect rotate180(KisPaintDeviceSP dev,
QRect boundRect,
KoUpdaterPtr progressUpdater,
int portion);
private:
KisPaintDeviceSP m_dev;
double m_xscale, m_yscale;
double m_xshear, m_yshear, m_rotation;
double m_xshearOrigin, m_yshearOrigin;
qint32 m_xtranslate, m_ytranslate;
KoUpdaterPtr m_progressUpdater;
KisFilterStrategy *m_filter;
QRect m_boundRect;
};
#endif // KIS_TRANSFORM_VISITOR_H_
diff --git a/libs/image/lazybrush/KisWatershedWorker.h b/libs/image/lazybrush/KisWatershedWorker.h
index 95fd52c714..aa7c595699 100644
--- a/libs/image/lazybrush/KisWatershedWorker.h
+++ b/libs/image/lazybrush/KisWatershedWorker.h
@@ -1,82 +1,83 @@
/*
* Copyright (c) 2017 Dmitry Kazakov <dimula73@gmail.com>
*
* 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 KISWATERSHEDWORKER_H
#define KISWATERSHEDWORKER_H
#include <QScopedPointer>
#include "kis_types.h"
#include "kritaimage_export.h"
class KoColor;
class KRITAIMAGE_EXPORT KisWatershedWorker
{
public:
/**
- * Creates an empty watershed worker withouth any strokes attached. The strokes
+ * Creates an empty watershed worker without any strokes attached. The strokes
* should be attached manually with addKeyStroke() call.
*
* @param heightMap prefiltered height map in alpha8 colorspace, with "0" meaning
* background color and "255" meaning line art. Heightmap is *never*
* modified by the worker!
* @param dst destination device where the result will be written
* @param boundingRect the worker refuses to fill outside the bounding rect, considering
* that outer area as having +inf height
+ * @param progress the progress value
*/
KisWatershedWorker(KisPaintDeviceSP heightMap,
KisPaintDeviceSP dst,
const QRect &boundingRect,
KoUpdater *progress = 0);
~KisWatershedWorker();
/**
* @brief Adds a key stroke to the worker.
*
* The key strokes may intersect, in which case the lastly added stroke will have
* a priority over all the previous ones.
*
* @param dev alpha8 paint device of the key stroke, may contain disjoint areas
* @param color the color of the stroke
*/
void addKeyStroke(KisPaintDeviceSP dev, const KoColor &color);
/**
* @brief run the filling process using the passes height map, strokes, and write
* the result coloring into the destination device
* @param cleanUpAmount shows how aggressively we should try to clean up the final
* coloring. Should be in range [0.0...1.0]
*/
void run(qreal cleanUpAmount = 0.0);
int testingGroupPositiveEdge(qint32 group, quint8 level);
int testingGroupNegativeEdge(qint32 group, quint8 level);
int testingGroupForeignEdge(qint32 group, quint8 level);
int testingGroupAllyEdge(qint32 group, quint8 level);
int testingGroupConflicts(qint32 group, quint8 level, qint32 withGroup);
void testingTryRemoveGroup(qint32 group, quint8 level);
private:
struct Private;
const QScopedPointer<Private> m_d;
};
#endif // KISWATERSHEDWORKER_H
diff --git a/libs/image/lazybrush/kis_lazy_fill_tools.h b/libs/image/lazybrush/kis_lazy_fill_tools.h
index f8fc15eab9..ad81e3c4a3 100644
--- a/libs/image/lazybrush/kis_lazy_fill_tools.h
+++ b/libs/image/lazybrush/kis_lazy_fill_tools.h
@@ -1,98 +1,98 @@
/*
* Copyright (c) 2016 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_LAZY_FILL_TOOLS_H
#define __KIS_LAZY_FILL_TOOLS_H
#include "kis_types.h"
#include "kritaimage_export.h"
#include <KoColor.h>
#include <boost/operators.hpp>
class KoColor;
namespace KisLazyFillTools
{
KRITAIMAGE_EXPORT
void normalizeAndInvertAlpha8Device(KisPaintDeviceSP dev, const QRect &rect);
KRITAIMAGE_EXPORT
void normalizeAlpha8Device(KisPaintDeviceSP dev, const QRect &rect);
/**
* Uses Boykov-Kolmogorov Max-Flow/Min-Cut algorithm to split the
- * device \src into two parts. The first part is defined by \p
+ * device \p src into two parts. The first part is defined by \p
* colorScribble and the second part --- by \p
* backgroundScribble. In the result of the split the area defined
* by \p colorScribble in \p resultDevice is filled with \p
* color. Also the same area in \p maskDevice is filled with a
* non-null scribble index.
*
* \p maskDevice is used for limiting the area used for filling
* the color.
*/
KRITAIMAGE_EXPORT
void cutOneWay(const KoColor &color,
KisPaintDeviceSP src,
KisPaintDeviceSP colorScribble,
KisPaintDeviceSP backgroundScribble,
KisPaintDeviceSP resultDevice,
KisPaintDeviceSP maskDevice,
const QRect &boundingRect);
/**
* Returns one pixel from each connected component of \p src.
*
* WARNING: \p src is used as a temporary device, so it will be
* cleared(!) after the execution of the algorithm
*/
KRITAIMAGE_EXPORT
QVector<QPoint> splitIntoConnectedComponents(KisPaintDeviceSP src, const QRect &boundingRect);
struct KRITAIMAGE_EXPORT KeyStroke : public boost::equality_comparable<KeyStroke>
{
KeyStroke();
KeyStroke(KisPaintDeviceSP _dev, const KoColor &_color, bool isTransparent = false);
friend bool operator==(const KeyStroke& t1, const KeyStroke&t2);
KisPaintDeviceSP dev;
KoColor color;
bool isTransparent;
};
struct KRITAIMAGE_EXPORT FilteringOptions : public boost::equality_comparable<FilteringOptions>
{
FilteringOptions() = default;
FilteringOptions(bool _useEdgeDetection, qreal _edgeDetectionSize, qreal _fuzzyRadius, qreal _cleanUpAmount);
friend bool operator==(const FilteringOptions &t1, const FilteringOptions &t2);
// default values for filtering: disabled
bool useEdgeDetection = false;
qreal edgeDetectionSize = 4;
qreal fuzzyRadius = 0;
qreal cleanUpAmount = 0.0;
};
};
#endif /* __KIS_LAZY_FILL_TOOLS_H */
diff --git a/libs/image/lazybrush/patched_boykov_kolmogorov_max_flow.hpp b/libs/image/lazybrush/patched_boykov_kolmogorov_max_flow.hpp
index c9cf90a076..b2a13e70de 100644
--- a/libs/image/lazybrush/patched_boykov_kolmogorov_max_flow.hpp
+++ b/libs/image/lazybrush/patched_boykov_kolmogorov_max_flow.hpp
@@ -1,882 +1,882 @@
// Copyright (c) 2006, Stephan Diederich
//
// This code may be used under either of the following two licences:
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE. OF SUCH DAMAGE.
//
// Or:
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_BOYKOV_KOLMOGOROV_MAX_FLOW_HPP
#define BOOST_BOYKOV_KOLMOGOROV_MAX_FLOW_HPP
#include <boost/config.hpp>
#include <boost/assert.hpp>
#include <vector>
#include <list>
#include <utility>
#include <iosfwd>
#include <algorithm> // for std::min and std::max
#include <boost/pending/queue.hpp>
#include <boost/limits.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/none_t.hpp>
#include <boost/graph/graph_concepts.hpp>
#include <boost/graph/named_function_params.hpp>
#include <boost/graph/lookup_edge.hpp>
#include <boost/concept/assert.hpp>
// The algorithm impelemented here is described in:
//
// Boykov, Y., Kolmogorov, V. "An Experimental Comparison of Min-Cut/Max-Flow
// Algorithms for Energy Minimization in Vision", In IEEE Transactions on
// Pattern Analysis and Machine Intelligence, vol. 26, no. 9, pp. 1124-1137,
// Sep 2004.
//
// For further reading, also see:
//
// Kolmogorov, V. "Graph Based Algorithms for Scene Reconstruction from Two or
// More Views". PhD thesis, Cornell University, Sep 2003.
namespace boost {
namespace detail {
template <class Graph,
class EdgeCapacityMap,
class ResidualCapacityEdgeMap,
class ReverseEdgeMap,
class PredecessorMap,
class ColorMap,
class DistanceMap,
class IndexMap>
class bk_max_flow {
typedef typename property_traits<EdgeCapacityMap>::value_type tEdgeVal;
typedef graph_traits<Graph> tGraphTraits;
typedef typename tGraphTraits::vertex_iterator vertex_iterator;
typedef typename tGraphTraits::vertex_descriptor vertex_descriptor;
typedef typename tGraphTraits::edge_descriptor edge_descriptor;
typedef typename tGraphTraits::edge_iterator edge_iterator;
typedef typename tGraphTraits::out_edge_iterator out_edge_iterator;
typedef boost::queue<vertex_descriptor> tQueue; //queue of vertices, used in adoption-stage
typedef typename property_traits<ColorMap>::value_type tColorValue;
typedef color_traits<tColorValue> tColorTraits;
typedef typename property_traits<DistanceMap>::value_type tDistanceVal;
public:
bk_max_flow(Graph& g,
EdgeCapacityMap cap,
ResidualCapacityEdgeMap res,
ReverseEdgeMap rev,
PredecessorMap pre,
ColorMap color,
DistanceMap dist,
IndexMap idx,
vertex_descriptor src,
vertex_descriptor sink):
m_g(g),
m_index_map(idx),
m_cap_map(cap),
m_res_cap_map(res),
m_rev_edge_map(rev),
m_pre_map(pre),
m_tree_map(color),
m_dist_map(dist),
m_source(src),
m_sink(sink),
m_active_nodes(),
m_in_active_list_vec(num_vertices(g), false),
m_in_active_list_map(make_iterator_property_map(m_in_active_list_vec.begin(), m_index_map)),
m_has_parent_vec(num_vertices(g), false),
m_has_parent_map(make_iterator_property_map(m_has_parent_vec.begin(), m_index_map)),
m_time_vec(num_vertices(g), 0),
m_time_map(make_iterator_property_map(m_time_vec.begin(), m_index_map)),
m_flow(0),
m_time(1),
m_last_grow_vertex(graph_traits<Graph>::null_vertex()){
// initialize the color-map with gray-values
vertex_iterator vi, v_end;
for(boost::tie(vi, v_end) = vertices(m_g); vi != v_end; ++vi){
set_tree(*vi, tColorTraits::gray());
}
// Initialize flow to zero which means initializing
// the residual capacity equal to the capacity
edge_iterator ei, e_end;
for(boost::tie(ei, e_end) = edges(m_g); ei != e_end; ++ei) {
put(m_res_cap_map, *ei, get(m_cap_map, *ei));
BOOST_ASSERT(get(m_rev_edge_map, get(m_rev_edge_map, *ei)) == *ei); //check if the reverse edge map is build up properly
}
//init the search trees with the two terminals
set_tree(m_source, tColorTraits::black());
set_tree(m_sink, tColorTraits::white());
put(m_time_map, m_source, 1);
put(m_time_map, m_sink, 1);
}
tEdgeVal max_flow(){
//augment direct paths from SOURCE->SINK and SOURCE->VERTEX->SINK
augment_direct_paths();
//start the main-loop
while(true){
bool path_found;
edge_descriptor connecting_edge;
boost::tie(connecting_edge, path_found) = grow(); //find a path from source to sink
if(!path_found){
//we're finished, no more paths were found
break;
}
++m_time;
augment(connecting_edge); //augment that path
adopt(); //rebuild search tree structure
}
return m_flow;
}
// the complete class is protected, as we want access to members in
// derived test-class (see test/boykov_kolmogorov_max_flow_test.cpp)
protected:
void augment_direct_paths(){
// in a first step, we augment all direct paths from source->NODE->sink
// and additionally paths from source->sink. This improves especially
// graphcuts for segmentation, as most of the nodes have source/sink
// connects but shouldn't have an impact on other maxflow problems
// (this is done in grow() anyway)
out_edge_iterator ei, e_end;
for(boost::tie(ei, e_end) = out_edges(m_source, m_g); ei != e_end; ++ei){
edge_descriptor from_source = *ei;
vertex_descriptor current_node = target(from_source, m_g);
if(current_node == m_sink){
tEdgeVal cap = get(m_res_cap_map, from_source);
put(m_res_cap_map, from_source, 0);
m_flow += cap;
continue;
}
edge_descriptor to_sink;
bool is_there;
boost::tie(to_sink, is_there) = lookup_edge(current_node, m_sink, m_g);
if(is_there){
tEdgeVal cap_from_source = get(m_res_cap_map, from_source);
tEdgeVal cap_to_sink = get(m_res_cap_map, to_sink);
if(cap_from_source > cap_to_sink){
set_tree(current_node, tColorTraits::black());
add_active_node(current_node);
set_edge_to_parent(current_node, from_source);
put(m_dist_map, current_node, 1);
put(m_time_map, current_node, 1);
// add stuff to flow and update residuals. we don't need to
// update reverse_edges, as incoming/outgoing edges to/from
// source/sink don't count for max-flow
put(m_res_cap_map, from_source, get(m_res_cap_map, from_source) - cap_to_sink);
put(m_res_cap_map, to_sink, 0);
m_flow += cap_to_sink;
} else if(cap_to_sink > 0){
set_tree(current_node, tColorTraits::white());
add_active_node(current_node);
set_edge_to_parent(current_node, to_sink);
put(m_dist_map, current_node, 1);
put(m_time_map, current_node, 1);
// add stuff to flow and update residuals. we don't need to update
// reverse_edges, as incoming/outgoing edges to/from source/sink
// don't count for max-flow
put(m_res_cap_map, to_sink, get(m_res_cap_map, to_sink) - cap_from_source);
put(m_res_cap_map, from_source, 0);
m_flow += cap_from_source;
}
} else if(get(m_res_cap_map, from_source)){
// there is no sink connect, so we can't augment this path, but to
// avoid adding m_source to the active nodes, we just activate this
// node and set the appropriate things
set_tree(current_node, tColorTraits::black());
set_edge_to_parent(current_node, from_source);
put(m_dist_map, current_node, 1);
put(m_time_map, current_node, 1);
add_active_node(current_node);
}
}
for(boost::tie(ei, e_end) = out_edges(m_sink, m_g); ei != e_end; ++ei){
edge_descriptor to_sink = get(m_rev_edge_map, *ei);
vertex_descriptor current_node = source(to_sink, m_g);
if(get(m_res_cap_map, to_sink)){
set_tree(current_node, tColorTraits::white());
set_edge_to_parent(current_node, to_sink);
put(m_dist_map, current_node, 1);
put(m_time_map, current_node, 1);
add_active_node(current_node);
}
}
}
/**
* Returns a pair of an edge and a boolean. if the bool is true, the
* edge is a connection of a found path from s->t , read "the link" and
* source(returnVal, m_g) is the end of the path found in the source-tree
* target(returnVal, m_g) is the beginning of the path found in the sink-tree
*/
std::pair<edge_descriptor, bool> grow(){
BOOST_ASSERT(m_orphans.empty());
vertex_descriptor current_node;
while((current_node = get_next_active_node()) != graph_traits<Graph>::null_vertex()){ //if there is one
BOOST_ASSERT(get_tree(current_node) != tColorTraits::gray() &&
(has_parent(current_node) ||
current_node == m_source ||
current_node == m_sink));
if(get_tree(current_node) == tColorTraits::black()){
//source tree growing
out_edge_iterator ei, e_end;
if(current_node != m_last_grow_vertex){
m_last_grow_vertex = current_node;
boost::tie(m_last_grow_edge_it, m_last_grow_edge_end) = out_edges(current_node, m_g);
}
for(; m_last_grow_edge_it != m_last_grow_edge_end; ++m_last_grow_edge_it) {
edge_descriptor out_edge = *m_last_grow_edge_it;
if(get(m_res_cap_map, out_edge) > 0){ //check if we have capacity left on this edge
vertex_descriptor other_node = target(out_edge, m_g);
if(get_tree(other_node) == tColorTraits::gray()){ //it's a free node
set_tree(other_node, tColorTraits::black()); //acquire other node to our search tree
set_edge_to_parent(other_node, out_edge); //set us as parent
put(m_dist_map, other_node, get(m_dist_map, current_node) + 1); //and update the distance-heuristic
put(m_time_map, other_node, get(m_time_map, current_node));
add_active_node(other_node);
} else if(get_tree(other_node) == tColorTraits::black()) {
// we do this to get shorter paths. check if we are nearer to
// the source as its parent is
if(is_closer_to_terminal(current_node, other_node)){
set_edge_to_parent(other_node, out_edge);
put(m_dist_map, other_node, get(m_dist_map, current_node) + 1);
put(m_time_map, other_node, get(m_time_map, current_node));
}
} else{
BOOST_ASSERT(get_tree(other_node)==tColorTraits::white());
//kewl, found a path from one to the other search tree, return
// the connecting edge in src->sink dir
return std::make_pair(out_edge, true);
}
}
} //for all out-edges
} //source-tree-growing
else{
BOOST_ASSERT(get_tree(current_node) == tColorTraits::white());
out_edge_iterator ei, e_end;
if(current_node != m_last_grow_vertex){
m_last_grow_vertex = current_node;
boost::tie(m_last_grow_edge_it, m_last_grow_edge_end) = out_edges(current_node, m_g);
}
for(; m_last_grow_edge_it != m_last_grow_edge_end; ++m_last_grow_edge_it){
edge_descriptor in_edge = get(m_rev_edge_map, *m_last_grow_edge_it);
if(get(m_res_cap_map, in_edge) > 0){ //check if there is capacity left
vertex_descriptor other_node = source(in_edge, m_g);
if(get_tree(other_node) == tColorTraits::gray()){ //it's a free node
set_tree(other_node, tColorTraits::white()); //acquire that node to our search tree
set_edge_to_parent(other_node, in_edge); //set us as parent
add_active_node(other_node); //activate that node
put(m_dist_map, other_node, get(m_dist_map, current_node) + 1); //set its distance
put(m_time_map, other_node, get(m_time_map, current_node));//and time
} else if(get_tree(other_node) == tColorTraits::white()){
if(is_closer_to_terminal(current_node, other_node)){
//we are closer to the sink than its parent is, so we "adopt" him
set_edge_to_parent(other_node, in_edge);
put(m_dist_map, other_node, get(m_dist_map, current_node) + 1);
put(m_time_map, other_node, get(m_time_map, current_node));
}
} else{
BOOST_ASSERT(get_tree(other_node)==tColorTraits::black());
//kewl, found a path from one to the other search tree,
// return the connecting edge in src->sink dir
return std::make_pair(in_edge, true);
}
}
} //for all out-edges
} //sink-tree growing
//all edges of that node are processed, and no more paths were found.
// remove if from the front of the active queue
finish_node(current_node);
} //while active_nodes not empty
//no active nodes anymore and no path found, we're done
return std::make_pair(edge_descriptor(), false);
}
/**
* augments path from s->t and updates residual graph
* source(e, m_g) is the end of the path found in the source-tree
* target(e, m_g) is the beginning of the path found in the sink-tree
* this phase generates orphans on satured edges, if the attached verts are
* from different search-trees orphans are ordered in distance to
* sink/source. first the farest from the source are front_inserted into
* the orphans list, and after that the sink-tree-orphans are
* front_inserted. when going to adoption stage the orphans are popped_front,
* and so we process the nearest verts to the terminals first
*/
void augment(edge_descriptor e) {
BOOST_ASSERT(get_tree(target(e, m_g)) == tColorTraits::white());
BOOST_ASSERT(get_tree(source(e, m_g)) == tColorTraits::black());
BOOST_ASSERT(m_orphans.empty());
const tEdgeVal bottleneck = find_bottleneck(e);
//now we push the found flow through the path
//for each edge we saturate we have to look for the verts that belong to that edge, one of them becomes an orphans
//now process the connecting edge
put(m_res_cap_map, e, get(m_res_cap_map, e) - bottleneck);
BOOST_ASSERT(get(m_res_cap_map, e) >= 0);
put(m_res_cap_map, get(m_rev_edge_map, e), get(m_res_cap_map, get(m_rev_edge_map, e)) + bottleneck);
//now we follow the path back to the source
vertex_descriptor current_node = source(e, m_g);
while(current_node != m_source){
edge_descriptor pred = get_edge_to_parent(current_node);
const tEdgeVal new_pred_cap = get(m_res_cap_map, pred) - bottleneck;
put(m_res_cap_map, pred, new_pred_cap);
BOOST_ASSERT(get(m_res_cap_map, pred) >= 0);
const edge_descriptor pred_rev = get(m_rev_edge_map, pred);
put(m_res_cap_map, pred_rev, get(m_res_cap_map, pred_rev) + bottleneck);
if(new_pred_cap == 0){
set_no_parent(current_node);
m_orphans.push_front(current_node);
}
current_node = source(pred, m_g);
}
//then go forward in the sink-tree
current_node = target(e, m_g);
while(current_node != m_sink){
edge_descriptor pred = get_edge_to_parent(current_node);
const tEdgeVal new_pred_cap = get(m_res_cap_map, pred) - bottleneck;
put(m_res_cap_map, pred, new_pred_cap);
BOOST_ASSERT(get(m_res_cap_map, pred) >= 0);
const edge_descriptor pred_rev = get(m_rev_edge_map, pred);
put(m_res_cap_map, pred_rev, get(m_res_cap_map, pred_rev) + bottleneck);
if(new_pred_cap == 0){
set_no_parent(current_node);
m_orphans.push_front(current_node);
}
current_node = target(pred, m_g);
}
//and add it to the max-flow
m_flow += bottleneck;
}
/**
* returns the bottleneck of a s->t path (end_of_path is last vertex in
* source-tree, begin_of_path is first vertex in sink-tree)
*/
inline tEdgeVal find_bottleneck(edge_descriptor e){
BOOST_USING_STD_MIN();
tEdgeVal minimum_cap = get(m_res_cap_map, e);
vertex_descriptor current_node = source(e, m_g);
//first go back in the source tree
while(current_node != m_source){
edge_descriptor pred = get_edge_to_parent(current_node);
minimum_cap = min BOOST_PREVENT_MACRO_SUBSTITUTION(minimum_cap, get(m_res_cap_map, pred));
current_node = source(pred, m_g);
}
//then go forward in the sink-tree
current_node = target(e, m_g);
while(current_node != m_sink){
edge_descriptor pred = get_edge_to_parent(current_node);
minimum_cap = min BOOST_PREVENT_MACRO_SUBSTITUTION(minimum_cap, get(m_res_cap_map, pred));
current_node = target(pred, m_g);
}
return minimum_cap;
}
/**
* rebuild search trees
* empty the queue of orphans, and find new parents for them or just drop
* them from the search trees
*/
void adopt(){
while(!m_orphans.empty() || !m_child_orphans.empty()){
vertex_descriptor current_node;
if(m_child_orphans.empty()){
//get the next orphan from the main-queue and remove it
current_node = m_orphans.front();
m_orphans.pop_front();
} else{
current_node = m_child_orphans.front();
m_child_orphans.pop();
}
if(get_tree(current_node) == tColorTraits::black()){
//we're in the source-tree
tDistanceVal min_distance = (std::numeric_limits<tDistanceVal>::max)();
edge_descriptor new_parent_edge;
out_edge_iterator ei, e_end;
for(boost::tie(ei, e_end) = out_edges(current_node, m_g); ei != e_end; ++ei){
const edge_descriptor in_edge = get(m_rev_edge_map, *ei);
BOOST_ASSERT(target(in_edge, m_g) == current_node); //we should be the target of this edge
if(get(m_res_cap_map, in_edge) > 0){
vertex_descriptor other_node = source(in_edge, m_g);
if(get_tree(other_node) == tColorTraits::black() && has_source_connect(other_node)){
if(get(m_dist_map, other_node) < min_distance){
min_distance = get(m_dist_map, other_node);
new_parent_edge = in_edge;
}
}
}
}
if(min_distance != (std::numeric_limits<tDistanceVal>::max)()){
set_edge_to_parent(current_node, new_parent_edge);
put(m_dist_map, current_node, min_distance + 1);
put(m_time_map, current_node, m_time);
} else{
put(m_time_map, current_node, 0);
for(boost::tie(ei, e_end) = out_edges(current_node, m_g); ei != e_end; ++ei){
edge_descriptor in_edge = get(m_rev_edge_map, *ei);
vertex_descriptor other_node = source(in_edge, m_g);
if(get_tree(other_node) == tColorTraits::black() && other_node != m_source){
if(get(m_res_cap_map, in_edge) > 0){
add_active_node(other_node);
}
if(has_parent(other_node) && source(get_edge_to_parent(other_node), m_g) == current_node){
//we are the parent of that node
//it has to find a new parent, too
set_no_parent(other_node);
m_child_orphans.push(other_node);
}
}
}
set_tree(current_node, tColorTraits::gray());
} //no parent found
} //source-tree-adoption
else{
//now we should be in the sink-tree, check that...
BOOST_ASSERT(get_tree(current_node) == tColorTraits::white());
out_edge_iterator ei, e_end;
edge_descriptor new_parent_edge;
tDistanceVal min_distance = (std::numeric_limits<tDistanceVal>::max)();
for(boost::tie(ei, e_end) = out_edges(current_node, m_g); ei != e_end; ++ei){
const edge_descriptor out_edge = *ei;
if(get(m_res_cap_map, out_edge) > 0){
const vertex_descriptor other_node = target(out_edge, m_g);
if(get_tree(other_node) == tColorTraits::white() && has_sink_connect(other_node))
if(get(m_dist_map, other_node) < min_distance){
min_distance = get(m_dist_map, other_node);
new_parent_edge = out_edge;
}
}
}
if(min_distance != (std::numeric_limits<tDistanceVal>::max)()){
set_edge_to_parent(current_node, new_parent_edge);
put(m_dist_map, current_node, min_distance + 1);
put(m_time_map, current_node, m_time);
} else{
put(m_time_map, current_node, 0);
for(boost::tie(ei, e_end) = out_edges(current_node, m_g); ei != e_end; ++ei){
const edge_descriptor out_edge = *ei;
const vertex_descriptor other_node = target(out_edge, m_g);
if(get_tree(other_node) == tColorTraits::white() && other_node != m_sink){
if(get(m_res_cap_map, out_edge) > 0){
add_active_node(other_node);
}
if(has_parent(other_node) && target(get_edge_to_parent(other_node), m_g) == current_node){
//we were it's parent, so it has to find a new one, too
set_no_parent(other_node);
m_child_orphans.push(other_node);
}
}
}
set_tree(current_node, tColorTraits::gray());
} //no parent found
} //sink-tree adoption
} //while !orphans.empty()
} //adopt
/**
* return next active vertex if there is one, otherwise a null_vertex
*/
inline vertex_descriptor get_next_active_node(){
while(true){
if(m_active_nodes.empty())
return graph_traits<Graph>::null_vertex();
vertex_descriptor v = m_active_nodes.front();
//if it has no parent, this node can't be active (if its not source or sink)
if(!has_parent(v) && v != m_source && v != m_sink){
m_active_nodes.pop();
put(m_in_active_list_map, v, false);
} else{
BOOST_ASSERT(get_tree(v) == tColorTraits::black() || get_tree(v) == tColorTraits::white());
return v;
}
}
}
/**
* adds v as an active vertex, but only if its not in the list already
*/
inline void add_active_node(vertex_descriptor v){
BOOST_ASSERT(get_tree(v) != tColorTraits::gray());
if(get(m_in_active_list_map, v)){
if (m_last_grow_vertex == v) {
m_last_grow_vertex = graph_traits<Graph>::null_vertex();
}
return;
} else{
put(m_in_active_list_map, v, true);
m_active_nodes.push(v);
}
}
/**
* finish_node removes a node from the front of the active queue (its called in grow phase, if no more paths can be found using this node)
*/
inline void finish_node(vertex_descriptor v){
BOOST_ASSERT(m_active_nodes.front() == v);
m_active_nodes.pop();
put(m_in_active_list_map, v, false);
m_last_grow_vertex = graph_traits<Graph>::null_vertex();
}
/**
* removes a vertex from the queue of active nodes (actually this does nothing,
* but checks if this node has no parent edge, as this is the criteria for
* being no more active)
*/
inline void remove_active_node(vertex_descriptor v){
(void)v; // disable unused parameter warning
BOOST_ASSERT(!has_parent(v));
}
/**
* returns the search tree of v; tColorValue::black() for source tree,
* white() for sink tree, gray() for no tree
*/
inline tColorValue get_tree(vertex_descriptor v) const {
return get(m_tree_map, v);
}
/**
* sets search tree of v; tColorValue::black() for source tree, white()
* for sink tree, gray() for no tree
*/
inline void set_tree(vertex_descriptor v, tColorValue t){
put(m_tree_map, v, t);
}
/**
* returns edge to parent vertex of v;
*/
inline edge_descriptor get_edge_to_parent(vertex_descriptor v) const{
return get(m_pre_map, v);
}
/**
* returns true if the edge stored in m_pre_map[v] is a valid entry
*/
inline bool has_parent(vertex_descriptor v) const{
return get(m_has_parent_map, v);
}
/**
* sets edge to parent vertex of v;
*/
inline void set_edge_to_parent(vertex_descriptor v, edge_descriptor f_edge_to_parent){
BOOST_ASSERT(get(m_res_cap_map, f_edge_to_parent) > 0);
put(m_pre_map, v, f_edge_to_parent);
put(m_has_parent_map, v, true);
}
/**
* removes the edge to parent of v (this is done by invalidating the
* entry an additional map)
*/
inline void set_no_parent(vertex_descriptor v){
put(m_has_parent_map, v, false);
}
/**
- * checks if vertex v has a connect to the sink-vertex (@var m_sink)
+ * checks if vertex v has a connect to the sink-vertex (@p m_sink)
* @param v the vertex which is checked
* @return true if a path to the sink was found, false if not
*/
inline bool has_sink_connect(vertex_descriptor v){
tDistanceVal current_distance = 0;
vertex_descriptor current_vertex = v;
while(true){
if(get(m_time_map, current_vertex) == m_time){
//we found a node which was already checked this round. use it for distance calculations
current_distance += get(m_dist_map, current_vertex);
break;
}
if(current_vertex == m_sink){
put(m_time_map, m_sink, m_time);
break;
}
if(has_parent(current_vertex)){
//it has a parent, so get it
current_vertex = target(get_edge_to_parent(current_vertex), m_g);
++current_distance;
} else{
//no path found
return false;
}
}
current_vertex=v;
while(get(m_time_map, current_vertex) != m_time){
put(m_dist_map, current_vertex, current_distance);
--current_distance;
put(m_time_map, current_vertex, m_time);
current_vertex = target(get_edge_to_parent(current_vertex), m_g);
}
return true;
}
/**
- * checks if vertex v has a connect to the source-vertex (@var m_source)
+ * checks if vertex v has a connect to the source-vertex (@p m_source)
* @param v the vertex which is checked
* @return true if a path to the source was found, false if not
*/
inline bool has_source_connect(vertex_descriptor v){
tDistanceVal current_distance = 0;
vertex_descriptor current_vertex = v;
while(true){
if(get(m_time_map, current_vertex) == m_time){
//we found a node which was already checked this round. use it for distance calculations
current_distance += get(m_dist_map, current_vertex);
break;
}
if(current_vertex == m_source){
put(m_time_map, m_source, m_time);
break;
}
if(has_parent(current_vertex)){
//it has a parent, so get it
current_vertex = source(get_edge_to_parent(current_vertex), m_g);
++current_distance;
} else{
//no path found
return false;
}
}
current_vertex=v;
while(get(m_time_map, current_vertex) != m_time){
put(m_dist_map, current_vertex, current_distance);
--current_distance;
put(m_time_map, current_vertex, m_time);
current_vertex = source(get_edge_to_parent(current_vertex), m_g);
}
return true;
}
/**
* returns true, if p is closer to a terminal than q
*/
inline bool is_closer_to_terminal(vertex_descriptor p, vertex_descriptor q){
//checks the timestamps first, to build no cycles, and after that the real distance
return (get(m_time_map, q) <= get(m_time_map, p) &&
get(m_dist_map, q) > get(m_dist_map, p)+1);
}
////////
// member vars
////////
Graph& m_g;
IndexMap m_index_map;
EdgeCapacityMap m_cap_map;
ResidualCapacityEdgeMap m_res_cap_map;
ReverseEdgeMap m_rev_edge_map;
PredecessorMap m_pre_map; //stores paths found in the growth stage
ColorMap m_tree_map; //maps each vertex into one of the two search tree or none (gray())
DistanceMap m_dist_map; //stores distance to source/sink nodes
vertex_descriptor m_source;
vertex_descriptor m_sink;
tQueue m_active_nodes;
std::vector<bool> m_in_active_list_vec;
iterator_property_map<std::vector<bool>::iterator, IndexMap> m_in_active_list_map;
std::list<vertex_descriptor> m_orphans;
tQueue m_child_orphans; // we use a second queuqe for child orphans, as they are FIFO processed
std::vector<bool> m_has_parent_vec;
iterator_property_map<std::vector<bool>::iterator, IndexMap> m_has_parent_map;
std::vector<long> m_time_vec; //timestamp of each node, used for sink/source-path calculations
iterator_property_map<std::vector<long>::iterator, IndexMap> m_time_map;
tEdgeVal m_flow;
long m_time;
vertex_descriptor m_last_grow_vertex;
out_edge_iterator m_last_grow_edge_it;
out_edge_iterator m_last_grow_edge_end;
};
} //namespace boost::detail
/**
* non-named-parameter version, given everything
* this is the catch all version
*/
template<class Graph,
class CapacityEdgeMap,
class ResidualCapacityEdgeMap,
class ReverseEdgeMap, class PredecessorMap,
class ColorMap,
class DistanceMap,
class IndexMap>
typename property_traits<CapacityEdgeMap>::value_type
boykov_kolmogorov_max_flow(Graph& g,
CapacityEdgeMap cap,
ResidualCapacityEdgeMap res_cap,
ReverseEdgeMap rev_map,
PredecessorMap pre_map,
ColorMap color,
DistanceMap dist,
IndexMap idx,
typename graph_traits<Graph>::vertex_descriptor src,
typename graph_traits<Graph>::vertex_descriptor sink)
{
typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
//as this method is the last one before we instantiate the solver, we do the concept checks here
BOOST_CONCEPT_ASSERT(( VertexListGraphConcept<Graph> )); //to have vertices(), num_vertices(),
BOOST_CONCEPT_ASSERT(( EdgeListGraphConcept<Graph> )); //to have edges()
BOOST_CONCEPT_ASSERT(( IncidenceGraphConcept<Graph> )); //to have source(), target() and out_edges()
BOOST_CONCEPT_ASSERT(( ReadablePropertyMapConcept<CapacityEdgeMap, edge_descriptor> )); //read flow-values from edges
BOOST_CONCEPT_ASSERT(( ReadWritePropertyMapConcept<ResidualCapacityEdgeMap, edge_descriptor> )); //write flow-values to residuals
BOOST_CONCEPT_ASSERT(( ReadablePropertyMapConcept<ReverseEdgeMap, edge_descriptor> )); //read out reverse edges
BOOST_CONCEPT_ASSERT(( ReadWritePropertyMapConcept<PredecessorMap, vertex_descriptor> )); //store predecessor there
BOOST_CONCEPT_ASSERT(( ReadWritePropertyMapConcept<ColorMap, vertex_descriptor> )); //write corresponding tree
BOOST_CONCEPT_ASSERT(( ReadWritePropertyMapConcept<DistanceMap, vertex_descriptor> )); //write distance to source/sink
BOOST_CONCEPT_ASSERT(( ReadablePropertyMapConcept<IndexMap, vertex_descriptor> )); //get index 0...|V|-1
BOOST_ASSERT(num_vertices(g) >= 2 && src != sink);
detail::bk_max_flow<
Graph, CapacityEdgeMap, ResidualCapacityEdgeMap, ReverseEdgeMap,
PredecessorMap, ColorMap, DistanceMap, IndexMap
> algo(g, cap, res_cap, rev_map, pre_map, color, dist, idx, src, sink);
return algo.max_flow();
}
/**
* non-named-parameter version, given capacity, residucal_capacity,
* reverse_edges, and an index map.
*/
template<class Graph,
class CapacityEdgeMap,
class ResidualCapacityEdgeMap,
class ReverseEdgeMap,
class IndexMap>
typename property_traits<CapacityEdgeMap>::value_type
boykov_kolmogorov_max_flow(Graph& g,
CapacityEdgeMap cap,
ResidualCapacityEdgeMap res_cap,
ReverseEdgeMap rev,
IndexMap idx,
typename graph_traits<Graph>::vertex_descriptor src,
typename graph_traits<Graph>::vertex_descriptor sink)
{
typename graph_traits<Graph>::vertices_size_type n_verts = num_vertices(g);
std::vector<typename graph_traits<Graph>::edge_descriptor> predecessor_vec(n_verts);
std::vector<default_color_type> color_vec(n_verts);
std::vector<typename graph_traits<Graph>::vertices_size_type> distance_vec(n_verts);
return
boykov_kolmogorov_max_flow(
g, cap, res_cap, rev,
make_iterator_property_map(predecessor_vec.begin(), idx),
make_iterator_property_map(color_vec.begin(), idx),
make_iterator_property_map(distance_vec.begin(), idx),
idx, src, sink);
}
/**
* non-named-parameter version, some given: capacity, residual_capacity,
* reverse_edges, color_map and an index map. Use this if you are interested in
* the minimum cut, as the color map provides that info.
*/
template<class Graph,
class CapacityEdgeMap,
class ResidualCapacityEdgeMap,
class ReverseEdgeMap,
class ColorMap,
class IndexMap>
typename property_traits<CapacityEdgeMap>::value_type
boykov_kolmogorov_max_flow(Graph& g,
CapacityEdgeMap cap,
ResidualCapacityEdgeMap res_cap,
ReverseEdgeMap rev,
ColorMap color,
IndexMap idx,
typename graph_traits<Graph>::vertex_descriptor src,
typename graph_traits<Graph>::vertex_descriptor sink)
{
typename graph_traits<Graph>::vertices_size_type n_verts = num_vertices(g);
std::vector<typename graph_traits<Graph>::edge_descriptor> predecessor_vec(n_verts);
std::vector<typename graph_traits<Graph>::vertices_size_type> distance_vec(n_verts);
return
boykov_kolmogorov_max_flow(
g, cap, res_cap, rev,
make_iterator_property_map(predecessor_vec.begin(), idx),
color,
make_iterator_property_map(distance_vec.begin(), idx),
idx, src, sink);
}
/**
* named-parameter version, some given
*/
template<class Graph, class P, class T, class R>
typename property_traits<typename property_map<Graph, edge_capacity_t>::const_type>::value_type
boykov_kolmogorov_max_flow(Graph& g,
typename graph_traits<Graph>::vertex_descriptor src,
typename graph_traits<Graph>::vertex_descriptor sink,
const bgl_named_params<P, T, R>& params)
{
return
boykov_kolmogorov_max_flow(
g,
choose_const_pmap(get_param(params, edge_capacity), g, edge_capacity),
choose_pmap(get_param(params, edge_residual_capacity), g, edge_residual_capacity),
choose_const_pmap(get_param(params, edge_reverse), g, edge_reverse),
choose_pmap(get_param(params, vertex_predecessor), g, vertex_predecessor),
choose_pmap(get_param(params, vertex_color), g, vertex_color),
choose_pmap(get_param(params, vertex_distance), g, vertex_distance),
choose_const_pmap(get_param(params, vertex_index), g, vertex_index),
src, sink);
}
/**
* named-parameter version, none given
*/
template<class Graph>
typename property_traits<typename property_map<Graph, edge_capacity_t>::const_type>::value_type
boykov_kolmogorov_max_flow(Graph& g,
typename graph_traits<Graph>::vertex_descriptor src,
typename graph_traits<Graph>::vertex_descriptor sink)
{
bgl_named_params<int, buffer_param_t> params(0); // bogus empty param
return boykov_kolmogorov_max_flow(g, src, sink, params);
}
} // namespace boost
#endif // BOOST_BOYKOV_KOLMOGOROV_MAX_FLOW_HPP
diff --git a/libs/image/tiles3/kis_tiled_data_manager.h b/libs/image/tiles3/kis_tiled_data_manager.h
index b6f8384d17..0ddb815456 100644
--- a/libs/image/tiles3/kis_tiled_data_manager.h
+++ b/libs/image/tiles3/kis_tiled_data_manager.h
@@ -1,420 +1,430 @@
/*
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
* (c) 2009 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_TILEDDATAMANAGER_H_
#define KIS_TILEDDATAMANAGER_H_
#include <QtGlobal>
#include <QVector>
#include <QRegion>
#include <kis_shared.h>
#include <kis_shared_ptr.h>
#include "config-hash-table-implementaion.h"
//#include "kis_debug.h"
#include "kritaimage_export.h"
#ifdef USE_LOCK_FREE_HASH_TABLE
#include "kis_tile_hash_table2.h"
#else
#include "kis_tile_hash_table.h"
#endif // USE_LOCK_FREE_HASH_TABLE
#include "kis_memento_manager.h"
#include "kis_memento.h"
#include "KisTiledExtentManager.h"
class KisTiledDataManager;
typedef KisSharedPtr<KisTiledDataManager> KisTiledDataManagerSP;
class KisTiledIterator;
class KisTiledRandomAccessor;
class KisPaintDeviceWriter;
class QIODevice;
/**
* KisTiledDataManager implements the interface that KisDataManager defines
*
* The interface definition is enforced by KisDataManager calling all the methods
* which must also be defined in KisTiledDataManager. It is not allowed to change the interface
* as other datamangers may also rely on the same interface.
*
* * Storing undo/redo data
* * Offering ordered and unordered iterators over rects of pixels
* * (eventually) efficiently loading and saving data in a format
* that may allow deferred loading.
*
* A datamanager knows nothing about the type of pixel data except
* how many quint8's a single pixel takes.
*/
class KRITAIMAGE_EXPORT KisTiledDataManager : public KisShared
{
private:
static const qint32 LEGACY_VERSION = 1;
static const qint32 CURRENT_VERSION = 2;
protected:
/*FIXME:*/
public:
KisTiledDataManager(quint32 pixelSize, const quint8 *defPixel);
virtual ~KisTiledDataManager();
KisTiledDataManager(const KisTiledDataManager &dm);
KisTiledDataManager & operator=(const KisTiledDataManager &dm);
protected:
// Allow the baseclass of iterators access to the interior
// derived iterator classes must go through KisTiledIterator
friend class KisTiledIterator;
friend class KisBaseIterator;
friend class KisTiledRandomAccessor;
friend class KisRandomAccessor2;
friend class KisStressJob;
public:
void setDefaultPixel(const quint8 *defPixel);
const quint8 *defaultPixel() const {
return m_defaultPixel;
}
/**
* Every iterator fetches both types of tiles all the time: old and new.
* For projection devices these tiles are **always** the same, but doing
* two distinct calls makes double pressure on the read-write lock in the
* hash table.
*
* Merging two calls into one allows us to avoid additional tile fetch from
* the hash table and therefore reduce waiting time.
*/
inline void getTilesPair(qint32 col, qint32 row, bool writable, KisTileSP *tile, KisTileSP *oldTile) {
*tile = getTile(col, row, writable);
bool unused;
*oldTile = m_mementoManager->getCommitedTile(col, row, unused);
if (!*oldTile) {
*oldTile = *tile;
}
}
inline KisTileSP getTile(qint32 col, qint32 row, bool writable) {
if (writable) {
bool newTile;
KisTileSP tile = m_hashTable->getTileLazy(col, row, newTile);
if (newTile) {
m_extentManager.notifyTileAdded(col, row);
}
return tile;
} else {
bool unused;
return m_hashTable->getReadOnlyTileLazy(col, row, unused);
}
}
inline KisTileSP getReadOnlyTileLazy(qint32 col, qint32 row, bool &existingTile) {
return m_hashTable->getReadOnlyTileLazy(col, row, existingTile);
}
inline KisTileSP getOldTile(qint32 col, qint32 row, bool &existingTile) {
KisTileSP tile = m_mementoManager->getCommitedTile(col, row, existingTile);
return tile ? tile : getReadOnlyTileLazy(col, row, existingTile);
}
inline KisTileSP getOldTile(qint32 col, qint32 row) {
bool unused;
return getOldTile(col, row, unused);
}
KisMementoSP getMemento() {
QWriteLocker locker(&m_lock);
KisMementoSP memento = m_mementoManager->getMemento();
memento->saveOldDefaultPixel(m_defaultPixel, m_pixelSize);
return memento;
}
/**
* Finishes having already started transaction
*/
void commit() {
QWriteLocker locker(&m_lock);
KisMementoSP memento = m_mementoManager->currentMemento();
if(memento) {
memento->saveNewDefaultPixel(m_defaultPixel, m_pixelSize);
}
m_mementoManager->commit();
}
void rollback(KisMementoSP memento) {
commit();
QWriteLocker locker(&m_lock);
m_mementoManager->rollback(m_hashTable);
const quint8 *defaultPixel = memento->oldDefaultPixel();
if(memcmp(m_defaultPixel, defaultPixel, m_pixelSize)) {
setDefaultPixelImpl(defaultPixel);
}
recalculateExtent();
}
void rollforward(KisMementoSP memento) {
commit();
QWriteLocker locker(&m_lock);
m_mementoManager->rollforward(m_hashTable);
const quint8 *defaultPixel = memento->newDefaultPixel();
if(memcmp(m_defaultPixel, defaultPixel, m_pixelSize)) {
setDefaultPixelImpl(defaultPixel);
}
recalculateExtent();
}
bool hasCurrentMemento() const {
return m_mementoManager->hasCurrentMemento();
//return true;
}
/**
* Removes all the history that preceds the revision
* pointed by oldestMemento. That is after calling to
* purgeHistory(someMemento) you won't be able to do
* rollback(someMemento) anymore.
*/
void purgeHistory(KisMementoSP oldestMemento) {
QWriteLocker locker(&m_lock);
m_mementoManager->purgeHistory(oldestMemento);
}
static void releaseInternalPools();
protected:
/**
* Reads and writes the tiles
*/
bool write(KisPaintDeviceWriter &store);
bool read(QIODevice *stream);
void purge(const QRect& area);
inline quint32 pixelSize() const {
return m_pixelSize;
}
/* FIXME:*/
public:
void extent(qint32 &x, qint32 &y, qint32 &w, qint32 &h) const;
void setExtent(qint32 x, qint32 y, qint32 w, qint32 h);
QRect extent() const;
void setExtent(QRect newRect);
QRegion region() const;
void clear(QRect clearRect, quint8 clearValue);
void clear(QRect clearRect, const quint8 *clearPixel);
void clear(qint32 x, qint32 y, qint32 w, qint32 h, quint8 clearValue);
void clear(qint32 x, qint32 y, qint32 w, qint32 h, const quint8 *clearPixel);
void clear();
/**
* Clones rect from another datamanager. The cloned area will be
* shared between both datamanagers as much as possible using
* copy-on-write. Parts of the rect that cannot be shared
* (cross tiles) are deep-copied,
*/
void bitBlt(KisTiledDataManager *srcDM, const QRect &rect);
/**
* The same as \ref bitBlt(), but reads old data
*/
void bitBltOldData(KisTiledDataManager *srcDM, const QRect &rect);
/**
* Clones rect from another datamanager in a rough and fast way.
* All the tiles touched by rect will be shared, between both
* managers, that means it will copy a bigger area than was
* requested. This method is supposed to be used for bitBlt'ing
* into temporary paint devices.
*/
void bitBltRough(KisTiledDataManager *srcDM, const QRect &rect);
/**
* The same as \ref bitBltRough(), but reads old data
*/
void bitBltRoughOldData(KisTiledDataManager *srcDM, const QRect &rect);
/**
* write the specified data to x, y. There is no checking on pixelSize!
*/
void setPixel(qint32 x, qint32 y, const quint8 * data);
/**
* Copy the bytes in the specified rect to a vector. The caller is responsible
* for managing the vector.
*
+ * \param bytes the bytes
+ * \param x x of top left corner
+ * \param y y of top left corner
+ * \param w width
+ * \param h height
* \param dataRowStride is the step (in bytes) which should be
* added to \p bytes pointer to get to the
* next row
*/
void readBytes(quint8 * bytes,
qint32 x, qint32 y,
qint32 w, qint32 h,
qint32 dataRowStride = -1) const;
/**
* Copy the bytes in the vector to the specified rect. If there are bytes left
* in the vector after filling the rect, they will be ignored. If there are
* not enough bytes, the rest of the rect will be filled with the default value
* given (by default, 0);
*
+ * \param bytes the bytes
+ * \param x x of top left corner
+ * \param y y of top left corner
+ * \param w width
+ * \param h height
* \param dataRowStride is the step (in bytes) which should be
* added to \p bytes pointer to get to the
* next row
*/
void writeBytes(const quint8 * bytes,
qint32 x, qint32 y,
qint32 w, qint32 h,
qint32 dataRowStride = -1);
/**
* Copy the bytes in the paint device into a vector of arrays of bytes,
* where the number of arrays is the number of channels in the
* paint device. If the specified area is larger than the paint
* device's extent, the default pixel will be read.
*/
QVector<quint8*> readPlanarBytes(QVector<qint32> channelsizes, qint32 x, qint32 y, qint32 w, qint32 h) const;
/**
* Write the data in the separate arrays to the channels. If there
* are less vectors than channels, the remaining channels will not
* be copied. If any of the arrays points to 0, the channel in
* that location will not be touched. If the specified area is
* larger than the paint device, the paint device will be
* extended. There are no guards: if the area covers more pixels
* than there are bytes in the arrays, krita will happily fill
* your paint device with areas of memory you never wanted to be
* read. Krita may also crash.
*/
void writePlanarBytes(QVector<quint8*> planes, QVector<qint32> channelsizes, qint32 x, qint32 y, qint32 w, qint32 h);
/**
* Get the number of contiguous columns starting at x, valid for all values
* of y between minY and maxY.
*/
qint32 numContiguousColumns(qint32 x, qint32 minY, qint32 maxY) const;
/**
* Get the number of contiguous rows starting at y, valid for all values
* of x between minX and maxX.
*/
qint32 numContiguousRows(qint32 y, qint32 minX, qint32 maxX) const;
/**
* Get the row stride at pixel (x, y). This is the number of bytes to add to a
* pointer to pixel (x, y) to access (x, y + 1).
*/
qint32 rowStride(qint32 x, qint32 y) const;
private:
KisTileHashTable *m_hashTable;
KisMementoManager *m_mementoManager;
quint8* m_defaultPixel;
qint32 m_pixelSize;
KisTiledExtentManager m_extentManager;
mutable QReadWriteLock m_lock;
private:
// Allow compression routines to calculate (col,row) coordinates
// and pixel size
friend class KisAbstractTileCompressor;
friend class KisTileDataWrapper;
qint32 xToCol(qint32 x) const;
qint32 yToRow(qint32 y) const;
private:
void setDefaultPixelImpl(const quint8 *defPixel);
bool writeTilesHeader(KisPaintDeviceWriter &store, quint32 numTiles);
bool processTilesHeader(QIODevice *stream, quint32 &numTiles);
qint32 divideRoundDown(qint32 x, const qint32 y) const;
void recalculateExtent();
quint8* duplicatePixel(qint32 num, const quint8 *pixel);
template<bool useOldSrcData>
void bitBltImpl(KisTiledDataManager *srcDM, const QRect &rect);
template<bool useOldSrcData>
void bitBltRoughImpl(KisTiledDataManager *srcDM, const QRect &rect);
void writeBytesBody(const quint8 *data,
qint32 x, qint32 y,
qint32 width, qint32 height,
qint32 dataRowStride = -1);
void readBytesBody(quint8 *data,
qint32 x, qint32 y,
qint32 width, qint32 height,
qint32 dataRowStride = -1) const;
template <bool allChannelsPresent>
void writePlanarBytesBody(QVector<quint8*> planes,
QVector<qint32> channelsizes,
qint32 x, qint32 y, qint32 w, qint32 h);
QVector<quint8*> readPlanarBytesBody(QVector<qint32> channelsizes,
qint32 x, qint32 y,
qint32 w, qint32 h) const;
public:
void debugPrintInfo() {
m_mementoManager->debugPrintInfo();
}
};
inline qint32 KisTiledDataManager::divideRoundDown(qint32 x, const qint32 y) const
{
/**
* Equivalent to the following:
* -(( -x + (y-1) ) / y)
*/
return x >= 0 ?
x / y :
-(((-x - 1) / y) + 1);
}
inline qint32 KisTiledDataManager::xToCol(qint32 x) const
{
return divideRoundDown(x, KisTileData::WIDTH);
}
inline qint32 KisTiledDataManager::yToRow(qint32 y) const
{
return divideRoundDown(y, KisTileData::HEIGHT);
}
// during development the following line helps to check the interface is correct
// it should be safe to keep it here even during normal compilation
//#include "kis_datamanager.h"
#endif // KIS_TILEDDATAMANAGER_H_
diff --git a/libs/image/tiles3/swap/kis_abstract_compression.h b/libs/image/tiles3/swap/kis_abstract_compression.h
index 31e7ca0a73..c34eb7ed37 100644
--- a/libs/image/tiles3/swap/kis_abstract_compression.h
+++ b/libs/image/tiles3/swap/kis_abstract_compression.h
@@ -1,88 +1,94 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_ABSTRACT_COMPRESSION_H
#define __KIS_ABSTRACT_COMPRESSION_H
#include "kritaimage_export.h"
#include <QtGlobal>
/**
* Base class for compression operations
*/
class KRITAIMAGE_EXPORT KisAbstractCompression
{
public:
KisAbstractCompression();
virtual ~KisAbstractCompression();
/**
- * Compresses \a input buffer into \a output buffer.
+ * Compresses \p input buffer into \p output buffer.
* WARNING: Be careful, output buffer must be at least
* outputBufferSize(inputLength) size!
+ * \param input the input
+ * \param inputLength the input length
+ * \param output the output
* \param outputLength is not used!
* \return number of bytes written to the output buffer
* and 0 if error occurred.
*
* \see outputBufferSize()
*/
virtual qint32 compress(const quint8* input, qint32 inputLength, quint8* output, qint32 outputLength) = 0;
/**
- * Decompresses \a input buffer into \a output buffer.
+ * Decompresses \p input buffer into \p output buffer.
* WARNING: output buffer must be able to fit the input data
+ * \param input the input
+ * \param inputLength the input length
+ * \param output the output
* \param outputLength is not used!
* \return number of bytes written to the output buffer
* and 0 if error occurred.
*/
virtual qint32 decompress(const quint8* input, qint32 inputLength, quint8* output, qint32 outputLength) = 0;
/**
* Returns minimal allowed size of output buffer for compression
*/
virtual qint32 outputBufferSize(qint32 dataSize) = 0;
/**
* Some algorithms may decide to optimize them work depending on
* the usual size of the data.
* Default implementation of KisAbstractCompression class does nothing.
*/
virtual void adjustForDataSize(qint32 dataSize);
public:
/**
* Additional interface for jumbling color channels order
*/
/**
* e.g. RGBARGBARGBA -> RRRGGGBBBAAA
* NOTE: performs mixing of bytes, not channels!
*/
static void linearizeColors(quint8 *input, quint8 *output,
qint32 dataSize, qint32 pixelSize);
/**
* e.g. RRRGGGBBBAAA -> RGBARGBARGBA
* NOTE: performs mixing of bytes, not channels!
*/
static void delinearizeColors(quint8 *input, quint8 *output,
qint32 dataSize, qint32 pixelSize);
};
#endif /* __KIS_ABSTRACT_COMPRESSION_H */
diff --git a/libs/image/tiles3/swap/kis_abstract_tile_compressor.h b/libs/image/tiles3/swap/kis_abstract_tile_compressor.h
index d5cbdfb6cd..1acd1eb847 100644
--- a/libs/image/tiles3/swap/kis_abstract_tile_compressor.h
+++ b/libs/image/tiles3/swap/kis_abstract_tile_compressor.h
@@ -1,103 +1,108 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_ABSTRACT_TILE_COMPRESSOR_H
#define __KIS_ABSTRACT_TILE_COMPRESSOR_H
#include "kritaimage_export.h"
#include "../kis_tile.h"
#include "../kis_tiled_data_manager.h"
class KisPaintDeviceWriter;
/**
* Base class for compressing a tile and wrapping it with a header
*/
class KisAbstractTileCompressor;
typedef KisSharedPtr<KisAbstractTileCompressor> KisAbstractTileCompressorSP;
class KRITAIMAGE_EXPORT KisAbstractTileCompressor : public KisShared
{
public:
KisAbstractTileCompressor();
virtual ~KisAbstractTileCompressor();
public:
/**
* Compresses the \a tile and writes it into the \a stream.
* Used by datamanager in load/save routines
*
* \see compressTile()
*/
virtual bool writeTile(KisTileSP tile, KisPaintDeviceWriter &store) = 0;
/**
* Decompresses the \a tile from the \a stream.
* Used by datamanager in load/save routines
*
* \see decompressTile()
*/
virtual bool readTile(QIODevice *stream, KisTiledDataManager *dm) = 0;
/**
- * Compresses a \a tileData and writes it into the \a buffer.
+ * Compresses a \p tileData and writes it into the \p buffer.
* The buffer must be at least tileDataBufferSize() bytes long.
* Actual number of bytes written is returned using out-parameter
- * \a bytesWritten
+ * \p bytesWritten
*
* \param tileData an existing tile data. It should be created
* and acquired by the caller.
+ * \param buffer the buffer
+ * \param bufferSize the size of the buffer
+ * \param bytesWritten the number of written bytes
*
* \see tileDataBufferSize()
*/
virtual void compressTileData(KisTileData *tileData,quint8 *buffer,
qint32 bufferSize, qint32 &bytesWritten) = 0;
/**
- * Decompresses a \a tileData from a given \a buffer.
+ * Decompresses a \p tileData from a given \p buffer.
*
- * \param tileData an existing tile data wrere the result
+ * \param buffer the buffer
+ * \param bufferSize the size of the buffer
+ * \param tileData an existing tile data where the result
* will be written to. It should be created and acquired
* by the caller.
*
*/
virtual bool decompressTileData(quint8 *buffer, qint32 bufferSize,
KisTileData *tileData) = 0;
/**
* Return the number of bytes needed for compressing one tile
*/
virtual qint32 tileDataBufferSize(KisTileData *tileData) = 0;
protected:
inline qint32 xToCol(KisTiledDataManager *dm, qint32 x) {
return dm->xToCol(x);
}
inline qint32 yToRow(KisTiledDataManager *dm, qint32 y) {
return dm->yToRow(y);
}
inline qint32 pixelSize(KisTiledDataManager *dm) {
return dm->pixelSize();
}
};
#endif /* __KIS_ABSTRACT_TILE_COMPRESSOR_H */
diff --git a/libs/image/tiles3/swap/kis_memory_window.h b/libs/image/tiles3/swap/kis_memory_window.h
index 8e4fa29190..4f54a4e3bb 100644
--- a/libs/image/tiles3/swap/kis_memory_window.h
+++ b/libs/image/tiles3/swap/kis_memory_window.h
@@ -1,82 +1,83 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_MEMORY_WINDOW_H
#define __KIS_MEMORY_WINDOW_H
#include <QTemporaryFile>
#include "kis_chunk_allocator.h"
#define DEFAULT_WINDOW_SIZE (16*MiB)
class KRITAIMAGE_EXPORT KisMemoryWindow
{
public:
/**
- * @param swapDir. If the dir doesn't exist, it'll be created, if it's empty QDir::tempPath will be used.
+ * @param swapDir If the dir doesn't exist, it'll be created, if it's empty QDir::tempPath will be used.
+ * @param writeWindowSize write window size.
*/
KisMemoryWindow(const QString &swapDir, quint64 writeWindowSize = DEFAULT_WINDOW_SIZE);
~KisMemoryWindow();
inline quint8* getReadChunkPtr(KisChunk readChunk) {
return getReadChunkPtr(readChunk.data());
}
inline quint8* getWriteChunkPtr(KisChunk writeChunk) {
return getWriteChunkPtr(writeChunk.data());
}
quint8* getReadChunkPtr(const KisChunkData &readChunk);
quint8* getWriteChunkPtr(const KisChunkData &writeChunk);
private:
struct MappingWindow {
MappingWindow(quint64 _defaultSize)
: chunk(0,0),
window(0),
defaultSize(_defaultSize)
{
}
quint8* calculatePointer(const KisChunkData &other) const {
return window + other.m_begin - chunk.m_begin;
}
KisChunkData chunk;
quint8 *window;
const quint64 defaultSize;
};
private:
bool adjustWindow(const KisChunkData &requestedChunk,
MappingWindow *adjustingWindow,
MappingWindow *otherWindow);
private:
QTemporaryFile m_file;
bool m_valid;
MappingWindow m_readWindowEx;
MappingWindow m_writeWindowEx;
};
#endif /* __KIS_MEMORY_WINDOW_H */
diff --git a/libs/koplugin/KisMimeDatabase.cpp b/libs/koplugin/KisMimeDatabase.cpp
index e3fd692def..97d6a160bf 100644
--- a/libs/koplugin/KisMimeDatabase.cpp
+++ b/libs/koplugin/KisMimeDatabase.cpp
@@ -1,292 +1,287 @@
/*
* Copyright (c) 2016 Boudewijn Rempt <boud@valdyas.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisMimeDatabase.h"
#include <QMimeDatabase>
#include <QMimeType>
#include <QFileInfo>
#include <KritaPluginDebug.h>
#include <klocalizedstring.h>
QList<KisMimeDatabase::KisMimeType> KisMimeDatabase::s_mimeDatabase;
QString KisMimeDatabase::mimeTypeForFile(const QString &file, bool checkExistingFiles)
{
fillMimeData();
QFileInfo fi(file);
QString suffix = fi.suffix().toLower();
Q_FOREACH(const KisMimeDatabase::KisMimeType &mimeType, s_mimeDatabase) {
if (mimeType.suffixes.contains(suffix)) {
debugPlugin << "mimeTypeForFile(). KisMimeDatabase returned" << mimeType.mimeType << "for" << file;
return mimeType.mimeType;
}
}
QMimeDatabase db;
QMimeType mime;
if (checkExistingFiles && fi.size() > 0) {
mime = db.mimeTypeForFile(file, QMimeDatabase::MatchContent);
if (mime.name() != "application/octet-stream" && mime.name() != "application/zip") {
debugPlugin << "mimeTypeForFile(). QMimeDatabase returned" << mime.name() << "for" << file;
return mime.name();
}
}
mime = db.mimeTypeForFile(file);
if (mime.name() != "application/octet-stream") {
debugPlugin << "mimeTypeForFile(). QMimeDatabase returned" << mime.name() << "for" << file;
return mime.name();
}
return "";
}
QString KisMimeDatabase::mimeTypeForSuffix(const QString &suffix)
{
fillMimeData();
QMimeDatabase db;
QString s = suffix.toLower();
Q_FOREACH(const KisMimeDatabase::KisMimeType &mimeType, s_mimeDatabase) {
if (mimeType.suffixes.contains(s)) {
debugPlugin << "mimeTypeForSuffix(). KisMimeDatabase returned" << mimeType.mimeType << "for" << s;
return mimeType.mimeType;
}
}
// make the file look like a file so Qt would recognize it
s = "file." + s;
return mimeTypeForFile(s);
}
QString KisMimeDatabase::mimeTypeForData(const QByteArray ba)
{
QMimeDatabase db;
QMimeType mtp = db.mimeTypeForData(ba);
debugPlugin << "mimeTypeForData(). QMimeDatabase returned" << mtp.name();
return mtp.name();
}
QString KisMimeDatabase::descriptionForMimeType(const QString &mimeType)
{
fillMimeData();
Q_FOREACH(const KisMimeDatabase::KisMimeType &m, s_mimeDatabase) {
if (m.mimeType == mimeType) {
debugPlugin << "descriptionForMimeType. KisMimeDatabase returned" << m.description << "for" << mimeType;
return m.description;
}
}
QMimeDatabase db;
QMimeType mime = db.mimeTypeForName(mimeType);
if (mime.name() != "application/octet-stream") {
debugPlugin << "descriptionForMimeType. QMimeDatabase returned" << mime.comment() << "for" << mimeType;
return mime.comment();
}
return mimeType;
}
QStringList KisMimeDatabase::suffixesForMimeType(const QString &mimeType)
{
fillMimeData();
Q_FOREACH(const KisMimeDatabase::KisMimeType &m, s_mimeDatabase) {
if (m.mimeType == mimeType) {
debugPlugin << "suffixesForMimeType. KisMimeDatabase returned" << m.suffixes;
return m.suffixes;
}
}
QMimeDatabase db;
QMimeType mime = db.mimeTypeForName(mimeType);
if (mime.name() != "application/octet-stream" && !mime.suffixes().isEmpty()) {
QString preferredSuffix = mime.preferredSuffix();
if (mimeType == "image/x-tga") {
preferredSuffix = "tga";
}
if (mimeType == "image/jpeg") {
preferredSuffix = "jpg";
}
QStringList suffixes = mime.suffixes();
if (preferredSuffix != suffixes.first()) {
suffixes.removeAll(preferredSuffix);
suffixes.prepend(preferredSuffix);
}
debugPlugin << "suffixesForMimeType. QMimeDatabase returned" << suffixes;
return suffixes;
}
return QStringList();
}
QString KisMimeDatabase::iconNameForMimeType(const QString &mimeType)
{
QMimeDatabase db;
QMimeType mime = db.mimeTypeForName(mimeType);
debugPlugin << "iconNameForMimeType" << mime.iconName();
return mime.iconName();
}
void KisMimeDatabase::fillMimeData()
{
// This should come from the import/export plugins, but the json files aren't translated,
// which is bad for the description field
if (s_mimeDatabase.isEmpty()) {
KisMimeType mimeType;
mimeType.mimeType = "image/x-gimp-brush";
mimeType.description = i18nc("description of a file type", "Gimp Brush");
mimeType.suffixes = QStringList() << "gbr" << "vbr";
s_mimeDatabase << mimeType;
mimeType.mimeType = "image/x-gimp-brush-animated";
mimeType.description = i18nc("description of a file type", "Gimp Image Hose Brush");
mimeType.suffixes = QStringList() << "gih";
s_mimeDatabase << mimeType;
mimeType.mimeType = "image/x-adobe-brushlibrary";
mimeType.description = i18nc("description of a file type", "Adobe Brush Library");
mimeType.suffixes = QStringList() << "abr";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-krita-paintoppreset";
mimeType.description = i18nc("description of a file type", "Krita Brush Preset");
mimeType.suffixes = QStringList() << "kpp";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-krita-assistant";
mimeType.description = i18nc("description of a file type", "Krita Assistant");
mimeType.suffixes = QStringList() << "paintingassistant";
s_mimeDatabase << mimeType;
mimeType.mimeType = "image/x-r32";
mimeType.description = i18nc("description of a file type", "R32 Heightmap");
mimeType.suffixes = QStringList() << "r32";
s_mimeDatabase << mimeType;
mimeType.mimeType = "image/x-r16";
mimeType.description = i18nc("description of a file type", "R16 Heightmap");
mimeType.suffixes = QStringList() << "r16";
s_mimeDatabase << mimeType;
mimeType.mimeType = "image/x-r8";
mimeType.description = i18nc("description of a file type", "R8 Heightmap");
mimeType.suffixes = QStringList() << "r8";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-spriter";
mimeType.description = i18nc("description of a file type", "Spriter SCML");
mimeType.suffixes = QStringList() << "scml";
s_mimeDatabase << mimeType;
mimeType.mimeType = "image/x-svm";
mimeType.description = i18nc("description of a file type", "Starview Metafile");
mimeType.suffixes = QStringList() << "svm";
s_mimeDatabase << mimeType;
mimeType.mimeType = "image/openraster";
mimeType.description = i18nc("description of a file type", "OpenRaster Image");
mimeType.suffixes = QStringList() << "ora";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-photoshop-style-library";
mimeType.description = i18nc("description of a file type", "Photoshop Layer Style Library");
mimeType.suffixes = QStringList() << "asl";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-gimp-color-palette";
mimeType.description = i18nc("description of a file type", "Color Palette");
mimeType.suffixes = QStringList() << "gpl" << "pal" << "act" << "aco" << "colors" << "xml" << "sbz";
s_mimeDatabase << mimeType;
mimeType.mimeType = "krita/x-colorset";
mimeType.description = i18nc("description of a file type", "Krita Color Palette");
mimeType.suffixes = QStringList() << "kpl";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-opencolorio-configuration";
mimeType.description = i18nc("description of a file type", "OpenColorIO Configuration");
mimeType.suffixes = QStringList() << "ocio";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-gimp-gradient";
mimeType.description = i18nc("description of a file type", "GIMP Gradients");
mimeType.suffixes = QStringList() << "ggr";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-gimp-pattern";
mimeType.description = i18nc("description of a file type", "GIMP Patterns");
mimeType.suffixes = QStringList() << "pat";
s_mimeDatabase << mimeType;
- mimeType.mimeType = "application/x-karbon-gradient";
- mimeType.description = i18nc("description of a file type", "Karbon Gradients");
- mimeType.suffixes = QStringList() << "kgr";
- s_mimeDatabase << mimeType;
-
mimeType.mimeType = "application/x-krita-bundle";
mimeType.description = i18nc("description of a file type", "Krita Resource Bundle");
mimeType.suffixes = QStringList() << "bundle";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-krita-workspace";
mimeType.description = i18nc("description of a file type", "Krita Workspace");
mimeType.suffixes = QStringList() << "kws";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-krita-taskset";
mimeType.description = i18nc("description of a file type", "Krita Taskset");
mimeType.suffixes = QStringList() << "kts";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-krita-reference-images";
mimeType.description = i18nc("description of a file type", "Krita Reference Image Collection");
mimeType.suffixes = QStringList() << "krf";
s_mimeDatabase << mimeType;
mimeType.mimeType = "image/x-krita-raw";
mimeType.description = i18nc("description of a file type", "Camera Raw Files");
mimeType.suffixes = QStringList() << "bay" << "bmq" << "cr2" << "crw" << "cs1" << "dc2" << "dcr" << "dng" << "erf" << "fff" << "hdr" << "k25" << "kdc" << "mdc" << "mos" << "mrw" << "nef" << "orf" << "pef" << "pxn" << "raf" << "raw" << "rdc" << "sr2" << "srf" << "x3f" << "arw" << "3fr" << "cine" << "ia" << "kc2" << "mef" << "nrw" << "qtk" << "rw2" << "sti" << "rwl" << "srw";
s_mimeDatabase << mimeType;
mimeType.mimeType = "application/x-extension-exr";
mimeType.description = i18nc("description of a file type", "OpenEXR (Extended)");
mimeType.suffixes = QStringList() << "exr";
s_mimeDatabase << mimeType;
mimeType.mimeType = "image/x-psb";
mimeType.description = i18nc("description of a file type", "Photoshop Image (Large)");
mimeType.suffixes = QStringList() << "psb";
s_mimeDatabase << mimeType;
mimeType.mimeType = "image/heic";
mimeType.description = i18nc("description of a file type", "HEIC/HEIF Image");
mimeType.suffixes = QStringList() << "heic" << "heif";
s_mimeDatabase << mimeType;
debugPlugin << "Filled mimedatabase with" << s_mimeDatabase.count() << "special mimetypes";
}
}
diff --git a/libs/libkis/Canvas.h b/libs/libkis/Canvas.h
index e6329e2ab1..e143e6366d 100644
--- a/libs/libkis/Canvas.h
+++ b/libs/libkis/Canvas.h
@@ -1,126 +1,126 @@
/*
* Copyright (c) 2016 Boudewijn Rempt <boud@valdyas.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef LIBKIS_CANVAS_H
#define LIBKIS_CANVAS_H
#include <QObject>
#include "kritalibkis_export.h"
#include "libkis.h"
class KoCanvasBase;
class KisDisplayColorConverter;
/**
* Canvas wraps the canvas inside a view on an image/document.
* It is responsible for the view parameters of the document:
* zoom, rotation, mirror, wraparound and instant preview.
*/
class KRITALIBKIS_EXPORT Canvas : public QObject
{
Q_OBJECT
public:
explicit Canvas(KoCanvasBase *canvas, QObject *parent = 0);
~Canvas() override;
bool operator==(const Canvas &other) const;
bool operator!=(const Canvas &other) const;
public Q_SLOTS:
/**
* @return the current zoomlevel. 1.0 is 100%.
*/
qreal zoomLevel() const;
/**
- * @brief setZoomLevel set the zoomlevel to the given @param value. 1.0 is 100%.
+ * @brief setZoomLevel set the zoomlevel to the given @p value. 1.0 is 100%.
*/
void setZoomLevel(qreal value);
/**
* @brief resetZoom set the zoomlevel to 100%
*/
void resetZoom();
/**
* @return the rotation of the canvas in degrees.
*/
qreal rotation() const;
/**
* @brief setRotation set the rotation of the canvas to the given @param angle in degrees.
*/
void setRotation(qreal angle);
/**
* @brief resetRotation reset the canvas rotation.
*/
void resetRotation();
/**
* @return return true if the canvas is mirrored, false otherwise.
*/
bool mirror() const;
/**
* @brief setMirror turn the canvas mirroring on or off depending on @param value
*/
void setMirror(bool value);
/**
* @return true if the canvas is in wraparound mode, false if not. Only when OpenGL is enabled,
* is wraparound mode available.
*/
bool wrapAroundMode() const;
/**
* @brief setWrapAroundMode set wraparound mode to @param enable
*/
void setWrapAroundMode(bool enable);
/**
* @return true if the canvas is in Instant Preview mode, false if not. Only when OpenGL is enabled,
* is Instant Preview mode available.
*/
bool levelOfDetailMode() const;
/**
* @brief setLevelOfDetailMode sets Instant Preview to @param enable
*/
void setLevelOfDetailMode(bool enable);
/**
* @return the view that holds this canvas
*/
View *view() const;
private:
friend class ManagedColor;
KisDisplayColorConverter *displayColorConverter() const;
struct Private;
Private *const d;
};
#endif // LIBKIS_CANVAS_H
diff --git a/libs/libkis/CloneLayer.h b/libs/libkis/CloneLayer.h
index 7d523a8cbe..177be530ca 100644
--- a/libs/libkis/CloneLayer.h
+++ b/libs/libkis/CloneLayer.h
@@ -1,65 +1,65 @@
/*
* Copyright (c) 2017 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef LIBKIS_CLONELAYER_H
#define LIBKIS_CLONELAYER_H
#include <QObject>
#include "Node.h"
#include <kis_types.h>
#include "kritalibkis_export.h"
#include "libkis.h"
/**
* @brief The CloneLayer class
* A clone layer is a layer that takes a reference inside the image
* and shows the exact same pixeldata.
*
* If the original is updated, the clone layer will update too.
*/
class KRITALIBKIS_EXPORT CloneLayer : public Node
{
Q_OBJECT
Q_DISABLE_COPY(CloneLayer)
public:
explicit CloneLayer(KisImageSP image, QString name, KisLayerSP source, QObject *parent = 0);
/**
* @brief CloneLayer
* function for wrapping a preexisting node into a clonelayer object.
- * @param clone
- * @param parent
+ * @param layer the clone layer
+ * @param parent the parent QObject
*/
explicit CloneLayer(KisCloneLayerSP layer, QObject *parent = 0);
~CloneLayer() override;
public Q_SLOTS:
/**
* @brief type Krita has several types of nodes, split in layers and masks. Group
* layers can contain other layers, any layer can contain masks.
*
* @return clonelayer
*/
virtual QString type() const override;
};
#endif // LIBKIS_PAINTLAYER_H
diff --git a/libs/libkis/Document.h b/libs/libkis/Document.h
index 4d6abccbc1..49352fe25c 100644
--- a/libs/libkis/Document.h
+++ b/libs/libkis/Document.h
@@ -1,851 +1,855 @@
/*
* Copyright (c) 2016 Boudewijn Rempt <boud@valdyas.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef LIBKIS_DOCUMENT_H
#define LIBKIS_DOCUMENT_H
#include <QObject>
#include "kritalibkis_export.h"
#include "libkis.h"
#include "GroupLayer.h"
#include "CloneLayer.h"
#include "FileLayer.h"
#include "FilterLayer.h"
#include "FillLayer.h"
#include "VectorLayer.h"
#include "FilterMask.h"
#include "SelectionMask.h"
class KisDocument;
/**
* The Document class encapsulates a Krita Document/Image. A Krita document is an Image with
* a filename. Libkis does not differentiate between a document and an image, like Krita does
* internally.
*/
class KRITALIBKIS_EXPORT Document : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(Document)
public:
explicit Document(KisDocument *document, QObject *parent = 0);
~Document() override;
bool operator==(const Document &other) const;
bool operator!=(const Document &other) const;
/**
* @brief horizontalGuides
* The horizontal guides.
* @return a list of the horizontal positions of guides.
*/
QList<qreal> horizontalGuides() const;
/**
* @brief verticalGuides
* The vertical guide lines.
* @return a list of vertical guides.
*/
QList<qreal> verticalGuides() const;
/**
* @brief guidesVisible
* Returns guide visibility.
* @return whether the guides are visible.
*/
bool guidesVisible() const;
/**
* @brief guidesLocked
* Returns guide lockedness.
* @return whether the guides are locked.
*/
bool guidesLocked() const;
public Q_SLOTS:
/**
* @brief clone create a shallow clone of this document.
* @return a new Document that should be identical to this one in every respect.
*/
Document *clone() const;
/**
* Batchmode means that no actions on the document should show dialogs or popups.
* @return true if the document is in batchmode.
*/
bool batchmode() const;
/**
- * Set batchmode to @param value. If batchmode is true, then there should be no popups
+ * Set batchmode to @p value. If batchmode is true, then there should be no popups
* or dialogs shown to the user.
*/
void setBatchmode(bool value);
/**
* @brief activeNode retrieve the node that is currently active in the currently active window
* @return the active node. If there is no active window, the first child node is returned.
*/
Node* activeNode() const;
/**
* @brief setActiveNode make the given node active in the currently active view and window
* @param value the node to make active.
*/
void setActiveNode(Node* value);
/**
* @brief toplevelNodes return a list with all top level nodes in the image graph
*/
QList<Node*> topLevelNodes() const;
/**
* @brief nodeByName searches the node tree for a node with the given name and returns it
* @param name the name of the node
* @return the first node with the given name or 0 if no node is found
*/
Node *nodeByName(const QString &name) const;
/**
* colorDepth A string describing the color depth of the image:
* <ul>
* <li>U8: unsigned 8 bits integer, the most common type</li>
* <li>U16: unsigned 16 bits integer</li>
* <li>F16: half, 16 bits floating point. Only available if Krita was built with OpenEXR</li>
* <li>F32: 32 bits floating point</li>
* </ul>
* @return the color depth.
*/
QString colorDepth() const;
/**
* @brief colorModel retrieve the current color model of this document:
* <ul>
* <li>A: Alpha mask</li>
* <li>RGBA: RGB with alpha channel (The actual order of channels is most often BGR!)</li>
* <li>XYZA: XYZ with alpha channel</li>
* <li>LABA: LAB with alpha channel</li>
* <li>CMYKA: CMYK with alpha channel</li>
* <li>GRAYA: Gray with alpha channel</li>
* <li>YCbCrA: YCbCr with alpha channel</li>
* </ul>
* @return the internal color model string.
*/
QString colorModel() const;
/**
* @return the name of the current color profile
*/
QString colorProfile() const;
/**
* @brief setColorProfile set the color profile of the image to the given profile. The profile has to
* be registered with krita and be compatible with the current color model and depth; the image data
* is <i>not</i> converted.
* @param colorProfile
* @return false if the colorProfile name does not correspond to to a registered profile or if assigning
* the profile failed.
*/
bool setColorProfile(const QString &colorProfile);
/**
* @brief setColorSpace convert the nodes and the image to the given colorspace. The conversion is
* done with Perceptual as intent, High Quality and No LCMS Optimizations as flags and no blackpoint
* compensation.
*
* @param colorModel A string describing the color model of the image:
* <ul>
* <li>A: Alpha mask</li>
* <li>RGBA: RGB with alpha channel (The actual order of channels is most often BGR!)</li>
* <li>XYZA: XYZ with alpha channel</li>
* <li>LABA: LAB with alpha channel</li>
* <li>CMYKA: CMYK with alpha channel</li>
* <li>GRAYA: Gray with alpha channel</li>
* <li>YCbCrA: YCbCr with alpha channel</li>
* </ul>
* @param colorDepth A string describing the color depth of the image:
* <ul>
* <li>U8: unsigned 8 bits integer, the most common type</li>
* <li>U16: unsigned 16 bits integer</li>
* <li>F16: half, 16 bits floating point. Only available if Krita was built with OpenEXR</li>
* <li>F32: 32 bits floating point</li>
* </ul>
* @param colorProfile a valid color profile for this color model and color depth combination.
* @return false the combination of these arguments does not correspond to a colorspace.
*/
bool setColorSpace(const QString &colorModel, const QString &colorDepth, const QString &colorProfile);
/**
* @brief backgroundColor returns the current background color of the document. The color will
* also include the opacity.
*
* @return QColor
*/
QColor backgroundColor();
/**
* @brief setBackgroundColor sets the background color of the document. It will trigger a projection
* update.
*
* @param color A QColor. The color will be converted from sRGB.
* @return bool
*/
bool setBackgroundColor(const QColor &color);
/**
* @brief documentInfo creates and XML document representing document and author information.
* @return a string containing a valid XML document with the right information about the document
* and author. The DTD can be found here:
*
* https://phabricator.kde.org/source/krita/browse/master/krita/dtd/
*
* @code
* <?xml version="1.0" encoding="UTF-8"?>
* <!DOCTYPE document-info PUBLIC '-//KDE//DTD document-info 1.1//EN' 'http://www.calligra.org/DTD/document-info-1.1.dtd'>
* <document-info xmlns="http://www.calligra.org/DTD/document-info">
* <about>
* <title>My Document</title>
* <description></description>
* <subject></subject>
* <abstract><![CDATA[]]></abstract>
* <keyword></keyword>
* <initial-creator>Unknown</initial-creator>
* <editing-cycles>1</editing-cycles>
* <editing-time>35</editing-time>
* <date>2017-02-27T20:15:09</date>
* <creation-date>2017-02-27T20:14:33</creation-date>
* <language></language>
* </about>
* <author>
* <full-name>Boudewijn Rempt</full-name>
* <initial></initial>
* <author-title></author-title>
* <email></email>
* <telephone></telephone>
* <telephone-work></telephone-work>
* <fax></fax>
* <country></country>
* <postal-code></postal-code>
* <city></city>
* <street></street>
* <position></position>
* <company></company>
* </author>
* </document-info>
* @endcode
*
*/
QString documentInfo() const;
/**
* @brief setDocumentInfo set the Document information to the information contained in document
* @param document A string containing a valid XML document that conforms to the document-info DTD
* that can be found here:
*
* https://phabricator.kde.org/source/krita/browse/master/krita/dtd/
*/
void setDocumentInfo(const QString &document);
/**
* @return the full path to the document, if it has been set.
*/
QString fileName() const;
/**
* @brief setFileName set the full path of the document to @param value
*/
void setFileName(QString value);
/**
* @return the height of the image in pixels
*/
int height() const;
/**
* @brief setHeight resize the document to @param value height. This is a canvas resize, not a scale.
*/
void setHeight(int value);
/**
- * @return the name of the document. This is the title field in the @see documentInfo
+ * @return the name of the document. This is the title field in the @ref documentInfo
*/
QString name() const;
/**
- * @brief setName sets the name of the document to @param value. This is the title field in the @see documentInfo
+ * @brief setName sets the name of the document to @p value. This is the title field in the @ref documentInfo
*/
void setName(QString value);
/**
* @return the resolution in pixels per inch
*/
int resolution() const;
/**
* @brief setResolution set the resolution of the image; this does not scale the image
* @param value the resolution in pixels per inch
*/
void setResolution(int value);
/**
* @brief rootNode the root node is the invisible group layer that contains the entire node
* hierarchy.
* @return the root of the image
*/
Node* rootNode() const;
/**
* @brief selection Create a Selection object around the global selection, if there is one.
* @return the global selection or None if there is no global selection.
*/
Selection* selection() const;
/**
* @brief setSelection set or replace the global selection
* @param value a valid selection object.
*/
void setSelection(Selection* value);
/**
* @return the width of the image in pixels.
*/
int width() const;
/**
* @brief setWidth resize the document to @param value width. This is a canvas resize, not a scale.
*/
void setWidth(int value);
/**
* @return the left edge of the canvas in pixels.
*/
int xOffset() const;
/**
- * @brief setXOffset sets the left edge of the canvas to @param x.
+ * @brief setXOffset sets the left edge of the canvas to @p x.
*/
void setXOffset(int x);
/**
* @return the top edge of the canvas in pixels.
*/
int yOffset() const;
/**
- * @brief setYOffset sets the top edge of the canvas to @param y.
+ * @brief setYOffset sets the top edge of the canvas to @p y.
*/
void setYOffset(int y);
/**
* @return xRes the horizontal resolution of the image in pixels per pt (there are 72 pts to an inch)
*/
double xRes() const;
/**
* @brief setXRes set the horizontal resolution of the image to xRes in pixels per pt. (there are 72 pts to an inch)
*/
void setXRes(double xRes) const;
/**
* @return yRes the vertical resolution of the image in pixels per pt (there are 72 pts to an inch)
*/
double yRes() const;
/**
* @brief setYRes set the vertical resolution of the image to yRes in pixels per pt. (there are 72 pts to an inch)
*/
void setYRes(double yRes) const;
/**
* @brief pixelData reads the given rectangle from the image projection and returns it as a byte
* array. The pixel data starts top-left, and is ordered row-first.
*
* The byte array can be interpreted as follows: 8 bits images have one byte per channel,
* and as many bytes as there are channels. 16 bits integer images have two bytes per channel,
* representing an unsigned short. 16 bits float images have two bytes per channel, representing
* a half, or 16 bits float. 32 bits float images have four bytes per channel, representing a
* float.
*
* You can read outside the image boundaries; those pixels will be transparent black.
*
* The order of channels is:
*
* <ul>
* <li>Integer RGBA: Blue, Green, Red, Alpha
* <li>Float RGBA: Red, Green, Blue, Alpha
* <li>LabA: L, a, b, Alpha
* <li>CMYKA: Cyan, Magenta, Yellow, Key, Alpha
* <li>XYZA: X, Y, Z, A
* <li>YCbCrA: Y, Cb, Cr, Alpha
* </ul>
*
* The byte array is a copy of the original image data. In Python, you can use bytes, bytearray
* and the struct module to interpret the data and construct, for instance, a Pillow Image object.
*
* @param x x position from where to start reading
* @param y y position from where to start reading
* @param w row length to read
* @param h number of rows to read
* @return a QByteArray with the pixel data. The byte array may be empty.
*/
QByteArray pixelData(int x, int y, int w, int h) const;
/**
* @brief close Close the document: remove it from Krita's internal list of documents and
* close all views. If the document is modified, you should save it first. There will be
* no prompt for saving.
*
* After closing the document it becomes invalid.
*
* @return true if the document is closed.
*/
bool close();
/**
- * @brief crop the image to rectangle described by @param x, @param y,
- * @param w and @param h
+ * @brief crop the image to rectangle described by @p x, @p y,
+ * @p w and @p h
+ * @param x x coordinate of the top left corner
+ * @param y y coordinate of the top left corner
+ * @param w width
+ * @param h height
*/
void crop(int x, int y, int w, int h);
/**
* @brief exportImage export the image, without changing its URL to the given path.
* @param filename the full path to which the image is to be saved
* @param exportConfiguration a configuration object appropriate to the file format.
* An InfoObject will used to that configuration.
*
* The supported formats have specific configurations that must be used when in
* batchmode. They are described below:
*
*\b png
* <ul>
* <li>alpha: bool (True or False)
* <li>compression: int (1 to 9)
* <li>forceSRGB: bool (True or False)
* <li>indexed: bool (True or False)
* <li>interlaced: bool (True or False)
* <li>saveSRGBProfile: bool (True or False)
* <li>transparencyFillcolor: rgb (Ex:[255,255,255])
* </ul>
*
*\b jpeg
* <ul>
* <li>baseline: bool (True or False)
* <li>exif: bool (True or False)
* <li>filters: bool (['ToolInfo', 'Anonymizer'])
* <li>forceSRGB: bool (True or False)
* <li>iptc: bool (True or False)
* <li>is_sRGB: bool (True or False)
* <li>optimize: bool (True or False)
* <li>progressive: bool (True or False)
* <li>quality: int (0 to 100)
* <li>saveProfile: bool (True or False)
* <li>smoothing: int (0 to 100)
* <li>subsampling: int (0 to 3)
* <li>transparencyFillcolor: rgb (Ex:[255,255,255])
* <li>xmp: bool (True or False)
* </ul>
* @return true if the export succeeded, false if it failed.
*/
bool exportImage(const QString &filename, const InfoObject &exportConfiguration);
/**
* @brief flatten all layers in the image
*/
void flatten();
/**
* @brief resizeImage resizes the canvas to the given left edge, top edge, width and height.
* Note: This doesn't scale, use scale image for that.
* @param x the new left edge
* @param y the new top edge
* @param w the new width
* @param h the new height
*/
void resizeImage(int x, int y, int w, int h);
/**
* @brief scaleImage
* @param w the new width
* @param h the new height
* @param xres the new xres
* @param yres the new yres
* @param strategy the scaling strategy. There's several ones amongst these that aren't available in the regular UI.
* The list of filters is extensible and can be retrieved with Krita::filter
* <ul>
* <li>Hermite</li>
* <li>Bicubic - Adds pixels using the color of surrounding pixels. Produces smoother tonal gradations than Bilinear.</li>
* <li>Box - Replicate pixels in the image. Preserves all the original detail, but can produce jagged effects.</li>
* <li>Bilinear - Adds pixels averaging the color values of surrounding pixels. Produces medium quality results when the image is scaled from half to two times the original size.</li>
* <li>Bell</li>
* <li>BSpline</li>
* <li>Kanczos3 - Offers similar results than Bicubic, but maybe a little bit sharper. Can produce light and dark halos along strong edges.</li>
* <li>Mitchell</li>
* </ul>
*/
void scaleImage(int w, int h, int xres, int yres, QString strategy);
/**
* @brief rotateImage
* Rotate the image by the given radians.
* @param radians the amount you wish to rotate the image in radians
*/
void rotateImage(double radians);
/**
* @brief shearImage shear the whole image.
* @param angleX the X-angle in degrees to shear by
* @param angleY the Y-angle in degrees to shear by
*/
void shearImage(double angleX, double angleY);
/**
* @brief save the image to its currently set path. The modified flag of the
* document will be reset
* @return true if saving succeeded, false otherwise.
*/
bool save();
/**
- * @brief saveAs save the document under the @param filename. The document's
- * filename will be reset to @param filename.
+ * @brief saveAs save the document under the @p filename. The document's
+ * filename will be reset to @p filename.
* @param filename the new filename (full path) for the document
* @return true if saving succeeded, false otherwise.
*/
bool saveAs(const QString &filename);
/**
* @brief createNode create a new node of the given type. The node is not added
* to the node hierarchy; you need to do that by finding the right parent node,
* getting its list of child nodes and adding the node in the right place, then
* calling Node::SetChildNodes
*
* @param name The name of the node
*
* @param nodeType The type of the node. Valid types are:
* <ul>
* <li>paintlayer
* <li>grouplayer
* <li>filelayer
* <li>filterlayer
* <li>filllayer
* <li>clonelayer
* <li>vectorlayer
* <li>transparencymask
* <li>filtermask
* <li>transformmask
* <li>selectionmask
* </ul>
*
* When relevant, the new Node will have the colorspace of the image by default;
* that can be changed with Node::setColorSpace.
*
* The settings and selections for relevant layer and mask types can also be set
* after the Node has been created.
*
@code
d = Application.createDocument(1000, 1000, "Test", "RGBA", "U8", "", 120.0)
root = d.rootNode();
print(root.childNodes())
l2 = d.createNode("layer2", "paintLayer")
print(l2)
root.addChildNode(l2, None)
print(root.childNodes())
@endcode
*
*
* @return the new Node.
*/
Node* createNode(const QString &name, const QString &nodeType);
/**
* @brief createGroupLayer
* Returns a grouplayer object. Grouplayers are nodes that can have
* other layers as children and have the passthrough mode.
* @param name the name of the layer.
* @return a GroupLayer object.
*/
GroupLayer* createGroupLayer(const QString &name);
/**
* @brief createFileLayer returns a layer that shows an external image.
* @param name name of the file layer.
* @param fileName the absolute filename of the file referenced. Symlinks will be resolved.
* @param scalingMethod how the dimensions of the file are interpreted
* can be either "None", "ImageToSize" or "ImageToPPI"
* @return a FileLayer
*/
FileLayer* createFileLayer(const QString &name, const QString fileName, const QString scalingMethod);
/**
* @brief createFilterLayer creates a filter layer, which is a layer that represents a filter
* applied non-destructively.
* @param name name of the filterLayer
* @param filter the filter that this filter layer will us.
* @param selection the selection.
* @return a filter layer object.
*/
FilterLayer* createFilterLayer(const QString &name, Filter &filter, Selection &selection);
/**
* @brief createFillLayer creates a fill layer object, which is a layer
* @param name
* @param generatorName - name of the generation filter.
* @param configuration - the configuration for the generation filter.
* @param selection - the selection.
* @return a filllayer object.
*
* @code
* from krita import *
* d = Krita.instance().activeDocument()
* i = InfoObject();
* i.setProperty("pattern", "Cross01.pat")
* s = Selection();
* s.select(0, 0, d.width(), d.height(), 255)
* n = d.createFillLayer("test", "pattern", i, s)
* r = d.rootNode();
* c = r.childNodes();
* r.addChildNode(n, c[0])
* d.refreshProjection()
* @endcode
*/
FillLayer* createFillLayer(const QString &name, const QString generatorName, InfoObject &configuration, Selection &selection);
/**
* @brief createCloneLayer
* @param name
* @param source
* @return
*/
CloneLayer* createCloneLayer(const QString &name, const Node* source);
/**
* @brief createVectorLayer
* Creates a vector layer that can contain vector shapes.
* @param name the name of this layer.
* @return a VectorLayer.
*/
VectorLayer* createVectorLayer(const QString &name);
/**
* @brief createFilterMask
* Creates a filter mask object that much like a filterlayer can apply a filter non-destructively.
* @param name the name of the layer.
* @param filter the filter assigned.
* @return a FilterMask
*/
FilterMask* createFilterMask(const QString &name, Filter &filter);
/**
* @brief createSelectionMask
* Creates a selection mask, which can be used to store selections.
* @param name - the name of the layer.
* @return a SelectionMask
*/
SelectionMask* createSelectionMask(const QString &name);
/**
* @brief projection creates a QImage from the rendered image or
* a cutout rectangle.
*/
QImage projection(int x = 0, int y = 0, int w = 0, int h = 0) const;
/**
* @brief thumbnail create a thumbnail of the given dimensions.
*
* If the requested size is too big a null QImage is created.
*
* @return a QImage representing the layer contents.
*/
QImage thumbnail(int w, int h) const;
/**
* Why this should be used, When it should be used, How it should be used,
* and warnings about when not.
*/
void lock();
/**
* Why this should be used, When it should be used, How it should be used,
* and warnings about when not.
*/
void unlock();
/**
* Why this should be used, When it should be used, How it should be used,
* and warnings about when not.
*/
void waitForDone();
/**
* Why this should be used, When it should be used, How it should be used,
* and warnings about when not.
*/
bool tryBarrierLock();
/**
* Why this should be used, When it should be used, How it should be used,
* and warnings about when not.
*/
bool isIdle();
/**
* Starts a synchronous recomposition of the projection: everything will
* wait until the image is fully recomputed.
*/
void refreshProjection();
/**
* @brief setHorizontalGuides
* replace all existing horizontal guides with the entries in the list.
- * @param list a list of floats containing the new guides.
+ * @param lines a list of floats containing the new guides.
*/
void setHorizontalGuides(const QList<qreal> &lines);
/**
* @brief setVerticalGuides
* replace all existing horizontal guides with the entries in the list.
- * @param list a list of floats containing the new guides.
+ * @param lines a list of floats containing the new guides.
*/
void setVerticalGuides(const QList<qreal> &lines);
/**
* @brief setGuidesVisible
* set guides visible on this document.
* @param visible whether or not the guides are visible.
*/
void setGuidesVisible(bool visible);
/**
* @brief setGuidesLocked
* set guides locked on this document
* @param locked whether or not to lock the guides on this document.
*/
void setGuidesLocked(bool locked);
/**
* @brief modified returns true if the document has unsaved modifications.
*/
bool modified() const;
/**
* @brief bounds return the bounds of the image
* @return the bounds
*/
QRect bounds() const;
/****
* Animation Related API
*****/
/**
* @brief Import an image sequence of files from a directory. This will grab all
* images from the directory and import them with a potential offset (firstFrame)
* and step (images on 2s, 3s, etc)
* @returns whether the animation import was successful
*/
bool importAnimation(const QList<QString> &files, int firstFrame, int step);
/**
* @brief frames per second of document
* @return the fps of the document
*/
int framesPerSecond();
/**
* @brief set frames per second of document
*/
void setFramesPerSecond(int fps);
/**
* @brief set start time of animation
*/
void setFullClipRangeStartTime(int startTime);
/**
* @brief get the full clip range start time
* @return full clip range start time
*/
int fullClipRangeStartTime();
/**
* @brief set full clip range end time
*/
void setFullClipRangeEndTime(int endTime);
/**
* @brief get the full clip range end time
* @return full clip range end time
*/
int fullClipRangeEndTime();
/**
* @brief get total frame range for animation
* @return total frame range for animation
*/
int animationLength();
/**
* @brief set temporary playback range of document
*/
void setPlayBackRange(int start, int stop);
/**
* @brief get start time of current playback
* @return start time of current playback
*/
int playBackStartTime();
/**
* @brief get end time of current playback
* @return end time of current playback
*/
int playBackEndTime();
/**
* @brief get current frame selected of animation
* @return current frame selected of animation
*/
int currentTime();
/**
* @brief set current time of document's animation
*/
void setCurrentTime(int time);
private:
friend class Krita;
friend class Window;
friend class Filter;
QPointer<KisDocument> document() const;
private:
struct Private;
Private *const d;
};
#endif // LIBKIS_DOCUMENT_H
diff --git a/libs/libkis/Extension.h b/libs/libkis/Extension.h
index eb9502a953..911792901a 100644
--- a/libs/libkis/Extension.h
+++ b/libs/libkis/Extension.h
@@ -1,86 +1,86 @@
/*
* Copyright (c) 2015 Cyrille Berger <cberger@cberger.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef LIBKIS_EXTENSION_H
#define LIBKIS_EXTENSION_H
#include "kritalibkis_export.h"
#include <QObject>
#include <Window.h>
/**
* An Extension is the base for classes that extend Krita. An Extension
* is loaded on startup, when the setup() method will be executed.
*
* The extension instance should be added to the Krita Application object
* using Krita.instance().addViewExtension or Application.addViewExtension
* or Scripter.addViewExtension.
*
* Example:
*
* @code
* import sys
* from PyQt5.QtGui import *
* from PyQt5.QtWidgets import *
* from krita import *
* class HelloExtension(Extension):
*
* def __init__(self, parent):
* super().__init__(parent)
*
* def hello(self):
* QMessageBox.information(QWidget(), "Test", "Hello! This is Krita " + Application.version())
*
* def setup(self):
* qDebug("Hello Setup")
*
* def createActions(self, window)
* action = window.createAction("hello")
* action.triggered.connect(self.hello)
*
* Scripter.addExtension(HelloExtension(Krita.instance()))
*
* @endcode
*/
class KRITALIBKIS_EXPORT Extension : public QObject
{
Q_OBJECT
public:
/**
* Create a new extension. The extension will be
- * owned by @param parent.
+ * owned by @p parent.
*/
explicit Extension(QObject *parent = 0);
~Extension() override;
/**
* Override this function to setup your Extension. You can use it to integrate
* with the Krita application instance.
*/
virtual void setup() = 0;
virtual void createActions(Window *window) = 0;
};
#endif
diff --git a/libs/libkis/FileLayer.h b/libs/libkis/FileLayer.h
index 1468345c4b..6e9ec8178a 100644
--- a/libs/libkis/FileLayer.h
+++ b/libs/libkis/FileLayer.h
@@ -1,106 +1,108 @@
/*
* Copyright (c) 2017 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef LIBKIS_FILELAYER_H
#define LIBKIS_FILELAYER_H
#include <QObject>
#include "Node.h"
#include <kis_types.h>
#include "kritalibkis_export.h"
#include "libkis.h"
/**
* @brief The FileLayer class
* A file layer is a layer that can reference an external image
* and show said reference in the layer stack.
*
* If the external image is updated, Krita will try to update the
* file layer image as well.
*/
class KRITALIBKIS_EXPORT FileLayer : public Node
{
Q_OBJECT
Q_DISABLE_COPY(FileLayer)
public:
explicit FileLayer(KisImageSP image,
const QString name = QString(),
const QString baseName=QString(),
const QString fileName=QString(),
const QString scalingMethod=QString(),
QObject *parent = 0);
explicit FileLayer(KisFileLayerSP layer, QObject *parent = 0);
~FileLayer() override;
public Q_SLOTS:
/**
* @brief type Krita has several types of nodes, split in layers and masks. Group
* layers can contain other layers, any layer can contain masks.
*
* @return "filelayer"
*/
QString type() const override;
/**
* @brief setProperties
* Change the properties of the file layer.
* @param fileName - A String containing the absolute file name.
* @param scalingMethod - a string with the scaling method, defaults to "None",
* other options are "ToImageSize" and "ToImagePPI"
*/
void setProperties(QString fileName, QString scalingMethod = QString("None"));
/**
* @brief makes the file layer to reload the connected image from disk
*/
void resetCache();
/**
* @brief path
* @return A QString with the full path of the referenced image.
*/
QString path() const;
/**
* @brief scalingMethod
* returns how the file referenced is scaled.
* @return one of the following:
+ * <ul>
* <li> None - The file is not scaled in any way.
* <li> ToImageSize - The file is scaled to the full image size;
* <li> ToImagePPI - The file is scaled by the PPI of the image. This keep the physical dimensions the same.
+ * </ul>
*/
QString scalingMethod() const;
private:
/**
* @brief getFileNameFromAbsolute
* referenced from the fileLayer dialog, this will jumps through all the hoops
* to ensure that an appropriate filename will be gotten.
* @param baseName the location of the document.
* @param absolutePath the absolute location of the file referenced.
* @return the appropriate relative path.
*/
QString getFileNameFromAbsolute(const QString &basePath, QString filePath);
QString m_baseName;
};
#endif // LIBKIS_FILELAYER_H
diff --git a/libs/libkis/Filter.h b/libs/libkis/Filter.h
index a81aaa39cf..ba885a25d0 100644
--- a/libs/libkis/Filter.h
+++ b/libs/libkis/Filter.h
@@ -1,114 +1,120 @@
/*
* Copyright (c) 2016 Boudewijn Rempt <boud@valdyas.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef LIBKIS_FILTER_H
#define LIBKIS_FILTER_H
#include <QObject>
#include "kritalibkis_export.h"
#include "libkis.h"
#include <kis_filter_configuration.h>
/**
* Filter: represents a filter and its configuration. A filter is identified by
* an internal name. The configuration for each filter is defined as an InfoObject:
* a map of name and value pairs.
*
* Currently available filters are:
*
* 'autocontrast', 'blur', 'bottom edge detections', 'brightnesscontrast', 'burn', 'colorbalance', 'colortoalpha', 'colortransfer',
* 'desaturate', 'dodge', 'emboss', 'emboss all directions', 'emboss horizontal and vertical', 'emboss horizontal only',
* 'emboss laplascian', 'emboss vertical only', 'gaussian blur', 'gaussiannoisereducer', 'gradientmap', 'halftone', 'hsvadjustment',
* 'indexcolors', 'invert', 'left edge detections', 'lens blur', 'levels', 'maximize', 'mean removal', 'minimize', 'motion blur',
* 'noise', 'normalize', 'oilpaint', 'perchannel', 'phongbumpmap', 'pixelize', 'posterize', 'raindrops', 'randompick',
* 'right edge detections', 'roundcorners', 'sharpen', 'smalltiles', 'sobel', 'threshold', 'top edge detections', 'unsharp',
* 'wave', 'waveletnoisereducer']
*/
class KRITALIBKIS_EXPORT Filter : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(Filter)
public:
/**
* @brief Filter: create an empty filter object. Until a name is set, the filter cannot
* be applied.
*/
explicit Filter();
~Filter() override;
bool operator==(const Filter &other) const;
bool operator!=(const Filter &other) const;
public Q_SLOTS:
/**
* @brief name the internal name of this filter.
* @return the name.
*/
QString name() const;
/**
* @brief setName set the filter's name to the given name.
*/
void setName(const QString &name);
/**
* @return the configuration object for the filter
*/
InfoObject* configuration() const;
/**
* @brief setConfiguration set the configuration object for the filter
*/
void setConfiguration(InfoObject* value);
/**
* @brief Apply the filter to the given node.
* @param node the node to apply the filter to
- * @params x, y, w, h: describe the rectangle the filter should be apply.
+ * @param x
+ * @param y
+ * @param w
+ * @param h describe the rectangle the filter should be apply.
* This is always in image pixel coordinates and not relative to the x, y
* of the node.
- * @return true if the filter was applied successfully, or
- * false if the filter could not be applied because the node is locked or
+ * @return @c true if the filter was applied successfully, or
+ * @c false if the filter could not be applied because the node is locked or
* does not have an editable paint device.
*/
bool apply(Node *node, int x, int y, int w, int h);
/**
* @brief startFilter starts the given filter on the given node.
*
* @param node the node to apply the filter to
- * @params x, y, w, h: describe the rectangle the filter should be apply.
+ * @param x
+ * @param y
+ * @param w
+ * @param h describe the rectangle the filter should be apply.
* This is always in image pixel coordinates and not relative to the x, y
* of the node.
*/
bool startFilter(Node *node, int x, int y, int w, int h);
private:
friend class FilterLayer;
friend class FilterMask;
struct Private;
Private *const d;
KisFilterConfigurationSP filterConfig();
};
#endif // LIBKIS_FILTER_H
diff --git a/libs/libkis/InfoObject.h b/libs/libkis/InfoObject.h
index d3db7e7583..ee8c4b9ff7 100644
--- a/libs/libkis/InfoObject.h
+++ b/libs/libkis/InfoObject.h
@@ -1,87 +1,88 @@
/*
* Copyright (c) 2016 Boudewijn Rempt <boud@valdyas.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef LIBKIS_INFOOBJECT_H
#define LIBKIS_INFOOBJECT_H
#include <QObject>
#include <kis_properties_configuration.h>
#include "kritalibkis_export.h"
#include "libkis.h"
/**
* InfoObject wrap a properties map. These maps can be used to set the
* configuration for filters.
*/
class KRITALIBKIS_EXPORT InfoObject : public QObject
{
Q_OBJECT
public:
InfoObject(KisPropertiesConfigurationSP configuration);
/**
* Create a new, empty InfoObject.
*/
explicit InfoObject(QObject *parent = 0);
~InfoObject() override;
bool operator==(const InfoObject &other) const;
bool operator!=(const InfoObject &other) const;
/**
* Return all properties this InfoObject manages.
*/
QMap<QString, QVariant> properties() const;
/**
- * Add all properties in the @param propertyMap to this InfoObject
+ * Add all properties in the @p propertyMap to this InfoObject
*/
- void setProperties(QMap<QString, QVariant> proprertyMap);
+ void setProperties(QMap<QString, QVariant> propertyMap);
public Q_SLOTS:
/**
- * set the property identified by @key to @value
+ * set the property identified by @p key to @p value
*
* If you want create a property that represents a color, you can use a QColor
* or hex string, as defined in http://doc.qt.io/qt-5/qcolor.html#setNamedColor.
*
*/
void setProperty(const QString &key, QVariant value);
/**
* return the value for the property identified by key, or None if there is no such key.
*/
QVariant property(const QString &key);
private:
friend class Filter;
friend class Document;
+ friend class Node;
/**
* @brief configuration gives access to the internal configuration object. Must
* be used used internally in libkis
* @return the internal configuration object.
*/
KisPropertiesConfigurationSP configuration() const;
struct Private;
Private *d;
};
#endif // LIBKIS_INFOOBJECT_H
diff --git a/libs/libkis/Krita.cpp b/libs/libkis/Krita.cpp
index 48df9ca5c1..7b0e90723d 100644
--- a/libs/libkis/Krita.cpp
+++ b/libs/libkis/Krita.cpp
@@ -1,423 +1,423 @@
/*
* Copyright (c) 2016 Boudewijn Rempt <boud@valdyas.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "Krita.h"
#include <QPointer>
#include <QVariant>
#include <QStringList>
#include <ksharedconfig.h>
#include <kconfiggroup.h>
#include <klocalizedstring.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorProfile.h>
#include <KoColorSpace.h>
#include <KoDockRegistry.h>
#include <KoColorSpaceEngine.h>
#include <KoColorModelStandardIds.h>
#include <KoID.h>
#include <kis_filter_strategy.h>
#include <kactioncollection.h>
#include <KisPart.h>
#include <KisMainWindow.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <kis_action.h>
#include <KisViewManager.h>
#include <KritaVersionWrapper.h>
#include <kis_filter_registry.h>
#include <kis_filter.h>
#include <kis_filter_configuration.h>
#include <kis_properties_configuration.h>
#include <kis_config.h>
#include <KisResourceServerProvider.h>
#include <kis_workspace_resource.h>
#include <brushengine/kis_paintop_preset.h>
#include <kis_brush_server.h>
#include <KoResourceServerProvider.h>
#include <kis_action_registry.h>
#include <kis_icon_utils.h>
#include "View.h"
#include "Document.h"
#include "Window.h"
#include "Extension.h"
#include "DockWidgetFactoryBase.h"
#include "Filter.h"
#include "InfoObject.h"
#include "Resource.h"
Krita* Krita::s_instance = 0;
struct Krita::Private {
Private() {}
QList<Extension*> extensions;
bool batchMode {false};
Notifier *notifier{new Notifier()};
};
Krita::Krita(QObject *parent)
: QObject(parent)
, d(new Private)
{
qRegisterMetaType<Notifier*>();
connect(KisPart::instance(), SIGNAL(sigWindowAdded(KisMainWindow*)), SLOT(mainWindowAdded(KisMainWindow*)));
}
Krita::~Krita()
{
qDeleteAll(d->extensions);
delete d->notifier;
delete d;
}
QList<QAction *> Krita::actions() const
{
KisMainWindow *mainWindow = KisPart::instance()->currentMainwindow();
if (!mainWindow) {
return QList<QAction*>();
}
KActionCollection *actionCollection = mainWindow->actionCollection();
return actionCollection->actions();
}
QAction *Krita::action(const QString &name) const
{
KisMainWindow *mainWindow = KisPart::instance()->currentMainwindow();
if (!mainWindow) {
return 0;
}
KActionCollection *actionCollection = mainWindow->actionCollection();
QAction *action = actionCollection->action(name);
return action;
}
Document* Krita::activeDocument() const
{
KisMainWindow *mainWindow = KisPart::instance()->currentMainwindow();
if (!mainWindow) {
return 0;
}
KisView *view = mainWindow->activeView();
if (!view) {
return 0;
}
KisDocument *document = view->document();
return new Document(document);
}
void Krita::setActiveDocument(Document* value)
{
Q_FOREACH(KisView *view, KisPart::instance()->views()) {
if (view->document() == value->document().data()) {
view->activateWindow();
break;
}
}
}
bool Krita::batchmode() const
{
return d->batchMode;
}
void Krita::setBatchmode(bool value)
{
d->batchMode = value;
}
QList<Document *> Krita::documents() const
{
QList<Document *> ret;
foreach(QPointer<KisDocument> doc, KisPart::instance()->documents()) {
ret << new Document(doc);
}
return ret;
}
QStringList Krita::filters() const
{
QStringList ls = KisFilterRegistry::instance()->keys();
std::sort(ls.begin(), ls.end());
return ls;
}
Filter *Krita::filter(const QString &name) const
{
if (!filters().contains(name)) return 0;
Filter *filter = new Filter();
filter->setName(name);
KisFilterSP f = KisFilterRegistry::instance()->value(name);
KisFilterConfigurationSP fc = f->defaultConfiguration();
InfoObject *info = new InfoObject(fc);
filter->setConfiguration(info);
return filter;
}
QStringList Krita::colorModels() const
{
QSet<QString> colorModelsIds;
QList<KoID> ids = KoColorSpaceRegistry::instance()->colorModelsList(KoColorSpaceRegistry::AllColorSpaces);
Q_FOREACH(KoID id, ids) {
colorModelsIds << id.id();
}
return colorModelsIds.toList();
}
QStringList Krita::colorDepths(const QString &colorModel) const
{
QSet<QString> colorDepthsIds;
QList<KoID> ids = KoColorSpaceRegistry::instance()->colorDepthList(colorModel, KoColorSpaceRegistry::AllColorSpaces);
Q_FOREACH(KoID id, ids) {
colorDepthsIds << id.id();
}
return colorDepthsIds.toList();
}
QStringList Krita::filterStrategies() const
{
return KisFilterStrategyRegistry::instance()->keys();
}
QStringList Krita::profiles(const QString &colorModel, const QString &colorDepth) const
{
QSet<QString> profileNames;
QString id = KoColorSpaceRegistry::instance()->colorSpaceId(colorModel, colorDepth);
QList<const KoColorProfile *> profiles = KoColorSpaceRegistry::instance()->profilesFor(id);
Q_FOREACH(const KoColorProfile *profile, profiles) {
profileNames << profile->name();
}
QStringList r = profileNames.toList();
r.sort();
return r;
}
bool Krita::addProfile(const QString &profilePath)
{
KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc");
return iccEngine->addProfile(profilePath);
}
Notifier* Krita::notifier() const
{
return d->notifier;
}
QString Krita::version() const
{
return KritaVersionWrapper::versionString(true);
}
QList<View *> Krita::views() const
{
QList<View *> ret;
foreach(QPointer<KisView> view, KisPart::instance()->views()) {
ret << new View(view);
}
return ret;
}
Window *Krita::activeWindow() const
{
KisMainWindow *mainWindow = KisPart::instance()->currentMainwindow();
if (!mainWindow) {
return 0;
}
return new Window(mainWindow);
}
QList<Window*> Krita::windows() const
{
QList<Window*> ret;
foreach(QPointer<KisMainWindow> mainWin, KisPart::instance()->mainWindows()) {
ret << new Window(mainWin);
}
return ret;
}
QMap<QString, Resource *> Krita::resources(const QString &type) const
{
QMap<QString, Resource *> resources = QMap<QString, Resource *> ();
if (type.toLower() == "pattern") {
KoResourceServer<KoPattern>* server = KoResourceServerProvider::instance()->patternServer();
Q_FOREACH (KoResource *res, server->resources()) {
resources[res->name()] = new Resource(res);
}
}
else if (type.toLower() == "gradient") {
KoResourceServer<KoAbstractGradient>* server = KoResourceServerProvider::instance()->gradientServer();
Q_FOREACH (KoResource *res, server->resources()) {
resources[res->name()] = new Resource(res);
}
}
else if (type.toLower() == "brush") {
KisBrushResourceServer* server = KisBrushServer::instance()->brushServer();
Q_FOREACH (KisBrushSP res, server->resources()) {
resources[res->name()] = new Resource(res.data());
}
}
else if (type.toLower() == "preset") {
KisPaintOpPresetResourceServer* server = KisResourceServerProvider::instance()->paintOpPresetServer();
Q_FOREACH (KisPaintOpPresetSP res, server->resources()) {
resources[res->name()] = new Resource(res.data());
}
}
else if (type.toLower() == "palette") {
KoResourceServer<KoColorSet>* server = KoResourceServerProvider::instance()->paletteServer();
Q_FOREACH (KoResource *res, server->resources()) {
resources[res->name()] = new Resource(res);
}
}
else if (type.toLower() == "workspace") {
KoResourceServer< KisWorkspaceResource >* server = KisResourceServerProvider::instance()->workspaceServer();
Q_FOREACH (KoResource *res, server->resources()) {
resources[res->name()] = new Resource(res);
}
}
return resources;
}
QStringList Krita::recentDocuments() const
{
KConfigGroup grp = KSharedConfig::openConfig()->group(QString("RecentFiles"));
QStringList keys = grp.keyList();
QStringList recentDocuments;
for(int i = 0; i <= keys.filter("File").count(); i++)
recentDocuments << grp.readEntry(QString("File%1").arg(i), QString(""));
return recentDocuments;
}
Document* Krita::createDocument(int width, int height, const QString &name, const QString &colorModel, const QString &colorDepth, const QString &profile, double resolution)
{
KisDocument *document = KisPart::instance()->createDocument();
KisPart::instance()->addDocument(document);
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace(colorModel, colorDepth, profile);
Q_ASSERT(cs);
QColor qc(Qt::white);
qc.setAlpha(0);
KoColor bgColor(qc, cs);
- if (!document->newImage(name, width, height, cs, bgColor, true, 1, "", double(resolution / 72) )) {
+ if (!document->newImage(name, width, height, cs, bgColor, KisConfig::RASTER_LAYER, 1, "", double(resolution / 72) )) {
return 0;
}
Q_ASSERT(document->image());
return new Document(document);
}
Document* Krita::openDocument(const QString &filename)
{
KisDocument *document = KisPart::instance()->createDocument();
document->setFileBatchMode(this->batchmode());
KisPart::instance()->addDocument(document);
document->openUrl(QUrl::fromLocalFile(filename), KisDocument::DontAddToRecent);
document->setFileBatchMode(false);
return new Document(document);
}
Window* Krita::openWindow()
{
KisMainWindow *mw = KisPart::instance()->createMainWindow();
return new Window(mw);
}
void Krita::addExtension(Extension* extension)
{
d->extensions.append(extension);
}
QList< Extension* > Krita::extensions()
{
return d->extensions;
}
void Krita::writeSetting(const QString &group, const QString &name, const QString &value)
{
KConfigGroup grp = KSharedConfig::openConfig()->group(group);
grp.writeEntry(name, value);
}
QString Krita::readSetting(const QString &group, const QString &name, const QString &defaultValue)
{
KConfigGroup grp = KSharedConfig::openConfig()->group(group);
return grp.readEntry(name, defaultValue);
}
QIcon Krita::icon(QString &iconName) const
{
return KisIconUtils::loadIcon(iconName);
}
void Krita::addDockWidgetFactory(DockWidgetFactoryBase* factory)
{
KoDockRegistry::instance()->add(factory);
}
Krita* Krita::instance()
{
if (!s_instance)
{
s_instance = new Krita;
}
return s_instance;
}
/**
* Scripter.fromVariant(variant)
* variant is a QVariant
* returns instance of QObject-subclass
*
* This is a helper method for PyQt because PyQt cannot cast a variant to a QObject or QWidget
*/
QObject *Krita::fromVariant(const QVariant& v)
{
if (v.canConvert< QWidget* >())
{
QObject* obj = qvariant_cast< QWidget* >(v);
return obj;
}
else if (v.canConvert< QObject* >())
{
QObject* obj = qvariant_cast< QObject* >(v);
return obj;
}
else
return 0;
}
QString Krita::krita_i18n(const QString &text)
{
return i18n(text.toUtf8().constData());
}
void Krita::mainWindowAdded(KisMainWindow *kisWindow)
{
Q_FOREACH(Extension *extension, d->extensions) {
Window window(kisWindow);
extension->createActions(&window);
}
}
diff --git a/libs/libkis/ManagedColor.h b/libs/libkis/ManagedColor.h
index 190a7e00bd..5b5837ee17 100644
--- a/libs/libkis/ManagedColor.h
+++ b/libs/libkis/ManagedColor.h
@@ -1,212 +1,211 @@
/*
* Copyright (C) 2017 Boudewijn Rempt <boud@valdyas.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef MANAGEDCOLOR_H
#define MANAGEDCOLOR_H
#include <QObject>
#include <QVector>
#include <QScopedPointer>
#include "kritalibkis_export.h"
#include "libkis.h"
class KoColor;
/**
* @brief The ManagedColor class is a class to handle colors that are color managed.
* A managed color is a color of which we know the model(RGB, LAB, CMYK, etc), the bitdepth and
* the specific properties of its colorspace, such as the whitepoint, chromacities, trc, etc, as represented
* by the color profile.
*
* Krita has two color management systems. LCMS and OCIO.
* LCMS is the one handling the ICC profile stuff, and the major one handling that ManagedColor deals with.
* OCIO support is only in the display of the colors. ManagedColor has some support for it in colorForCanvas()
*
* All colors in Krita are color managed. QColors are understood as RGB-type colors in the sRGB space.
*
* We recommend you make a color like this:
*
* @code
* colorYellow = ManagedColor("RGBA", "U8", "")
* QVector<float> yellowComponents = colorYellow.components()
* yellowComponents[0] = 1.0
* yellowComponents[1] = 1.0
* yellowComponents[2] = 0
* yellowComponents[3] = 1.0
*
* colorYellow.setComponents(yellowComponents)
* QColor yellow = colorYellow.colorForCanvas(canvas)
* @endcode
*/
class KRITALIBKIS_EXPORT ManagedColor : public QObject
{
Q_OBJECT
public:
/**
* @brief ManagedColor
* Create a ManagedColor that is black and transparent.
*/
explicit ManagedColor(QObject *parent = 0);
/**
* @brief ManagedColor create a managed color with the given color space properties.
* @see setColorModel() for more details.
*/
ManagedColor(const QString &colorModel, const QString &colorDepth, const QString &colorProfile, QObject *parent = 0);
ManagedColor(KoColor color, QObject *parent = 0);
~ManagedColor() override;
bool operator==(const ManagedColor &other) const;
/**
* @brief colorForCanvas
* @param canvas the canvas whose color management you'd like to use. In Krita, different views have
* separate canvasses, and these can have different OCIO configurations active.
* @return the QColor as it would be displaying on the canvas. This result can be used to draw widgets with
* the correct configuration applied.
*/
QColor colorForCanvas(Canvas *canvas) const;
/**
* colorDepth A string describing the color depth of the image:
* <ul>
* <li>U8: unsigned 8 bits integer, the most common type</li>
* <li>U16: unsigned 16 bits integer</li>
* <li>F16: half, 16 bits floating point. Only available if Krita was built with OpenEXR</li>
* <li>F32: 32 bits floating point</li>
* </ul>
* @return the color depth.
*/
QString colorDepth() const;
/**
* @brief colorModel retrieve the current color model of this document:
* <ul>
* <li>A: Alpha mask</li>
* <li>RGBA: RGB with alpha channel (The actual order of channels is most often BGR!)</li>
* <li>XYZA: XYZ with alpha channel</li>
* <li>LABA: LAB with alpha channel</li>
* <li>CMYKA: CMYK with alpha channel</li>
* <li>GRAYA: Gray with alpha channel</li>
* <li>YCbCrA: YCbCr with alpha channel</li>
* </ul>
* @return the internal color model string.
*/
QString colorModel() const;
/**
* @return the name of the current color profile
*/
QString colorProfile() const;
/**
* @brief setColorProfile set the color profile of the image to the given profile. The profile has to
* be registered with krita and be compatible with the current color model and depth; the image data
* is <i>not</i> converted.
* @param colorProfile
* @return false if the colorProfile name does not correspond to to a registered profile or if assigning
* the profile failed.
*/
bool setColorProfile(const QString &colorProfile);
/**
* @brief setColorSpace convert the nodes and the image to the given colorspace. The conversion is
* done with Perceptual as intent, High Quality and No LCMS Optimizations as flags and no blackpoint
* compensation.
*
* @param colorModel A string describing the color model of the image:
* <ul>
* <li>A: Alpha mask</li>
* <li>RGBA: RGB with alpha channel (The actual order of channels is most often BGR!)</li>
* <li>XYZA: XYZ with alpha channel</li>
* <li>LABA: LAB with alpha channel</li>
* <li>CMYKA: CMYK with alpha channel</li>
* <li>GRAYA: Gray with alpha channel</li>
* <li>YCbCrA: YCbCr with alpha channel</li>
* </ul>
* @param colorDepth A string describing the color depth of the image:
* <ul>
* <li>U8: unsigned 8 bits integer, the most common type</li>
* <li>U16: unsigned 16 bits integer</li>
* <li>F16: half, 16 bits floating point. Only available if Krita was built with OpenEXR</li>
* <li>F32: 32 bits floating point</li>
* </ul>
* @param colorProfile a valid color profile for this color model and color depth combination.
* @return false the combination of these arguments does not correspond to a colorspace.
*/
bool setColorSpace(const QString &colorModel, const QString &colorDepth, const QString &colorProfile);
/**
* @brief components
* @return a QVector containing the channel/components of this color normalized. This includes the alphachannel.
*/
QVector<float> components() const;
/**
* @brief componentsOrdered()
* @return same as Components, except the values are ordered to the display.
*/
QVector<float> componentsOrdered() const;
/**
* @brief setComponents
* Set the channel/components with normalized values. For integer colorspace, this obviously means the limit
* is between 0.0-1.0, but for floating point colorspaces, 2.4 or 103.5 are still meaningful (if bright) values.
* @param values the QVector containing the new channel/component values. These should be normalized.
*/
void setComponents(const QVector<float> &values);
/**
* Serialize this color following Create's swatch color specification available
* at http://create.freedesktop.org/wiki/index.php/Swatches_-_colour_file_format
*/
QString toXML() const;
/**
* Unserialize a color following Create's swatch color specification available
* at http://create.freedesktop.org/wiki/index.php/Swatches_-_colour_file_format
*
- * @param XXX
+ * @param xml an XML color
*
* @return the unserialized color, or an empty color object if the function failed
* to unserialize the color
*/
void fromXML(const QString &xml);
/**
* @brief toQString create a user-visible string of the channel names and the channel values
- * @param color the color to create the string from
* @return a string that can be used to display the values of this color to the user.
*/
QString toQString();
private:
friend class View;
friend class PaletteView;
friend class Swatch;
KoColor color() const;
struct Private;
const QScopedPointer<Private> d;
};
#endif // MANAGEDCOLOR_H
diff --git a/libs/libkis/Node.cpp b/libs/libkis/Node.cpp
index b6841f479c..5a31ecbf0c 100644
--- a/libs/libkis/Node.cpp
+++ b/libs/libkis/Node.cpp
@@ -1,627 +1,651 @@
/*
* Copyright (c) 2016 Boudewijn Rempt <boud@valdyas.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <QUrl>
#include <QScopedPointer>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorTransformation.h>
#include <KisDocument.h>
#include <KisMimeDatabase.h>
#include <KisPart.h>
#include <kis_change_profile_visitor.h>
#include <kis_colorspace_convert_visitor.h>
#include <kis_image.h>
#include <kis_types.h>
#include <kis_node.h>
#include <kis_paint_layer.h>
#include <kis_group_layer.h>
#include <kis_file_layer.h>
#include <kis_adjustment_layer.h>
#include <kis_generator_layer.h>
#include <kis_clone_layer.h>
#include <kis_shape_layer.h>
#include <KisReferenceImagesLayer.h>
#include <kis_transparency_mask.h>
#include <kis_filter_mask.h>
#include <kis_transform_mask.h>
#include <kis_selection_mask.h>
#include <lazybrush/kis_colorize_mask.h>
#include <kis_layer.h>
#include <kis_meta_data_merge_strategy.h>
#include <kis_meta_data_merge_strategy_registry.h>
#include <kis_filter_strategy.h>
#include <kis_raster_keyframe_channel.h>
#include <kis_keyframe.h>
#include "kis_selection.h"
+#include "InfoObject.h"
#include "Krita.h"
#include "Node.h"
#include "Channel.h"
#include "Filter.h"
#include "Selection.h"
#include "GroupLayer.h"
#include "CloneLayer.h"
#include "FilterLayer.h"
#include "FillLayer.h"
#include "FileLayer.h"
#include "VectorLayer.h"
#include "FilterMask.h"
#include "SelectionMask.h"
#include "LibKisUtils.h"
struct Node::Private {
Private() {}
KisImageWSP image;
KisNodeSP node;
};
Node::Node(KisImageSP image, KisNodeSP node, QObject *parent)
: QObject(parent)
, d(new Private)
{
d->image = image;
d->node = node;
}
Node::~Node()
{
delete d;
}
bool Node::operator==(const Node &other) const
{
return (d->node == other.d->node
&& d->image == other.d->image);
}
bool Node::operator!=(const Node &other) const
{
return !(operator==(other));
}
Node *Node::clone() const
{
KisNodeSP clone = d->node->clone();
Node *node = new Node(0, clone);
return node;
}
bool Node::alphaLocked() const
{
if (!d->node) return false;
KisPaintLayerSP paintLayer = qobject_cast<KisPaintLayer*>(d->node.data());
if (paintLayer) {
return paintLayer->alphaLocked();
}
return false;
}
void Node::setAlphaLocked(bool value)
{
if (!d->node) return;
KisPaintLayerSP paintLayer = qobject_cast<KisPaintLayer*>(d->node.data());
if (paintLayer) {
paintLayer->setAlphaLocked(value);
}
}
QString Node::blendingMode() const
{
if (!d->node) return QString();
return d->node->compositeOpId();
}
void Node::setBlendingMode(QString value)
{
if (!d->node) return;
d->node->setCompositeOpId(value);
}
QList<Channel*> Node::channels() const
{
QList<Channel*> channels;
if (!d->node) return channels;
if (!d->node->inherits("KisLayer")) return channels;
Q_FOREACH(KoChannelInfo *info, d->node->colorSpace()->channels()) {
Channel *channel = new Channel(d->node, info);
channels << channel;
}
return channels;
}
QList<Node*> Node::childNodes() const
{
QList<Node*> nodes;
if (d->node) {
KisNodeList nodeList;
int childCount = d->node->childCount();
for (int i = 0; i < childCount; ++i) {
nodeList << d->node->at(i);
}
nodes = LibKisUtils::createNodeList(nodeList, d->image);
}
return nodes;
}
bool Node::addChildNode(Node *child, Node *above)
{
if (!d->node) return false;
if (above) {
return d->image->addNode(child->node(), d->node, above->node());
}
else {
return d->image->addNode(child->node(), d->node, d->node->childCount());
}
}
bool Node::removeChildNode(Node *child)
{
if (!d->node) return false;
return d->image->removeNode(child->node());
}
void Node::setChildNodes(QList<Node*> nodes)
{
if (!d->node) return;
KisNodeSP node = d->node->firstChild();
while (node) {
d->image->removeNode(node);
node = node->nextSibling();
}
Q_FOREACH(Node *node, nodes) {
d->image->addNode(node->node(), d->node);
}
}
int Node::colorLabel() const
{
if (!d->node) return 0;
return d->node->colorLabelIndex();
}
void Node::setColorLabel(int index)
{
if (!d->node) return;
d->node->setColorLabelIndex(index);
}
QString Node::colorDepth() const
{
if (!d->node) return "";
- return d->node->colorSpace()->colorDepthId().id();
+ if (!d->node->projection()) return d->node->colorSpace()->colorDepthId().id();
+ return d->node->projection()->colorSpace()->colorDepthId().id();
}
QString Node::colorModel() const
{
if (!d->node) return "";
- return d->node->colorSpace()->colorModelId().id();
+ if (!d->node->projection()) return d->node->colorSpace()->colorModelId().id();
+ return d->node->projection()->colorSpace()->colorModelId().id();
}
QString Node::colorProfile() const
{
if (!d->node) return "";
- return d->node->colorSpace()->profile()->name();
+ if (!d->node->projection()) return d->node->colorSpace()->profile()->name();
+ return d->node->projection()->colorSpace()->profile()->name();
}
bool Node::setColorProfile(const QString &colorProfile)
{
if (!d->node) return false;
if (!d->node->inherits("KisLayer")) return false;
KisLayer *layer = qobject_cast<KisLayer*>(d->node.data());
const KoColorProfile *profile = KoColorSpaceRegistry::instance()->profileByName(colorProfile);
const KoColorSpace *srcCS = layer->colorSpace();
const KoColorSpace *dstCs = KoColorSpaceRegistry::instance()->colorSpace(srcCS->colorModelId().id(),
srcCS->colorDepthId().id(),
profile);
KisChangeProfileVisitor v(srcCS, dstCs);
return layer->accept(v);
}
bool Node::setColorSpace(const QString &colorModel, const QString &colorDepth, const QString &colorProfile)
{
if (!d->node) return false;
if (!d->node->inherits("KisLayer")) return false;
KisLayer *layer = qobject_cast<KisLayer*>(d->node.data());
const KoColorProfile *profile = KoColorSpaceRegistry::instance()->profileByName(colorProfile);
const KoColorSpace *srcCS = layer->colorSpace();
const KoColorSpace *dstCs = KoColorSpaceRegistry::instance()->colorSpace(colorModel,
colorDepth,
profile);
KisColorSpaceConvertVisitor v(d->image, srcCS, dstCs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
return layer->accept(v);
}
bool Node::animated() const
{
if (!d->node) return false;
return d->node->isAnimated();
}
void Node::enableAnimation() const
{
if (!d->node) return;
d->node->enableAnimation();
}
void Node::setShowInTimeline(bool showInTimeline) const
{
if (!d->node) return;
d->node->setUseInTimeline(showInTimeline);
}
bool Node::showInTimeline() const
{
if (!d->node) return false;
return d->node->useInTimeline();
}
bool Node::collapsed() const
{
if (!d->node) return false;
return d->node->collapsed();
}
void Node::setCollapsed(bool collapsed)
{
if (!d->node) return;
d->node->setCollapsed(collapsed);
}
bool Node::inheritAlpha() const
{
if (!d->node) return false;
if (!d->node->inherits("KisLayer")) return false;
return qobject_cast<const KisLayer*>(d->node)->alphaChannelDisabled();
}
void Node::setInheritAlpha(bool value)
{
if (!d->node) return;
if (!d->node->inherits("KisLayer")) return;
const_cast<KisLayer*>(qobject_cast<const KisLayer*>(d->node))->disableAlphaChannel(value);
}
bool Node::locked() const
{
if (!d->node) return false;
return d->node->userLocked();
}
void Node::setLocked(bool value)
{
if (!d->node) return;
d->node->setUserLocked(value);
}
bool Node::hasExtents()
{
return !d->node->extent().isEmpty();
}
QString Node::name() const
{
if (!d->node) return QString();
return d->node->name();
}
void Node::setName(QString name)
{
if (!d->node) return;
d->node->setName(name);
}
int Node::opacity() const
{
if (!d->node) return 0;
return d->node->opacity();
}
void Node::setOpacity(int value)
{
if (!d->node) return;
if (value < 0) value = 0;
if (value > 255) value = 255;
d->node->setOpacity(value);
}
Node* Node::parentNode() const
{
if (!d->node) return 0;
return new Node(d->image, d->node->parent());
}
QString Node::type() const
{
if (!d->node) return QString();
if (qobject_cast<const KisPaintLayer*>(d->node)) {
return "paintlayer";
}
else if (qobject_cast<const KisGroupLayer*>(d->node)) {
return "grouplayer";
}
if (qobject_cast<const KisFileLayer*>(d->node)) {
return "filelayer";
}
if (qobject_cast<const KisAdjustmentLayer*>(d->node)) {
return "filterlayer";
}
if (qobject_cast<const KisGeneratorLayer*>(d->node)) {
return "filllayer";
}
if (qobject_cast<const KisCloneLayer*>(d->node)) {
return "clonelayer";
}
if (qobject_cast<const KisReferenceImagesLayer*>(d->node)) {
return "referenceimageslayer";
}
if (qobject_cast<const KisShapeLayer*>(d->node)) {
return "vectorlayer";
}
if (qobject_cast<const KisTransparencyMask*>(d->node)) {
return "transparencymask";
}
if (qobject_cast<const KisFilterMask*>(d->node)) {
return "filtermask";
}
if (qobject_cast<const KisTransformMask*>(d->node)) {
return "transformmask";
}
if (qobject_cast<const KisSelectionMask*>(d->node)) {
return "selectionmask";
}
if (qobject_cast<const KisColorizeMask*>(d->node)) {
return "colorizemask";
}
return QString();
}
QIcon Node::icon() const
{
QIcon icon;
if (d->node) {
icon = d->node->icon();
}
return icon;
}
bool Node::visible() const
{
if (!d->node) return false;
return d->node->visible();
}
+bool Node::hasKeyframeAtTime(int frameNumber)
+{
+ if (!d->node || !d->node->isAnimated()) return false;
+
+ KisRasterKeyframeChannel *rkc = dynamic_cast<KisRasterKeyframeChannel*>(d->node->getKeyframeChannel(KisKeyframeChannel::Content.id()));
+ if (!rkc) return false;
+
+ KisKeyframeSP timeOfCurrentKeyframe = rkc->keyframeAt(frameNumber);
+
+ if (!timeOfCurrentKeyframe) {
+ return false;
+ }
+
+ // do an assert just to be careful
+ KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(timeOfCurrentKeyframe->time() == frameNumber, false);
+ return true;
+}
+
void Node::setVisible(bool visible)
{
if (!d->node) return;
d->node->setVisible(visible);
}
QByteArray Node::pixelData(int x, int y, int w, int h) const
{
QByteArray ba;
if (!d->node) return ba;
KisPaintDeviceSP dev = d->node->paintDevice();
if (!dev) return ba;
ba.resize(w * h * dev->pixelSize());
dev->readBytes(reinterpret_cast<quint8*>(ba.data()), x, y, w, h);
return ba;
}
QByteArray Node::pixelDataAtTime(int x, int y, int w, int h, int time) const
{
QByteArray ba;
if (!d->node || !d->node->isAnimated()) return ba;
//
KisRasterKeyframeChannel *rkc = dynamic_cast<KisRasterKeyframeChannel*>(d->node->getKeyframeChannel(KisKeyframeChannel::Content.id()));
if (!rkc) return ba;
KisKeyframeSP frame = rkc->keyframeAt(time);
if (!frame) return ba;
KisPaintDeviceSP dev = d->node->paintDevice();
if (!dev) return ba;
rkc->fetchFrame(frame, dev);
ba.resize(w * h * dev->pixelSize());
dev->readBytes(reinterpret_cast<quint8*>(ba.data()), x, y, w, h);
return ba;
}
QByteArray Node::projectionPixelData(int x, int y, int w, int h) const
{
QByteArray ba;
if (!d->node) return ba;
KisPaintDeviceSP dev = d->node->projection();
+ if (!dev) return ba;
+
ba.resize(w * h * dev->pixelSize());
dev->readBytes(reinterpret_cast<quint8*>(ba.data()), x, y, w, h);
return ba;
}
void Node::setPixelData(QByteArray value, int x, int y, int w, int h)
{
if (!d->node) return;
KisPaintDeviceSP dev = d->node->paintDevice();
if (!dev) return;
dev->writeBytes((const quint8*)value.constData(), x, y, w, h);
}
QRect Node::bounds() const
{
if (!d->node) return QRect();
return d->node->exactBounds();
}
void Node::move(int x, int y)
{
if (!d->node) return;
d->node->setX(x);
d->node->setY(y);
}
QPoint Node::position() const
{
if (!d->node) return QPoint();
return QPoint(d->node->x(), d->node->y());
}
bool Node::remove()
{
if (!d->node) return false;
if (!d->node->parent()) return false;
return d->image->removeNode(d->node);
}
Node* Node::duplicate()
{
if (!d->node) return 0;
return new Node(d->image, d->node->clone());
}
-bool Node::save(const QString &filename, double xRes, double yRes)
+bool Node::save(const QString &filename, double xRes, double yRes, const InfoObject &exportConfiguration)
{
if (!d->node) return false;
if (filename.isEmpty()) return false;
KisPaintDeviceSP projection = d->node->projection();
QRect bounds = d->node->exactBounds();
QString mimeType = KisMimeDatabase::mimeTypeForFile(filename, false);
QScopedPointer<KisDocument> doc(KisPart::instance()->createDocument());
KisImageSP dst = new KisImage(doc->createUndoStore(),
bounds.right(),
bounds.bottom(),
projection->compositionSourceColorSpace(),
d->node->name());
dst->setResolution(xRes, yRes);
doc->setFileBatchMode(Krita::instance()->batchmode());
doc->setCurrentImage(dst);
KisPaintLayer* paintLayer = new KisPaintLayer(dst, "paint device", d->node->opacity());
paintLayer->paintDevice()->makeCloneFrom(projection, bounds);
dst->addNode(paintLayer, dst->rootLayer(), KisLayerSP(0));
dst->cropImage(bounds);
dst->initialRefreshGraph();
- bool r = doc->exportDocumentSync(QUrl::fromLocalFile(filename), mimeType.toLatin1());
+ bool r = doc->exportDocumentSync(QUrl::fromLocalFile(filename), mimeType.toLatin1(), exportConfiguration.configuration());
if (!r) {
qWarning() << doc->errorMessage();
}
return r;
}
Node* Node::mergeDown()
{
if (!d->node) return 0;
if (!qobject_cast<KisLayer*>(d->node.data())) return 0;
if (!d->node->prevSibling()) return 0;
d->image->mergeDown(qobject_cast<KisLayer*>(d->node.data()), KisMetaData::MergeStrategyRegistry::instance()->get("Drop"));
d->image->waitForDone();
return new Node(d->image, d->node->prevSibling());
}
-void Node::scaleNode(const QPointF &origin, int width, int height, QString strategy)
+void Node::scaleNode(QPointF origin, int width, int height, QString strategy)
{
if (!d->node) return;
if (!qobject_cast<KisLayer*>(d->node.data())) return;
if (!d->node->parent()) return;
KisFilterStrategy *actualStrategy = KisFilterStrategyRegistry::instance()->get(strategy);
if (!actualStrategy) actualStrategy = KisFilterStrategyRegistry::instance()->get("Bicubic");
const QRect bounds(d->node->exactBounds());
d->image->scaleNode(d->node,
origin,
qreal(width) / bounds.width(),
qreal(height) / bounds.height(),
actualStrategy, 0);
}
void Node::rotateNode(double radians)
{
if (!d->node) return;
if (!qobject_cast<KisLayer*>(d->node.data())) return;
if (!d->node->parent()) return;
d->image->rotateNode(d->node, radians, 0);
}
void Node::cropNode(int x, int y, int w, int h)
{
if (!d->node) return;
if (!qobject_cast<KisLayer*>(d->node.data())) return;
if (!d->node->parent()) return;
QRect rect = QRect(x, y, w, h);
d->image->cropNode(d->node, rect);
}
void Node::shearNode(double angleX, double angleY)
{
if (!d->node) return;
if (!qobject_cast<KisLayer*>(d->node.data())) return;
if (!d->node->parent()) return;
d->image->shearNode(d->node, angleX, angleY, 0);
}
QImage Node::thumbnail(int w, int h)
{
if (!d->node) return QImage();
return d->node->createThumbnail(w, h);
}
KisPaintDeviceSP Node::paintDevice() const
{
return d->node->paintDevice();
}
KisImageSP Node::image() const
{
return d->image;
}
KisNodeSP Node::node() const
{
return d->node;
}
diff --git a/libs/libkis/Node.h b/libs/libkis/Node.h
index d9b884ef8a..c9723f30f2 100644
--- a/libs/libkis/Node.h
+++ b/libs/libkis/Node.h
@@ -1,562 +1,570 @@
/*
* Copyright (c) 2016 Boudewijn Rempt <boud@valdyas.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef LIBKIS_NODE_H
#define LIBKIS_NODE_H
#include <QObject>
#include <kis_types.h>
#include "kritalibkis_export.h"
#include "libkis.h"
/**
* Node represents a layer or mask in a Krita image's Node hierarchy. Group layers can contain
* other layers and masks; layers can contain masks.
*
*/
class KRITALIBKIS_EXPORT Node : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(Node)
public:
explicit Node(KisImageSP image, KisNodeSP node, QObject *parent = 0);
~Node() override;
bool operator==(const Node &other) const;
bool operator!=(const Node &other) const;
public Q_SLOTS:
/**
* @brief clone clone the current node. The node is not associated with any image.
*/
Node *clone() const;
/**
* @brief alphaLocked checks whether the node is a paint layer and returns whether it is alpha locked
* @return whether the paint layer is alpha locked, or false if the node is not a paint layer
*/
bool alphaLocked() const;
/**
* @brief setAlphaLocked set the layer to value if the node is paint layer.
*/
void setAlphaLocked(bool value);
/**
* @return the blending mode of the layer. The values of the blending modes are defined in @see KoCompositeOpRegistry.h
*/
QString blendingMode() const;
/**
* @brief setBlendingMode set the blending mode of the node to the given value
* @param value one of the string values from @see KoCompositeOpRegistry.h
*/
void setBlendingMode(QString value);
/**
* @brief channels creates a list of Channel objects that can be used individually to
* show or hide certain channels, and to retrieve the contents of each channel in a
* node separately.
*
* Only layers have channels, masks do not, and calling channels on a Node that is a mask
* will return an empty list.
*
* @return the list of channels ordered in by position of the channels in pixel position
*/
QList<Channel*> channels() const;
/**
* Return a list of child nodes of the current node. The nodes are ordered from the bottommost up.
* The function is not recursive.
*/
QList<Node*> childNodes() const;
/**
* @brief addChildNode adds the given node in the list of children.
* @param child the node to be added
* @param above the node above which this node will be placed
* @return false if adding the node failed
*/
bool addChildNode(Node *child, Node *above);
/**
* @brief removeChildNode removes the given node from the list of children.
* @param child the node to be removed
*/
bool removeChildNode(Node *child);
/**
* @brief setChildNodes this replaces the existing set of child nodes with the new set.
* @param nodes The list of nodes that will become children, bottom-up -- the first node,
* is the bottom-most node in the stack.
*/
void setChildNodes(QList<Node*> nodes);
/**
* colorDepth A string describing the color depth of the image:
* <ul>
* <li>U8: unsigned 8 bits integer, the most common type</li>
* <li>U16: unsigned 16 bits integer</li>
* <li>F16: half, 16 bits floating point. Only available if Krita was built with OpenEXR</li>
* <li>F32: 32 bits floating point</li>
* </ul>
* @return the color depth.
*/
QString colorDepth() const;
/**
* @brief colorModel retrieve the current color model of this document:
* <ul>
* <li>A: Alpha mask</li>
* <li>RGBA: RGB with alpha channel (The actual order of channels is most often BGR!)</li>
* <li>XYZA: XYZ with alpha channel</li>
* <li>LABA: LAB with alpha channel</li>
* <li>CMYKA: CMYK with alpha channel</li>
* <li>GRAYA: Gray with alpha channel</li>
* <li>YCbCrA: YCbCr with alpha channel</li>
* </ul>
* @return the internal color model string.
*/
QString colorModel() const;
/**
* @return the name of the current color profile
*/
QString colorProfile() const;
/**
* @brief setColorProfile set the color profile of the image to the given profile. The profile has to
* be registered with krita and be compatible with the current color model and depth; the image data
* is <i>not</i> converted.
* @param colorProfile
* @return if assigning the color profile worked
*/
bool setColorProfile(const QString &colorProfile);
/**
* @brief setColorSpace convert the node to the given colorspace
* @param colorModel A string describing the color model of the node:
* <ul>
* <li>A: Alpha mask</li>
* <li>RGBA: RGB with alpha channel (The actual order of channels is most often BGR!)</li>
* <li>XYZA: XYZ with alpha channel</li>
* <li>LABA: LAB with alpha channel</li>
* <li>CMYKA: CMYK with alpha channel</li>
* <li>GRAYA: Gray with alpha channel</li>
* <li>YCbCrA: YCbCr with alpha channel</li>
* </ul>
* @param colorDepth A string describing the color depth of the image:
* <ul>
* <li>U8: unsigned 8 bits integer, the most common type</li>
* <li>U16: unsigned 16 bits integer</li>
* <li>F16: half, 16 bits floating point. Only available if Krita was built with OpenEXR</li>
* <li>F32: 32 bits floating point</li>
* </ul>
* @param colorProfile a valid color profile for this color model and color depth combination.
*/
bool setColorSpace(const QString &colorModel, const QString &colorDepth, const QString &colorProfile);
/**
* @brief Krita layers can be animated, i.e., have frames.
* @return return true if the layer has frames. Currently, the scripting framework
* does not give access to the animation features.
*/
bool animated() const;
/**
* @brief enableAnimation make the current layer animated, so it can have frames.
*/
void enableAnimation() const;
/**
* @brief Should the node be visible in the timeline. It defaults to false
* with new layer
*/
void setShowInTimeline(bool showInTimeline) const;
/**
* @return is layer is shown in the timeline
*/
bool showInTimeline() const;
/**
* Sets the state of the node to the value of @param collapsed
*/
void setCollapsed(bool collapsed);
/**
* returns the collapsed state of this node
*/
bool collapsed() const;
/**
* Sets a color label index associated to the layer. The actual
* color of the label and the number of available colors is
* defined by Krita GUI configuration.
*/
int colorLabel() const;
/**
* @brief setColorLabel sets a color label index associated to the layer. The actual
* color of the label and the number of available colors is
* defined by Krita GUI configuration.
* @param index an integer corresponding to the set of available color labels.
*/
void setColorLabel(int index);
/**
* @brief inheritAlpha checks whether this node has the inherits alpha flag set
* @return true if the Inherit Alpha is set
*/
bool inheritAlpha() const;
/**
* set the Inherit Alpha flag to the given value
*/
void setInheritAlpha(bool value);
/**
* @brief locked checks whether the Node is locked. A locked node cannot be changed.
* @return true if the Node is locked, false if it hasn't been locked.
*/
bool locked() const;
/**
* set the Locked flag to the give value
*/
void setLocked(bool value);
/**
* @brief does the node have any content in it?
* @return if node has any content in it
*/
bool hasExtents();
/**
* @return the user-visible name of this node.
*/
QString name() const;
/**
* rename the Node to the given name
*/
void setName(QString name);
/**
* return the opacity of the Node. The opacity is a value between 0 and 255.
*/
int opacity() const;
/**
* set the opacity of the Node to the given value. The opacity is a value between 0 and 255.
*/
void setOpacity(int value);
/**
* return the Node that is the parent of the current Node, or 0 if this is the root Node.
*/
Node* parentNode() const;
/**
* @brief type Krita has several types of nodes, split in layers and masks. Group
* layers can contain other layers, any layer can contain masks.
*
* @return The type of the node. Valid types are:
* <ul>
* <li>paintlayer
* <li>grouplayer
* <li>filelayer
* <li>filterlayer
* <li>filllayer
* <li>clonelayer
* <li>vectorlayer
* <li>transparencymask
* <li>filtermask
* <li>transformmask
* <li>selectionmask
* <li>colorizemask
* </ul>
*
* If the Node object isn't wrapping a valid Krita layer or mask object, and
* empty string is returned.
*/
virtual QString type() const;
/**
* @brief icon
* @return the icon associated with the layer.
*/
QIcon icon() const;
/**
* Check whether the current Node is visible in the layer stack
*/
bool visible() const;
+ /**
+ * Check to see if frame number on layer is a keyframe
+ */
+ bool hasKeyframeAtTime(int frameNumber);
+
/**
* Set the visibility of the current node to @param visible
*/
void setVisible(bool visible);
/**
* @brief pixelData reads the given rectangle from the Node's paintable pixels, if those
* exist, and returns it as a byte array. The pixel data starts top-left, and is ordered row-first.
*
* The byte array can be interpreted as follows: 8 bits images have one byte per channel,
* and as many bytes as there are channels. 16 bits integer images have two bytes per channel,
* representing an unsigned short. 16 bits float images have two bytes per channel, representing
* a half, or 16 bits float. 32 bits float images have four bytes per channel, representing a
* float.
*
* You can read outside the node boundaries; those pixels will be transparent black.
*
* The order of channels is:
*
* <ul>
* <li>Integer RGBA: Blue, Green, Red, Alpha
* <li>Float RGBA: Red, Green, Blue, Alpha
* <li>GrayA: Gray, Alpha
* <li>Selection: selectedness
* <li>LabA: L, a, b, Alpha
* <li>CMYKA: Cyan, Magenta, Yellow, Key, Alpha
* <li>XYZA: X, Y, Z, A
* <li>YCbCrA: Y, Cb, Cr, Alpha
* </ul>
*
* The byte array is a copy of the original node data. In Python, you can use bytes, bytearray
* and the struct module to interpret the data and construct, for instance, a Pillow Image object.
*
* If you read the pixeldata of a mask, a filter or generator layer, you get the selection bytes,
* which is one channel with values in the range from 0..255.
*
* If you want to change the pixels of a node you can write the pixels back after manipulation
* with setPixelData(). This will only succeed on nodes with writable pixel data, e.g not on groups
* or file layers.
*
* @param x x position from where to start reading
* @param y y position from where to start reading
* @param w row length to read
* @param h number of rows to read
* @return a QByteArray with the pixel data. The byte array may be empty.
*/
QByteArray pixelData(int x, int y, int w, int h) const;
/**
* @brief pixelDataAtTime a basic function to get pixeldata from an animated node at a given time.
* @param x the position from the left to start reading.
* @param y the position from the top to start reader
* @param w the row length to read
* @param h the number of rows to read
* @param time the frame number
* @return a QByteArray with the pixel data. The byte array may be empty.
*/
QByteArray pixelDataAtTime(int x, int y, int w, int h, int time) const;
/**
* @brief projectionPixelData reads the given rectangle from the Node's projection (that is, what the node
* looks like after all sub-Nodes (like layers in a group or masks on a layer) have been applied,
* and returns it as a byte array. The pixel data starts top-left, and is ordered row-first.
*
* The byte array can be interpreted as follows: 8 bits images have one byte per channel,
* and as many bytes as there are channels. 16 bits integer images have two bytes per channel,
* representing an unsigned short. 16 bits float images have two bytes per channel, representing
* a half, or 16 bits float. 32 bits float images have four bytes per channel, representing a
* float.
*
* You can read outside the node boundaries; those pixels will be transparent black.
*
* The order of channels is:
*
* <ul>
* <li>Integer RGBA: Blue, Green, Red, Alpha
* <li>Float RGBA: Red, Green, Blue, Alpha
* <li>GrayA: Gray, Alpha
* <li>Selection: selectedness
* <li>LabA: L, a, b, Alpha
* <li>CMYKA: Cyan, Magenta, Yellow, Key, Alpha
* <li>XYZA: X, Y, Z, A
* <li>YCbCrA: Y, Cb, Cr, Alpha
* </ul>
*
* The byte array is a copy of the original node data. In Python, you can use bytes, bytearray
* and the struct module to interpret the data and construct, for instance, a Pillow Image object.
*
* If you read the projection of a mask, you get the selection bytes, which is one channel with
* values in the range from 0..255.
*
* If you want to change the pixels of a node you can write the pixels back after manipulation
* with setPixelData(). This will only succeed on nodes with writable pixel data, e.g not on groups
* or file layers.
*
* @param x x position from where to start reading
* @param y y position from where to start reading
* @param w row length to read
* @param h number of rows to read
* @return a QByteArray with the pixel data. The byte array may be empty.
*/
QByteArray projectionPixelData(int x, int y, int w, int h) const;
/**
* @brief setPixelData writes the given bytes, of which there must be enough, into the
* Node, if the Node has writable pixel data:
*
* <ul>
* <li>paint layer: the layer's original pixels are overwritten
* <li>filter layer, generator layer, any mask: the embedded selection's pixels are overwritten.
* <b>Note:</b> for these
* </ul>
*
* File layers, Group layers, Clone layers cannot be written to. Calling setPixelData on
* those layer types will silently do nothing.
*
* @param value the byte array representing the pixels. There must be enough bytes available.
* Krita will take the raw pointer from the QByteArray and start reading, not stopping before
* (number of channels * size of channel * w * h) bytes are read.
*
* @param x the x position to start writing from
* @param y the y position to start writing from
* @param w the width of each row
* @param h the number of rows to write
*/
void setPixelData(QByteArray value, int x, int y, int w, int h);
/**
* @brief bounds return the exact bounds of the node's paint device
* @return the bounds, or an empty QRect if the node has no paint device or is empty.
*/
QRect bounds() const;
/**
* move the pixels to the given x, y location in the image coordinate space.
*/
void move(int x, int y);
/**
* @brief position returns the position of the paint device of this node. The position is
* always 0,0 unless the layer has been moved. If you want to know the topleft position of
* the rectangle around the actual non-transparent pixels in the node, use bounds().
* @return the top-left position of the node
*/
QPoint position() const;
/**
* @brief remove removes this node from its parent image.
*/
bool remove();
/**
* @brief duplicate returns a full copy of the current node. The node is not inserted in the graphic
* @return a valid Node object or 0 if the node couldn't be duplicated.
*/
Node* duplicate();
/**
* @brief save exports the given node with this filename. The extension of the filename determines the filetype.
* @param filename the filename including extension
* @param xRes the horizontal resolution in pixels per pt (there are 72 pts in an inch)
* @param yRes the horizontal resolution in pixels per pt (there are 72 pts in an inch)
+ * @param exportConfiguration a configuration object appropriate to the file format.
+ * See Document->exportImage for InfoObject details.
* @return true if saving succeeded, false if it failed.
*/
- bool save(const QString &filename, double xRes, double yRes);
+ bool save(const QString &filename, double xRes, double yRes, const InfoObject &exportConfiguration);
/**
* @brief mergeDown merges the given node with the first visible node underneath this node in the layerstack.
* This will drop all per-layer metadata.
*/
Node *mergeDown();
/**
* @brief scaleNode
- * @param width
- * @param height
+ * @param origin the origin point
+ * @param width the width
+ * @param height the height
* @param strategy the scaling strategy. There's several ones amongst these that aren't available in the regular UI.
* <ul>
* <li>Hermite</li>
* <li>Bicubic - Adds pixels using the color of surrounding pixels. Produces smoother tonal gradations than Bilinear.</li>
* <li>Box - Replicate pixels in the image. Preserves all the original detail, but can produce jagged effects.</li>
* <li>Bilinear - Adds pixels averaging the color values of surrounding pixels. Produces medium quality results when the image is scaled from half to two times the original size.</li>
* <li>Bell</li>
* <li>BSpline</li>
* <li>Lanczos3 - Offers similar results than Bicubic, but maybe a little bit sharper. Can produce light and dark halos along strong edges.</li>
* <li>Mitchell</li>
* </ul>
*/
- void scaleNode(const QPointF &origin, int width, int height, QString strategy);
+ void scaleNode(QPointF origin, int width, int height, QString strategy);
/**
* @brief rotateNode rotate this layer by the given radians.
* @param radians amount the layer should be rotated in, in radians.
*/
void rotateNode(double radians);
/**
* @brief cropNode crop this layer.
* @param x the left edge of the cropping rectangle.
* @param y the top edge of the cropping rectangle
* @param w the right edge of the cropping rectangle
* @param h the bottom edge of the cropping rectangle
*/
void cropNode(int x, int y, int w, int h);
/**
* @brief shearNode perform a shear operation on this node.
* @param angleX the X-angle in degrees to shear by
* @param angleY the Y-angle in degrees to shear by
*/
void shearNode(double angleX, double angleY);
/**
* @brief thumbnail create a thumbnail of the given dimensions. The thumbnail is sized according
* to the layer dimensions, not the image dimensions. If the requested size is too big a null
* QImage is created. If the current node cannot generate a thumbnail, a transparent QImage of the
* requested size is generated.
* @return a QImage representing the layer contents.
*/
QImage thumbnail(int w, int h);
private:
friend class Filter;
friend class Document;
friend class Selection;
friend class GroupLayer;
friend class FileLayer;
friend class FilterLayer;
friend class FillLayer;
friend class VectorLayer;
friend class FilterMask;
friend class SelectionMask;
/**
* @brief paintDevice gives access to the internal paint device of this Node
* @return the paintdevice or 0 if the node does not have an editable paint device.
*/
KisPaintDeviceSP paintDevice() const;
KisImageSP image() const;
KisNodeSP node() const;
struct Private;
Private *const d;
};
#endif // LIBKIS_NODE_H
diff --git a/libs/libkis/Palette.h b/libs/libkis/Palette.h
index d6aa1d431d..2d853240b5 100644
--- a/libs/libkis/Palette.h
+++ b/libs/libkis/Palette.h
@@ -1,180 +1,180 @@
/*
* Copyright (c) 2017 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef LIBKIS_PALETTE_H
#define LIBKIS_PALETTE_H
#include <QObject>
#include <QList>
#include "kritalibkis_export.h"
#include "libkis.h"
#include "Resource.h"
#include "KoColorSet.h"
#include <Swatch.h>
class ManagedColor;
/**
* @brief The Palette class
* Palette is a resource object that stores organised color data.
* It's purpose is to allow artists to save colors and store them.
*
* An example for printing all the palettes and the entries:
*
* @code
import sys
from krita import *
resources = Application.resources("palette")
for (k, v) in resources.items():
print(k)
palette = Palette(v)
for x in range(palette.numberOfEntries()):
entry = palette.colorSetEntryByIndex(x)
c = palette.colorForEntry(entry);
print(x, entry.name(), entry.id(), entry.spotColor(), c.toQString())
* @endcode
*/
class KRITALIBKIS_EXPORT Palette : public QObject
{
public:
Palette(Resource *resource);
~Palette() override;
/**
* @brief numberOfEntries
* @return
*/
int numberOfEntries() const;
/**
* @brief columnCount
* @return the amount of columns this palette is set to use.
*/
int columnCount();
/**
* @brief setColumnCount
* Set the amount of columns this palette should use.
*/
void setColumnCount(int columns);
/**
* @brief comment
* @return the comment or description associated with the palette.
*/
QString comment();
/**
* @brief setComment
* set the comment or description associated with the palette.
* @param comment
*/
void setComment(QString comment);
/**
* @brief groupNames
* @return the list of group names. This is list is in the order these groups are in the file.
*/
QStringList groupNames() const;
/**
* @brief addGroup
* @param name of the new group
* @return whether adding the group was successful.
*/
bool addGroup(QString name);
/**
* @brief removeGroup
* @param name the name of the group to remove.
* @param keepColors whether or not to delete all the colors inside, or to move them to the default group.
* @return
*/
bool removeGroup(QString name, bool keepColors = true);
/**
* @brief colorsCountTotal
* @return the total amount of entries in the whole group
*/
int colorsCountTotal();
/**
* @brief colorSetEntryByIndex
* get the colorsetEntry from the global index.
* @param index the global index
* @return the colorset entry
*/
Swatch *colorSetEntryByIndex(int index);
/**
* @brief colorSetEntryFromGroup
* @param index index in the group.
* @param groupName the name of the group to get the color from.
* @return the colorsetentry.
*/
Swatch *colorSetEntryFromGroup(int index, const QString &groupName);
/**
* @brief addEntry
* add an entry to a group. Gets appended to the end.
* @param entry the entry
* @param groupName the name of the group to add to.
*/
void addEntry(Swatch entry, QString groupName = QString());
/**
* @brief removeEntry
- * remove the entry at @param index from the group @param groupName.
+ * remove the entry at @p index from the group @p groupName.
*/
void removeEntry(int index, const QString &groupName);
/**
* @brief changeGroupName
* change the group name.
* @param oldGroupName the old groupname to change.
* @param newGroupName the new name to change it into.
* @return whether successful. Reasons for failure include not knowing have oldGroupName
*/
bool changeGroupName(QString oldGroupName, QString newGroupName);
/**
* @brief moveGroup
* move the group to before groupNameInsertBefore.
* @param groupName group to move.
* @param groupNameInsertBefore group to inset before.
* @return whether successful. Reasons for failure include either group not existing.
*/
bool moveGroup(const QString &groupName, const QString &groupNameInsertBefore = QString());
/**
* @brief save
* save the palette
* @return whether it was successful.
*/
bool save();
private:
friend class PaletteView;
struct Private;
Private *const d;
/**
* @brief colorSet
* @return gives qa KoColorSet object back
*/
KoColorSet *colorSet();
};
#endif // LIBKIS_PALETTE_H
diff --git a/libs/libkis/PaletteView.h b/libs/libkis/PaletteView.h
index f9ee621fd3..968e209186 100644
--- a/libs/libkis/PaletteView.h
+++ b/libs/libkis/PaletteView.h
@@ -1,110 +1,111 @@
/*
* Copyright (c) 2017 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef LIBKIS_PALETTE_VIEW_H
#define LIBKIS_PALETTE_VIEW_H
#include <QObject>
#include <QScopedPointer>
#include "kritalibkis_export.h"
#include "libkis.h"
#include "Palette.h"
#include "ManagedColor.h"
#include "KoColorSet.h"
#include <kis_palette_view.h>
#include <KisPaletteModel.h>
#include <Swatch.h>
class KisSwatch;
/**
+ * @class PaletteView
* @brief The PaletteView class is a wrapper around a MVC method for handling
* palettes. This class shows a nice widget that can drag and drop, edit colors in a colorset
* and will handle adding and removing entries if you'd like it to.
*/
class KRITALIBKIS_EXPORT PaletteView : public QWidget
{
Q_OBJECT
public:
PaletteView(QWidget *parent = 0);
~PaletteView();
public Q_SLOTS:
/**
* @brief setPalette
* Set a new palette.
* @param palette
*/
void setPalette(Palette *palette);
/**
* @brief addEntryWithDialog
* This gives a simple dialog for adding colors, with options like
* adding name, id, and to which group the color should be added.
* @param color the default color to add
* @return whether it was successful.
*/
bool addEntryWithDialog(ManagedColor *color);
/**
* @brief addGroupWithDialog
* gives a little dialog to ask for the desired groupname.
* @return whether this was successful.
*/
bool addGroupWithDialog();
/**
* @brief removeSelectedEntryWithDialog
* removes the selected entry. If it is a group, it pop up a dialog
* asking whether the colors should also be removed.
* @return whether this was successful
*/
bool removeSelectedEntryWithDialog();
/**
* @brief trySelectClosestColor
* tries to select the closest color to the one given.
* It does not force a change on the active color.
* @param color the color to compare to.
*/
void trySelectClosestColor(ManagedColor *color);
Q_SIGNALS:
/**
* @brief entrySelectedForeGround
* fires when a swatch is selected with leftclick.
* @param entry
*/
void entrySelectedForeGround(Swatch entry);
/**
* @brief entrySelectedBackGround
* fires when a swatch is selected with rightclick.
* @param entry
*/
void entrySelectedBackGround(Swatch entry);
private Q_SLOTS:
void fgSelected(KisSwatch swatch);
void bgSelected(KisSwatch swatch);
private:
struct Private;
const QScopedPointer<Private> d;
};
#endif // LIBKIS_PALETTE_VIEW_H
diff --git a/libs/libkis/Window.h b/libs/libkis/Window.h
index b0b0bebf58..aab7b5d2a8 100644
--- a/libs/libkis/Window.h
+++ b/libs/libkis/Window.h
@@ -1,110 +1,110 @@
/*
* Copyright (c) 2016 Boudewijn Rempt <boud@valdyas.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef LIBKIS_WINDOW_H
#define LIBKIS_WINDOW_H
#include <QObject>
#include <QAction>
#include <QMainWindow>
#include "kritalibkis_export.h"
#include "libkis.h"
#include <KisMainWindow.h>
/**
* Window represents one Krita mainwindow. A window can have any number
* of views open on any number of documents.
*/
class KRITALIBKIS_EXPORT Window : public QObject
{
Q_OBJECT
public:
explicit Window(KisMainWindow *window, QObject *parent = 0);
~Window() override;
bool operator==(const Window &other) const;
bool operator!=(const Window &other) const;
public Q_SLOTS:
/**
* Return a handle to the QMainWindow widget. This is useful
* to e.g. parent dialog boxes and message box.
*/
QMainWindow *qwindow() const;
/**
* @return a list of open views in this window
*/
QList<View*> views() const;
/**
* Open a new view on the given document in this window
*/
View *addView(Document *document);
/**
* Make the given view active in this window. If the view
* does not belong to this window, nothing happens.
*/
void showView(View *view);
/**
* @return the currently active view or 0 if no view is active
*/
View *activeView() const;
/**
* @brief activate activates this Window.
*/
void activate();
/**
* @brief close the active window and all its Views. If there
* are no Views left for a given Document, that Document will
* also be closed.
*/
void close();
/**
* @brief createAction creates a QAction object and adds it to the action
* manager for this Window.
* @param id The unique id for the action. This will be used to
* propertize the action if any .action file is present
* @param text The user-visible text of the action. If empty, the text from the
* .action file is used.
- * @param menu a /-separated string that describes which menu the action should
+ * @param menuLocation a /-separated string that describes which menu the action should
* be places in. Default is "tools/scripts"
* @return the new action.
*/
QAction *createAction(const QString &id, const QString &text = QString(), const QString &menuLocation = QString("tools/scripts"));
Q_SIGNALS:
/// Emitted when the window is closed.
void windowClosed();
private:
struct Private;
Private *const d;
};
#endif // LIBKIS_WINDOW_H
diff --git a/libs/libqml/DocumentListModel.cpp b/libs/libqml/DocumentListModel.cpp
index eeef8bc785..780c3a1389 100644
--- a/libs/libqml/DocumentListModel.cpp
+++ b/libs/libqml/DocumentListModel.cpp
@@ -1,188 +1,188 @@
/* This file is part of the KDE project
* Copyright (C) 2012 KO GmbH. Contact: Boudewijn Rempt <boud@kogmbh.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "DocumentListModel.h"
#include <QTimer>
#include <QLocale>
#include <klocalizedstring.h>
class DocumentListModel::Private
{
public:
Private( DocumentListModel *qq) : q(qq), filter(DocumentListModel::UnknownType) { }
void relayout();
DocumentListModel* q;
QList<DocumentInfo> allDocumentInfos;
QList<DocumentInfo> currentDocumentInfos;
DocumentType filter;
QString searchPattern;
QTimer *timer;
};
QHash<QString, DocumentListModel::DocumentType> DocumentListModel::sm_extensions = QHash<QString, DocumentListModel::DocumentType>();
DocumentListModel::DocumentListModel(QObject *parent)
: QAbstractListModel(parent), d(new Private(this))
{
qRegisterMetaType<DocumentListModel::DocumentInfo>();
}
DocumentListModel::~DocumentListModel()
{
}
QHash<int, QByteArray> DocumentListModel::roleNames() const
{
QHash<int, QByteArray> roleNames = QAbstractListModel::roleNames();
roleNames[FileNameRole] = "fileName";
roleNames[FilePathRole] = "filePath";
roleNames[DocTypeRole] = "docType";
roleNames[FileSizeRole] = "fileSize";
roleNames[AuthorNameRole] = "authorName";
roleNames[AccessedTimeRole] = "accessedTime";
roleNames[ModifiedTimeRole] = "modifiedTime";
roleNames[UUIDRole] = "uuid";
return roleNames;
}
void DocumentListModel::addDocument(const DocumentInfo &info)
{
if (d->allDocumentInfos.contains(info))
{
return;
}
d->allDocumentInfos.append(info);
}
int DocumentListModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return d->currentDocumentInfos.count();
}
int DocumentListModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return 1;
}
QVariant DocumentListModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
const int row = index.row();
const DocumentInfo &info = d->currentDocumentInfos[row];
switch (role) {
- case FileNameRole: // intentional fall through
+ case FileNameRole: Q_FALLTHROUGH();
case Qt::DisplayRole: return info.fileName;
case FilePathRole: return info.filePath;
case DocTypeRole: return info.docType;
case FileSizeRole: return info.fileSize;
case AuthorNameRole: return info.authorName;
case AccessedTimeRole: return prettyTime(info.accessedTime);
case ModifiedTimeRole: return prettyTime(info.modifiedTime);
case UUIDRole: return info.uuid;
default: return QVariant();
}
}
QString DocumentListModel::prettyTime( const QDateTime& theTime)
{
return QLocale().toString(theTime, QLocale::LongFormat);
}
QVariant DocumentListModel::headerData(int section, Qt::Orientation orientation, int role) const
{
Q_UNUSED(section)
Q_UNUSED(orientation)
Q_UNUSED(role)
return QVariant();
}
DocumentListModel::DocumentType DocumentListModel::filter()
{
return d->filter;
}
void DocumentListModel::setFilter( DocumentListModel::DocumentType newFilter)
{
d->filter = newFilter;
d->relayout();
}
DocumentListModel::DocumentType DocumentListModel::typeForFile ( const QString& file )
{
if (sm_extensions.isEmpty()) {
sm_extensions["odt"] = TextDocumentType;
sm_extensions["fodt"] = TextDocumentType;
sm_extensions["doc"] = TextDocumentType;
sm_extensions["docx"] = TextDocumentType;
sm_extensions["txt"] = TextDocumentType;
sm_extensions["odp"] = PresentationType;
sm_extensions["fodp"] = PresentationType;
sm_extensions["ppt"] = PresentationType;
sm_extensions["pptx"] = PresentationType;
sm_extensions["ods"] = SpreadsheetType;
sm_extensions["fods"] = SpreadsheetType;
sm_extensions["xls"] = SpreadsheetType;
sm_extensions["xlsx"] = SpreadsheetType;
sm_extensions["pdf"] = PDFDocumentType;
}
QString ext = file.split('.').last().toLower();
if (sm_extensions.contains(ext)) {
return sm_extensions.value(ext);
}
return UnknownType;
}
void DocumentListModel::Private::relayout()
{
emit q->layoutAboutToBeChanged();
QList<DocumentInfo> newList;
Q_FOREACH (const DocumentInfo &docInfo, allDocumentInfos) {
if (filter == UnknownType || docInfo.docType == filter) {
if (searchPattern.isEmpty() || docInfo.fileName.contains(searchPattern, Qt::CaseInsensitive)) {
newList.append(docInfo);
}
}
}
currentDocumentInfos = newList;
emit q->layoutChanged();
q->beginResetModel();
q->endResetModel();
}
diff --git a/libs/libqml/DocumentManager.cpp b/libs/libqml/DocumentManager.cpp
index 9df83a5680..c53b148800 100644
--- a/libs/libqml/DocumentManager.cpp
+++ b/libs/libqml/DocumentManager.cpp
@@ -1,311 +1,311 @@
/* This file is part of the KDE project
* Copyright (C) 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* 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 "DocumentManager.h"
#include "ProgressProxy.h"
#include "Settings.h"
#include "RecentFileManager.h"
#include <KoColor.h>
#include <KisPart.h>
#include <KoColorSpaceRegistry.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <KisMimeDatabase.h>
#include <QCoreApplication>
class DocumentManager::Private
{
public:
Private()
: proxy(0)
, document(0)
, settingsManager(0)
, recentFileManager(0)
, newDocWidth(0)
, newDocHeight(0)
, newDocResolution(0)
, importingDocument(false)
, temporaryFile(false)
{ }
ProgressProxy* proxy;
QPointer<KisDocument> document;
Settings* settingsManager;
RecentFileManager* recentFileManager;
QString saveAsFilename;
QString openDocumentFilename;
int newDocWidth, newDocHeight; float newDocResolution;
bool importingDocument;
QVariantMap newDocOptions;
bool temporaryFile;
};
DocumentManager *DocumentManager::sm_instance = 0;
KisDocument* DocumentManager::document() const
{
return d->document;
}
ProgressProxy* DocumentManager::progressProxy() const
{
return d->proxy;
}
Settings* DocumentManager::settingsManager() const
{
return d->settingsManager;
}
void DocumentManager::setSettingsManager(Settings* newManager)
{
d->settingsManager = newManager;
}
RecentFileManager* DocumentManager::recentFileManager() const
{
return d->recentFileManager;
}
bool DocumentManager::isTemporaryFile() const
{
return d->temporaryFile;
}
void DocumentManager::newDocument(int width, int height, float resolution)
{
closeDocument();
d->newDocWidth = width;
d->newDocHeight = height;
d->newDocResolution = resolution;
QTimer::singleShot(300, this, SLOT(delayedNewDocument()));
}
void DocumentManager::newDocument(const QVariantMap& options)
{
closeDocument();
d->newDocOptions = options;
QTimer::singleShot(300, this, SLOT(delayedNewDocument()));
}
void DocumentManager::delayedNewDocument()
{
d->document = KisPart::instance()->createDocument();
if (qAppName().contains("sketch")) {
d->document->setFileBatchMode(true);
}
if (d->newDocOptions.isEmpty())
{
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(0);
QColor qc(Qt::white);
qc.setAlpha(0);
KoColor bgColor(qc, cs);
- d->document->newImage("New Image", d->newDocWidth, d->newDocHeight, KoColorSpaceRegistry::instance()->rgb8(), bgColor, true, 2, "", d->newDocResolution);
+ d->document->newImage("New Image", d->newDocWidth, d->newDocHeight, KoColorSpaceRegistry::instance()->rgb8(), bgColor, KisConfig::RASTER_LAYER, 2, "", d->newDocResolution);
d->document->resetURL();
}
else if (d->newDocOptions.contains("template")) {
QUrl url(d->newDocOptions.value("template").toString().remove("template://"));
bool ok = d->document->loadNativeFormat(url.toLocalFile());
d->document->setModified(false);
d->document->undoStack()->clear();
if (ok) {
QString mimeType = KisMimeDatabase::mimeTypeForFile(url.toLocalFile());
// in case this is a open document template remove the -template from the end
mimeType.remove( QRegExp( "-template$" ) );
d->document->setMimeTypeAfterLoading(mimeType);
d->document->resetURL();
}
}
else
{
QString name = d->newDocOptions.value("name", "New Image").toString();
int width = d->newDocOptions.value("width").toInt();
int height = d->newDocOptions.value("height").toInt();
// internal resolution is pixels per point, not ppi
float res = d->newDocOptions.value("resolution", 72.0f).toFloat() / 72.0f;
QString colorModelId = d->newDocOptions.value("colorModelId").toString();
QString colorDepthId = d->newDocOptions.value("colorDepthId").toString();
QString colorProfileId = d->newDocOptions.value("colorProfileId").toString();
const KoColorSpace* cs;
if(colorModelId.isEmpty() || colorDepthId.isEmpty() || colorProfileId.isEmpty()) {
cs = KoColorSpaceRegistry::instance()->rgb8();
}
else
{
cs = KoColorSpaceRegistry::instance()->colorSpace(colorModelId, colorDepthId, colorProfileId);
}
QColor background = d->newDocOptions.value("backgroundColor", QColor("white")).value<QColor>();
background.setAlphaF(d->newDocOptions.value("backgroundOpacity", 1.0f).toFloat());
KoColor bg(background, cs);
- d->document->newImage(name, width, height, cs, bg, true, 1, "", res);
+ d->document->newImage(name, width, height, cs, bg, KisConfig::RASTER_LAYER, 1, "", res);
d->document->resetURL();
}
KisPart::instance()->addDocument(d->document);
d->temporaryFile = true;
emit documentChanged();
}
void DocumentManager::openDocument(const QString& document, bool import)
{
closeDocument();
d->openDocumentFilename = document;
d->importingDocument = import;
QTimer::singleShot(300, this, SLOT(delayedOpenDocument()));
}
void DocumentManager::delayedOpenDocument()
{
d->document = KisPart::instance()->createDocument();
if (qAppName().contains("sketch")) {
d->document->setFileBatchMode(true);
}
connect(d->document, SIGNAL(completed()), this, SLOT(onLoadCompleted()));
connect(d->document, SIGNAL(canceled(QString)), this, SLOT(onLoadCanceled(QString)));
// TODO: still needed?
d->document->setModified(false);
if (d->importingDocument)
d->document->importDocument(QUrl::fromLocalFile(d->openDocumentFilename));
else
d->document->openUrl(QUrl::fromLocalFile(d->openDocumentFilename));
// TODO: handle fail of open/import
d->recentFileManager->addRecent(d->openDocumentFilename);
KisPart::instance()->addDocument(d->document);
d->temporaryFile = false;
}
// Separate from openDocument to handle async loading (remote URLs)
void DocumentManager::onLoadCompleted()
{
KisDocument *newdoc = qobject_cast<KisDocument*>(sender());
disconnect(newdoc, SIGNAL(completed()), this, SLOT(onLoadCompleted()));
disconnect(newdoc, SIGNAL(canceled(QString)), this, SLOT(onLoadCanceled(QString)));
emit documentChanged();
}
void DocumentManager::onLoadCanceled(const QString &/*errMsg*/)
{
// if (!errMsg.isEmpty()) // empty when canceled by user
// QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg);
// ... can't delete the document, it's the one who emitted the signal...
KisDocument* newdoc = qobject_cast<KisDocument*>(sender());
Q_ASSERT(newdoc);
disconnect(newdoc, SIGNAL(completed()), this, SLOT(onLoadCompleted()));
disconnect(newdoc, SIGNAL(canceled(QString)), this, SLOT(onLoadCanceled(QString)));
}
void DocumentManager::closeDocument()
{
if (d->document) {
emit aboutToDeleteDocument();
d->document->closeUrl(false);
//d->document->deleteLater();
d->document = 0;
}
}
bool DocumentManager::save()
{
// if (d->document->save())
// {
// d->recentFileManager->addRecent(d->document->url().toLocalFile());
// d->settingsManager->setCurrentFile(d->document->url().toLocalFile());
// emit documentSaved();
// return true;
// }
return false;
}
void DocumentManager::saveAs(const QString &filename, const QString &mimetype)
{
d->document->setMimeType(mimetype.toLatin1());
d->saveAsFilename = filename;
// Yes. This is a massive hack. Basically, we need to wait a little while, to ensure
// the save call happens late enough for a variety of UI things to happen first.
// A second seems like a long time, but well, we do have file system interaction here,
// so for now, we can get away with it.
QTimer::singleShot(300, this, SLOT(delayedSaveAs()));
}
void DocumentManager::delayedSaveAs()
{
//d->document->saveAs(QUrl::fromLocalFile(d->saveAsFilename));
d->settingsManager->setCurrentFile(d->saveAsFilename);
d->recentFileManager->addRecent(d->saveAsFilename);
emit documentSaved();
}
void DocumentManager::reload()
{
QUrl url = d->document->url();
closeDocument();
d->openDocumentFilename = url.toLocalFile();
QTimer::singleShot(0, this, SLOT(delayedOpenDocument()));
}
void DocumentManager::setTemporaryFile(bool temp)
{
d->temporaryFile = temp;
emit documentSaved();
}
DocumentManager* DocumentManager::instance()
{
if (!sm_instance) {
sm_instance = new DocumentManager(QCoreApplication::instance());
}
return sm_instance;
}
DocumentManager::DocumentManager(QObject* parent)
: QObject(parent), d(new Private)
{
d->proxy = new ProgressProxy(this);
d->recentFileManager = new RecentFileManager(this);
}
DocumentManager::~DocumentManager()
{
delete d;
}
diff --git a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-Black.otf b/libs/libqml/qmlthemes/default/fonts/SourceSansPro-Black.otf
index e69de29bb2..ca6aa8b422 100644
Binary files a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-Black.otf and b/libs/libqml/qmlthemes/default/fonts/SourceSansPro-Black.otf differ
diff --git a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-BlackIt.otf b/libs/libqml/qmlthemes/default/fonts/SourceSansPro-BlackIt.otf
index e69de29bb2..623cad710e 100644
Binary files a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-BlackIt.otf and b/libs/libqml/qmlthemes/default/fonts/SourceSansPro-BlackIt.otf differ
diff --git a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-Bold.otf b/libs/libqml/qmlthemes/default/fonts/SourceSansPro-Bold.otf
index e69de29bb2..3646cb5849 100644
Binary files a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-Bold.otf and b/libs/libqml/qmlthemes/default/fonts/SourceSansPro-Bold.otf differ
diff --git a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-BoldIt.otf b/libs/libqml/qmlthemes/default/fonts/SourceSansPro-BoldIt.otf
index e69de29bb2..adb1ae8706 100644
Binary files a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-BoldIt.otf and b/libs/libqml/qmlthemes/default/fonts/SourceSansPro-BoldIt.otf differ
diff --git a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-ExtraLight.otf b/libs/libqml/qmlthemes/default/fonts/SourceSansPro-ExtraLight.otf
index e69de29bb2..74bc5f8fe8 100644
Binary files a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-ExtraLight.otf and b/libs/libqml/qmlthemes/default/fonts/SourceSansPro-ExtraLight.otf differ
diff --git a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-ExtraLightIt.otf b/libs/libqml/qmlthemes/default/fonts/SourceSansPro-ExtraLightIt.otf
index e69de29bb2..7031e12d9e 100644
Binary files a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-ExtraLightIt.otf and b/libs/libqml/qmlthemes/default/fonts/SourceSansPro-ExtraLightIt.otf differ
diff --git a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-It.otf b/libs/libqml/qmlthemes/default/fonts/SourceSansPro-It.otf
index e69de29bb2..767472d35b 100644
Binary files a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-It.otf and b/libs/libqml/qmlthemes/default/fonts/SourceSansPro-It.otf differ
diff --git a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-Light.otf b/libs/libqml/qmlthemes/default/fonts/SourceSansPro-Light.otf
index e69de29bb2..2e6da7c308 100644
Binary files a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-Light.otf and b/libs/libqml/qmlthemes/default/fonts/SourceSansPro-Light.otf differ
diff --git a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-LightIt.otf b/libs/libqml/qmlthemes/default/fonts/SourceSansPro-LightIt.otf
index e69de29bb2..556f475831 100644
Binary files a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-LightIt.otf and b/libs/libqml/qmlthemes/default/fonts/SourceSansPro-LightIt.otf differ
diff --git a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-Regular.otf b/libs/libqml/qmlthemes/default/fonts/SourceSansPro-Regular.otf
index e69de29bb2..b9c6c5d3a9 100644
Binary files a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-Regular.otf and b/libs/libqml/qmlthemes/default/fonts/SourceSansPro-Regular.otf differ
diff --git a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-Semibold.otf b/libs/libqml/qmlthemes/default/fonts/SourceSansPro-Semibold.otf
index e69de29bb2..0c0636ec48 100644
Binary files a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-Semibold.otf and b/libs/libqml/qmlthemes/default/fonts/SourceSansPro-Semibold.otf differ
diff --git a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-SemiboldIt.otf b/libs/libqml/qmlthemes/default/fonts/SourceSansPro-SemiboldIt.otf
index e69de29bb2..b8922caf06 100644
Binary files a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-SemiboldIt.otf and b/libs/libqml/qmlthemes/default/fonts/SourceSansPro-SemiboldIt.otf differ
diff --git a/libs/odf/KoElementReference.h b/libs/odf/KoElementReference.h
index 613a925db7..750c3406cf 100644
--- a/libs/odf/KoElementReference.h
+++ b/libs/odf/KoElementReference.h
@@ -1,131 +1,131 @@
/*
* Copyright (c) 2011-2012 Boudewijn Rempt <boud@valdyas.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOELEMENTREFERENCE_H
#define KOELEMENTREFERENCE_H
#include <QSharedDataPointer>
#include <QSharedData>
#include <QUuid>
#include "KoXmlReaderForward.h"
#include "kritaodf_export.h"
class KoXmlWriter;
class KoElementReferenceData : public QSharedData
{
public:
KoElementReferenceData()
{
xmlid = QUuid::createUuid().toString();
xmlid.remove('{');
xmlid.remove('}');
}
KoElementReferenceData(const KoElementReferenceData &other)
: QSharedData(other)
, xmlid(other.xmlid)
{
}
~KoElementReferenceData() {}
QString xmlid;
};
/**
* KoElementReference is used to store unique identifiers for elements in an odf document.
* Element references are saved as xml:id and optionally for compatibility also as draw:id
* and text:id.
*
* You can use element references wherever you would have used a QString to refer to the id
* of an object.
*
* Element references are implicitly shared, so you can and should pass them along by value.
*/
class KRITAODF_EXPORT KoElementReference
{
public:
enum GenerationOption {
UUID = 0,
Counter = 1
};
enum SaveOption {
XmlId = 0x0,
DrawId = 0x1,
TextId = 0x2
};
Q_DECLARE_FLAGS(SaveOptions, SaveOption)
KoElementReference();
explicit KoElementReference(const QString &prefix);
KoElementReference(const QString &prefix, int counter);
KoElementReference(const KoElementReference &other);
KoElementReference &operator=(const KoElementReference &rhs);
bool operator==(const KoElementReference &other) const;
bool operator!=(const KoElementReference &other) const;
/**
* @return true if the xmlid is valid, i.e., not null
*/
bool isValid() const;
/**
* @brief loadOdf creates a new KoElementReference from the given element. If the element
* does not have an xml:id, draw:id or text:id attribute, and invalid element reference
* is returned.
* @param element the element that may contain xml:id, text:id or draw:id. xml:id has
* priority.
* @return a new element reference
*/
KoElementReference loadOdf(const KoXmlElement &element);
/**
* @brief saveOdf saves this element reference into the currently open element in the xml writer.
* @param writer the writer we save to
- * @param saveOptions determines which attributes we save. We always save the xml:id.
+ * @param saveOption determines which attributes we save. We always save the xml:id.
*/
void saveOdf(KoXmlWriter *writer, SaveOption saveOption = XmlId) const;
/**
* @brief toString creates a QString from the element reference
* @return a string that represents the element. Can be used in maps etc.
*/
QString toString() const;
/**
* Invalidate the reference
*/
void invalidate();
private:
QSharedDataPointer<KoElementReferenceData> d;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(KoElementReference::SaveOptions)
#endif // KOELEMENTREFERENCE_H
diff --git a/libs/odf/tests/CMakeLists.txt b/libs/odf/tests/CMakeLists.txt
index 1db98720bb..dc5ec1500d 100644
--- a/libs/odf/tests/CMakeLists.txt
+++ b/libs/odf/tests/CMakeLists.txt
@@ -1,21 +1,30 @@
set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} )
include(ECMAddTests)
include(KritaAddBrokenUnitTest)
ecm_add_tests(
TestKoGenStyles.cpp
TestOdfSettings.cpp
TestKoOdfLoadingContext.cpp
TestStorage.cpp
NAME_PREFIX "libs-odf-"
LINK_LIBRARIES kritaodf KF5::I18n Qt5::Test)
ecm_add_tests(
TestXmlWriter.cpp
kodomtest.cpp
TestKoUnit.cpp
- TestNumberStyle.cpp
TestKoElementReference.cpp
NAME_PREFIX "libs-odf-"
LINK_LIBRARIES kritaodf Qt5::Test)
+
+
+include(KritaAddBrokenUnitTest)
+
+krita_add_broken_unit_test(
+ TestNumberStyle.cpp
+ TEST_NAME TestNumberStyle
+ LINK_LIBRARIES kritaodf Qt5::Test
+ NAME_PREFIX "libs-odf-")
+
diff --git a/libs/pigment/CMakeLists.txt b/libs/pigment/CMakeLists.txt
index d41dfd9d0a..45102a9f80 100644
--- a/libs/pigment/CMakeLists.txt
+++ b/libs/pigment/CMakeLists.txt
@@ -1,123 +1,124 @@
project(kritapigment)
# we have to repeat platform specifics from top-level
if (WIN32)
include_directories(${CMAKE_SOURCE_DIR}/winquirks)
add_definitions(-D_USE_MATH_DEFINES)
add_definitions(-DNOMINMAX)
set(WIN32_PLATFORM_NET_LIBS ws2_32.lib netapi32.lib)
endif ()
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/resources
${CMAKE_CURRENT_SOURCE_DIR}/compositeops)
set(FILE_OPENEXR_SOURCES)
set(LINK_OPENEXR_LIB)
if(OPENEXR_FOUND)
include_directories(SYSTEM ${OPENEXR_INCLUDE_DIR})
set(LINK_OPENEXR_LIB ${OPENEXR_LIBRARIES})
add_definitions(${OPENEXR_DEFINITIONS})
endif()
set(LINK_VC_LIB)
if(HAVE_VC)
include_directories(SYSTEM ${Vc_INCLUDE_DIR})
set(LINK_VC_LIB ${Vc_LIBRARIES})
ko_compile_for_all_implementations_no_scalar(__per_arch_factory_objs compositeops/KoOptimizedCompositeOpFactoryPerArch.cpp)
message("Following objects are generated from the per-arch lib")
message("${__per_arch_factory_objs}")
endif()
add_subdirectory(tests)
add_subdirectory(benchmarks)
set(kritapigment_SRCS
DebugPigment.cpp
KoBasicHistogramProducers.cpp
KoColor.cpp
KoColorDisplayRendererInterface.cpp
KoColorConversionAlphaTransformation.cpp
KoColorConversionCache.cpp
KoColorConversions.cpp
KoColorConversionSystem.cpp
KoColorConversionTransformation.cpp
KoColorProofingConversionTransformation.cpp
KoColorConversionTransformationFactory.cpp
KoColorModelStandardIds.cpp
KoColorProfile.cpp
KoColorSpace.cpp
KoColorSpaceEngine.cpp
KoColorSpaceFactory.cpp
KoColorSpaceMaths.cpp
KoColorSpaceRegistry.cpp
KoColorProfileStorage.cpp
KoColorTransformation.cpp
KoColorTransformationFactory.cpp
KoColorTransformationFactoryRegistry.cpp
KoCompositeColorTransformation.cpp
KoCompositeOp.cpp
KoCompositeOpRegistry.cpp
KoCopyColorConversionTransformation.cpp
KoFallBackColorTransformation.cpp
KoHistogramProducer.cpp
KoMultipleColorConversionTransformation.cpp
KoUniqueNumberForIdServer.cpp
colorspaces/KoAlphaColorSpace.cpp
colorspaces/KoLabColorSpace.cpp
colorspaces/KoRgbU16ColorSpace.cpp
colorspaces/KoRgbU8ColorSpace.cpp
colorspaces/KoSimpleColorSpaceEngine.cpp
compositeops/KoOptimizedCompositeOpFactory.cpp
compositeops/KoOptimizedCompositeOpFactoryPerArch_Scalar.cpp
+ compositeops/KoAlphaDarkenParamsWrapper.cpp
${__per_arch_factory_objs}
colorprofiles/KoDummyColorProfile.cpp
resources/KoAbstractGradient.cpp
resources/KoColorSet.cpp
resources/KisSwatch.cpp
resources/KisSwatchGroup.cpp
resources/KoPattern.cpp
resources/KoResource.cpp
resources/KoMD5Generator.cpp
resources/KoHashGeneratorProvider.cpp
resources/KoStopGradient.cpp
resources/KoSegmentGradient.cpp
)
set (EXTRA_LIBRARIES ${LINK_OPENEXR_LIB} ${LINK_VC_LIB})
if(MSVC OR (WIN32 AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel"))
# avoid "cannot open file 'LIBC.lib'" error
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NODEFAULTLIB:LIBC.LIB")
endif()
add_library(kritapigment SHARED ${kritapigment_SRCS})
generate_export_header(kritapigment)
target_include_directories( kritapigment
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/resources>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/compositeops>
)
target_link_libraries( kritapigment
PUBLIC
kritaplugin
kritastore
kritaglobal
${EXTRA_LIBRARIES}
KF5::I18n
KF5::ConfigCore
Qt5::Core
Qt5::Gui
Qt5::Xml
${WIN32_PLATFORM_NET_LIBS}
)
set_target_properties(kritapigment PROPERTIES
VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION}
)
install(TARGETS kritapigment ${INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/libs/pigment/KoChannelInfo.h b/libs/pigment/KoChannelInfo.h
index 8c3a80d359..d56723f380 100644
--- a/libs/pigment/KoChannelInfo.h
+++ b/libs/pigment/KoChannelInfo.h
@@ -1,276 +1,277 @@
/*
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOCHANNELINFO_H_
#define KOCHANNELINFO_H_
#include <limits>
#include <QColor>
#include <QString>
#include <QList>
/**
* This class gives some basic information about a channel,
* that is, one of the components that makes up a particular
* pixel.
*/
class KoChannelInfo
{
public:
/**
* Used to represent a min and max range.
*/
struct DoubleRange
{
public:
double minVal, maxVal;
public:
/// creates an invalid range of 0,0
DoubleRange(void) : minVal(0), maxVal(0) { }
/// creates
DoubleRange(qreal _minVal, qreal _maxVal) : minVal(_minVal), maxVal(_maxVal) { Q_ASSERT(minVal <= maxVal); }
/// true if this range is usable
bool isValid(void) const { return minVal < maxVal; }
};
public:
/// enum to define the type of the channel
enum enumChannelType {
COLOR, ///< The channel represents a color
ALPHA ///< The channel represents the opacity of a pixel
//SUBSTANCE, ///< The channel represents a real-world substance like pigments or medium
//SUBSTRATE ///< The channel represents a real-world painting substrate like a canvas
};
/// enum to define the value of the channel
enum enumChannelValueType {
UINT8, ///< use this for an unsigned integer 8bits channel
UINT16, ///< use this for an integer 16bits channel
UINT32, ///< use this for an unsigned integer 21bits channel
FLOAT16, ///< use this for a float 16bits channel
FLOAT32, ///< use this for a float 32bits channel
FLOAT64, ///< use this for a float 64bits channel
INT8, ///< use this for an integer 8bits channel
INT16, ///< use this for an integer 16bits channel
OTHER ///< Use this if the channel is neither an integer or a float
};
public:
KoChannelInfo() { }
/**
* @param name of the channel
* @param npos position of the channel in the pixel (in bytes)
* @param displayPosition the position of the channel in the user-visible order
* @param channelType type of the channel
* @param channelValueType type of the numerical data used by the channel
* @param size number of bytes (not bits) of the channel (if -1, it is deduced from the channelType)
* @param color a color to represent that channel (for instance in an histogram)
+ * @param uiMinMax the UI range
*/
KoChannelInfo(const QString & name,
qint32 npos,
qint32 displayPosition,
enumChannelType channelType,
enumChannelValueType channelValueType,
qint32 size = -1,
const QColor &color = QColor(0, 0, 0),
const DoubleRange &uiMinMax = DoubleRange())
: m_name(name)
, m_pos(npos)
, m_displayPosition(displayPosition)
, m_channelType(channelType)
, m_channelValueType(channelValueType)
, m_size(size)
, m_color(color)
, m_uiMinMax(uiMinMax)
{
switch(m_channelValueType)
{
case UINT8:
case INT8:
Q_ASSERT(m_size == -1 || m_size == 1);
m_size = 1;
break;
case UINT16:
case INT16:
Q_ASSERT(m_size == -1 || m_size == 2);
m_size = 2;
break;
case UINT32:
Q_ASSERT(m_size == -1 || m_size == 4);
m_size = 4;
break;
case FLOAT16:
Q_ASSERT(m_size == -1 || m_size == 2);
m_size = 2;
break;
case FLOAT32:
Q_ASSERT(m_size == -1 || m_size == 4);
m_size = 4;
break;
case FLOAT64:
Q_ASSERT(m_size == -1 || m_size == 8);
m_size = 8;
break;
case OTHER:
Q_ASSERT(m_size != -1);
}
if (!uiMinMax.isValid()) {
switch (m_channelValueType) {
case UINT8:
m_uiMinMax.minVal = std::numeric_limits<quint8>::min();
m_uiMinMax.maxVal = std::numeric_limits<quint8>::max();
break;
case INT8:
m_uiMinMax.minVal = std::numeric_limits<qint8>::min();
m_uiMinMax.maxVal = std::numeric_limits<qint8>::max();
break;
case UINT16:
m_uiMinMax.minVal = std::numeric_limits<quint16>::min();
m_uiMinMax.maxVal = std::numeric_limits<quint16>::max();
break;
case INT16:
m_uiMinMax.minVal = std::numeric_limits<qint16>::min();
m_uiMinMax.maxVal = std::numeric_limits<qint16>::max();
break;
case UINT32:
m_uiMinMax.minVal = std::numeric_limits<quint32>::min();
m_uiMinMax.maxVal = std::numeric_limits<quint32>::max();
break;
default:
// assume real otherwise, which is 0..1 by default
m_uiMinMax.minVal = 0.0;
m_uiMinMax.maxVal = 1.0;
break;
}
}
Q_ASSERT(m_uiMinMax.isValid());
}
public:
/**
* converts the display position to the pixel-order index in the channels vector.
*/
static int displayPositionToChannelIndex(int displayPosition, const QList<KoChannelInfo*> &channels)
{
for (int i = 0; i < channels.size(); ++i) {
if (channels.at(i)->displayPosition() == displayPosition) {
return i;
}
}
return -1;
}
static QList<KoChannelInfo*> displayOrderSorted(const QList<KoChannelInfo*> &channels)
{
QList <KoChannelInfo*> sortedChannels;
for (int i = 0; i < channels.size(); ++i) {
Q_FOREACH (KoChannelInfo* channel, channels) {
if (channel->displayPosition() == i) {
sortedChannels << channel;
break;
}
}
}
Q_ASSERT(channels.size() == sortedChannels.size());
return sortedChannels;
}
/**
* User-friendly name for this channel for presentation purposes in the gui
*/
inline QString name() const {
return m_name;
}
/**
* @return the position of the first byte of the channel in the pixel
*/
inline qint32 pos() const {
return m_pos;
}
/**
* @return the displayPosition of the channel in the pixel
*/
inline qint32 displayPosition() const {
return m_displayPosition;
}
/**
* @return the number of bytes this channel takes
*/
inline qint32 size() const {
return m_size;
}
/**
* @return the type of the channel
*/
inline enumChannelType channelType() const {
return m_channelType;
}
/**
* @return the type of the value of the channel (float, uint8 or uint16)
*/
inline enumChannelValueType channelValueType() const {
return m_channelValueType;
}
/**
* This is a color that can be used to represent this channel in histograms and so.
* By default this is black, so keep in mind that many channels might look the same
*/
inline QColor color() const {
return m_color;
}
/**
* A channel is less than another channel if its pos is smaller.
*/
inline bool operator<(const KoChannelInfo & info) {
return m_pos < info.m_pos;
}
/**
* Gets the minimum value that this channel should have.
* This is suitable for UI use.
*/
inline double getUIMin(void) const {
return m_uiMinMax.minVal;
}
/**
* Gets the minimum value that this channel should have.
* This is suitable for UI use.
*/
inline double getUIMax(void) const {
return m_uiMinMax.maxVal;
}
private:
QString m_name;
qint32 m_pos;
qint32 m_displayPosition;
enumChannelType m_channelType;
enumChannelValueType m_channelValueType;
qint32 m_size;
QColor m_color;
DoubleRange m_uiMinMax;
};
#endif // KOCHANNELINFO_H_
diff --git a/libs/pigment/KoColor.h b/libs/pigment/KoColor.h
index 8db9b2aa88..eed5a90663 100644
--- a/libs/pigment/KoColor.h
+++ b/libs/pigment/KoColor.h
@@ -1,276 +1,276 @@
/*
* Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
* Copyright (C) 2007 Thomas Zander <zander@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOCOLOR_H
#define KOCOLOR_H
#include <QColor>
#include <QMetaType>
#include <QtGlobal>
#include "kritapigment_export.h"
#include "KoColorConversionTransformation.h"
#include "KoColorSpaceRegistry.h"
#include "KoColorSpaceTraits.h"
#include <boost/operators.hpp>
class QDomDocument;
class QDomElement;
class KoColorProfile;
class KoColorSpace;
/**
* A KoColor describes a color in a certain colorspace. The color is stored in a buffer
* that can be manipulated by the function of the color space.
*/
class KRITAPIGMENT_EXPORT KoColor : public boost::equality_comparable<KoColor>
{
public:
/// Create an empty KoColor. It will be valid, but also black and transparent
KoColor();
/// Create a null KoColor. It will be valid, but all channels will be set to 0
explicit KoColor(const KoColorSpace * colorSpace);
/// Create a KoColor from a QColor. The QColor is immediately converted to native. The QColor
/// is assumed to have the current monitor profile.
KoColor(const QColor & color, const KoColorSpace * colorSpace);
/// Create a KoColor using a native color strategy. The data is copied.
KoColor(const quint8 * data, const KoColorSpace * colorSpace);
/// Create a KoColor by converting src into another colorspace
KoColor(const KoColor &src, const KoColorSpace * colorSpace);
/// Copy constructor -- deep copies the colors.
KoColor(const KoColor & rhs) {
*this = rhs;
}
/**
* assignment operator to copy the data from the param color into this one.
- * @param other the color we are going to copy
+ * @param rhs the color we are going to copy
* @return this color
*/
inline KoColor &operator=(const KoColor &rhs) {
if (&rhs == this) {
return *this;
}
m_colorSpace = rhs.m_colorSpace;
m_size = rhs.m_size;
memcpy(m_data, rhs.m_data, m_size);
assertPermanentColorspace();
return *this;
}
bool operator==(const KoColor &other) const {
if (*colorSpace() != *other.colorSpace()) {
return false;
}
if (m_size != other.m_size) {
return false;
}
return memcmp(m_data, other.m_data, m_size) == 0;
}
/// return the current colorSpace
const KoColorSpace * colorSpace() const {
return m_colorSpace;
}
/// return the current profile
const KoColorProfile *profile() const;
/// Convert this KoColor to the specified colorspace. If the specified colorspace is the
/// same as the original colorspace, do nothing
void convertTo(const KoColorSpace * cs,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags);
void convertTo(const KoColorSpace * cs);
/// Copies this color and converts it to the specified colorspace. If the specified colorspace is the
/// same as the original colorspace, just returns a copy
KoColor convertedTo(const KoColorSpace * cs,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags) const;
/// Copies this color and converts it to the specified colorspace. If the specified colorspace is the
/// same as the original colorspace, just returns a copy
KoColor convertedTo(const KoColorSpace * cs) const;
/// assign new profile without converting pixel data
void setProfile(const KoColorProfile *profile);
/// Replace the existing color data, and colorspace with the specified data.
/// The data is copied.
void setColor(const quint8 * data, const KoColorSpace * colorSpace = 0);
/// Convert the color from src and replace the value of the current color with the converted data.
/// Don't convert the color if src and this have the same colorspace.
void fromKoColor(const KoColor& src);
/// a convenience method for the above.
void toQColor(QColor *c) const;
/// a convenience method for the above.
QColor toQColor() const;
/**
* Convenient function to set the opacity of the color.
*/
void setOpacity(quint8 alpha);
void setOpacity(qreal alpha);
/**
* Convenient function that return the opacity of the color
*/
quint8 opacityU8() const;
qreal opacityF() const;
/// Convenient function for converting from a QColor
void fromQColor(const QColor& c);
/**
* @return the buffer associated with this color object to be used with the
* transformation object created by the color space of this KoColor
* or to copy to a different buffer from the same color space
*/
quint8 * data() {
return m_data;
}
/**
* @return the buffer associated with this color object to be used with the
* transformation object created by the color space of this KoColor
* or to copy to a different buffer from the same color space
*/
const quint8 * data() const {
return m_data;
}
/**
* Channelwise subtracts \p value from *this and stores the result in *this
*
* Throws a safe assert if the colorspaces of the two colors are different
*/
void subtract(const KoColor &value);
/**
* Channelwise subtracts \p value from a copy of *this and returns the result
*
* Throws a safe assert if the colorspaces of the two colors are different
*/
KoColor subtracted(const KoColor &value) const;
/**
* Channelwise adds \p value to *this and stores the result in *this
*
* Throws a safe assert if the colorspaces of the two colors are different
*/
void add(const KoColor &value);
/**
* Channelwise adds \p value to a copy of *this and returns the result
*
* Throws a safe assert if the colorspaces of the two colors are different
*/
KoColor added(const KoColor &value) const;
/**
* Serialize this color following Create's swatch color specification available
* at http://create.freedesktop.org/wiki/index.php/Swatches_-_colour_file_format
*
- * This function doesn't create the <color /> element but rather the <CMYK />,
- * <sRGB />, <RGB /> ... elements. It is assumed that colorElt is the <color />
+ * This function doesn't create the \<color /\> element but rather the \<CMYK /\>,
+ * \<sRGB /\>, \<RGB /\> ... elements. It is assumed that colorElt is the \<color /\>
* element.
*
* @param colorElt root element for the serialization, it is assumed that this
- * element is <color />
+ * element is \<color /\>
* @param doc is the document containing colorElt
*/
void toXML(QDomDocument& doc, QDomElement& colorElt) const;
/**
* Unserialize a color following Create's swatch color specification available
* at http://create.freedesktop.org/wiki/index.php/Swatches_-_colour_file_format
*
- * @param elt the element to unserialize (<CMYK />, <sRGB />, <RGB />)
+ * @param elt the element to unserialize (\<CMYK /\>, \<sRGB /\>, \<RGB /\>)
* @param bitDepthId the bit depth is unspecified by the spec, this allow to select
* a preferred bit depth for creating the KoColor object (if that
* bit depth isn't available, this function will randomly select
* an other bit depth)
* @return the unserialize color, or an empty color object if the function failed
* to unserialize the color
*/
static KoColor fromXML(const QDomElement& elt, const QString & bitDepthId);
/**
* Unserialize a color following Create's swatch color specification available
* at http://create.freedesktop.org/wiki/index.php/Swatches_-_colour_file_format
*
- * @param elt the element to unserialize (<CMYK />, <sRGB />, <RGB />)
+ * @param elt the element to unserialize (\<CMYK /\>, \<sRGB /\>, \<RGB /\>)
* @param bitDepthId the bit depth is unspecified by the spec, this allow to select
* a preferred bit depth for creating the KoColor object (if that
* bit depth isn't available, this function will randomly select
* an other bit depth)
* @param ok If a an error occurs, *ok is set to false; otherwise it's set to true
* @return the unserialize color, or an empty color object if the function failed
* to unserialize the color
*/
static KoColor fromXML(const QDomElement& elt, const QString & bitDepthId, bool* ok);
/**
* @brief toQString create a user-visible string of the channel names and the channel values
* @param color the color to create the string from
* @return a string that can be used to display the values of this color to the user.
*/
static QString toQString(const KoColor &color);
#ifndef NODEBUG
/// use qDebug calls to print internal info
void dump() const;
#endif
private:
inline void assertPermanentColorspace() {
#ifndef NODEBUG
if (m_colorSpace) {
Q_ASSERT(*m_colorSpace == *KoColorSpaceRegistry::instance()->permanentColorspace(m_colorSpace));
}
#endif
}
const KoColorSpace *m_colorSpace;
quint8 m_data[MAX_PIXEL_SIZE];
quint8 m_size;
};
Q_DECLARE_METATYPE(KoColor)
KRITAPIGMENT_EXPORT QDebug operator<<(QDebug dbg, const KoColor &color);
#endif
diff --git a/libs/pigment/KoColorConversionCache.h b/libs/pigment/KoColorConversionCache.h
index cc8d9c6f55..d47895d58a 100644
--- a/libs/pigment/KoColorConversionCache.h
+++ b/libs/pigment/KoColorConversionCache.h
@@ -1,88 +1,90 @@
/*
* Copyright (C) 2007 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _KO_COLOR_CONVERSION_CACHE_HPP_
#define _KO_COLOR_CONVERSION_CACHE_HPP_
class KoCachedColorConversionTransformation;
class KoColorSpace;
#include "KoColorConversionTransformation.h"
/**
* This class holds a cache of KoColorConversionTransformations.
*
* This class is not part of public API, and can be changed without notice.
*/
class KoColorConversionCache
{
public:
struct CachedTransformation;
public:
KoColorConversionCache();
~KoColorConversionCache();
/**
* This function returns a cached color transformation if available
* or create one.
* @param src source color space
* @param dst destination color space
+ * @param _renderingIntent rendering intent
+ * @param conversionFlags conversion flags
*/
KoCachedColorConversionTransformation cachedConverter(const KoColorSpace* src,
const KoColorSpace* dst,
KoColorConversionTransformation::Intent _renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags);
/**
* This function is called by the destructor of the color space to
* warn the cache that any pointers to this color space is going to
* be invalid and that the cache needs to stop using those pointers.
* @param src source color space
*/
void colorSpaceIsDestroyed(const KoColorSpace* src);
private:
struct Private;
Private* const d;
};
/**
* This class hold a cached color conversion. It can only be created
* by the cache and when it's deleted it return the transformation to
* the pool of available color conversion transformation.
*
* This class is not part of public API, and can be changed without notice.
*/
class KoCachedColorConversionTransformation
{
friend class KoColorConversionCache;
private:
KoCachedColorConversionTransformation(KoColorConversionCache* cache,
KoColorConversionCache::CachedTransformation* transfo);
public:
KoCachedColorConversionTransformation(const KoCachedColorConversionTransformation&);
~KoCachedColorConversionTransformation();
public:
const KoColorConversionTransformation* transformation() const;
private:
struct Private;
Private* const d;
};
#endif
diff --git a/libs/pigment/KoColorConversionTransformationAbstractFactory.h b/libs/pigment/KoColorConversionTransformationAbstractFactory.h
index 449648e05f..585147bef6 100644
--- a/libs/pigment/KoColorConversionTransformationAbstractFactory.h
+++ b/libs/pigment/KoColorConversionTransformationAbstractFactory.h
@@ -1,68 +1,70 @@
/*
* Copyright (c) 2008 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _KO_COLOR_CONVERSION_TRANSFORMATION_ABSTRACT_FACTORY_H_
#define _KO_COLOR_CONVERSION_TRANSFORMATION_ABSTRACT_FACTORY_H_
#include "kritapigment_export.h"
#include <KoColorConversionTransformation.h>
#include <KoColorProofingConversionTransformation.h>
class KRITAPIGMENT_EXPORT KoColorConversionTransformationAbstractFactory
{
public:
KoColorConversionTransformationAbstractFactory() {}
virtual ~KoColorConversionTransformationAbstractFactory() {}
/**
* Creates a color transformation between the source color space and the destination
* color space.
*
* @param srcColorSpace source color space
* @param dstColorSpace destination color space
+ * @param renderingIntent rendering intent
+ * @param conversionFlags conversion flags
*/
virtual KoColorConversionTransformation* createColorTransformation(const KoColorSpace* srcColorSpace,
const KoColorSpace* dstColorSpace,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags) const = 0;
virtual KoColorProofingConversionTransformation* createColorProofingTransformation(const KoColorSpace* srcColorSpace,
const KoColorSpace* dstColorSpace,
const KoColorSpace* proofingSpace,
KoColorProofingConversionTransformation::Intent renderingIntent,
KoColorProofingConversionTransformation::Intent proofingIntent,
KoColorProofingConversionTransformation::ConversionFlags conversionFlags,
quint8 *gamutWarning,
double adaptationState) const
{
Q_UNUSED(srcColorSpace);
Q_UNUSED(dstColorSpace);
Q_UNUSED(proofingSpace);
Q_UNUSED(renderingIntent);
Q_UNUSED(proofingIntent);
Q_UNUSED(conversionFlags);
Q_UNUSED(gamutWarning);
Q_UNUSED(adaptationState);
qFatal("createColorProofinTransform undefined.");
return 0;
}
};
#endif
diff --git a/libs/pigment/KoColorSpace.h b/libs/pigment/KoColorSpace.h
index 67fa12d938..fc0d6a5032 100644
--- a/libs/pigment/KoColorSpace.h
+++ b/libs/pigment/KoColorSpace.h
@@ -1,644 +1,641 @@
/*
* Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2006-2007 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOCOLORSPACE_H
#define KOCOLORSPACE_H
#include <limits.h>
#include <QImage>
#include <QHash>
#include <QVector>
#include <QList>
#include <boost/operators.hpp>
#include "KoColorSpaceConstants.h"
#include "KoColorConversionTransformation.h"
#include "KoColorProofingConversionTransformation.h"
#include "KoCompositeOp.h"
#include <KoID.h>
#include "kritapigment_export.h"
class QDomDocument;
class QDomElement;
class KoChannelInfo;
class KoColorProfile;
class KoColorTransformation;
class QBitArray;
enum Deletability {
OwnedByRegistryDoNotDelete,
OwnedByRegistryRegistryDeletes,
NotOwnedByRegistry
};
enum ColorSpaceIndependence {
FULLY_INDEPENDENT,
TO_LAB16,
TO_RGBA8,
TO_RGBA16
};
class KoMixColorsOp;
class KoConvolutionOp;
/**
* A KoColorSpace is the definition of a certain color space.
*
* A color model and a color space are two related concepts. A color
* model is more general in that it describes the channels involved and
* how they in broad terms combine to describe a color. Examples are
* RGB, HSV, CMYK.
*
* A color space is more specific in that it also describes exactly how
* the channels are combined. So for each color model there can be a
* number of specific color spaces. So RGB is the model and sRGB,
* adobeRGB, etc are colorspaces.
*
* In Pigment KoColorSpace acts as both a color model and a color space.
* You can think of the class definition as the color model, but the
* instance of the class as representing a colorspace.
*
* A third concept is the profile represented by KoColorProfile. It
* represents the info needed to specialize a color model into a color
* space.
*
* KoColorSpace is an abstract class serving as an interface.
*
* Subclasses implement actual color spaces
* Some subclasses implement only some parts and are named Traits
*
*/
class KRITAPIGMENT_EXPORT KoColorSpace : public boost::equality_comparable<KoColorSpace>
{
friend class KoColorSpaceRegistry;
friend class KoColorSpaceFactory;
protected:
/// Only for use by classes that serve as baseclass for real color spaces
KoColorSpace();
public:
/// Should be called by real color spaces
KoColorSpace(const QString &id, const QString &name, KoMixColorsOp* mixColorsOp, KoConvolutionOp* convolutionOp);
virtual bool operator==(const KoColorSpace& rhs) const;
protected:
virtual ~KoColorSpace();
public:
//========== Gamut and other basic info ===================================//
/*
* @returns QPolygonF with 5*channel samples converted to xyY.
* maybe convert to 3d space in future?
*/
QPolygonF gamutXYY() const;
/*
* @returns a polygon with 5 samples per channel converted to xyY, but unlike
* gamutxyY it focuses on the luminance. This then can be used to visualise
* the approximate trc of a given colorspace.
*/
QPolygonF estimatedTRCXYY() const;
QVector <qreal> lumaCoefficients() const;
//========== Channels =====================================================//
/// Return a list describing all the channels this color model has. The order
/// of the channels in the list is the order of channels in the pixel. To find
/// out the preferred display position, use KoChannelInfo::displayPosition.
QList<KoChannelInfo *> channels() const;
/**
* The total number of channels for a single pixel in this color model
*/
virtual quint32 channelCount() const = 0;
/**
* The total number of color channels (excludes alpha) for a single
* pixel in this color model.
*/
virtual quint32 colorChannelCount() const = 0;
/**
* returns a QBitArray that contains true for the specified
* channel types:
*
* @param color if true, set all color channels to true
* @param alpha if true, set all alpha channels to true
*
* The order of channels is the colorspace descriptive order,
* not the pixel order.
*/
QBitArray channelFlags(bool color = true, bool alpha = false) const;
/**
* The size in bytes of a single pixel in this color model
*/
virtual quint32 pixelSize() const = 0;
/**
* Return a string with the channel's value suitable for display in the gui.
*/
virtual QString channelValueText(const quint8 *pixel, quint32 channelIndex) const = 0;
/**
* Return a string with the channel's value with integer
* channels normalised to the floating point range 0 to 1, if
* appropriate.
*/
virtual QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) const = 0;
/**
* Return a QVector of floats with channels' values normalized
* to floating point range 0 to 1.
*/
virtual void normalisedChannelsValue(const quint8 *pixel, QVector<float> &channels) const = 0;
/**
* Write in the pixel the value from the normalized vector.
*/
virtual void fromNormalisedChannelsValue(quint8 *pixel, const QVector<float> &values) const = 0;
/**
* Convert the value of the channel at the specified position into
* an 8-bit value. The position is not the number of bytes, but
* the position of the channel as defined in the channel info list.
*/
virtual quint8 scaleToU8(const quint8 * srcPixel, qint32 channelPos) const = 0;
/**
* Set dstPixel to the pixel containing only the given channel of srcPixel. The remaining channels
* should be set to whatever makes sense for 'empty' channels of this color space,
* with the intent being that the pixel should look like it only has the given channel.
*/
virtual void singleChannelPixel(quint8 *dstPixel, const quint8 *srcPixel, quint32 channelIndex) const = 0;
//========== Identification ===============================================//
/**
* ID for use in files and internally: unchanging name. As the id must be unique
* it is usually the concatenation of the id of the color model and of the color
* depth, for instance "RGBA8" or "CMYKA16" or "XYZA32f".
*/
QString id() const;
/**
* User visible name which contains the name of the color model and of the color depth.
* For instance "RGBA (8-bits)" or "CMYKA (16-bits)".
*/
QString name() const;
/**
* @return a string that identify the color model (for instance "RGB" or "CMYK" ...)
* @see KoColorModelStandardIds.h
*/
virtual KoID colorModelId() const = 0;
/**
* @return a string that identify the bit depth (for instance "U8" or "F16" ...)
* @see KoColorModelStandardIds.h
*/
virtual KoID colorDepthId() const = 0;
/**
* @return true if the profile given in argument can be used by this color space
*/
virtual bool profileIsCompatible(const KoColorProfile* profile) const = 0;
/**
* If false, images in this colorspace will degrade considerably by
* functions, tools and filters that have the given measure of colorspace
* independence.
*
* @param independence the measure to which this colorspace will suffer
* from the manipulations of the tool or filter asking
* @return false if no degradation will take place, true if degradation will
* take place
*/
virtual bool willDegrade(ColorSpaceIndependence independence) const = 0;
//========== Capabilities =================================================//
/**
* Tests if the colorspace offers the specific composite op.
*/
virtual bool hasCompositeOp(const QString & id) const;
/**
* Returns the list of user-visible composite ops supported by this colorspace.
*/
virtual QList<KoCompositeOp*> compositeOps() const;
/**
* Retrieve a single composite op from the ones this colorspace offers.
* If the requeste composite op does not exist, COMPOSITE_OVER is returned.
*/
const KoCompositeOp * compositeOp(const QString & id) const;
/**
* add a composite op to this colorspace.
*/
virtual void addCompositeOp(const KoCompositeOp * op);
/**
* Returns true if the colorspace supports channel values outside the
* (normalised) range 0 to 1.
*/
virtual bool hasHighDynamicRange() const = 0;
//========== Display profiles =============================================//
/**
* Return the profile of this color space.
*/
virtual const KoColorProfile * profile() const = 0;
//================= Conversion functions ==================================//
/**
* The fromQColor methods take a given color defined as an RGB QColor
* and fills a byte array with the corresponding color in the
* the colorspace managed by this strategy.
*
* @param color the QColor that will be used to fill dst
* @param dst a pointer to a pixel
* @param profile the optional profile that describes the color values of QColor
*/
virtual void fromQColor(const QColor& color, quint8 *dst, const KoColorProfile * profile = 0) const = 0;
/**
* The toQColor methods take a byte array that is at least pixelSize() long
* and converts the contents to a QColor, using the given profile as a source
* profile and the optional profile as a destination profile.
*
* @param src a pointer to the source pixel
* @param c the QColor that will be filled with the color at src
* @param profile the optional profile that describes the color in c, for instance the monitor profile
*/
virtual void toQColor(const quint8 *src, QColor *c, const KoColorProfile * profile = 0) const = 0;
/**
* Convert the pixels in data to (8-bit BGRA) QImage using the specified profiles.
*
* @param data A pointer to a contiguous memory region containing width * height pixels
* @param width in pixels
* @param height in pixels
* @param dstProfile destination profile
* @param renderingIntent the rendering intent
+ * @param conversionFlags conversion flags
*/
virtual QImage convertToQImage(const quint8 *data, qint32 width, qint32 height,
const KoColorProfile * dstProfile,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags) const;
/**
* Convert the specified data to Lab (D50). All colorspaces are guaranteed to support this
*
* @param src the source data
* @param dst the destination data
* @param nPixels the number of source pixels
*/
virtual void toLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const;
/**
* Convert the specified data from Lab (D50). to this colorspace. All colorspaces are
* guaranteed to support this.
*
* @param src the pixels in 16 bit lab format
* @param dst the destination data
* @param nPixels the number of pixels in the array
*/
virtual void fromLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const;
/**
* Convert the specified data to sRGB 16 bits. All colorspaces are guaranteed to support this
*
* @param src the source data
* @param dst the destination data
* @param nPixels the number of source pixels
*/
virtual void toRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const;
/**
* Convert the specified data from sRGB 16 bits. to this colorspace. All colorspaces are
* guaranteed to support this.
*
* @param src the pixels in 16 bit rgb format
* @param dst the destination data
* @param nPixels the number of pixels in the array
*/
virtual void fromRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const;
/**
* Create a color conversion transformation.
*/
virtual KoColorConversionTransformation* createColorConverter(const KoColorSpace * dstColorSpace,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags) const;
/**
* Convert a byte array of srcLen pixels *src to the specified color space
* and put the converted bytes into the prepared byte array *dst.
*
* Returns false if the conversion failed, true if it succeeded
*
* This function is not thread-safe. If you want to apply multiple conversion
* in different threads at the same time, you need to create one color converter
* per-thread using createColorConverter.
*/
virtual bool convertPixelsTo(const quint8 * src,
quint8 * dst, const KoColorSpace * dstColorSpace,
quint32 numPixels,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags) const;
virtual KoColorConversionTransformation *createProofingTransform(const KoColorSpace * dstColorSpace,
const KoColorSpace * proofingSpace,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::Intent proofingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags,
quint8 *gamutWarning, double adaptationState) const;
/**
* @brief proofPixelsTo
- * @param src
- * @param dst
- * @param dstColorSpace the colorspace to which we go to.
- * @param proofingSpace the proofing space.
+ * @param src source
+ * @param dst destination
* @param numPixels the amount of pixels.
- * @param renderingIntent the rendering intent used for rendering.
- * @param proofingIntent the intent used for proofing.
- * @param conversionFlags the conversion flags.
- * @param gamutWarning the data() of a KoColor.
- * @param adaptationState the state of adaptation, only affects absolute colorimetric.
+ * @param proofingTransform the intent used for proofing.
* @return
*/
virtual bool proofPixelsTo(const quint8 * src,
quint8 * dst,
quint32 numPixels,
KoColorConversionTransformation *proofingTransform) const;
//============================== Manipulation functions ==========================//
//
// The manipulation functions have default implementations that _convert_ the pixel
// to a QColor and back. Reimplement these methods in your color strategy!
//
/**
* Get the alpha value of the given pixel, downscaled to an 8-bit value.
*/
virtual quint8 opacityU8(const quint8 * pixel) const = 0;
virtual qreal opacityF(const quint8 * pixel) const = 0;
/**
* Set the alpha channel of the given run of pixels to the given value.
*
* pixels -- a pointer to the pixels that will have their alpha set to this value
* alpha -- a downscaled 8-bit value for opacity
* nPixels -- the number of pixels
*
*/
virtual void setOpacity(quint8 * pixels, quint8 alpha, qint32 nPixels) const = 0;
virtual void setOpacity(quint8 * pixels, qreal alpha, qint32 nPixels) const = 0;
/**
* Multiply the alpha channel of the given run of pixels by the given value.
*
* pixels -- a pointer to the pixels that will have their alpha set to this value
* alpha -- a downscaled 8-bit value for opacity
* nPixels -- the number of pixels
*
*/
virtual void multiplyAlpha(quint8 * pixels, quint8 alpha, qint32 nPixels) const = 0;
/**
* Applies the specified 8-bit alpha mask to the pixels. We assume that there are just
* as many alpha values as pixels but we do not check this; the alpha values
* are assumed to be 8-bits.
*/
virtual void applyAlphaU8Mask(quint8 * pixels, const quint8 * alpha, qint32 nPixels) const = 0;
/**
* Applies the inverted 8-bit alpha mask to the pixels. We assume that there are just
* as many alpha values as pixels but we do not check this; the alpha values
* are assumed to be 8-bits.
*/
virtual void applyInverseAlphaU8Mask(quint8 * pixels, const quint8 * alpha, qint32 nPixels) const = 0;
/**
* Applies the specified float alpha mask to the pixels. We assume that there are just
* as many alpha values as pixels but we do not check this; alpha values have to be between 0.0 and 1.0
*/
virtual void applyAlphaNormedFloatMask(quint8 * pixels, const float * alpha, qint32 nPixels) const = 0;
/**
* Applies the inverted specified float alpha mask to the pixels. We assume that there are just
* as many alpha values as pixels but we do not check this; alpha values have to be between 0.0 and 1.0
*/
virtual void applyInverseNormedFloatMask(quint8 * pixels, const float * alpha, qint32 nPixels) const = 0;
/**
* Create an adjustment object for adjusting the brightness and contrast
* transferValues is a 256 bins array with values from 0 to 0xFFFF
* This function is thread-safe, but you need to create one KoColorTransformation per thread.
*/
virtual KoColorTransformation *createBrightnessContrastAdjustment(const quint16 *transferValues) const = 0;
/**
* Create an adjustment object for adjusting individual channels
* transferValues is an array of colorChannelCount number of 256 bins array with values from 0 to 0xFFFF
* This function is thread-safe, but you need to create one KoColorTransformation per thread.
*
* The layout of the channels must be the following:
*
* 0..N-2 - color channels of the pixel;
* N-1 - alpha channel of the pixel (if exists)
*/
virtual KoColorTransformation *createPerChannelAdjustment(const quint16 * const* transferValues) const = 0;
/**
* Darken all color channels with the given amount. If compensate is true,
* the compensation factor will be used to limit the darkening.
*
*/
virtual KoColorTransformation *createDarkenAdjustment(qint32 shade, bool compensate, qreal compensation) const = 0;
/**
* Invert color channels of the given pixels
* This function is thread-safe, but you need to create one KoColorTransformation per thread.
*/
virtual KoColorTransformation *createInvertTransformation() const = 0;
/**
* Get the difference between 2 colors, normalized in the range (0,255). Only completely
* opaque and completely transparent are taken into account when computing the difference;
* other transparency levels are not regarded when finding the difference.
*/
virtual quint8 difference(const quint8* src1, const quint8* src2) const = 0;
/**
* Get the difference between 2 colors, normalized in the range (0,255). This function
* takes the Alpha channel of the pixel into account. Alpha channel has the same
* weight as Lightness channel.
*/
virtual quint8 differenceA(const quint8* src1, const quint8* src2) const = 0;
/**
* @return the mix color operation of this colorspace (do not delete it locally, it's deleted by the colorspace).
*/
virtual KoMixColorsOp* mixColorsOp() const;
/**
* @return the convolution operation of this colorspace (do not delete it locally, it's deleted by the colorspace).
*/
virtual KoConvolutionOp* convolutionOp() const;
/**
* Calculate the intensity of the given pixel, scaled down to the range 0-255. XXX: Maybe this should be more flexible
*/
virtual quint8 intensity8(const quint8 * src) const = 0;
/*
*increase luminosity by step
*/
virtual void increaseLuminosity(quint8 * pixel, qreal step) const;
virtual void decreaseLuminosity(quint8 * pixel, qreal step) const;
virtual void increaseSaturation(quint8 * pixel, qreal step) const;
virtual void decreaseSaturation(quint8 * pixel, qreal step) const;
virtual void increaseHue(quint8 * pixel, qreal step) const;
virtual void decreaseHue(quint8 * pixel, qreal step) const;
virtual void increaseRed(quint8 * pixel, qreal step) const;
virtual void increaseGreen(quint8 * pixel, qreal step) const;
virtual void increaseBlue(quint8 * pixel, qreal step) const;
virtual void increaseYellow(quint8 * pixel, qreal step) const;
virtual void toHSY(const QVector<double> &channelValues, qreal *hue, qreal *sat, qreal *luma) const = 0;
virtual QVector <double> fromHSY(qreal *hue, qreal *sat, qreal *luma) const = 0;
virtual void toYUV(const QVector<double> &channelValues, qreal *y, qreal *u, qreal *v) const = 0;
virtual QVector <double> fromYUV(qreal *y, qreal *u, qreal *v) const = 0;
/**
* Compose two arrays of pixels together. If source and target
* are not the same color model, the source pixels will be
* converted to the target model. We're "dst" -- "dst" pixels are always in _this_
* colorspace.
*
* @param srcSpace the colorspace of the source pixels that will be composited onto "us"
- * @param param the information needed for blitting e.g. the source and destination pixel data,
+ * @param params the information needed for blitting e.g. the source and destination pixel data,
* the opacity and flow, ...
* @param op the composition operator to use, e.g. COPY_OVER
+ * @param renderingIntent the rendering intent
+ * @param conversionFlags the conversion flags.
*
*/
virtual void bitBlt(const KoColorSpace* srcSpace, const KoCompositeOp::ParameterInfo& params, const KoCompositeOp* op,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags) const;
/**
* Serialize this color following Create's swatch color specification available
* at http://create.freedesktop.org/wiki/index.php/Swatches_-_colour_file_format
*
- * This function doesn't create the <color /> element but rather the <CMYK />,
- * <sRGB />, <RGB /> ... elements. It is assumed that colorElt is the <color />
+ * This function doesn't create the \<color /\> element but rather the \<CMYK /\>,
+ * \<sRGB /\>, \<RGB /\> ... elements. It is assumed that colorElt is the \<color /\>
* element.
*
* @param pixel buffer to serialized
* @param colorElt root element for the serialization, it is assumed that this
- * element is <color />
+ * element is \<color /\>
* @param doc is the document containing colorElt
*/
virtual void colorToXML(const quint8* pixel, QDomDocument& doc, QDomElement& colorElt) const = 0;
/**
* Unserialize a color following Create's swatch color specification available
* at http://create.freedesktop.org/wiki/index.php/Swatches_-_colour_file_format
*
* @param pixel buffer where the color will be unserialized
- * @param elt the element to unserialize (<CMYK />, <sRGB />, <RGB />)
+ * @param elt the element to unserialize (\<CMYK /\>, \<sRGB /\>, \<RGB /\>)
* @return the unserialize color, or an empty color object if the function failed
* to unserialize the color
*/
virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const = 0;
KoColorTransformation* createColorTransformation(const QString & id, const QHash<QString, QVariant> & parameters) const;
protected:
/**
* Use this function in the constructor of your colorspace to add the information about a channel.
* @param ci a pointer to the information about a channel
*/
virtual void addChannel(KoChannelInfo * ci);
const KoColorConversionTransformation* toLabA16Converter() const;
const KoColorConversionTransformation* fromLabA16Converter() const;
const KoColorConversionTransformation* toRgbA16Converter() const;
const KoColorConversionTransformation* fromRgbA16Converter() const;
/**
* Returns the thread-local conversion cache. If it doesn't exist
* yet, it is created. If it is currently too small, it is resized.
*/
QVector<quint8> * threadLocalConversionCache(quint32 size) const;
/**
* This function defines the behavior of the bitBlt function
* when the composition of pixels in different colorspaces is
* requested, that is in case:
*
* srcCS == any
* dstCS == this
*
* 1) preferCompositionInSourceColorSpace() == false,
*
* the source pixels are first converted to *this color space
* and then composition is performed.
*
* 2) preferCompositionInSourceColorSpace() == true,
*
* the destination pixels are first converted into *srcCS color
* space, then the composition is done, and the result is finally
* converted into *this colorspace.
*
* This is used by alpha8() color space mostly, because it has
* weaker representation of the color, so the composition
* should be done in CS with richer functionality.
*/
virtual bool preferCompositionInSourceColorSpace() const;
struct Private;
Private * const d;
};
inline QDebug operator<<(QDebug dbg, const KoColorSpace *cs)
{
if (cs) {
dbg.nospace() << cs->name() << " (" << cs->colorModelId().id() << "," << cs->colorDepthId().id() << " )";
} else {
dbg.nospace() << "0x0";
}
return dbg.space();
}
#endif // KOCOLORSPACE_H
diff --git a/libs/pigment/KoColorSpaceMaths.h b/libs/pigment/KoColorSpaceMaths.h
index 09f22b899f..3feed870c8 100644
--- a/libs/pigment/KoColorSpaceMaths.h
+++ b/libs/pigment/KoColorSpaceMaths.h
@@ -1,829 +1,864 @@
/*
* Copyright (c) 2006,2007,2010 Cyrille Berger <cberger@cberger.bet
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOCOLORSPACEMATHS_H_
#define KOCOLORSPACEMATHS_H_
#include <cmath>
#include <limits>
#include "kritapigment_export.h"
#include <KoIntegerMaths.h>
#include "KoChannelInfo.h"
#include "KoLut.h"
#undef _T
/**
* This is an empty mainWindow that needs to be "specialized" for each possible
* numerical type (quint8, quint16...).
*
* It needs to defines some static constant fields :
* - zeroValue : the zero for this numerical type
* - unitValue : the maximum value of the normal dynamic range
* - max : the maximum value
* - min : the minimum value
* - epsilon : a value close to zero but different of zero
* - bits : the bit depth
*
* And some types :
* - compositetype the type used for composite operations (usually one with
* a higher bit depth)
*/
template<typename _T>
class KoColorSpaceMathsTraits
{
public:
};
template<>
class KRITAPIGMENT_EXPORT KoColorSpaceMathsTraits<quint8>
{
public:
typedef qint32 compositetype;
static const quint8 zeroValue = 0;
static const quint8 unitValue = 0x00FF;
static const quint8 halfValue = 0x00FF / 2;
static const quint8 max = 0x00FF;
static const quint8 min = 0;
static const quint8 epsilon = 1;
static const qint8 bits = 8;
static const KoChannelInfo::enumChannelValueType channelValueType;
};
template<>
class KRITAPIGMENT_EXPORT KoColorSpaceMathsTraits<quint16>
{
public:
typedef qint64 compositetype;
static const quint16 zeroValue = 0;
static const quint16 unitValue = 0xFFFF;
static const quint16 halfValue = 0xFFFF / 2;
static const quint16 max = 0xFFFF;
static const quint16 min = 0;
static const quint16 epsilon = 1;
static const qint8 bits = 16;
static const KoChannelInfo::enumChannelValueType channelValueType;
};
template<>
class KRITAPIGMENT_EXPORT KoColorSpaceMathsTraits<qint16>
{
public:
typedef qint64 compositetype;
static const qint16 zeroValue = 0;
static const qint16 unitValue = 32767;
static const qint16 halfValue = 32767 / 2;
static const qint16 max = 32767;
static const qint16 min = -32768;
static const qint16 epsilon = 1;
static const qint8 bits = 16;
static const KoChannelInfo::enumChannelValueType channelValueType;
};
template<>
class KRITAPIGMENT_EXPORT KoColorSpaceMathsTraits<quint32>
{
public:
typedef qint64 compositetype;
static const quint32 zeroValue = 0;
static const quint32 unitValue = 0xFFFFFFFF;
static const quint32 halfValue = 0xFFFFFFFF / 2;
static const quint32 max = 0xFFFFFFFF;
static const quint32 min = 0;
static const quint32 epsilon = 1;
static const qint8 bits = 32;
static const KoChannelInfo::enumChannelValueType channelValueType;
};
#include <KoConfig.h>
#ifdef HAVE_OPENEXR
#include <half.h>
template<>
class KRITAPIGMENT_EXPORT KoColorSpaceMathsTraits<half>
{
public:
typedef double compositetype;
static const half zeroValue;
static const half unitValue;
static const half halfValue;
static const half max;
static const half min;
static const half epsilon;
static const qint8 bits = 16;
static const KoChannelInfo::enumChannelValueType channelValueType;
};
#endif
template<>
class KRITAPIGMENT_EXPORT KoColorSpaceMathsTraits<float>
{
public:
typedef double compositetype;
static const float zeroValue;
static const float unitValue;
static const float halfValue;
static const float max;
static const float min;
static const float epsilon;
static const qint8 bits = 32;
static const KoChannelInfo::enumChannelValueType channelValueType;
};
template<>
class KRITAPIGMENT_EXPORT KoColorSpaceMathsTraits<double>
{
public:
typedef double compositetype;
static const double zeroValue;
static const double unitValue;
static const double halfValue;
static const double max;
static const double min;
static const double epsilon;
static const qint8 bits = 64;
static const KoChannelInfo::enumChannelValueType channelValueType;
};
#ifdef Q_CC_MSVC
// MSVC do not have lrint
const double _double2fixmagic = 68719476736.0*1.5;
const qint32 _shiftamt = 16; //16.16 fixed point representation,
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
#define iexp_ 0
#define iman_ 1
#else
#define iexp_ 1
#define iman_ 0
#endif //BigEndian_
inline int float2int(double val)
{
val = val + _double2fixmagic;
return ((int*)&val)[iman_] >> _shiftamt;
}
inline int float2int(float val)
{
return float2int((double)val);
}
#else
inline int float2int(float x)
{
return lrintf(x);
}
inline int float2int(double x)
{
return lrint(x);
}
#endif
template<typename _T_>
struct KoIntegerToFloat {
inline float operator()(_T_ f) const
{
return f / float(KoColorSpaceMathsTraits<_T_>::max);
}
};
struct KoLuts {
static KRITAPIGMENT_EXPORT const Ko::FullLut< KoIntegerToFloat<quint16>, float, quint16> Uint16ToFloat;
static KRITAPIGMENT_EXPORT const Ko::FullLut< KoIntegerToFloat<quint8>, float, quint8> Uint8ToFloat;
};
/**
* This class defines some elementary operations used by various color
* space. It's intended to be generic, but some specialization exists
* either for optimization or just for being buildable.
*
* @param _T some numerical type with an existing trait
* @param _Tdst some other numerical type with an existing trait, it is
* only needed if different of _T
*/
template < typename _T, typename _Tdst = _T >
class KoColorSpaceMaths
{
typedef KoColorSpaceMathsTraits<_T> traits;
typedef typename traits::compositetype src_compositetype;
typedef typename KoColorSpaceMathsTraits<_Tdst>::compositetype dst_compositetype;
public:
inline static _Tdst multiply(_T a, _Tdst b) {
return (dst_compositetype(a)*b) / KoColorSpaceMathsTraits<_Tdst>::unitValue;
}
inline static _Tdst multiply(_T a, _Tdst b, _Tdst c) {
return (dst_compositetype(a)*b*c) / (dst_compositetype(KoColorSpaceMathsTraits<_Tdst>::unitValue) * KoColorSpaceMathsTraits<_T>::unitValue);
}
/**
* Division : (a * MAX ) / b
* @param a
* @param b
*/
inline static dst_compositetype divide(_T a, _Tdst b) {
return (dst_compositetype(a) * KoColorSpaceMathsTraits<_Tdst>::unitValue) / b;
}
+ inline static dst_compositetype modulus(_T a, _Tdst b) {
+ return (dst_compositetype(a) - floor(dst_compositetype(a)/((b != (KoColorSpaceMathsTraits<_T>::zeroValue - traits::epsilon) ? b : KoColorSpaceMathsTraits<_T>::zeroValue) + traits::epsilon))*(b + traits::epsilon));
+ }
+
+ inline static dst_compositetype xor(_T a, _Tdst b) {
+ return (int (a * std::numeric_limits<int>::max() - traits::epsilon) ^ int (b * std::numeric_limits<int>::max() - traits::epsilon));
+ }
+
+ inline static dst_compositetype and(_T a, _Tdst b) {
+ return (int (a * std::numeric_limits<int>::max() - traits::epsilon) & int (b * std::numeric_limits<int>::max() - traits::epsilon));
+ }
+
+ inline static dst_compositetype or(_T a, _Tdst b) {
+ return (int (a * std::numeric_limits<int>::max() - traits::epsilon) | int (b * std::numeric_limits<int>::max() - traits::epsilon));
+ }
+
/**
* Inversion : unitValue - a
* @param a
*/
inline static _T invert(_T a) {
return traits::unitValue - a;
}
/**
* Blending : (a * alpha) + b * (1 - alpha)
* @param a
* @param b
* @param alpha
*/
inline static _T blend(_T a, _T b, _T alpha) {
src_compositetype c = ((src_compositetype(a) - b) * alpha) / traits::unitValue;
return c + b;
}
/**
* This function will scale a value of type _T to fit into a _Tdst.
*/
inline static _Tdst scaleToA(_T a) {
return _Tdst(dst_compositetype(a) * KoColorSpaceMathsTraits<_Tdst>::unitValue / KoColorSpaceMathsTraits<_T>::unitValue);
}
inline static dst_compositetype clamp(dst_compositetype val) {
return qBound<dst_compositetype>(KoColorSpaceMathsTraits<_Tdst>::min, val, KoColorSpaceMathsTraits<_Tdst>::max);
}
/**
* Clamps the composite type on higher border only. That is a fast path
* for scale-only transformations
*/
inline static _Tdst clampAfterScale(dst_compositetype val) {
return qMin<dst_compositetype>(val, KoColorSpaceMathsTraits<_Tdst>::max);
}
};
//------------------------------ double specialization ------------------------------//
template<>
inline quint8 KoColorSpaceMaths<double, quint8>::scaleToA(double a)
{
double v = a * 255;
return float2int(CLAMP(v, 0, 255));
}
template<>
inline double KoColorSpaceMaths<quint8, double>::scaleToA(quint8 a)
{
return KoLuts::Uint8ToFloat(a);
}
template<>
inline quint16 KoColorSpaceMaths<double, quint16>::scaleToA(double a)
{
double v = a * 0xFFFF;
return float2int(CLAMP(v, 0, 0xFFFF));
}
template<>
inline double KoColorSpaceMaths<quint16, double>::scaleToA(quint16 a)
{
return KoLuts::Uint16ToFloat(a);
}
template<>
inline double KoColorSpaceMaths<double>::clamp(double a)
{
return a;
}
//------------------------------ float specialization ------------------------------//
template<>
inline float KoColorSpaceMaths<double, float>::scaleToA(double a)
{
return (float)a;
}
template<>
inline double KoColorSpaceMaths<float, double>::scaleToA(float a)
{
return a;
}
template<>
inline quint16 KoColorSpaceMaths<float, quint16>::scaleToA(float a)
{
float v = a * 0xFFFF;
return (quint16)float2int(CLAMP(v, 0, 0xFFFF));
}
template<>
inline float KoColorSpaceMaths<quint16, float>::scaleToA(quint16 a)
{
return KoLuts::Uint16ToFloat(a);
}
template<>
inline quint8 KoColorSpaceMaths<float, quint8>::scaleToA(float a)
{
float v = a * 255;
return (quint8)float2int(CLAMP(v, 0, 255));
}
template<>
inline float KoColorSpaceMaths<quint8, float>::scaleToA(quint8 a)
{
return KoLuts::Uint8ToFloat(a);
}
template<>
inline float KoColorSpaceMaths<float>::blend(float a, float b, float alpha)
{
return (a - b) * alpha + b;
}
template<>
inline double KoColorSpaceMaths<float>::clamp(double a)
{
return a;
}
//------------------------------ half specialization ------------------------------//
#ifdef HAVE_OPENEXR
template<>
inline half KoColorSpaceMaths<double, half>::scaleToA(double a)
{
return (half)a;
}
template<>
inline double KoColorSpaceMaths<half, double>::scaleToA(half a)
{
return a;
}
template<>
inline float KoColorSpaceMaths<half, float>::scaleToA(half a)
{
return a;
}
template<>
inline half KoColorSpaceMaths<float, half>::scaleToA(float a)
{
return (half) a;
}
template<>
inline quint8 KoColorSpaceMaths<half, quint8>::scaleToA(half a)
{
half v = a * 255;
return (quint8)(CLAMP(v, 0, 255));
}
template<>
inline half KoColorSpaceMaths<quint8, half>::scaleToA(quint8 a)
{
return a *(1.0 / 255.0);
}
template<>
inline quint16 KoColorSpaceMaths<half, quint16>::scaleToA(half a)
{
double v = a * 0xFFFF;
return (quint16)(CLAMP(v, 0, 0xFFFF));
}
template<>
inline half KoColorSpaceMaths<quint16, half>::scaleToA(quint16 a)
{
return a *(1.0 / 0xFFFF);
}
template<>
inline half KoColorSpaceMaths<half, half>::scaleToA(half a)
{
return a;
}
template<>
inline half KoColorSpaceMaths<half>::blend(half a, half b, half alpha)
{
return (a - b) * alpha + b;
}
template<>
inline double KoColorSpaceMaths<half>::clamp(double a)
{
return a;
}
#endif
//------------------------------ quint8 specialization ------------------------------//
template<>
inline quint8 KoColorSpaceMaths<quint8>::multiply(quint8 a, quint8 b)
{
return (quint8)UINT8_MULT(a, b);
}
template<>
inline quint8 KoColorSpaceMaths<quint8>::multiply(quint8 a, quint8 b, quint8 c)
{
return (quint8)UINT8_MULT3(a, b, c);
}
template<>
inline KoColorSpaceMathsTraits<quint8>::compositetype
KoColorSpaceMaths<quint8>::divide(quint8 a, quint8 b)
{
return UINT8_DIVIDE(a, b);
}
template<>
inline quint8 KoColorSpaceMaths<quint8>::invert(quint8 a)
{
return ~a;
}
template<>
inline quint8 KoColorSpaceMaths<quint8>::blend(quint8 a, quint8 b, quint8 c)
{
return UINT8_BLEND(a, b, c);
}
//------------------------------ quint16 specialization ------------------------------//
template<>
inline quint16 KoColorSpaceMaths<quint16>::multiply(quint16 a, quint16 b)
{
return (quint16)UINT16_MULT(a, b);
}
template<>
inline KoColorSpaceMathsTraits<quint16>::compositetype
KoColorSpaceMaths<quint16>::divide(quint16 a, quint16 b)
{
return UINT16_DIVIDE(a, b);
}
template<>
inline quint16 KoColorSpaceMaths<quint16>::invert(quint16 a)
{
return ~a;
}
//------------------------------ various specialization ------------------------------//
// TODO: use more functions from KoIntegersMaths to do the computation
/// This specialization is needed because the default implementation won't work when scaling up
template<>
inline quint16 KoColorSpaceMaths<quint8, quint16>::scaleToA(quint8 a)
{
return UINT8_TO_UINT16(a);
}
template<>
inline quint8 KoColorSpaceMaths<quint16, quint8>::scaleToA(quint16 a)
{
return UINT16_TO_UINT8(a);
}
// Due to once again a bug in gcc, there is the need for those specialized functions:
template<>
inline quint8 KoColorSpaceMaths<quint8, quint8>::scaleToA(quint8 a)
{
return a;
}
template<>
inline quint16 KoColorSpaceMaths<quint16, quint16>::scaleToA(quint16 a)
{
return a;
}
template<>
inline float KoColorSpaceMaths<float, float>::scaleToA(float a)
{
return a;
}
namespace Arithmetic
{
const static qreal pi = 3.14159265358979323846;
template<class T>
inline T mul(T a, T b) { return KoColorSpaceMaths<T>::multiply(a, b); }
template<class T>
inline T mul(T a, T b, T c) { return KoColorSpaceMaths<T>::multiply(a, b, c); }
// template<class T>
// inline T mul(T a, T b) {
// typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
// return T(composite_type(a) * b / KoColorSpaceMathsTraits<T>::unitValue);
// }
//
// template<class T>
// inline T mul(T a, T b, T c) {
// typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
// return T((composite_type(a) * b * c) / (composite_type(KoColorSpaceMathsTraits<T>::unitValue) * KoColorSpaceMathsTraits<T>::unitValue));
// }
template<class T>
inline T inv(T a) { return KoColorSpaceMaths<T>::invert(a); }
template<class T>
inline T lerp(T a, T b, T alpha) { return KoColorSpaceMaths<T>::blend(b, a, alpha); }
template<class TRet, class T>
inline TRet scale(T a) { return KoColorSpaceMaths<T,TRet>::scaleToA(a); }
template<class T>
inline typename KoColorSpaceMathsTraits<T>::compositetype
div(T a, T b) { return KoColorSpaceMaths<T>::divide(a, b); }
-
+
+ template<class T>
+ inline typename KoColorSpaceMathsTraits<T>::compositetype
+ xor(T a, T b) { return KoColorSpaceMaths<T>::xor(a, b); }
+
+ template<class T>
+ inline typename KoColorSpaceMathsTraits<T>::compositetype
+ and(T a, T b) { return KoColorSpaceMaths<T>::and(a, b); }
+
+ template<class T>
+ inline typename KoColorSpaceMathsTraits<T>::compositetype
+ or(T a, T b) { return KoColorSpaceMaths<T>::or(a, b); }
+
template<class T>
inline T clamp(typename KoColorSpaceMathsTraits<T>::compositetype a) {
return KoColorSpaceMaths<T>::clamp(a);
}
template<class T>
inline T min(T a, T b, T c) {
b = (a < b) ? a : b;
return (b < c) ? b : c;
}
template<class T>
inline T max(T a, T b, T c) {
b = (a > b) ? a : b;
return (b > c) ? b : c;
}
template<class T>
inline T zeroValue() { return KoColorSpaceMathsTraits<T>::zeroValue; }
template<class T>
inline T halfValue() { return KoColorSpaceMathsTraits<T>::halfValue; }
template<class T>
inline T unitValue() { return KoColorSpaceMathsTraits<T>::unitValue; }
template<class T>
inline T unionShapeOpacity(T a, T b) {
typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
return T(composite_type(a) + b - mul(a,b));
}
template<class T>
inline T blend(T src, T srcAlpha, T dst, T dstAlpha, T cfValue) {
return mul(inv(srcAlpha), dstAlpha, dst) + mul(inv(dstAlpha), srcAlpha, src) + mul(dstAlpha, srcAlpha, cfValue);
}
+
+ template<class T>
+ inline T epsilon() { return KoColorSpaceMathsTraits<T>::epsilon; }
+
+ template<class T>
+ inline typename KoColorSpaceMathsTraits<T>::compositetype
+ mod(T a, T b) { return KoColorSpaceMaths<T>::modulus(a, b); }
}
struct HSYType
{
template<class TReal>
inline static TReal getLightness(TReal r, TReal g, TReal b) {
return TReal(0.299)*r + TReal(0.587)*g + TReal(0.114)*b;
}
template<class TReal>
inline static TReal getSaturation(TReal r, TReal g, TReal b) {
return Arithmetic::max(r,g,b) - Arithmetic::min(r,g,b);
}
};
struct HSIType
{
template<class TReal>
inline static TReal getLightness(TReal r, TReal g, TReal b) {
return (r + g + b) * TReal(0.33333333333333333333); // (r + g + b) / 3.0
}
template<class TReal>
inline static TReal getSaturation(TReal r, TReal g, TReal b) {
TReal max = Arithmetic::max(r, g, b);
TReal min = Arithmetic::min(r, g, b);
TReal chroma = max - min;
return (chroma > std::numeric_limits<TReal>::epsilon()) ?
(TReal(1.0) - min / getLightness(r, g, b)) : TReal(0.0);
}
};
struct HSLType
{
template<class TReal>
inline static TReal getLightness(TReal r, TReal g, TReal b) {
TReal max = Arithmetic::max(r, g, b);
TReal min = Arithmetic::min(r, g, b);
return (max + min) * TReal(0.5);
}
template<class TReal>
inline static TReal getSaturation(TReal r, TReal g, TReal b) {
TReal max = Arithmetic::max(r, g, b);
TReal min = Arithmetic::min(r, g, b);
TReal chroma = max - min;
TReal light = (max + min) * TReal(0.5);
TReal div = TReal(1.0) - std::abs(TReal(2.0)*light - TReal(1.0));
if(div > std::numeric_limits<TReal>::epsilon())
return chroma / div;
return TReal(1.0);
}
};
struct HSVType
{
template<class TReal>
inline static TReal getLightness(TReal r, TReal g, TReal b) {
return Arithmetic::max(r,g,b);
}
template<class TReal>
inline static TReal getSaturation(TReal r, TReal g, TReal b) {
TReal max = Arithmetic::max(r, g, b);
TReal min = Arithmetic::min(r, g, b);
return (max == TReal(0.0)) ? TReal(0.0) : (max - min) / max;
}
};
template<class TReal>
TReal getHue(TReal r, TReal g, TReal b) {
TReal min = Arithmetic::min(r, g, b);
TReal max = Arithmetic::max(r, g, b);
TReal chroma = max - min;
TReal hue = TReal(-1.0);
if(chroma > std::numeric_limits<TReal>::epsilon()) {
// return atan2(TReal(2.0)*r - g - b, TReal(1.73205080756887729353)*(g - b));
if(max == r) // between yellow and magenta
hue = (g - b) / chroma;
else if(max == g) // between cyan and yellow
hue = TReal(2.0) + (b - r) / chroma;
else if(max == b) // between magenta and cyan
hue = TReal(4.0) + (r - g) / chroma;
if(hue < -std::numeric_limits<TReal>::epsilon())
hue += TReal(6.0);
hue /= TReal(6.0);
}
// hue = (r == max) ? (b-g) : (g == max) ? TReal(2.0)+(r-b) : TReal(4.0)+(g-r);
return hue;
}
template<class TReal>
void getRGB(TReal& r, TReal& g, TReal& b, TReal hue) {
// 0 red -> (1,0,0)
// 1 yellow -> (1,1,0)
// 2 green -> (0,1,0)
// 3 cyan -> (0,1,1)
// 4 blue -> (0,0,1)
// 5 maenta -> (1,0,1)
// 6 red -> (1,0,0)
if(hue < -std::numeric_limits<TReal>::epsilon()) {
r = g = b = TReal(0.0);
return;
}
int i = int(hue * TReal(6.0));
TReal x = hue * TReal(6.0) - i;
TReal y = TReal(1.0) - x;
switch(i % 6){
case 0: { r=TReal(1.0), g=x , b=TReal(0.0); } break;
case 1: { r=y , g=TReal(1.0), b=TReal(0.0); } break;
case 2: { r=TReal(0.0), g=TReal(1.0), b=x ; } break;
case 3: { r=TReal(0.0), g=y , b=TReal(1.0); } break;
case 4: { r=x , g=TReal(0.0), b=TReal(1.0); } break;
case 5: { r=TReal(1.0), g=TReal(0.0), b=y ; } break;
}
}
template<class HSXType, class TReal>
inline static TReal getLightness(TReal r, TReal g, TReal b) {
return HSXType::getLightness(r, g, b);
}
template<class HSXType, class TReal>
inline void addLightness(TReal& r, TReal& g, TReal& b, TReal light)
{
using namespace Arithmetic;
r += light;
g += light;
b += light;
TReal l = HSXType::getLightness(r, g, b);
TReal n = min(r, g, b);
TReal x = max(r, g, b);
if(n < TReal(0.0)) {
TReal iln = TReal(1.0) / (l-n);
r = l + ((r-l) * l) * iln;
g = l + ((g-l) * l) * iln;
b = l + ((b-l) * l) * iln;
}
if(x > TReal(1.0) && (x-l) > std::numeric_limits<TReal>::epsilon()) {
TReal il = TReal(1.0) - l;
TReal ixl = TReal(1.0) / (x - l);
r = l + ((r-l) * il) * ixl;
g = l + ((g-l) * il) * ixl;
b = l + ((b-l) * il) * ixl;
}
}
template<class HSXType, class TReal>
inline void setLightness(TReal& r, TReal& g, TReal& b, TReal light)
{
addLightness<HSXType>(r,g,b, light - HSXType::getLightness(r,g,b));
}
template<class HSXType, class TReal>
inline static TReal getSaturation(TReal r, TReal g, TReal b) {
return HSXType::getSaturation(r, g, b);
}
template<class HSXType, class TReal>
inline void setSaturation(TReal& r, TReal& g, TReal& b, TReal sat)
{
int min = 0;
int mid = 1;
int max = 2;
TReal rgb[3] = {r, g, b};
if(rgb[mid] < rgb[min]) {
int tmp = min;
min = mid;
mid = tmp;
}
if(rgb[max] < rgb[mid]) {
int tmp = mid;
mid = max;
max = tmp;
}
if(rgb[mid] < rgb[min]) {
int tmp = min;
min = mid;
mid = tmp;
}
if((rgb[max] - rgb[min]) > TReal(0.0)) {
rgb[mid] = ((rgb[mid]-rgb[min]) * sat) / (rgb[max]-rgb[min]);
rgb[max] = sat;
rgb[min] = TReal(0.0);
r = rgb[0];
g = rgb[1];
b = rgb[2];
}
else r = g = b = TReal(0.0);
}
#endif
diff --git a/libs/pigment/KoColorSpaceRegistry.h b/libs/pigment/KoColorSpaceRegistry.h
index df792ffa87..0f4b71d0bb 100644
--- a/libs/pigment/KoColorSpaceRegistry.h
+++ b/libs/pigment/KoColorSpaceRegistry.h
@@ -1,384 +1,384 @@
/*
* Copyright (c) 2003 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004,2010 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOCOLORSPACEREGISTRY_H
#define KOCOLORSPACEREGISTRY_H
#include <QObject>
#include <QList>
#include <QString>
#include "kritapigment_export.h"
#include <KoGenericRegistry.h>
#include <KoColorSpace.h>
#include <KoColorSpaceFactory.h>
+#include <KoConfig.h>
class KoColorProfile;
class KoColorConversionSystem;
class KoColorConversionCache;
class KoColorConversionTransformation;
/**
* The registry for colorspaces and profiles.
* This class contains:
* - a registry of colorspace instantiated with specific profiles.
* - a registry of singleton colorspace factories.
* - a registry of icc profiles
*
* Locking policy details:
*
* Basically, we have two levels of locks in the registry:
* 1) (outer level) is Private::registrylock, which controls the structures
* of the color space registry itself
* 2) (inner level) is KoColorProfileStorage::Private::lock controls
* the structures related to profiles.
*
* The locks can be taken individually, but if you are going to take both
* of them, you should always follow the order 1) registry; 2) profiles.
* Otherwise you'll get a deadlock.
*
* To avoid recursive deadlocks, all the dependent classes
* (KoColorConversionSystem and KoColorSpaceFactory) now do not use the direct
* links to the registry. Instead, they use special private interfaces that
* skip recursive locking and ensure we take a lock twice.
*/
class KRITAPIGMENT_EXPORT KoColorSpaceRegistry
{
public:
KoColorSpaceRegistry();
enum ColorSpaceListVisibility {
OnlyUserVisible = 1, ///< Only user visible color space
AllColorSpaces = 4 ///< All color space even those not visible to the user
};
enum ColorSpaceListProfilesSelection {
OnlyDefaultProfile = 1, ///< Only add the default profile
AllProfiles = 4 ///< Add all profiles
};
/**
* Return an instance of the KoColorSpaceRegistry
* Creates an instance if that has never happened before and returns the singleton instance.
*/
static KoColorSpaceRegistry * instance();
virtual ~KoColorSpaceRegistry();
public:
/**
* add a color space to the registry
* @param item the color space factory to add
*/
void add(KoColorSpaceFactory* item);
/**
* Remove a color space factory from the registry. Note that it is the
* responsibility of the caller to ensure that the colorspaces are not
* used anymore.
*/
void remove(KoColorSpaceFactory* item);
/**
* Add a profile to the profile map but do not add it to the
* color conversion system yet.
* @param profile the new profile to be registered.
*/
void addProfileToMap(KoColorProfile *p);
/**
* register the profile with the color space registry
* @param profile the new profile to be registered so it can be combined with
* colorspaces.
*/
void addProfile(KoColorProfile* profile);
void addProfile(const KoColorProfile* profile); // TODO why ?
void removeProfile(KoColorProfile* profile);
/**
* Create an alias to a profile with a different name. Then @ref profileByName
* will return the profile @p to when passed @p name as a parameter.
*/
void addProfileAlias(const QString& name, const QString& to);
/**
* @return the profile alias, or name if not aliased
*/
QString profileAlias(const QString& name) const;
/**
* create a profile of the specified type.
*/
const KoColorProfile *createColorProfile(const QString & colorModelId, const QString & colorDepthId, const QByteArray& rawData);
/**
* Return a profile by its given name, or 0 if none registered.
* @return a profile by its given name, or 0 if none registered.
* @param name the product name as set on the profile.
* @see addProfile()
* @see KoColorProfile::productName()
*/
const KoColorProfile * profileByName(const QString & name) const ;
/**
* Returns a profile by its unique id stored/calculated in the header.
* The first call to this function might take long, because the map is
* created on the first use only (atm used by SVG only)
* @param id unique ProfileID of the profile (MD5 sum of its header)
* @return the profile or 0 if not found
*/
const KoColorProfile *profileByUniqueId(const QByteArray &id) const;
bool profileIsCompatible(const KoColorProfile* profile, const QString &colorSpaceId);
/**
* Return the list of profiles for a colorspace with the argument id.
* Profiles will not work with any color space, you can query which profiles
* that are registered with this registry can be used in combination with the
* argument factory.
* @param colorSpaceId the colorspace-id with which all the returned profiles will work.
* @return a list of profiles for the factory
*/
QList<const KoColorProfile *> profilesFor(const QString& csID) const;
QString defaultProfileForColorSpace(const QString &colorSpaceId) const;
/**
* This function is called by the color space to create a color conversion
* between two color space. This function search in the graph of transformations
* the best possible path between the two color space.
*/
KoColorConversionTransformation* createColorConverter(const KoColorSpace * srcColorSpace, const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const;
/**
* This function creates two transformations, one from the color space and one to the
* color space. The destination color space is picked from a list of color space, such
* as the conversion between the two color space is of the best quality.
*
* The typical use case of this function is for KoColorTransformationFactory which
* doesn't support all color spaces, so unsupported color space have to find an
* acceptable conversion in order to use that KoColorTransformationFactory.
*
* @param colorSpace the source color space
* @param possibilities a list of color space among which we need to find the best
* conversion
* @param fromCS the conversion from the source color space will be affected to this
* variable
* @param toCS the revert conversion to the source color space will be affected to this
* variable
*/
void createColorConverters(const KoColorSpace* colorSpace, const QList< QPair<KoID, KoID> >& possibilities, KoColorConversionTransformation*& fromCS, KoColorConversionTransformation*& toCS) const;
/**
* Return a colorspace that works with the parameter profile.
* @param colorSpaceId the ID string of the colorspace that you want to have returned
* @param profile the profile be combined with the colorspace
* @return the wanted colorspace, or 0 when the cs and profile can not be combined.
*/
const KoColorSpace * colorSpace(const QString & colorModelId, const QString & colorDepthId, const KoColorProfile *profile);
/**
* Return a colorspace that works with the parameter profile.
* @param profileName the name of the KoColorProfile to be combined with the colorspace
* @return the wanted colorspace, or 0 when the cs and profile can not be combined.
*/
const KoColorSpace * colorSpace(const QString & colorModelId, const QString & colorDepthId, const QString &profileName);
const KoColorSpace * colorSpace(const QString & colorModelId, const QString & colorDepthId);
/**
* Return the id of the colorspace that have the defined colorModelId with colorDepthId.
* @param colorModelId id of the color model
* @param colorDepthId id of the color depth
* @return the id of the wanted colorspace, or "" if no colorspace correspond to those ids
*/
QString colorSpaceId(const QString & colorModelId, const QString & colorDepthId) const;
/**
* It's a convenient function that behave like the above.
* Return the id of the colorspace that have the defined colorModelId with colorDepthId.
* @param colorModelId id of the color model
* @param colorDepthId id of the color depth
* @return the id of the wanted colorspace, or "" if no colorspace correspond to those ids
*/
QString colorSpaceId(const KoID& colorModelId, const KoID& colorDepthId) const;
/**
* @return the identifier of the color model for the given color space id.
*
* This function is a compatibility function used to get the color space from
* all kra files.
*/
KoID colorSpaceColorModelId(const QString & _colorSpaceId) const;
/**
* @return the identifier of the color depth for the given color space id.
*
* This function is a compatibility function used to get the color space from
* all kra files.
*/
KoID colorSpaceColorDepthId(const QString & _colorSpaceId) const;
/**
* Convenience methods to get the often used alpha colorspaces
*/
const KoColorSpace *alpha8();
const KoColorSpace *alpha16();
-#include <KoConfig.h>
#ifdef HAVE_OPENEXR
const KoColorSpace *alpha16f();
#endif
const KoColorSpace *alpha32f();
/**
* Convenience method to get an RGBA 8bit colorspace. If a profile is not specified,
* an sRGB profile will be used.
* @param profileName the name of an RGB color profile
* @return the wanted colorspace, or 0 if the color space and profile can not be combined.
*/
const KoColorSpace * rgb8(const QString &profileName = QString());
/**
* Convenience method to get an RGBA 8bit colorspace with the given profile.
* @param profile an RGB profile
* @return the wanted colorspace, or 0 if the color space and profile can not be combined.
*/
const KoColorSpace * rgb8(const KoColorProfile * profile);
/**
* Convenience method to get an RGBA 16bit colorspace. If a profile is not specified,
* an sRGB profile will be used.
* @param profileName the name of an RGB color profile
* @return the wanted colorspace, or 0 if the color space and profile can not be combined.
*/
const KoColorSpace * rgb16(const QString &profileName = QString());
/**
* Convenience method to get an RGBA 16bit colorspace with the given profile.
* @param profile an RGB profile
* @return the wanted colorspace, or 0 if the color space and profile can not be combined.
*/
const KoColorSpace * rgb16(const KoColorProfile * profile);
/**
* Convenience method to get an Lab 16bit colorspace. If a profile is not specified,
* an Lab profile with a D50 whitepoint will be used.
* @param profileName the name of an Lab color profile
* @return the wanted colorspace, or 0 if the color space and profile can not be combined.
*/
const KoColorSpace * lab16(const QString &profileName = QString());
/**
* Convenience method to get an Lab 16bit colorspace with the given profile.
* @param profile an Lab profile
* @return the wanted colorspace, or 0 if the color space and profile can not be combined.
*/
const KoColorSpace * lab16(const KoColorProfile * profile);
/**
* Convenience method to get a standard profile for Rec.2020 linear light
* color space
*/
const KoColorProfile *p2020G10Profile() const;
/**
* Convenience method to get a standard profile for Rec.2020 PQ color space
*/
const KoColorProfile *p2020PQProfile() const;
/**
* Convenience method to get a standard profile for Rec. 709 linear light
* color space
*/
const KoColorProfile *p709G10Profile() const;
/**
* Convenience method to get a standard profile for Rec. 709 sRGB-tone-
* response-curve profile
*/
const KoColorProfile *p709SRGBProfile() const;
/**
* @return the list of available color models
*/
QList<KoID> colorModelsList(ColorSpaceListVisibility option) const;
/**
* @return the list of available color models for the given colorModelId
*/
QList<KoID> colorDepthList(const KoID& colorModelId, ColorSpaceListVisibility option) const;
/**
* @return the list of available color models for the given colorModelId
*/
QList<KoID> colorDepthList(const QString & colorModelId, ColorSpaceListVisibility option) const;
/**
* @return the cache of color conversion transformation to be use by KoColorSpace
*/
KoColorConversionCache* colorConversionCache() const;
/**
* @return a permanent colorspace owned by the registry, of the same type and profile
* as the one given in argument
*/
const KoColorSpace* permanentColorspace(const KoColorSpace* _colorSpace);
/**
* This function return a list of all the keys in KoID format by using the name() method
* on the objects stored in the registry.
*/
QList<KoID> listKeys() const;
private:
friend class KisCsConversionTest;
friend class KisIteratorTest;
friend class KisIteratorNGTest;
friend class KisPainterTest;
friend class KisCrashFilterTest;
friend class KoColorSpacesBenchmark;
friend class TestKoColorSpaceSanity;
friend class TestColorConversionSystem;
friend struct FriendOfColorSpaceRegistry;
/**
* @return a list with an instance of all color space with their default profile.
*/
QList<const KoColorSpace*> allColorSpaces(ColorSpaceListVisibility visibility, ColorSpaceListProfilesSelection pSelection);
/**
* @return the color conversion system use by the registry and the color
* spaces to create color conversion transformation.
*
* WARNING: conversion system is guarded by the registry locks, don't
* use it anywhere other than unittests!
*/
const KoColorConversionSystem* colorConversionSystem() const;
private:
KoColorSpaceRegistry(const KoColorSpaceRegistry&);
KoColorSpaceRegistry operator=(const KoColorSpaceRegistry&);
void init();
private:
struct Private;
Private * const d;
};
#endif // KOCOLORSPACEREGISTRY_H
diff --git a/libs/pigment/KoCompositeOp.cpp b/libs/pigment/KoCompositeOp.cpp
index ab9a599f9b..7f39acc81c 100644
--- a/libs/pigment/KoCompositeOp.cpp
+++ b/libs/pigment/KoCompositeOp.cpp
@@ -1,189 +1,191 @@
/*
* Copyright (c) 2005 Adrian Page <adrian@pagenet.plus.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoCompositeOp.h"
#include <klocalizedstring.h>
#include <KoID.h>
#include <QList>
#include "KoColorSpace.h"
#include "KoColorSpaceMaths.h"
QString KoCompositeOp::categoryColor()
{
return i18n("Color");
}
QString KoCompositeOp::categoryArithmetic() { return i18n("Arithmetic"); }
+QString KoCompositeOp::categoryBinary() { return i18n("Binary"); }
+QString KoCompositeOp::categoryModulo() { return i18n("Modulo"); }
QString KoCompositeOp::categoryNegative() { return i18n("Negative"); }
QString KoCompositeOp::categoryLight() { return i18n("Lighten"); }
QString KoCompositeOp::categoryDark() { return i18n("Darken"); }
QString KoCompositeOp::categoryHSY() { return i18n("HSY"); }
QString KoCompositeOp::categoryHSI() { return i18n("HSI"); }
QString KoCompositeOp::categoryHSL() { return i18n("HSL"); }
QString KoCompositeOp::categoryHSV() { return i18n("HSV"); }
QString KoCompositeOp::categoryMix() { return i18n("Mix"); }
QString KoCompositeOp::categoryMisc() { return i18n("Misc"); }
-QString KoCompositeOp::categoryQuadratic() { return i18n("Quadratic"); }
+QString KoCompositeOp::categoryQuadratic() { return i18n("Quadratic"); }
KoCompositeOp::ParameterInfo::ParameterInfo()
: opacity(1.0f),
flow(1.0f),
lastOpacity(&opacity)
{
}
KoCompositeOp::ParameterInfo::ParameterInfo(const ParameterInfo &rhs)
{
copy(rhs);
}
KoCompositeOp::ParameterInfo& KoCompositeOp::ParameterInfo::operator=(const ParameterInfo &rhs)
{
copy(rhs);
return *this;
}
void KoCompositeOp::ParameterInfo::setOpacityAndAverage(float _opacity, float _averageOpacity)
{
if (qFuzzyCompare(_opacity, _averageOpacity)) {
opacity = _opacity;
lastOpacity = &opacity;
} else {
opacity = _opacity;
_lastOpacityData = _averageOpacity;
lastOpacity = &_lastOpacityData;
}
}
void KoCompositeOp::ParameterInfo::copy(const ParameterInfo &rhs)
{
dstRowStart = rhs.dstRowStart;
dstRowStride = rhs.dstRowStride;
srcRowStart = rhs.srcRowStart;
srcRowStride = rhs.srcRowStride;
maskRowStart = rhs.maskRowStart;
maskRowStride = rhs.maskRowStride;
rows = rhs.rows;
cols = rhs.cols;
opacity = rhs.opacity;
flow = rhs.flow;
_lastOpacityData = rhs._lastOpacityData;
channelFlags = rhs.channelFlags;
lastOpacity = rhs.lastOpacity == &rhs.opacity ?
&opacity : &_lastOpacityData;
}
void KoCompositeOp::ParameterInfo::updateOpacityAndAverage(float value) {
const float exponent = 0.1;
opacity = value;
if (*lastOpacity < opacity) {
lastOpacity = &opacity;
} else {
_lastOpacityData = exponent * opacity + (1.0 - exponent) * (*lastOpacity);
lastOpacity = &_lastOpacityData;
}
}
struct Q_DECL_HIDDEN KoCompositeOp::Private {
const KoColorSpace * colorSpace;
QString id;
QString description;
QString category;
QBitArray defaultChannelFlags;
};
KoCompositeOp::KoCompositeOp() : d(new Private)
{
}
KoCompositeOp::~KoCompositeOp()
{
delete d;
}
KoCompositeOp::KoCompositeOp(const KoColorSpace * cs, const QString& id, const QString& description, const QString & category)
: d(new Private)
{
d->colorSpace = cs;
d->id = id;
d->description = description;
d->category = category;
if (d->category.isEmpty()) {
d->category = categoryMisc();
}
}
void KoCompositeOp::composite(quint8 *dstRowStart, qint32 dstRowStride,
const quint8 *srcRowStart, qint32 srcRowStride,
const quint8 *maskRowStart, qint32 maskRowStride,
qint32 rows, qint32 numColumns,
quint8 opacity, const QBitArray& channelFlags) const
{
KoCompositeOp::ParameterInfo params;
params.dstRowStart = dstRowStart;
params.dstRowStride = dstRowStride;
params.srcRowStart = srcRowStart;
params.srcRowStride = srcRowStride;
params.maskRowStart = maskRowStart;
params.maskRowStride = maskRowStride;
params.rows = rows;
params.cols = numColumns;
params.opacity = float(opacity) / 255.0f;
params.flow = 1.0f;
params.channelFlags = channelFlags;
composite(params);
}
void KoCompositeOp::composite(const KoCompositeOp::ParameterInfo& params) const
{
using namespace Arithmetic;
composite(params.dstRowStart , params.dstRowStride ,
params.srcRowStart , params.srcRowStride ,
params.maskRowStart , params.maskRowStride,
params.rows , params.cols ,
scale<quint8>(params.opacity), params.channelFlags );
}
QString KoCompositeOp::category() const
{
return d->category;
}
QString KoCompositeOp::id() const
{
return d->id;
}
QString KoCompositeOp::description() const
{
return d->description;
}
const KoColorSpace * KoCompositeOp::colorSpace() const
{
return d->colorSpace;
}
diff --git a/libs/pigment/KoCompositeOp.h b/libs/pigment/KoCompositeOp.h
index da2bc6996d..d3d8b41a89 100644
--- a/libs/pigment/KoCompositeOp.h
+++ b/libs/pigment/KoCompositeOp.h
@@ -1,149 +1,151 @@
/*
* Copyright (c) 2005 Adrian Page <adrian@pagenet.plus.com>
* Copyright (c) 2011 Silvio Heinrich <plassy@web.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOCOMPOSITEOP_H
#define KOCOMPOSITEOP_H
#include <QString>
#include <QList>
#include <QMultiMap>
#include <QBitArray>
#include <boost/optional.hpp>
#include "kritapigment_export.h"
class KoColorSpace;
class KoColorSpace;
/**
* Base for colorspace-specific blending modes.
*/
class KRITAPIGMENT_EXPORT KoCompositeOp
{
public:
static QString categoryColor();
static QString categoryArithmetic();
+ static QString categoryBinary();
+ static QString categoryModulo();
static QString categoryNegative();
static QString categoryLight();
static QString categoryDark();
static QString categoryHSY();
static QString categoryHSI();
static QString categoryHSL();
static QString categoryHSV();
static QString categoryMix();
- static QString categoryMisc();
+ static QString categoryMisc();
static QString categoryQuadratic();
struct KRITAPIGMENT_EXPORT ParameterInfo
{
ParameterInfo();
ParameterInfo(const ParameterInfo &rhs);
ParameterInfo& operator=(const ParameterInfo &rhs);
quint8* dstRowStart;
qint32 dstRowStride;
const quint8* srcRowStart;
qint32 srcRowStride;
const quint8* maskRowStart;
qint32 maskRowStride;
qint32 rows;
qint32 cols;
float opacity;
float flow;
float _lastOpacityData;
float* lastOpacity;
QBitArray channelFlags;
void setOpacityAndAverage(float _opacity, float _averageOpacity);
void updateOpacityAndAverage(float value);
private:
inline void copy(const ParameterInfo &rhs);
};
public:
/**
* @param cs a pointer to the color space that can be used with this composite op
* @param id the identifier for this composite op (not user visible)
* @param description a user visible string describing this composite operation
* @param category the name of the category where to put that composite op when displayed
* @param userVisible define whether or not that composite op should be visible in a user
* interface
*/
KoCompositeOp(const KoColorSpace * cs, const QString& id, const QString& description, const QString & category = KoCompositeOp::categoryMisc());
virtual ~KoCompositeOp();
/**
* @return the identifier of this composite op
*/
QString id() const;
/**
* @return the user visible string for this composite op
*/
QString description() const;
/**
* @return the color space that can use and own this composite op
*/
const KoColorSpace * colorSpace() const;
/**
* @return the category associated with the composite op
*/
QString category() const;
// WARNING: A derived class needs to overwrite at least one
// of the following virtual methods or a call to
// composite(...) will lead to an endless recursion/stack overflow
/**
* @param dstRowStart pointer to the start of the byte array we will composite the source on
* @param dstRowStride length of the rows of the block of destination pixels in bytes
* @param srcRowStart pointer to the start of the byte array we will mix with dest
* @param srcRowStride length of the rows of the block of src in bytes
* pixels (may be different from the rowstride of the dst pixels,
* in which case the smaller value is used). If srcRowStride is null
* it is assumed that the source is a constant color.
* @param maskRowStart start of the byte mask that determines whether and if so, then how much of src is used for blending
* @param maskRowStride length of the mask scanlines in bytes
* @param rows number of scanlines to blend
* @param numColumns length of the row of pixels in pixels
* @param opacity transparency with which to blend
* @param channelFlags a bit array that determines which channels should be processed (channels are in the order of the channels in the colorspace)
*/
virtual void composite(quint8 *dstRowStart, qint32 dstRowStride,
const quint8 *srcRowStart, qint32 srcRowStride,
const quint8 *maskRowStart, qint32 maskRowStride,
qint32 rows, qint32 numColumns,
quint8 opacity, const QBitArray& channelFlags=QBitArray()) const;
/**
* Same as previous, but uses a parameter structure
*/
virtual void composite(const ParameterInfo& params) const;
private:
KoCompositeOp();
struct Private;
Private* const d;
};
#endif // KOCOMPOSITEOP_H
diff --git a/libs/pigment/KoCompositeOpRegistry.cpp b/libs/pigment/KoCompositeOpRegistry.cpp
index b52ba982f0..4fe5575fff 100644
--- a/libs/pigment/KoCompositeOpRegistry.cpp
+++ b/libs/pigment/KoCompositeOpRegistry.cpp
@@ -1,222 +1,269 @@
/*
* Copyright (c) 2005 Adrian Page <adrian@pagenet.plus.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoCompositeOpRegistry.h"
#include <QGlobalStatic>
#include <QList>
#include <klocalizedstring.h>
#include <KoID.h>
#include "KoCompositeOp.h"
#include "KoColorSpace.h"
Q_GLOBAL_STATIC(KoCompositeOpRegistry, registry)
KoCompositeOpRegistry::KoCompositeOpRegistry()
{
m_categories
<< KoID("arithmetic", i18n("Arithmetic"))
+ << KoID("binary" , i18n("Binary"))
<< KoID("dark" , i18n("Darken"))
<< KoID("light" , i18n("Lighten"))
+ << KoID("modulo" , i18n("Modulo"))
<< KoID("negative" , i18n("Negative"))
<< KoID("mix" , i18n("Mix"))
<< KoID("misc" , i18n("Misc"))
<< KoID("hsy" , i18n("HSY"))
<< KoID("hsi" , i18n("HSI"))
<< KoID("hsl" , i18n("HSL"))
<< KoID("hsv" , i18n("HSV"))
<< KoID("quadratic" , i18n("Quadratic"));
m_map.insert(m_categories[0], KoID(COMPOSITE_ADD , i18n("Addition")));
m_map.insert(m_categories[0], KoID(COMPOSITE_SUBTRACT , i18n("Subtract")));
m_map.insert(m_categories[0], KoID(COMPOSITE_MULT , i18n("Multiply")));
m_map.insert(m_categories[0], KoID(COMPOSITE_DIVIDE , i18n("Divide")));
m_map.insert(m_categories[0], KoID(COMPOSITE_INVERSE_SUBTRACT, i18n("Inverse Subtract")));
+
+ m_map.insert(m_categories[1], KoID(COMPOSITE_XOR , i18n("XOR")));
+ m_map.insert(m_categories[1], KoID(COMPOSITE_OR , i18n("OR")));
+ m_map.insert(m_categories[1], KoID(COMPOSITE_AND , i18n("AND")));
+ m_map.insert(m_categories[1], KoID(COMPOSITE_NAND , i18n("NAND")));
+ m_map.insert(m_categories[1], KoID(COMPOSITE_NOR , i18n("NOR")));
+ m_map.insert(m_categories[1], KoID(COMPOSITE_XNOR , i18n("XNOR")));
+ m_map.insert(m_categories[1], KoID(COMPOSITE_IMPLICATION , i18n("IMPLICATION")));
+ m_map.insert(m_categories[1], KoID(COMPOSITE_NOT_IMPLICATION , i18n("NOT IMPLICATION")));
+ m_map.insert(m_categories[1], KoID(COMPOSITE_CONVERSE , i18n("CONVERSE")));
+ m_map.insert(m_categories[1], KoID(COMPOSITE_NOT_CONVERSE , i18n("NOT CONVERSE")));
+
+ m_map.insert(m_categories[2], KoID(COMPOSITE_BURN , i18n("Burn")));
+ m_map.insert(m_categories[2], KoID(COMPOSITE_BURN_LOGARITHMIC , i18n("Burn - Logarithmic")));
+ m_map.insert(m_categories[2], KoID(COMPOSITE_LINEAR_BURN, i18n("Linear Burn")));
+ m_map.insert(m_categories[2], KoID(COMPOSITE_DARKEN , i18n("Darken")));
+ m_map.insert(m_categories[2], KoID(COMPOSITE_GAMMA_DARK , i18n("Gamma Dark")));
+ m_map.insert(m_categories[2], KoID(COMPOSITE_DARKER_COLOR , i18n("Darker Color")));
+ m_map.insert(m_categories[2], KoID(COMPOSITE_SHADE_IFS_ILLUSIONS, i18n("Shade (IFS Illusions)")));
+ m_map.insert(m_categories[2], KoID(COMPOSITE_FOG_DARKEN_IFS_ILLUSIONS, i18n("Fog Darken (IFS Illusions)")));
+ m_map.insert(m_categories[2], KoID(COMPOSITE_EASY_BURN , i18n("Easy Burn")));
+
+ m_map.insert(m_categories[3], KoID(COMPOSITE_DODGE , i18n("Color Dodge")));
+ m_map.insert(m_categories[3], KoID(COMPOSITE_DODGE_LOGARITHMIC , i18n("Color Dodge - Logarithmic")));
+ m_map.insert(m_categories[3], KoID(COMPOSITE_LINEAR_DODGE, i18n("Linear Dodge")));
+ m_map.insert(m_categories[3], KoID(COMPOSITE_LIGHTEN , i18n("Lighten")));
+ m_map.insert(m_categories[3], KoID(COMPOSITE_LINEAR_LIGHT, i18n("Linear Light")));
+ m_map.insert(m_categories[3], KoID(COMPOSITE_SCREEN , i18n("Screen")));
+ m_map.insert(m_categories[3], KoID(COMPOSITE_PIN_LIGHT , i18n("Pin Light")));
+ m_map.insert(m_categories[3], KoID(COMPOSITE_VIVID_LIGHT , i18n("Vivid Light")));
+ m_map.insert(m_categories[3], KoID(COMPOSITE_FLAT_LIGHT , i18n("Flat Light")));
+ m_map.insert(m_categories[3], KoID(COMPOSITE_HARD_LIGHT , i18n("Hard Light")));
+ m_map.insert(m_categories[3], KoID(COMPOSITE_SOFT_LIGHT_IFS_ILLUSIONS, i18n("Soft Light (IFS Illusions)")));
+ m_map.insert(m_categories[3], KoID(COMPOSITE_SOFT_LIGHT_PEGTOP_DELPHI, i18n("Soft Light (Pegtop-Delphi)")));
+ m_map.insert(m_categories[3], KoID(COMPOSITE_SOFT_LIGHT_PHOTOSHOP, i18n("Soft Light (Photoshop)")));
+ m_map.insert(m_categories[3], KoID(COMPOSITE_SOFT_LIGHT_SVG, i18n("Soft Light (SVG)")));
+ m_map.insert(m_categories[3], KoID(COMPOSITE_GAMMA_LIGHT , i18n("Gamma Light")));
+ m_map.insert(m_categories[3], KoID(COMPOSITE_GAMMA_ILLUMINATION , i18n("Gamma Illumination")));
+ m_map.insert(m_categories[3], KoID(COMPOSITE_LIGHTER_COLOR , i18n("Lighter Color")));
+ m_map.insert(m_categories[3], KoID(COMPOSITE_PNORM_A , i18n("P-Norm A")));
+ m_map.insert(m_categories[3], KoID(COMPOSITE_PNORM_B , i18n("P-Norm B")));
+ m_map.insert(m_categories[3], KoID(COMPOSITE_SUPER_LIGHT , i18n("Super Light")));
+ m_map.insert(m_categories[3], KoID(COMPOSITE_TINT_IFS_ILLUSIONS, i18n("Tint (IFS Illusions)")));
+ m_map.insert(m_categories[3], KoID(COMPOSITE_FOG_LIGHTEN_IFS_ILLUSIONS, i18n("Fog Lighten (IFS Illusions)")));
+ m_map.insert(m_categories[3], KoID(COMPOSITE_EASY_DODGE , i18n("Easy Dodge")));
+
+ m_map.insert(m_categories[4], KoID(COMPOSITE_MOD , i18n("Modulo")));
+ m_map.insert(m_categories[4], KoID(COMPOSITE_MOD_CON , i18n("Modulo - Continuous")));
+ m_map.insert(m_categories[4], KoID(COMPOSITE_DIVISIVE_MOD , i18n("Divisive Modulo")));
+ m_map.insert(m_categories[4], KoID(COMPOSITE_DIVISIVE_MOD_CON , i18n("Divisive Modulo - Continuous")));
+ m_map.insert(m_categories[4], KoID(COMPOSITE_MODULO_SHIFT , i18n("Modulo Shift")));
+ m_map.insert(m_categories[4], KoID(COMPOSITE_MODULO_SHIFT_CON , i18n("Modulo Shift - Continuous")));
+
+ m_map.insert(m_categories[5], KoID(COMPOSITE_DIFF , i18n("Difference")));
+ m_map.insert(m_categories[5], KoID(COMPOSITE_EQUIVALENCE , i18n("Equivalence")));
+ m_map.insert(m_categories[5], KoID(COMPOSITE_ADDITIVE_SUBTRACTIVE , i18n("Additive Subtractive")));
+ m_map.insert(m_categories[5], KoID(COMPOSITE_EXCLUSION , i18n("Exclusion")));
+ m_map.insert(m_categories[5], KoID(COMPOSITE_ARC_TANGENT , i18n("Arcus Tangent")));
+ m_map.insert(m_categories[5], KoID(COMPOSITE_NEGATION , i18n("Negation")));
+
+ m_map.insert(m_categories[6], KoID(COMPOSITE_OVER , i18n("Normal")));
+ m_map.insert(m_categories[6], KoID(COMPOSITE_BEHIND , i18n("Behind")));
+ m_map.insert(m_categories[6], KoID(COMPOSITE_GREATER , i18n("Greater")));
+ m_map.insert(m_categories[6], KoID(COMPOSITE_OVERLAY , i18n("Overlay")));
+ m_map.insert(m_categories[6], KoID(COMPOSITE_ERASE , i18n("Erase")));
+ m_map.insert(m_categories[6], KoID(COMPOSITE_ALPHA_DARKEN , i18n("Alpha Darken")));
+ m_map.insert(m_categories[6], KoID(COMPOSITE_HARD_MIX , i18n("Hard Mix")));
+ m_map.insert(m_categories[6], KoID(COMPOSITE_HARD_MIX_PHOTOSHOP, i18n("Hard Mix (Photoshop)")));
+ m_map.insert(m_categories[6], KoID(COMPOSITE_GRAIN_MERGE , i18n("Grain Merge")));
+ m_map.insert(m_categories[6], KoID(COMPOSITE_GRAIN_EXTRACT , i18n("Grain Extract")));
+ m_map.insert(m_categories[6], KoID(COMPOSITE_PARALLEL , i18n("Parallel")));
+ m_map.insert(m_categories[6], KoID(COMPOSITE_ALLANON , i18n("Allanon")));
+ m_map.insert(m_categories[6], KoID(COMPOSITE_GEOMETRIC_MEAN , i18n("Geometric Mean")));
+ m_map.insert(m_categories[6], KoID(COMPOSITE_DESTINATION_ATOP, i18n("Destination Atop")));
+ m_map.insert(m_categories[6], KoID(COMPOSITE_DESTINATION_IN , i18n("Destination In")));
+ m_map.insert(m_categories[6], KoID(COMPOSITE_HARD_OVERLAY , i18n("Hard Overlay")));
+ m_map.insert(m_categories[6], KoID(COMPOSITE_INTERPOLATION , i18n("Interpolation")));
+ m_map.insert(m_categories[6], KoID(COMPOSITE_INTERPOLATIONB , i18n("Interpolation - 2X")));
+ m_map.insert(m_categories[6], KoID(COMPOSITE_PENUMBRAA , i18n("Penumbra A")));
+ m_map.insert(m_categories[6], KoID(COMPOSITE_PENUMBRAB , i18n("Penumbra B")));
+ m_map.insert(m_categories[6], KoID(COMPOSITE_PENUMBRAC , i18n("Penumbra C")));
+ m_map.insert(m_categories[6], KoID(COMPOSITE_PENUMBRAD , i18n("Penumbra D")));
+
+ m_map.insert(m_categories[7], KoID(COMPOSITE_BUMPMAP , i18n("Bumpmap")));
+ m_map.insert(m_categories[7], KoID(COMPOSITE_COMBINE_NORMAL, i18n("Combine Normal Map")));
+ m_map.insert(m_categories[7], KoID(COMPOSITE_DISSOLVE , i18n("Dissolve")));
+ m_map.insert(m_categories[7], KoID(COMPOSITE_COPY_RED , i18n("Copy Red")));
+ m_map.insert(m_categories[7], KoID(COMPOSITE_COPY_GREEN, i18n("Copy Green")));
+ m_map.insert(m_categories[7], KoID(COMPOSITE_COPY_BLUE , i18n("Copy Blue")));
+ m_map.insert(m_categories[7], KoID(COMPOSITE_COPY , i18n("Copy")));
+ m_map.insert(m_categories[7], KoID(COMPOSITE_TANGENT_NORMALMAP, i18n("Tangent Normalmap")));
+
+ m_map.insert(m_categories[8], KoID(COMPOSITE_COLOR , i18n("Color")));
+ m_map.insert(m_categories[8], KoID(COMPOSITE_HUE , i18n("Hue")));
+ m_map.insert(m_categories[8], KoID(COMPOSITE_SATURATION , i18n("Saturation")));
+ m_map.insert(m_categories[8], KoID(COMPOSITE_LUMINIZE , i18n("Luminosity")));
+ m_map.insert(m_categories[8], KoID(COMPOSITE_DEC_SATURATION, i18n("Decrease Saturation")));
+ m_map.insert(m_categories[8], KoID(COMPOSITE_INC_SATURATION, i18n("Increase Saturation")));
+ m_map.insert(m_categories[8], KoID(COMPOSITE_DEC_LUMINOSITY, i18n("Decrease Luminosity")));
+ m_map.insert(m_categories[8], KoID(COMPOSITE_INC_LUMINOSITY, i18n("Increase Luminosity")));
+
+ m_map.insert(m_categories[9], KoID(COMPOSITE_COLOR_HSI , i18n("Color HSI")));
+ m_map.insert(m_categories[9], KoID(COMPOSITE_HUE_HSI , i18n("Hue HSI")));
+ m_map.insert(m_categories[9], KoID(COMPOSITE_SATURATION_HSI , i18n("Saturation HSI")));
+ m_map.insert(m_categories[9], KoID(COMPOSITE_INTENSITY , i18n("Intensity")));
+ m_map.insert(m_categories[9], KoID(COMPOSITE_DEC_SATURATION_HSI, i18n("Decrease Saturation HSI")));
+ m_map.insert(m_categories[9], KoID(COMPOSITE_INC_SATURATION_HSI, i18n("Increase Saturation HSI")));
+ m_map.insert(m_categories[9], KoID(COMPOSITE_DEC_INTENSITY , i18n("Decrease Intensity")));
+ m_map.insert(m_categories[9], KoID(COMPOSITE_INC_INTENSITY , i18n("Increase Intensity")));
+
+ m_map.insert(m_categories[10], KoID(COMPOSITE_COLOR_HSL , i18n("Color HSL")));
+ m_map.insert(m_categories[10], KoID(COMPOSITE_HUE_HSL , i18n("Hue HSL")));
+ m_map.insert(m_categories[10], KoID(COMPOSITE_SATURATION_HSL , i18n("Saturation HSL")));
+ m_map.insert(m_categories[10], KoID(COMPOSITE_LIGHTNESS , i18n("Lightness")));
+ m_map.insert(m_categories[10], KoID(COMPOSITE_DEC_SATURATION_HSL, i18n("Decrease Saturation HSL")));
+ m_map.insert(m_categories[10], KoID(COMPOSITE_INC_SATURATION_HSL, i18n("Increase Saturation HSL")));
+ m_map.insert(m_categories[10], KoID(COMPOSITE_DEC_LIGHTNESS , i18n("Decrease Lightness")));
+ m_map.insert(m_categories[10], KoID(COMPOSITE_INC_LIGHTNESS , i18n("Increase Lightness")));
- m_map.insert(m_categories[1], KoID(COMPOSITE_BURN , i18n("Burn")));
- m_map.insert(m_categories[1], KoID(COMPOSITE_LINEAR_BURN, i18n("Linear Burn")));
- m_map.insert(m_categories[1], KoID(COMPOSITE_DARKEN , i18n("Darken")));
- m_map.insert(m_categories[1], KoID(COMPOSITE_GAMMA_DARK , i18n("Gamma Dark")));
- m_map.insert(m_categories[1], KoID(COMPOSITE_DARKER_COLOR , i18n("Darker Color")));
-
- m_map.insert(m_categories[2], KoID(COMPOSITE_DODGE , i18n("Color Dodge")));
- m_map.insert(m_categories[2], KoID(COMPOSITE_LINEAR_DODGE, i18n("Linear Dodge")));
- m_map.insert(m_categories[2], KoID(COMPOSITE_LIGHTEN , i18n("Lighten")));
- m_map.insert(m_categories[2], KoID(COMPOSITE_LINEAR_LIGHT, i18n("Linear Light")));
- m_map.insert(m_categories[2], KoID(COMPOSITE_SCREEN , i18n("Screen")));
- m_map.insert(m_categories[2], KoID(COMPOSITE_PIN_LIGHT , i18n("Pin Light")));
- m_map.insert(m_categories[2], KoID(COMPOSITE_VIVID_LIGHT , i18n("Vivid Light")));
- m_map.insert(m_categories[2], KoID(COMPOSITE_HARD_LIGHT , i18n("Hard Light")));
- m_map.insert(m_categories[2], KoID(COMPOSITE_SOFT_LIGHT_PHOTOSHOP, i18n("Soft Light (Photoshop)")));
- m_map.insert(m_categories[2], KoID(COMPOSITE_SOFT_LIGHT_SVG, i18n("Soft Light (SVG)")));
- m_map.insert(m_categories[2], KoID(COMPOSITE_GAMMA_LIGHT , i18n("Gamma Light")));
- m_map.insert(m_categories[2], KoID(COMPOSITE_LIGHTER_COLOR , i18n("Lighter Color")));
-
- m_map.insert(m_categories[3], KoID(COMPOSITE_DIFF , i18n("Difference")));
- m_map.insert(m_categories[3], KoID(COMPOSITE_EQUIVALENCE , i18n("Equivalence")));
- m_map.insert(m_categories[3], KoID(COMPOSITE_ADDITIVE_SUBTRACTIVE, i18n("Additive Subtractive")));
- m_map.insert(m_categories[3], KoID(COMPOSITE_EXCLUSION , i18n("Exclusion")));
- m_map.insert(m_categories[3], KoID(COMPOSITE_ARC_TANGENT , i18n("Arcus Tangent")));
-
- m_map.insert(m_categories[4], KoID(COMPOSITE_OVER , i18n("Normal")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_BEHIND , i18n("Behind")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_GREATER , i18n("Greater")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_OVERLAY , i18n("Overlay")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_ERASE , i18n("Erase")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_ALPHA_DARKEN , i18n("Alpha Darken")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_HARD_MIX , i18n("Hard Mix")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_HARD_MIX_PHOTOSHOP, i18n("Hard Mix (Photoshop)")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_GRAIN_MERGE , i18n("Grain Merge")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_GRAIN_EXTRACT , i18n("Grain Extract")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_PARALLEL , i18n("Parallel")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_ALLANON , i18n("Allanon")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_GEOMETRIC_MEAN , i18n("Geometric Mean")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_DESTINATION_ATOP, i18n("Destination Atop")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_DESTINATION_IN , i18n("Destination In")));
- m_map.insert(m_categories[4], KoID(COMPOSITE_HARD_OVERLAY , i18n("Hard Overlay")));
-
- m_map.insert(m_categories[5], KoID(COMPOSITE_BUMPMAP , i18n("Bumpmap")));
- m_map.insert(m_categories[5], KoID(COMPOSITE_COMBINE_NORMAL, i18n("Combine Normal Map")));
- m_map.insert(m_categories[5], KoID(COMPOSITE_DISSOLVE , i18n("Dissolve")));
- m_map.insert(m_categories[5], KoID(COMPOSITE_COPY_RED , i18n("Copy Red")));
- m_map.insert(m_categories[5], KoID(COMPOSITE_COPY_GREEN, i18n("Copy Green")));
- m_map.insert(m_categories[5], KoID(COMPOSITE_COPY_BLUE , i18n("Copy Blue")));
- m_map.insert(m_categories[5], KoID(COMPOSITE_COPY , i18n("Copy")));
- m_map.insert(m_categories[5], KoID(COMPOSITE_TANGENT_NORMALMAP, i18n("Tangent Normalmap")));
-
- m_map.insert(m_categories[6], KoID(COMPOSITE_COLOR , i18n("Color")));
- m_map.insert(m_categories[6], KoID(COMPOSITE_HUE , i18n("Hue")));
- m_map.insert(m_categories[6], KoID(COMPOSITE_SATURATION , i18n("Saturation")));
- m_map.insert(m_categories[6], KoID(COMPOSITE_LUMINIZE , i18n("Luminosity")));
- m_map.insert(m_categories[6], KoID(COMPOSITE_DEC_SATURATION, i18n("Decrease Saturation")));
- m_map.insert(m_categories[6], KoID(COMPOSITE_INC_SATURATION, i18n("Increase Saturation")));
- m_map.insert(m_categories[6], KoID(COMPOSITE_DEC_LUMINOSITY, i18n("Decrease Luminosity")));
- m_map.insert(m_categories[6], KoID(COMPOSITE_INC_LUMINOSITY, i18n("Increase Luminosity")));
-
- m_map.insert(m_categories[7], KoID(COMPOSITE_COLOR_HSI , i18n("Color HSI")));
- m_map.insert(m_categories[7], KoID(COMPOSITE_HUE_HSI , i18n("Hue HSI")));
- m_map.insert(m_categories[7], KoID(COMPOSITE_SATURATION_HSI , i18n("Saturation HSI")));
- m_map.insert(m_categories[7], KoID(COMPOSITE_INTENSITY , i18n("Intensity")));
- m_map.insert(m_categories[7], KoID(COMPOSITE_DEC_SATURATION_HSI, i18n("Decrease Saturation HSI")));
- m_map.insert(m_categories[7], KoID(COMPOSITE_INC_SATURATION_HSI, i18n("Increase Saturation HSI")));
- m_map.insert(m_categories[7], KoID(COMPOSITE_DEC_INTENSITY , i18n("Decrease Intensity")));
- m_map.insert(m_categories[7], KoID(COMPOSITE_INC_INTENSITY , i18n("Increase Intensity")));
-
- m_map.insert(m_categories[8], KoID(COMPOSITE_COLOR_HSL , i18n("Color HSL")));
- m_map.insert(m_categories[8], KoID(COMPOSITE_HUE_HSL , i18n("Hue HSL")));
- m_map.insert(m_categories[8], KoID(COMPOSITE_SATURATION_HSL , i18n("Saturation HSL")));
- m_map.insert(m_categories[8], KoID(COMPOSITE_LIGHTNESS , i18n("Lightness")));
- m_map.insert(m_categories[8], KoID(COMPOSITE_DEC_SATURATION_HSL, i18n("Decrease Saturation HSL")));
- m_map.insert(m_categories[8], KoID(COMPOSITE_INC_SATURATION_HSL, i18n("Increase Saturation HSL")));
- m_map.insert(m_categories[8], KoID(COMPOSITE_DEC_LIGHTNESS , i18n("Decrease Lightness")));
- m_map.insert(m_categories[8], KoID(COMPOSITE_INC_LIGHTNESS , i18n("Increase Lightness")));
-
- m_map.insert(m_categories[9], KoID(COMPOSITE_COLOR_HSV , i18n("Color HSV")));
- m_map.insert(m_categories[9], KoID(COMPOSITE_HUE_HSV , i18n("Hue HSV")));
- m_map.insert(m_categories[9], KoID(COMPOSITE_SATURATION_HSV , i18n("Saturation HSV")));
- m_map.insert(m_categories[9], KoID(COMPOSITE_VALUE , i18nc("HSV Value", "Value")));
- m_map.insert(m_categories[9], KoID(COMPOSITE_DEC_SATURATION_HSV, i18n("Decrease Saturation HSV")));
- m_map.insert(m_categories[9], KoID(COMPOSITE_INC_SATURATION_HSV, i18n("Increase Saturation HSV")));
- m_map.insert(m_categories[9], KoID(COMPOSITE_DEC_VALUE , i18n("Decrease Value")));
- m_map.insert(m_categories[9], KoID(COMPOSITE_INC_VALUE , i18n("Increase Value")));
+ m_map.insert(m_categories[11], KoID(COMPOSITE_COLOR_HSV , i18n("Color HSV")));
+ m_map.insert(m_categories[11], KoID(COMPOSITE_HUE_HSV , i18n("Hue HSV")));
+ m_map.insert(m_categories[11], KoID(COMPOSITE_SATURATION_HSV , i18n("Saturation HSV")));
+ m_map.insert(m_categories[11], KoID(COMPOSITE_VALUE , i18nc("HSV Value", "Value")));
+ m_map.insert(m_categories[11], KoID(COMPOSITE_DEC_SATURATION_HSV, i18n("Decrease Saturation HSV")));
+ m_map.insert(m_categories[11], KoID(COMPOSITE_INC_SATURATION_HSV, i18n("Increase Saturation HSV")));
+ m_map.insert(m_categories[11], KoID(COMPOSITE_DEC_VALUE , i18n("Decrease Value")));
+ m_map.insert(m_categories[11], KoID(COMPOSITE_INC_VALUE , i18n("Increase Value")));
- m_map.insert(m_categories[10], KoID(COMPOSITE_REFLECT , i18n("Reflect")));
- m_map.insert(m_categories[10], KoID(COMPOSITE_GLOW , i18n("Glow")));
- m_map.insert(m_categories[10], KoID(COMPOSITE_FREEZE , i18n("Freeze")));
- m_map.insert(m_categories[10], KoID(COMPOSITE_HEAT , i18n("Heat")));
+ m_map.insert(m_categories[12], KoID(COMPOSITE_REFLECT , i18n("Reflect")));
+ m_map.insert(m_categories[12], KoID(COMPOSITE_GLOW , i18n("Glow")));
+ m_map.insert(m_categories[12], KoID(COMPOSITE_FREEZE , i18n("Freeze")));
+ m_map.insert(m_categories[12], KoID(COMPOSITE_HEAT , i18n("Heat")));
+ m_map.insert(m_categories[12], KoID(COMPOSITE_GLEAT , i18n("Glow-Heat")));
+ m_map.insert(m_categories[12], KoID(COMPOSITE_HELOW , i18n("Heat-Glow")));
+ m_map.insert(m_categories[12], KoID(COMPOSITE_REEZE , i18n("Reflect-Freeze")));
+ m_map.insert(m_categories[12], KoID(COMPOSITE_FRECT , i18n("Freeze-Reflect")));
+ m_map.insert(m_categories[12], KoID(COMPOSITE_FHYRD , i18n("Heat-Glow & Freeze-Reflect Hybrid")));
}
const KoCompositeOpRegistry& KoCompositeOpRegistry::instance()
{
return *registry;
}
KoID KoCompositeOpRegistry::getDefaultCompositeOp() const
{
return KoID(COMPOSITE_OVER, i18n("Normal"));
}
KoID KoCompositeOpRegistry::getKoID(const QString& compositeOpID) const
{
KoIDMap::const_iterator itr = std::find(m_map.begin(), m_map.end(), KoID(compositeOpID));
return (itr != m_map.end()) ? *itr : KoID();
}
KoCompositeOpRegistry::KoIDMap KoCompositeOpRegistry::getCompositeOps() const
{
return m_map;
}
KoCompositeOpRegistry::KoIDList KoCompositeOpRegistry::getCategories() const
{
return m_categories;
}
KoCompositeOpRegistry::KoIDList KoCompositeOpRegistry::getCompositeOps(const KoID& category, const KoColorSpace* colorSpace) const
{
qint32 num = m_map.count(category);
KoIDMap::const_iterator beg = m_map.find(category);
KoIDMap::const_iterator end = beg + num;
KoIDList list;
list.reserve(num);
if(colorSpace) {
for(; beg!=end; ++beg){
if(colorSpace->hasCompositeOp(beg->id()))
list.push_back(*beg);
}
}
else {
for(; beg!=end; ++beg)
list.push_back(*beg);
}
return list;
}
KoCompositeOpRegistry::KoIDList KoCompositeOpRegistry::getCompositeOps(const KoColorSpace* colorSpace) const
{
KoIDMap::const_iterator beg = m_map.begin();
KoIDMap::const_iterator end = m_map.end();
KoIDList list;
list.reserve(m_map.size());
if(colorSpace) {
for(; beg!=end; ++beg){
if(colorSpace->hasCompositeOp(beg->id()))
list.push_back(*beg);
}
}
else {
for(; beg!=end; ++beg)
list.push_back(*beg);
}
return list;
}
bool KoCompositeOpRegistry::colorSpaceHasCompositeOp(const KoColorSpace* colorSpace, const KoID& compositeOp) const
{
return colorSpace ? colorSpace->hasCompositeOp(compositeOp.id()) : false;
}
diff --git a/libs/pigment/KoCompositeOpRegistry.h b/libs/pigment/KoCompositeOpRegistry.h
index bedfb5e43b..58b9641625 100644
--- a/libs/pigment/KoCompositeOpRegistry.h
+++ b/libs/pigment/KoCompositeOpRegistry.h
@@ -1,183 +1,227 @@
/*
* Copyright (c) 2005 Adrian Page <adrian@pagenet.plus.com>
* Copyright (c) 2011 Silvio Heinrich <plassy@web.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOCOMPOSITEOPREGISTRY_H
#define KOCOMPOSITEOPREGISTRY_H
#include <QString>
#include <QList>
#include <QMultiMap>
#include <QBitArray>
#include "kritapigment_export.h"
class KoColorSpace;
#include <KoID.h>
// TODO : convert this data blob into a modern design with an enum class.
// This will reduce the need for runtime string comparisons.
const QString COMPOSITE_OVER = "normal";
const QString COMPOSITE_ERASE = "erase";
const QString COMPOSITE_IN = "in";
const QString COMPOSITE_OUT = "out";
const QString COMPOSITE_ALPHA_DARKEN = "alphadarken";
const QString COMPOSITE_DESTINATION_IN = "destination-in";
const QString COMPOSITE_DESTINATION_ATOP = "destination-atop";
const QString COMPOSITE_XOR = "xor";
+const QString COMPOSITE_OR = "or";
+const QString COMPOSITE_AND = "and";
+const QString COMPOSITE_NAND = "nand";
+const QString COMPOSITE_NOR = "nor";
+const QString COMPOSITE_XNOR = "xnor";
+const QString COMPOSITE_IMPLICATION = "implication";
+const QString COMPOSITE_NOT_IMPLICATION = "not_implication";
+const QString COMPOSITE_CONVERSE = "converse";
+const QString COMPOSITE_NOT_CONVERSE = "not_converse";
+
const QString COMPOSITE_PLUS = "plus";
const QString COMPOSITE_MINUS = "minus";
const QString COMPOSITE_ADD = "add";
const QString COMPOSITE_SUBTRACT = "subtract";
const QString COMPOSITE_INVERSE_SUBTRACT = "inverse_subtract";
const QString COMPOSITE_DIFF = "diff";
const QString COMPOSITE_MULT = "multiply";
const QString COMPOSITE_DIVIDE = "divide";
const QString COMPOSITE_ARC_TANGENT = "arc_tangent";
const QString COMPOSITE_GEOMETRIC_MEAN = "geometric_mean";
const QString COMPOSITE_ADDITIVE_SUBTRACTIVE = "additive_subtractive";
+const QString COMPOSITE_NEGATION = "negation";
+
+const QString COMPOSITE_MOD = "modulo";
+const QString COMPOSITE_MOD_CON = "modulo_continuous";
+const QString COMPOSITE_DIVISIVE_MOD = "divisive_modulo";
+const QString COMPOSITE_DIVISIVE_MOD_CON = "divisive_modulo_continuous";
+const QString COMPOSITE_MODULO_SHIFT = "modulo_shift";
+const QString COMPOSITE_MODULO_SHIFT_CON = "modulo_shift_continuous";
const QString COMPOSITE_EQUIVALENCE = "equivalence";
const QString COMPOSITE_ALLANON = "allanon";
const QString COMPOSITE_PARALLEL = "parallel";
const QString COMPOSITE_GRAIN_MERGE = "grain_merge";
const QString COMPOSITE_GRAIN_EXTRACT = "grain_extract";
const QString COMPOSITE_EXCLUSION = "exclusion";
const QString COMPOSITE_HARD_MIX = "hard mix";
const QString COMPOSITE_HARD_MIX_PHOTOSHOP = "hard_mix_photoshop";
const QString COMPOSITE_OVERLAY = "overlay";
const QString COMPOSITE_BEHIND = "behind";
const QString COMPOSITE_GREATER = "greater";
const QString COMPOSITE_HARD_OVERLAY = "hard overlay";
+const QString COMPOSITE_INTERPOLATION = "interpolation";
+const QString COMPOSITE_INTERPOLATIONB = "interpolation 2x";
+const QString COMPOSITE_PENUMBRAA = "penumbra a";
+const QString COMPOSITE_PENUMBRAB = "penumbra b";
+const QString COMPOSITE_PENUMBRAC = "penumbra c";
+const QString COMPOSITE_PENUMBRAD = "penumbra d";
const QString COMPOSITE_DARKEN = "darken";
const QString COMPOSITE_BURN = "burn";//this is also known as 'color burn'.
+const QString COMPOSITE_BURN_LOGARITHMIC = "burn_logarithmic";
const QString COMPOSITE_LINEAR_BURN = "linear_burn";
const QString COMPOSITE_GAMMA_DARK = "gamma_dark";
+const QString COMPOSITE_SHADE_IFS_ILLUSIONS = "shade_ifs_illusions";
+const QString COMPOSITE_FOG_DARKEN_IFS_ILLUSIONS = "fog_darken_ifs_illusions";
+const QString COMPOSITE_EASY_BURN = "easy burn";
const QString COMPOSITE_LIGHTEN = "lighten";
const QString COMPOSITE_DODGE = "dodge";
+const QString COMPOSITE_DODGE_LOGARITHMIC = "dodge_logarithmic";
const QString COMPOSITE_LINEAR_DODGE = "linear_dodge";
const QString COMPOSITE_SCREEN = "screen";
const QString COMPOSITE_HARD_LIGHT = "hard_light";
+const QString COMPOSITE_SOFT_LIGHT_IFS_ILLUSIONS = "soft_light_ifs_illusions";
+const QString COMPOSITE_SOFT_LIGHT_PEGTOP_DELPHI = "soft_light_pegtop_delphi";
const QString COMPOSITE_SOFT_LIGHT_PHOTOSHOP = "soft_light";
const QString COMPOSITE_SOFT_LIGHT_SVG = "soft_light_svg";
const QString COMPOSITE_GAMMA_LIGHT = "gamma_light";
+const QString COMPOSITE_GAMMA_ILLUMINATION = "gamma_illumination";
const QString COMPOSITE_VIVID_LIGHT = "vivid_light";
+const QString COMPOSITE_FLAT_LIGHT = "flat_light";
const QString COMPOSITE_LINEAR_LIGHT = "linear light";
const QString COMPOSITE_PIN_LIGHT = "pin_light";
+const QString COMPOSITE_PNORM_A = "pnorm_a";
+const QString COMPOSITE_PNORM_B = "pnorm_b";
+const QString COMPOSITE_SUPER_LIGHT = "super_light";
+const QString COMPOSITE_TINT_IFS_ILLUSIONS = "tint_ifs_illusions";
+const QString COMPOSITE_FOG_LIGHTEN_IFS_ILLUSIONS = "fog_lighten_ifs_illusions";
+const QString COMPOSITE_EASY_DODGE = "easy dodge";
const QString COMPOSITE_HUE = "hue";
const QString COMPOSITE_COLOR = "color";
const QString COMPOSITE_SATURATION = "saturation";
const QString COMPOSITE_INC_SATURATION = "inc_saturation";
const QString COMPOSITE_DEC_SATURATION = "dec_saturation";
const QString COMPOSITE_LUMINIZE = "luminize";
const QString COMPOSITE_INC_LUMINOSITY = "inc_luminosity";
const QString COMPOSITE_DEC_LUMINOSITY = "dec_luminosity";
const QString COMPOSITE_HUE_HSV = "hue_hsv";
const QString COMPOSITE_COLOR_HSV = "color_hsv";
const QString COMPOSITE_SATURATION_HSV = "saturation_hsv";
const QString COMPOSITE_INC_SATURATION_HSV = "inc_saturation_hsv";
const QString COMPOSITE_DEC_SATURATION_HSV = "dec_saturation_hsv";
const QString COMPOSITE_VALUE = "value";
const QString COMPOSITE_INC_VALUE = "inc_value";
const QString COMPOSITE_DEC_VALUE = "dec_value";
const QString COMPOSITE_HUE_HSL = "hue_hsl";
const QString COMPOSITE_COLOR_HSL = "color_hsl";
const QString COMPOSITE_SATURATION_HSL = "saturation_hsl";
const QString COMPOSITE_INC_SATURATION_HSL = "inc_saturation_hsl";
const QString COMPOSITE_DEC_SATURATION_HSL = "dec_saturation_hsl";
const QString COMPOSITE_LIGHTNESS = "lightness";
const QString COMPOSITE_INC_LIGHTNESS = "inc_lightness";
const QString COMPOSITE_DEC_LIGHTNESS = "dec_lightness";
const QString COMPOSITE_HUE_HSI = "hue_hsi";
const QString COMPOSITE_COLOR_HSI = "color_hsi";
const QString COMPOSITE_SATURATION_HSI = "saturation_hsi";
const QString COMPOSITE_INC_SATURATION_HSI = "inc_saturation_hsi";
const QString COMPOSITE_DEC_SATURATION_HSI = "dec_saturation_hsi";
const QString COMPOSITE_INTENSITY = "intensity";
const QString COMPOSITE_INC_INTENSITY = "inc_intensity";
const QString COMPOSITE_DEC_INTENSITY = "dec_intensity";
const QString COMPOSITE_COPY = "copy";
const QString COMPOSITE_COPY_RED = "copy_red";
const QString COMPOSITE_COPY_GREEN = "copy_green";
const QString COMPOSITE_COPY_BLUE = "copy_blue";
const QString COMPOSITE_TANGENT_NORMALMAP = "tangent_normalmap";
const QString COMPOSITE_COLORIZE = "colorize";
const QString COMPOSITE_BUMPMAP = "bumpmap";
const QString COMPOSITE_COMBINE_NORMAL = "combine_normal";
const QString COMPOSITE_CLEAR = "clear";
const QString COMPOSITE_DISSOLVE = "dissolve";
const QString COMPOSITE_DISPLACE = "displace";
const QString COMPOSITE_NO = "nocomposition";
const QString COMPOSITE_PASS_THROUGH = "pass through"; // XXX: not implemented anywhere yet
const QString COMPOSITE_DARKER_COLOR = "darker color";
const QString COMPOSITE_LIGHTER_COLOR = "lighter color";
const QString COMPOSITE_UNDEF = "undefined";
const QString COMPOSITE_REFLECT = "reflect";
const QString COMPOSITE_GLOW = "glow";
const QString COMPOSITE_FREEZE = "freeze";
const QString COMPOSITE_HEAT = "heat";
+const QString COMPOSITE_GLEAT = "glow_heat";
+const QString COMPOSITE_HELOW = "heat_glow";
+const QString COMPOSITE_REEZE = "reflect_freeze";
+const QString COMPOSITE_FRECT = "freeze_reflect";
+const QString COMPOSITE_FHYRD = "heat_glow_freeze_reflect_hybrid";
class KRITAPIGMENT_EXPORT KoCompositeOpRegistry
{
typedef QMultiMap<KoID,KoID> KoIDMap;
typedef QList<KoID> KoIDList;
public:
KoCompositeOpRegistry();
static const KoCompositeOpRegistry& instance();
KoID getDefaultCompositeOp() const;
KoID getKoID(const QString& compositeOpID) const;
KoIDMap getCompositeOps() const;
KoIDList getCategories() const;
KoIDList getCompositeOps(const KoColorSpace* colorSpace) const;
KoIDList getCompositeOps(const KoID& category, const KoColorSpace* colorSpace=0) const;
bool colorSpaceHasCompositeOp(const KoColorSpace* colorSpace, const KoID& compositeOp) const;
template<class TKoIdIterator>
KoIDList filterCompositeOps(TKoIdIterator begin, TKoIdIterator end, const KoColorSpace* colorSpace, bool removeInvaliOps=true) const {
KoIDList list;
for(; begin!=end; ++begin){
if( colorSpaceHasCompositeOp(colorSpace, *begin) == removeInvaliOps)
list.push_back(*begin);
}
return list;
}
private:
KoIDList m_categories;
KoIDMap m_map;
};
#endif // KOCOMPOSITEOPREGISTRY_H
diff --git a/libs/pigment/KoConvolutionOpImpl.h b/libs/pigment/KoConvolutionOpImpl.h
index 130c4ea8df..4b54d1877e 100644
--- a/libs/pigment/KoConvolutionOpImpl.h
+++ b/libs/pigment/KoConvolutionOpImpl.h
@@ -1,153 +1,153 @@
/*
* Copyright (c) 2006 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2007 Emanuele Tamponi <emanuele@valinor.it>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KO_CONVOLUTION_OP_IMPL_H
#define KO_CONVOLUTION_OP_IMPL_H
#include "DebugPigment.h"
#include "KoColorSpaceMaths.h"
#include "KoConvolutionOp.h"
#include "KoColorSpaceTraits.h"
template<class _CSTrait>
class KoConvolutionOpImpl : public KoConvolutionOp
{
typedef typename KoColorSpaceMathsTraits<typename _CSTrait::channels_type>::compositetype compositetype;
typedef typename _CSTrait::channels_type channels_type;
public:
KoConvolutionOpImpl() { }
~KoConvolutionOpImpl() override { }
/**
- * Calculates a weighted average of the pixels, mentioned in @colors
- * using weight values from @kernelValues
+ * Calculates a weighted average of the pixels, mentioned in @p colors
+ * using weight values from @p kernelValues
*
* Note:
* It behaves in a quite unclear way, when at least one pixel is
* fully transparent. There are three cases:
* Case A) None of the pixels is fully transparent.
- * * Every color channel AND alpha channel of @dst stores a sum
- * of the corresponding channels from @colors, divided by @factor
- * and incremented by @offset
- * Case B) At least one pixel of @colors is transparent and @factor
+ * * Every color channel AND alpha channel of @p dst stores a sum
+ * of the corresponding channels from @p colors, divided by @p factor
+ * and incremented by @p offset
+ * Case B) At least one pixel of @p colors is transparent and @p factor
* stores a weight of the kernel (sum of it's items).
- * * Every color channel of @dst stores a sum of the corresponding
+ * * Every color channel of @p dst stores a sum of the corresponding
* channels from non-transparent pixels, divided by a weight
- * of non-transparent pixels and incremented by @offset.
- * * Alpha channel of @dst stores a sum of the corresponding
+ * of non-transparent pixels and incremented by @p offset.
+ * * Alpha channel of @p dst stores a sum of the corresponding
* channels from non-transparent pixels, divided by a weight
- * of all the pixels (equals to @factor) and incremented
- * by @offset.
- * Case C) At least one pixel of @colors is transparent and @factor
+ * of all the pixels (equals to @p factor) and incremented
+ * by @p offset.
+ * Case C) At least one pixel of @p colors is transparent and @p factor
* is set to an arbitrary value.
- * * Every color channel of @dst stores a sum of the corresponding
+ * * Every color channel of @p dst stores a sum of the corresponding
* channels from non-transparent pixels, divided by a "scaled
- * down factor" and incremented by @offset. "Scaled
+ * down factor" and incremented by @p offset. "Scaled
* down factor" is calculated in the following way:
*
* [weight of non-transparent pixels]
- * scaledDownFactor = @factor * ----------------------------------
+ * scaledDownFactor = @p factor * ----------------------------------
* [weight of all the pixels]
*
- * * Alpha channel of @dst stores a sum of the corresponding
+ * * Alpha channel of @p dst stores a sum of the corresponding
* channels from non-transparent pixels, divided by unscaled
- * @factor and incremented by @offset.
+ * @p factor and incremented by @p offset.
*/
void convolveColors(const quint8* const* colors, const qreal* kernelValues, quint8 *dst, qreal factor, qreal offset, qint32 nPixels, const QBitArray & channelFlags) const override {
// Create and initialize to 0 the array of totals
qreal totals[_CSTrait::channels_nb];
qreal totalWeight = 0;
qreal totalWeightTransparent = 0;
memset(totals, 0, sizeof(qreal) * _CSTrait::channels_nb);
for (; nPixels--; colors++, kernelValues++) {
qreal weight = *kernelValues;
const channels_type* color = _CSTrait::nativeArray(*colors);
if (weight != 0) {
if (_CSTrait::opacityU8(*colors) == 0) {
totalWeightTransparent += weight;
} else {
for (uint i = 0; i < _CSTrait::channels_nb; i++) {
totals[i] += color[i] * weight;
}
}
totalWeight += weight;
}
}
typename _CSTrait::channels_type* dstColor = _CSTrait::nativeArray(dst);
bool allChannels = channelFlags.isEmpty();
Q_ASSERT(allChannels || channelFlags.size() == (int)_CSTrait::channels_nb);
if (totalWeightTransparent == 0) {
// Case A)
for (uint i = 0; i < _CSTrait::channels_nb; i++) {
if (allChannels || channelFlags.testBit(i)) {
compositetype v = totals[i] / factor + offset;
dstColor[ i ] = CLAMP(v, KoColorSpaceMathsTraits<channels_type>::min,
KoColorSpaceMathsTraits<channels_type>::max);
}
}
} else if (totalWeightTransparent != totalWeight) {
if (totalWeight == factor) {
// Case B)
qint64 a = (totalWeight - totalWeightTransparent);
for (uint i = 0; i < _CSTrait::channels_nb; i++) {
if (allChannels || channelFlags.testBit(i)) {
if (i == (uint)_CSTrait::alpha_pos) {
compositetype v = totals[i] / totalWeight + offset;
dstColor[ i ] = CLAMP(v, KoColorSpaceMathsTraits<channels_type>::min,
KoColorSpaceMathsTraits<channels_type>::max);
} else {
compositetype v = totals[i] / a + offset;
dstColor[ i ] = CLAMP(v, KoColorSpaceMathsTraits<channels_type>::min,
KoColorSpaceMathsTraits<channels_type>::max);
}
}
}
} else {
// Case C)
qreal a = qreal(totalWeight) / (factor * (totalWeight - totalWeightTransparent)); // use qreal as it easily saturate
for (uint i = 0; i < _CSTrait::channels_nb; i++) {
if (allChannels || channelFlags.testBit(i)) {
if (i == (uint)_CSTrait::alpha_pos) {
compositetype v = totals[i] / factor + offset;
dstColor[ i ] = CLAMP(v, KoColorSpaceMathsTraits<channels_type>::min,
KoColorSpaceMathsTraits<channels_type>::max);
} else {
compositetype v = (compositetype)(totals[i] * a + offset);
dstColor[ i ] = CLAMP(v, KoColorSpaceMathsTraits<channels_type>::min,
KoColorSpaceMathsTraits<channels_type>::max);
}
}
}
}
}
}
};
#endif
diff --git a/libs/pigment/benchmarks/KoCompositeOpsBenchmark.cpp b/libs/pigment/benchmarks/KoCompositeOpsBenchmark.cpp
index 68876cf453..77accdf082 100644
--- a/libs/pigment/benchmarks/KoCompositeOpsBenchmark.cpp
+++ b/libs/pigment/benchmarks/KoCompositeOpsBenchmark.cpp
@@ -1,91 +1,112 @@
/*
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "KoCompositeOpsBenchmark.h"
#include "../compositeops/KoCompositeOpAlphaDarken.h"
#include "../compositeops/KoCompositeOpOver.h"
#include <KoOptimizedCompositeOpFactory.h>
#include <KoColorSpaceTraits.h>
#include <KoColorSpaceRegistry.h>
#include <QTest>
const int TILE_WIDTH = 64;
const int TILE_HEIGHT = 64;
-const int IMG_WIDTH = 4096;
-const int IMG_HEIGHT = 4096;
+const int IMG_WIDTH = 2048;
+const int IMG_HEIGHT = 2048;
const quint8 OPACITY_HALF = 128;
const int TILES_IN_WIDTH = IMG_WIDTH / TILE_WIDTH;
const int TILES_IN_HEIGHT = IMG_HEIGHT / TILE_HEIGHT;
#define COMPOSITE_BENCHMARK \
for (int y = 0; y < TILES_IN_HEIGHT; y++){ \
- for (int x = 0; x < TILES_IN_WIDTH; x++){ \
- compositeOp->composite(m_dstBuffer, TILE_WIDTH * KoBgrU16Traits::pixelSize, \
- m_srcBuffer, TILE_WIDTH * KoBgrU16Traits::pixelSize, \
- 0, 0, \
+ for (int x = 0; x < TILES_IN_WIDTH; x++) { \
+ const int rowStride = IMG_WIDTH * KoBgrU8Traits::pixelSize; \
+ const int bufOffset = y * rowStride + x * TILE_WIDTH * KoBgrU8Traits::pixelSize; \
+ compositeOp->composite(m_dstBuffer + bufOffset, rowStride, \
+ m_srcBuffer + bufOffset, rowStride, \
+ m_mskBuffer + bufOffset, rowStride, \
TILE_WIDTH, TILE_HEIGHT, \
OPACITY_HALF); \
} \
}
void KoCompositeOpsBenchmark::initTestCase()
{
- m_dstBuffer = new quint8[ TILE_WIDTH * TILE_HEIGHT * KoBgrU16Traits::pixelSize ];
- m_srcBuffer = new quint8[ TILE_WIDTH * TILE_HEIGHT * KoBgrU16Traits::pixelSize ];
+ const int bufLen = IMG_HEIGHT * IMG_WIDTH * KoBgrU8Traits::pixelSize;
+
+ m_dstBuffer = new quint8[bufLen];
+ m_srcBuffer = new quint8[bufLen];
+ m_mskBuffer = new quint8[bufLen];
}
// this is called before every benchmark
void KoCompositeOpsBenchmark::init()
{
- memset(m_dstBuffer, 42 , TILE_WIDTH * TILE_HEIGHT * KoBgrU16Traits::pixelSize);
- memset(m_srcBuffer, 42 , TILE_WIDTH * TILE_HEIGHT * KoBgrU16Traits::pixelSize);
+ qsrand(42);
+
+ for (int i = 0; i < int(IMG_WIDTH * IMG_HEIGHT * KoBgrU8Traits::pixelSize); i++) {
+ const int randVal = qrand();
+
+ m_srcBuffer[i] = randVal & 0x0000FF;
+ m_dstBuffer[i] = (randVal & 0x00FF000) >> 8;
+ m_mskBuffer[i] = (randVal & 0xFF0000) >> 16;
+ }
}
void KoCompositeOpsBenchmark::cleanupTestCase()
{
delete [] m_dstBuffer;
delete [] m_srcBuffer;
+ delete [] m_mskBuffer;
}
void KoCompositeOpsBenchmark::benchmarkCompositeOver()
{
- KoCompositeOp *compositeOp = KoOptimizedCompositeOpFactory::createOverOp32(KoColorSpaceRegistry::instance()->rgb16());
+ KoCompositeOp *compositeOp = KoOptimizedCompositeOpFactory::createOverOp32(KoColorSpaceRegistry::instance()->rgb8());
QBENCHMARK{
COMPOSITE_BENCHMARK
}
}
-void KoCompositeOpsBenchmark::benchmarkCompositeAlphaDarken()
+void KoCompositeOpsBenchmark::benchmarkCompositeAlphaDarkenHard()
+{
+ KoCompositeOp *compositeOp = KoOptimizedCompositeOpFactory::createAlphaDarkenOpHard32(KoColorSpaceRegistry::instance()->rgb8());
+ QBENCHMARK{
+ COMPOSITE_BENCHMARK
+ }
+}
+
+
+void KoCompositeOpsBenchmark::benchmarkCompositeAlphaDarkenCreamy()
{
- //KoCompositeOpAlphaDarken<KoBgrU16Traits> compositeOp(0);
- KoCompositeOp *compositeOp = KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(KoColorSpaceRegistry::instance()->rgb16());
+ KoCompositeOp *compositeOp = KoOptimizedCompositeOpFactory::createAlphaDarkenOpCreamy32(KoColorSpaceRegistry::instance()->rgb8());
QBENCHMARK{
COMPOSITE_BENCHMARK
}
}
QTEST_GUILESS_MAIN(KoCompositeOpsBenchmark)
diff --git a/libs/pigment/benchmarks/KoCompositeOpsBenchmark.h b/libs/pigment/benchmarks/KoCompositeOpsBenchmark.h
index 0272aa23ca..56dd1f0b24 100644
--- a/libs/pigment/benchmarks/KoCompositeOpsBenchmark.h
+++ b/libs/pigment/benchmarks/KoCompositeOpsBenchmark.h
@@ -1,43 +1,45 @@
/*
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KO_COMPOSITEOPS_BENCHMARK_H_
#define KO_COMPOSITEOPS_BENCHMARK_H_
#include <QObject>
class KoCompositeOpsBenchmark : public QObject
{
Q_OBJECT
private Q_SLOTS:
void init();
void initTestCase();
void cleanupTestCase();
void benchmarkCompositeOver();
- void benchmarkCompositeAlphaDarken();
+ void benchmarkCompositeAlphaDarkenHard();
+ void benchmarkCompositeAlphaDarkenCreamy();
private:
quint8 * m_dstBuffer;
quint8 * m_srcBuffer;
+ quint8 * m_mskBuffer;
};
#endif
diff --git a/libs/pigment/colorspaces/KoAlphaColorSpace.cpp b/libs/pigment/colorspaces/KoAlphaColorSpace.cpp
index 5abdd3a5c2..9651d361e1 100644
--- a/libs/pigment/colorspaces/KoAlphaColorSpace.cpp
+++ b/libs/pigment/colorspaces/KoAlphaColorSpace.cpp
@@ -1,304 +1,305 @@
/*
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2006 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "KoAlphaColorSpace.h"
#include <limits.h>
#include <stdlib.h>
#include <QImage>
#include <QBitArray>
#include <klocalizedstring.h>
#include "KoChannelInfo.h"
#include "KoID.h"
#include "KoIntegerMaths.h"
#include "KoCompositeOpOver.h"
#include "KoCompositeOpErase.h"
#include "KoCompositeOpCopy2.h"
#include "KoCompositeOpAlphaDarken.h"
#include "KoCompositeOpBase.h"
+#include "KoCompositeOps.h"
#include <colorprofiles/KoDummyColorProfile.h>
namespace {
template <typename channel_type> KoChannelInfo::enumChannelValueType channelInfoIdFromChannelType();
template <> inline KoChannelInfo::enumChannelValueType channelInfoIdFromChannelType<quint8>() { return KoChannelInfo::UINT8; }
template <> inline KoChannelInfo::enumChannelValueType channelInfoIdFromChannelType<quint16>() { return KoChannelInfo::UINT16; }
#ifdef HAVE_OPENEXR
template <> inline KoChannelInfo::enumChannelValueType channelInfoIdFromChannelType<half>() { return KoChannelInfo::FLOAT16; }
#endif
template <> inline KoChannelInfo::enumChannelValueType channelInfoIdFromChannelType<float>() { return KoChannelInfo::FLOAT32; }
}
template<class Traits>
class AlphaColorSpaceMultiplyOp : public KoCompositeOpBase< Traits, AlphaColorSpaceMultiplyOp<Traits>>
{
typedef KoCompositeOpBase<Traits, AlphaColorSpaceMultiplyOp<Traits>> base_class;
typedef typename Traits::channels_type channels_type;
public:
AlphaColorSpaceMultiplyOp(const KoColorSpace* cs)
: base_class(cs, COMPOSITE_MULT, i18n("Multiply"), KoCompositeOp::categoryArithmetic()) { }
public:
template<bool alphaLocked, bool allChannelFlags>
inline static channels_type composeColorChannels(const channels_type* src, channels_type srcAlpha,
channels_type* dst, channels_type dstAlpha, channels_type maskAlpha,
channels_type opacity, const QBitArray& channelFlags) {
using namespace Arithmetic;
Q_UNUSED(allChannelFlags);
Q_UNUSED(src);
Q_UNUSED(dst);
Q_UNUSED(channelFlags);
if (!alphaLocked) {
// use internal parallelism for multiplication!
srcAlpha = mul(srcAlpha, maskAlpha);
dstAlpha = mul(dstAlpha, opacity);
dstAlpha = mul(srcAlpha, dstAlpha);
}
return dstAlpha;
}
};
template <class _CSTrait>
KoAlphaColorSpaceImpl<_CSTrait>::KoAlphaColorSpaceImpl()
: KoColorSpaceAbstract<_CSTrait>(alphaIdFromChannelType<channels_type>().id(),
alphaIdFromChannelType<channels_type>().name())
{
this->addChannel(new KoChannelInfo(i18n("Alpha"), 0, 0, KoChannelInfo::ALPHA, channelInfoIdFromChannelType<channels_type>()));
m_compositeOps << new KoCompositeOpOver<_CSTrait>(this)
<< new KoCompositeOpErase<_CSTrait>(this)
<< new KoCompositeOpCopy2<_CSTrait>(this)
- << new KoCompositeOpAlphaDarken<_CSTrait>(this)
+ << createAlphaDarkenCompositeOp<_CSTrait>(this)
<< new AlphaColorSpaceMultiplyOp<_CSTrait>(this);
Q_FOREACH (KoCompositeOp *op, m_compositeOps) {
this->addCompositeOp(op);
}
m_profile = new KoDummyColorProfile;
}
template <class _CSTrait>
KoAlphaColorSpaceImpl<_CSTrait>::~KoAlphaColorSpaceImpl()
{
qDeleteAll(m_compositeOps);
delete m_profile;
m_profile = 0;
}
template <class _CSTrait>
void KoAlphaColorSpaceImpl<_CSTrait>::fromQColor(const QColor& c, quint8 *dst, const KoColorProfile * /*profile*/) const
{
_CSTrait::nativeArray(dst)[0] = _MathsFromU8::scaleToA(c.alpha());
}
template <class _CSTrait>
void KoAlphaColorSpaceImpl<_CSTrait>::toQColor(const quint8 * src, QColor *c, const KoColorProfile * /*profile*/) const
{
c->setRgba(qRgba(255, 255, 255, _MathsToU8::scaleToA(_CSTrait::nativeArray(src)[0])));
}
template <class _CSTrait>
quint8 KoAlphaColorSpaceImpl<_CSTrait>::difference(const quint8 *src1, const quint8 *src2) const
{
return qAbs(_MathsToU8::scaleToA(_CSTrait::nativeArray(src2)[0] - _CSTrait::nativeArray(src1)[0]));
}
template <class _CSTrait>
quint8 KoAlphaColorSpaceImpl<_CSTrait>::differenceA(const quint8 *src1, const quint8 *src2) const
{
return difference(src1, src2);
}
template <class _CSTrait>
QString KoAlphaColorSpaceImpl<_CSTrait>::channelValueText(const quint8 *pixel, quint32 channelIndex) const
{
Q_ASSERT(channelIndex < this->channelCount());
const quint32 channelPosition = this->channels()[channelIndex]->pos();
return QString().setNum(_CSTrait::nativeArray(pixel)[channelPosition]);
}
template <class _CSTrait>
QString KoAlphaColorSpaceImpl<_CSTrait>::normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) const
{
Q_ASSERT(channelIndex < this->channelCount());
const quint32 channelPosition = this->channels()[channelIndex]->pos();
return QString().setNum(KoColorSpaceMaths<channels_type, float>::scaleToA(_CSTrait::nativeArray(pixel)[channelPosition]));
}
template <class _CSTrait>
void KoAlphaColorSpaceImpl<_CSTrait>::convolveColors(quint8** colors, qreal * kernelValues, quint8 *dst, qreal factor, qreal offset, qint32 nColors, const QBitArray & channelFlags) const
{
qreal totalAlpha = 0;
while (nColors--) {
qreal weight = *kernelValues;
if (weight != 0) {
totalAlpha += _CSTrait::nativeArray(*colors)[0] * weight;
}
++colors;
++kernelValues;
}
if (channelFlags.isEmpty() || channelFlags.testBit(0)) {
_CSTrait::nativeArray(dst)[0] = _Maths::clamp((totalAlpha / factor) + offset);
}
}
template <class _CSTrait>
QImage KoAlphaColorSpaceImpl<_CSTrait>::convertToQImage(const quint8 *data, qint32 width, qint32 height,
const KoColorProfile * /*dstProfile*/,
KoColorConversionTransformation::Intent /*renderingIntent*/,
KoColorConversionTransformation::ConversionFlags /*conversionFlags*/) const
{
const channels_type *srcPtr = _CSTrait::nativeArray(data);
QImage img(width, height, QImage::Format_Indexed8);
QVector<QRgb> table;
for (int i = 0; i < 256; ++i) table.append(qRgb(i, i, i));
img.setColorTable(table);
quint8* data_img;
for (int i = 0; i < height; ++i) {
data_img = img.scanLine(i);
for (int j = 0; j < width; ++j) {
data_img[j] = _MathsToU8::scaleToA(*(srcPtr++));
}
}
return img;
}
template <class _CSTrait>
void KoAlphaColorSpaceImpl<_CSTrait>::toLabA16(const quint8 *src, quint8 *dst, quint32 nPixels) const {
const channels_type* srcPtr = _CSTrait::nativeArray(src);
quint16* dstPtr = reinterpret_cast<quint16*>(dst);
while (nPixels--) {
dstPtr[0] = KoColorSpaceMaths<channels_type, quint16>::scaleToA(srcPtr[0]);
dstPtr[1] = UINT16_MAX / 2;
dstPtr[2] = UINT16_MAX / 2;
dstPtr[3] = UINT16_MAX;
srcPtr++;
dstPtr += 4;
}
}
template <class _CSTrait>
void KoAlphaColorSpaceImpl<_CSTrait>::fromLabA16(const quint8 *src, quint8 *dst, quint32 nPixels) const {
const quint16* srcPtr = reinterpret_cast<const quint16*>(src);
channels_type* dstPtr = _CSTrait::nativeArray(dst);
while (nPixels--) {
dstPtr[0] = KoColorSpaceMaths<quint16, channels_type>::scaleToA(UINT16_MULT(srcPtr[0], srcPtr[3]));
dstPtr++;
srcPtr += 4;
}
}
template <class _CSTrait>
void KoAlphaColorSpaceImpl<_CSTrait>::toRgbA16(const quint8 *src, quint8 *dst, quint32 nPixels) const {
const channels_type* srcPtr = _CSTrait::nativeArray(src);
quint16* dstPtr = reinterpret_cast<quint16*>(dst);
while (nPixels--) {
const quint16 gray = KoColorSpaceMaths<channels_type, quint16>::scaleToA(srcPtr[0]);
dstPtr[0] = gray;
dstPtr[1] = gray;
dstPtr[2] = gray;
dstPtr[3] = UINT16_MAX;
srcPtr++;
dstPtr += 4;
}
}
template <class _CSTrait>
void KoAlphaColorSpaceImpl<_CSTrait>::fromRgbA16(const quint8 *src, quint8 *dst, quint32 nPixels) const {
const quint16* srcPtr = reinterpret_cast<const quint16*>(src);
channels_type* dstPtr = _CSTrait::nativeArray(dst);
while (nPixels--) {
// WARNING: we consider red channel only!
dstPtr[0] = KoColorSpaceMaths<quint16, channels_type>::scaleToA(UINT16_MULT(srcPtr[0], srcPtr[3]));
dstPtr++;
srcPtr += 4;
}
}
template <class _CSTrait>
KoColorSpace* KoAlphaColorSpaceImpl<_CSTrait>::clone() const
{
return new KoAlphaColorSpaceImpl<_CSTrait>();
}
template <class _CSTrait>
bool KoAlphaColorSpaceImpl<_CSTrait>::preferCompositionInSourceColorSpace() const
{
return true;
}
template class KoAlphaColorSpaceImpl<AlphaU8Traits>;
template class KoAlphaColorSpaceImpl<AlphaU16Traits>;
#ifdef HAVE_OPENEXR
template class KoAlphaColorSpaceImpl<AlphaF16Traits>;
#endif
template class KoAlphaColorSpaceImpl<AlphaF32Traits>;
/*********************************************************************************************/
/* KoAlphaColorSpaceFactoryImpl */
/*********************************************************************************************/
#include <KoColorConversionAlphaTransformation.h>
template <class _CSTrait>
QList<KoColorConversionTransformationFactory *> KoAlphaColorSpaceFactoryImpl<_CSTrait>::colorConversionLinks() const
{
QList<KoColorConversionTransformationFactory*> factories;
factories << new KoColorConversionFromAlphaTransformationFactoryImpl<channels_type>(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), "Gray-D50-elle-V2-srgbtrc.icc");
factories << new KoColorConversionToAlphaTransformationFactoryImpl<channels_type>(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), "Gray-D50-elle-V2-srgbtrc.icc");
factories << new KoColorConversionFromAlphaTransformationFactoryImpl<channels_type>(LABAColorModelID.id(), Integer16BitsColorDepthID.id(), "default");
factories << new KoColorConversionToAlphaTransformationFactoryImpl<channels_type>(LABAColorModelID.id(), Integer16BitsColorDepthID.id(), "default");
factories << new KoColorConversionFromAlphaTransformationFactoryImpl<channels_type>(LABAColorModelID.id(), Integer16BitsColorDepthID.id(), "Lab identity built-in");
factories << new KoColorConversionToAlphaTransformationFactoryImpl<channels_type>(LABAColorModelID.id(), Integer16BitsColorDepthID.id(), "Lab identity built-in");
return factories;
}
template class KoAlphaColorSpaceFactoryImpl<AlphaU8Traits>;
template class KoAlphaColorSpaceFactoryImpl<AlphaU16Traits>;
#ifdef HAVE_OPENEXR
template class KoAlphaColorSpaceFactoryImpl<AlphaF16Traits>;
#endif
template class KoAlphaColorSpaceFactoryImpl<AlphaF32Traits>;
diff --git a/plugins/extensions/imagesplit/wdg_imagesplit.cpp b/libs/pigment/compositeops/KoAlphaDarkenParamsWrapper.cpp
similarity index 52%
copy from plugins/extensions/imagesplit/wdg_imagesplit.cpp
copy to libs/pigment/compositeops/KoAlphaDarkenParamsWrapper.cpp
index dbe488fe19..2780ed3750 100644
--- a/plugins/extensions/imagesplit/wdg_imagesplit.cpp
+++ b/libs/pigment/compositeops/KoAlphaDarkenParamsWrapper.cpp
@@ -1,44 +1,42 @@
/*
- * dlg_imagesplit.cc - part of KimageShop^WKrayon^WKrita
- *
- * Copyright (c) 2009 Boudewijn Rempt <boud@valdyas.org>
- * Copyright (c) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
+ * Copyright (c) 2019 Dmitry Kazakov <dimula73@gmail.com>
*
* 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 "wdg_imagesplit.h"
-#include <QPainter>
-#include <QStringList>
-#include <QImage>
-#include <QListWidgetItem>
-#include <kis_debug.h>
+#include "KoAlphaDarkenParamsWrapper.h"
+
+#include <ksharedconfig.h>
+#include <kconfiggroup.h>
+#include "kis_debug.h"
-#include "kis_config.h"
-WdgImagesplit::WdgImagesplit(QWidget* parent)
- : QWidget(parent)
+bool useCreamyAlphaDarken()
{
- setupUi(this);
+ static bool isConfigInitialized = false;
+ static bool useCreamyAlphaDarken = true;
- KisConfig cfg(true);
+ if (!isConfigInitialized) {
+ KConfigGroup cfg = KSharedConfig::openConfig()->group("");
+ useCreamyAlphaDarken = cfg.readEntry("useCreamyAlphaDarken", true);
+ isConfigInitialized = true;
+ }
- intHorizontalSplitLines->setValue(cfg.horizontalSplitLines());
- intVerticalSplitLines->setValue(cfg.verticalSplitLines());
+ if (!useCreamyAlphaDarken) {
+ qInfo() << "INFO: requested old version of AlphaDarken composite op. Switching...";
+ }
- chkAutoSave->setChecked(true);
+ return useCreamyAlphaDarken;
}
-
-
diff --git a/libs/pigment/compositeops/KoAlphaDarkenParamsWrapper.h b/libs/pigment/compositeops/KoAlphaDarkenParamsWrapper.h
new file mode 100644
index 0000000000..7f9d881e57
--- /dev/null
+++ b/libs/pigment/compositeops/KoAlphaDarkenParamsWrapper.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2019 Dmitry Kazakov <dimula73@gmail.com>
+ *
+ * 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 KOALPHADARKENPARAMSWRAPPER_H
+#define KOALPHADARKENPARAMSWRAPPER_H
+
+#include <KoCompositeOp.h>
+#include "KoColorSpaceMaths.h"
+
+bool KRITAPIGMENT_EXPORT useCreamyAlphaDarken();
+
+struct KoAlphaDarkenParamsWrapperHard {
+ KoAlphaDarkenParamsWrapperHard(const KoCompositeOp::ParameterInfo& params)
+ : opacity(params.flow * params.opacity),
+ flow(params.flow),
+ averageOpacity(params.flow * (*params.lastOpacity))
+ {
+ }
+ float opacity;
+ float flow;
+ float averageOpacity;
+
+ template <typename T>
+ static inline T calculateZeroFlowAlpha(T srcAlpha, T dstAlpha, T normCoeff) {
+ return srcAlpha + dstAlpha - srcAlpha * dstAlpha * normCoeff;
+ }
+
+ template <typename T>
+ static inline T calculateZeroFlowAlpha(T srcAlpha, T dstAlpha) {
+ return srcAlpha + dstAlpha - srcAlpha * dstAlpha;
+ }
+
+ template<typename channels_type>
+ static inline channels_type calculateZeroFlowAlphaLegacy(channels_type srcAlpha, channels_type dstAlpha) {
+ return Arithmetic::unionShapeOpacity(srcAlpha, dstAlpha);
+ }
+};
+
+
+struct KoAlphaDarkenParamsWrapperCreamy {
+ KoAlphaDarkenParamsWrapperCreamy(const KoCompositeOp::ParameterInfo& params)
+ : opacity(params.opacity),
+ flow(params.flow),
+ averageOpacity(*params.lastOpacity)
+ {
+ }
+ float opacity;
+ float flow;
+ float averageOpacity;
+
+ template <typename T>
+ static inline T calculateZeroFlowAlpha(T srcAlpha, T dstAlpha, T normCoeff) {
+ Q_UNUSED(srcAlpha);
+ Q_UNUSED(normCoeff);
+
+ return dstAlpha;
+ }
+
+ template <typename T>
+ static inline T calculateZeroFlowAlpha(T srcAlpha, T dstAlpha) {
+ Q_UNUSED(srcAlpha);
+
+ return dstAlpha;
+ }
+
+ template<typename channels_type>
+ static inline channels_type calculateZeroFlowAlphaLegacy(channels_type srcAlpha, channels_type dstAlpha) {
+ Q_UNUSED(srcAlpha);
+ return dstAlpha;
+ }
+};
+
+#endif // KOALPHADARKENPARAMSWRAPPER_H
diff --git a/libs/pigment/compositeops/KoCompositeOpAlphaDarken.h b/libs/pigment/compositeops/KoCompositeOpAlphaDarken.h
index 894781692b..644c94ac1a 100644
--- a/libs/pigment/compositeops/KoCompositeOpAlphaDarken.h
+++ b/libs/pigment/compositeops/KoCompositeOpAlphaDarken.h
@@ -1,125 +1,144 @@
/*
* Copyright (c) 2006 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2011 Silvio Heinrich <plassy@web.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOCOMPOSITEOPALPHADARKEN_H_
#define KOCOMPOSITEOPALPHADARKEN_H_
-#include "KoCompositeOpFunctions.h"
+#include "KoColorSpaceMaths.h"
#include "KoCompositeOpBase.h"
#include <KoCompositeOpRegistry.h>
/**
* A template version of the alphadarken composite operation to use in colorspaces
*/
-template<class Traits>
+template<class Traits, class ParamsWrapper>
class KoCompositeOpAlphaDarken: public KoCompositeOp
{
typedef typename Traits::channels_type channels_type;
static const qint32 channels_nb = Traits::channels_nb;
static const qint32 alpha_pos = Traits::alpha_pos;
public:
KoCompositeOpAlphaDarken(const KoColorSpace* cs):
KoCompositeOp(cs, COMPOSITE_ALPHA_DARKEN, i18n("Alpha darken"), KoCompositeOp::categoryMix()) { }
using KoCompositeOp::composite;
void composite(const KoCompositeOp::ParameterInfo& params) const override
{
if(params.maskRowStart != 0)
genericComposite<true>(params);
else
genericComposite<false>(params);
}
template<bool useMask>
void genericComposite(const KoCompositeOp::ParameterInfo& params) const
{
using namespace Arithmetic;
+ ParamsWrapper paramsWrapper(params);
+
qint32 srcInc = (params.srcRowStride == 0) ? 0 : channels_nb;
- channels_type flow = scale<channels_type>(params.flow);
- channels_type opacity = mul(flow, scale<channels_type>(params.opacity));
+ channels_type flow = scale<channels_type>(paramsWrapper.flow);
+ channels_type opacity = scale<channels_type>(paramsWrapper.opacity);
quint8* dstRowStart = params.dstRowStart;
const quint8* srcRowStart = params.srcRowStart;
const quint8* maskRowStart = params.maskRowStart;
for(quint32 r=params.rows; r>0; --r) {
const channels_type* src = reinterpret_cast<const channels_type*>(srcRowStart);
channels_type* dst = reinterpret_cast<channels_type*>(dstRowStart);
const quint8* mask = maskRowStart;
for(qint32 c=params.cols; c>0; --c) {
channels_type srcAlpha = (alpha_pos == -1) ? unitValue<channels_type>() : src[alpha_pos];
channels_type dstAlpha = (alpha_pos == -1) ? unitValue<channels_type>() : dst[alpha_pos];
channels_type mskAlpha = useMask ? mul(scale<channels_type>(*mask), srcAlpha) : srcAlpha;
srcAlpha = mul(mskAlpha, opacity);
if(dstAlpha != zeroValue<channels_type>()) {
for(qint32 i=0; i <channels_nb; i++) {
if(i != alpha_pos)
dst[i] = lerp(dst[i], src[i], srcAlpha);
}
}
else {
for(qint32 i=0; i <channels_nb; i++) {
if(i != alpha_pos)
dst[i] = src[i];
}
}
if(alpha_pos != -1) {
channels_type fullFlowAlpha;
- channels_type averageOpacity = mul(flow, scale<channels_type>(*params.lastOpacity));
+ channels_type averageOpacity = scale<channels_type>(paramsWrapper.averageOpacity);
+ /**
+ * Here we calculate fullFlowAlpha, which shuold strive either to
+ * averageOpacity or opacity (whichever is the greater) or just keep old dstAlpha
+ * value, if both opacity values are not bit enough
+ */
if (averageOpacity > opacity) {
+ /**
+ * This crypty code is basically an optimized version of the folowing:
+ * fullFlowAlpha = averageOpacity *
+ * unionShapeOpacity(srcAlpha / averageOpacity,
+ * dstAlpha / averageOpacity);
+ *
+ * The main idea is: fullFlowAlpha should be as near to averageOpacity as
+ * maximum of srcAlpha and dstAlpha and a bit more. So that in consequent
+ * applications of the blending operation alpha channel would aim to
+ * averageOpacity.
+ */
+
channels_type reverseBlend = KoColorSpaceMaths<channels_type>::divide(dstAlpha, averageOpacity);
fullFlowAlpha = averageOpacity > dstAlpha ? lerp(srcAlpha, averageOpacity, reverseBlend) : dstAlpha;
} else {
fullFlowAlpha = opacity > dstAlpha ? lerp(dstAlpha, opacity, mskAlpha) : dstAlpha;
}
if (params.flow == 1.0) {
dstAlpha = fullFlowAlpha;
} else {
- channels_type zeroFlowAlpha = unionShapeOpacity(srcAlpha, dstAlpha);
+ channels_type zeroFlowAlpha = ParamsWrapper::calculateZeroFlowAlphaLegacy(srcAlpha, dstAlpha);
dstAlpha = lerp(zeroFlowAlpha, fullFlowAlpha, flow);
}
dst[alpha_pos] = dstAlpha;
}
src += srcInc;
dst += channels_nb;
if(useMask)
++mask;
}
srcRowStart += params.srcRowStride;
dstRowStart += params.dstRowStride;
maskRowStart += params.maskRowStride;
}
}
};
#endif // KOCOMPOSITEOPALPHADARKEN_H_
diff --git a/libs/pigment/compositeops/KoCompositeOpFunctions.h b/libs/pigment/compositeops/KoCompositeOpFunctions.h
index 9c2eb96373..910c34ab99 100644
--- a/libs/pigment/compositeops/KoCompositeOpFunctions.h
+++ b/libs/pigment/compositeops/KoCompositeOpFunctions.h
@@ -1,503 +1,959 @@
/*
* Copyright (c) 2011 Silvio Heinrich <plassy@web.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOCOMPOSITEOP_FUNCTIONS_H_
#define KOCOMPOSITEOP_FUNCTIONS_H_
#include <KoColorSpaceMaths.h>
template<class HSXType, class TReal>
inline void cfReorientedNormalMapCombine(TReal srcR, TReal srcG, TReal srcB, TReal& dstR, TReal& dstG, TReal& dstB)
{
// see http://blog.selfshadow.com/publications/blending-in-detail/ by Barre-Brisebois and Hill
TReal tx = 2*srcR-1;
TReal ty = 2*srcG-1;
TReal tz = 2*srcB;
TReal ux = -2*dstR+1;
TReal uy = -2*dstG+1;
TReal uz = 2*dstB-1;
TReal k = (tx*ux+ty*uy+tz*uz)/tz; // dot(t,u)/t.z
TReal rx = tx*k-ux;
TReal ry = ty*k-uy;
TReal rz = tz*k-uz;
k = 1/sqrt(rx*rx+ry*ry+rz*rz); // normalize result
rx *= k;
ry *= k;
rz *= k;
dstR = rx*0.5+0.5;
dstG = ry*0.5+0.5;
dstB = rz*0.5+0.5;
}
template<class HSXType, class TReal>
inline void cfColor(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
TReal lum = getLightness<HSXType>(dr, dg, db);
dr = sr;
dg = sg;
db = sb;
setLightness<HSXType>(dr, dg, db, lum);
}
template<class HSXType, class TReal>
inline void cfLightness(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
setLightness<HSXType>(dr, dg, db, getLightness<HSXType>(sr, sg, sb));
}
template<class HSXType, class TReal>
inline void cfIncreaseLightness(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
addLightness<HSXType>(dr, dg, db, getLightness<HSXType>(sr, sg, sb));
}
template<class HSXType, class TReal>
inline void cfDecreaseLightness(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
addLightness<HSXType>(dr, dg, db, getLightness<HSXType>(sr, sg, sb) - TReal(1.0));
}
template<class HSXType, class TReal>
inline void cfSaturation(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
TReal sat = getSaturation<HSXType>(sr, sg, sb);
TReal light = getLightness<HSXType>(dr, dg, db);
setSaturation<HSXType>(dr, dg, db, sat);
setLightness<HSXType>(dr, dg, db, light);
}
template<class HSXType, class TReal>
inline void cfIncreaseSaturation(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
using namespace Arithmetic;
TReal sat = lerp(getSaturation<HSXType>(dr,dg,db), unitValue<TReal>(), getSaturation<HSXType>(sr,sg,sb));
TReal light = getLightness<HSXType>(dr, dg, db);
setSaturation<HSXType>(dr, dg, db, sat);
setLightness<HSXType>(dr, dg, db, light);
}
template<class HSXType, class TReal>
inline void cfDecreaseSaturation(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
using namespace Arithmetic;
TReal sat = lerp(zeroValue<TReal>(), getSaturation<HSXType>(dr,dg,db), getSaturation<HSXType>(sr,sg,sb));
TReal light = getLightness<HSXType>(dr, dg, db);
setSaturation<HSXType>(dr, dg, db, sat);
setLightness<HSXType>(dr, dg, db, light);
}
template<class HSXType, class TReal>
inline void cfHue(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
TReal sat = getSaturation<HSXType>(dr, dg, db);
TReal lum = getLightness<HSXType>(dr, dg, db);
dr = sr;
dg = sg;
db = sb;
setSaturation<HSXType>(dr, dg, db, sat);
setLightness<HSXType>(dr, dg, db, lum);
}
template<class HSXType, class TReal>
inline void cfTangentNormalmap(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
using namespace Arithmetic;
TReal half=halfValue<TReal>();
dr = sr+(dr-half);
dg = sg+(dg-half);
db = sb+(db-unitValue<TReal>());
}
template<class HSXType, class TReal>
inline void cfDarkerColor(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
TReal lum = getLightness<HSXType>(dr, dg, db);
TReal lum2 = getLightness<HSXType>(sr, sg, sb);
if (lum<lum2) {
sr = dr;
sg = dg;
sb = db;
}
else {
dr = sr;
dg = sg;
db = sb;
}
}
template<class HSXType, class TReal>
inline void cfLighterColor(TReal sr, TReal sg, TReal sb, TReal& dr, TReal& dg, TReal& db) {
TReal lum = getLightness<HSXType>(dr, dg, db);
TReal lum2 = getLightness<HSXType>(sr, sg, sb);
if (lum>lum2) {
sr = dr;
sg = dg;
sb = db;
}
else {
dr = sr;
dg = sg;
db = sb;
}
}
template<class T>
inline T cfColorBurn(T src, T dst) {
using namespace Arithmetic;
if(dst == unitValue<T>())
return unitValue<T>();
T invDst = inv(dst);
if(src < invDst)
return zeroValue<T>();
return inv(clamp<T>(div(invDst, src)));
}
template<class T>
inline T cfLinearBurn(T src, T dst) {
using namespace Arithmetic;
typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
return clamp<T>(composite_type(src) + dst - unitValue<T>());
}
template<class T>
inline T cfColorDodge(T src, T dst) {
using namespace Arithmetic;
-
- if(dst == zeroValue<T>())
- return zeroValue<T>();
+ //Fixing Color Dodge to avoid ZX Colors on bright area.
+
+ if(src == unitValue<T>())
+ return unitValue<T>();
T invSrc = inv(src);
- if(invSrc < dst)
+ if(invSrc == zeroValue<T>())
return unitValue<T>();
return Arithmetic::clamp<T>(div(dst, invSrc));
}
template<class T>
inline T cfAddition(T src, T dst) {
typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
return Arithmetic::clamp<T>(composite_type(src) + dst);
}
template<class T>
inline T cfSubtract(T src, T dst) {
using namespace Arithmetic;
typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
return clamp<T>(composite_type(dst) - src);
}
template<class T>
inline T cfInverseSubtract(T src, T dst) {
using namespace Arithmetic;
typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
return clamp<T>(composite_type(dst) - inv(src));
}
template<class T>
inline T cfExclusion(T src, T dst) {
using namespace Arithmetic;
typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
composite_type x = mul(src, dst);
return clamp<T>(composite_type(dst) + src - (x + x));
}
template<class T>
inline T cfDivide(T src, T dst) {
using namespace Arithmetic;
//typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
if(src == zeroValue<T>())
return (dst == zeroValue<T>()) ? zeroValue<T>() : unitValue<T>();
return clamp<T>(div(dst, src));
}
template<class T>
inline T cfHardLight(T src, T dst) {
using namespace Arithmetic;
typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
composite_type src2 = composite_type(src) + src;
if(src > halfValue<T>()) {
// screen(src*2.0 - 1.0, dst)
src2 -= unitValue<T>();
// src2 is guaranteed to be smaller than unitValue<T>() now
return Arithmetic::unionShapeOpacity(T(src2), dst);
}
// src2 is guaranteed to be smaller than unitValue<T>() due to 'if'
return Arithmetic::mul(T(src2), dst);
}
template<class T>
inline T cfSoftLightSvg(T src, T dst) {
using namespace Arithmetic;
qreal fsrc = scale<qreal>(src);
qreal fdst = scale<qreal>(dst);
if(fsrc > 0.5f) {
qreal D = (fdst > 0.25f) ? sqrt(fdst) : ((16.0f*fdst - 12.0)*fdst + 4.0f)*fdst;
return scale<T>(fdst + (2.0f*fsrc - 1.0f) * (D - fdst));
}
return scale<T>(fdst - (1.0f - 2.0f * fsrc) * fdst * (1.0f - fdst));
}
template<class T>
inline T cfSoftLight(T src, T dst) {
using namespace Arithmetic;
qreal fsrc = scale<qreal>(src);
qreal fdst = scale<qreal>(dst);
if(fsrc > 0.5f) {
return scale<T>(fdst + (2.0f * fsrc - 1.0f) * (sqrt(fdst) - fdst));
}
return scale<T>(fdst - (1.0f - 2.0f*fsrc) * fdst * (1.0f - fdst));
}
template<class T>
inline T cfVividLight(T src, T dst) {
using namespace Arithmetic;
typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
if(src < halfValue<T>()) {
if(src == zeroValue<T>())
return (dst == unitValue<T>()) ? unitValue<T>() : zeroValue<T>();
// min(1,max(0,1-(1-dst) / (2*src)))
composite_type src2 = composite_type(src) + src;
composite_type dsti = inv(dst);
return clamp<T>(unitValue<T>() - (dsti * unitValue<T>() / src2));
}
if(src == unitValue<T>())
return (dst == zeroValue<T>()) ? zeroValue<T>() : unitValue<T>();
// min(1,max(0, dst / (2*(1-src)))
composite_type srci2 = inv(src);
srci2 += srci2;
return clamp<T>(composite_type(dst) * unitValue<T>() / srci2);
}
template<class T>
inline T cfPinLight(T src, T dst) {
typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
// TODO: verify that the formula is correct (the first max would be useless here)
// max(0, max(2*src-1, min(dst, 2*src)))
composite_type src2 = composite_type(src) + src;
composite_type a = qMin<composite_type>(dst, src2);
composite_type b = qMax<composite_type>(src2-Arithmetic::unitValue<T>(), a);
return T(b);
}
template<class T>
inline T cfArcTangent(T src, T dst) {
using namespace Arithmetic;
if(dst == zeroValue<T>())
return (src == zeroValue<T>()) ? zeroValue<T>() : unitValue<T>();
return scale<T>(2.0 * atan(scale<qreal>(src) / scale<qreal>(dst)) / Arithmetic::pi);
}
template<class T>
inline T cfAllanon(T src, T dst) {
using namespace Arithmetic;
typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
// (dst + src) / 2 [or (dst + src) * 0.5]
return T((composite_type(src) + dst) * halfValue<T>() / unitValue<T>());
}
template<class T>
inline T cfLinearLight(T src, T dst) {
using namespace Arithmetic;
typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
// min(1,max(0,(dst + 2*src)-1))
return clamp<T>((composite_type(src) + src + dst) - unitValue<T>());
}
template<class T>
inline T cfParallel(T src, T dst) {
using namespace Arithmetic;
typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
// min(max(2 / (1/dst + 1/src), 0), 1)
composite_type unit = unitValue<T>();
composite_type s = (src != zeroValue<T>()) ? div<T>(unit, src) : unit;
- composite_type d = (dst != zeroValue<T>()) ? div<T>(unit, dst) : unit;
+ composite_type d = (dst != zeroValue<T>()) ? div<T>(unit, dst) : unit;
+ if (src == zeroValue<T>()) {
+ return zeroValue<T>();
+ }
+
+ if (dst == zeroValue<T>()) {
+ return zeroValue<T>();
+ }
+
return clamp<T>((unit+unit) * unit / (d+s));
}
template<class T>
inline T cfEquivalence(T src, T dst) {
typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
// 1 - abs(dst - src)
composite_type x = composite_type(dst) - src;
return (x < Arithmetic::zeroValue<T>()) ? T(-x) : T(x);
}
template<class T>
inline T cfGrainMerge(T src, T dst) {
using namespace Arithmetic;
typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
return clamp<T>(composite_type(dst) + src - halfValue<T>());
}
template<class T>
inline T cfGrainExtract(T src, T dst) {
using namespace Arithmetic;
typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
return clamp<T>(composite_type(dst) - src + halfValue<T>());
}
template<class T>
inline T cfHardMix(T src, T dst) {
return (dst > Arithmetic::halfValue<T>()) ? cfColorDodge(src,dst) : cfColorBurn(src,dst);
}
template<class T>
inline T cfHardMixPhotoshop(T src, T dst) {
using namespace Arithmetic;
typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
const composite_type sum = composite_type(src) + dst;
return sum > unitValue<T>() ? unitValue<T>() : zeroValue<T>();
}
template<class T>
inline T cfAdditiveSubtractive(T src, T dst) {
using namespace Arithmetic;
// min(1,max(0,abs(sqr(CB)-sqr(CT))))
qreal x = sqrt(scale<qreal>(dst)) - sqrt(scale<qreal>(src));
return scale<T>((x < 0.0) ? -x : x);
}
template<class T>
inline T cfGammaDark(T src, T dst) {
using namespace Arithmetic;
if(src == zeroValue<T>())
return zeroValue<T>();
// power(dst, 1/src)
return scale<T>(pow(scale<qreal>(dst), 1.0/scale<qreal>(src)));
}
template<class T>
inline T cfGammaLight(T src, T dst) {
using namespace Arithmetic;
return scale<T>(pow(scale<qreal>(dst), scale<qreal>(src)));
}
+template<class T>
+inline T cfGammaIllumination(T src, T dst) {
+ using namespace Arithmetic;
+ return inv(cfGammaDark(inv(src),inv(dst)));
+}
+
template<class T>
inline T cfGeometricMean(T src, T dst) {
using namespace Arithmetic;
return scale<T>(sqrt(scale<qreal>(dst) * scale<qreal>(src)));
}
template<class T>
inline T cfOver(T src, T dst) { Q_UNUSED(dst); return src; }
template<class T>
inline T cfOverlay(T src, T dst) { return cfHardLight(dst, src); }
template<class T>
inline T cfMultiply(T src, T dst) { return Arithmetic::mul(src, dst); }
template<class T>
inline T cfHardOverlay(T src, T dst) {
using namespace Arithmetic;
qreal fsrc = scale<qreal>(src);
qreal fdst = scale<qreal>(dst);
+
+ if (fsrc == 1.0) {
+ return scale<T>(1.0);}
if(fsrc > 0.5f) {
return scale<T>(cfDivide(inv(2.0 * fsrc - 1.0f), fdst));
}
return scale<T>(mul(2.0 * fsrc, fdst));
}
template<class T>
inline T cfDifference(T src, T dst) { return qMax(src,dst) - qMin(src,dst); }
template<class T>
inline T cfScreen(T src, T dst) { return Arithmetic::unionShapeOpacity(src, dst); }
template<class T>
inline T cfDarkenOnly(T src, T dst) { return qMin(src, dst); }
template<class T>
inline T cfLightenOnly(T src, T dst) { return qMax(src, dst); }
template<class T>
inline T cfGlow(T src, T dst) {
using namespace Arithmetic;
// see http://www.pegtop.net/delphi/articles/blendmodes/quadratic.htm for formulas of Quadratic Blending Modes like Glow, Reflect, Freeze, and Heat
+
+ if (dst == unitValue<T>()) {
+ return unitValue<T>();
+ }
+
+ return clamp<T>(div(mul(src, src), inv(dst)));
+}
+
+template<class T>
+inline T cfReflect(T src, T dst) {
+ using namespace Arithmetic;
- if(dst == unitValue<T>()) {
+
+ return clamp<T>(cfGlow(dst,src));
+}
+
+template<class T>
+inline T cfHeat(T src, T dst) {
+ using namespace Arithmetic;
+
+ if(src == unitValue<T>()) {
return unitValue<T>();
}
- if(src == zeroValue<T>()) {
+ if(dst == zeroValue<T>()) {
return zeroValue<T>();
}
+
+ return inv(clamp<T>(div(mul(inv(src), inv(src)),dst)));
+}
+
+template<class T>
+inline T cfFreeze(T src, T dst) {
+ using namespace Arithmetic;
- return clamp<T>(div(mul(src, src), inv(dst)));
+ return (cfHeat(dst,src));
}
+
+template<class T>
+inline T cfHelow(T src, T dst) {
+ using namespace Arithmetic;
+ // see http://www.pegtop.net/delphi/articles/blendmodes/quadratic.htm for formulas of Quadratic Blending Modes like Glow, Reflect, Freeze, and Heat
+ if (cfHardMixPhotoshop(src,dst) == unitValue<T>()) {
+ return cfHeat(src,dst);
+ }
+
+ if (src == zeroValue<T>()) {
+ return zeroValue<T>();
+ }
+
+ return (cfGlow(src,dst));
+}
+
template<class T>
-inline T cfReflect(T src, T dst) {
+inline T cfFrect(T src, T dst) {
+ using namespace Arithmetic;
+
+ if (cfHardMixPhotoshop(src,dst) == unitValue<T>()) {
+ return cfFreeze(src,dst);
+ }
+
+ if (dst == zeroValue<T>()) {
+ return zeroValue<T>();
+ }
+
+ return (cfReflect(src,dst));
+}
+
+template<class T>
+inline T cfGleat(T src, T dst) {
using namespace Arithmetic;
+ // see http://www.pegtop.net/delphi/articles/blendmodes/quadratic.htm for formulas of Quadratic Blending Modes like Glow, Reflect, Freeze, and Heat
+
+ if(dst == unitValue<T>()) {
+ return unitValue<T>();
+ }
- return (cfGlow(dst,src));
+ if(cfHardMixPhotoshop(src,dst) == unitValue<T>()) {
+ return cfGlow(src,dst);
+ }
+
+ return (cfHeat(src,dst));
}
template<class T>
-inline T cfHeat(T src, T dst) {
+inline T cfReeze(T src, T dst) {
using namespace Arithmetic;
- // Heat, and Freeze only works properly on 8-bit images. It does not work properly on any other color depth. For now, if Heat and Freeze are proven useful for 8-bit painting, then there should be some way of solving this issue.
- if(dst == zeroValue<T>()) {
+ return (cfGleat(dst,src));
+}
+template<class T>
+inline T cfFhyrd(T src, T dst) {
+ using namespace Arithmetic;
+
+ return (cfAllanon(cfFrect(src,dst),cfHelow(src,dst)));
+}
+
+template<class T>
+inline T cfInterpolation(T src, T dst) {
+ using namespace Arithmetic;
+
+ qreal fsrc = scale<qreal>(src);
+ qreal fdst = scale<qreal>(dst);
+
+ if(dst == zeroValue<T>() && src == zeroValue<T>()) {
return zeroValue<T>();
+ }
+
+ return scale<T>(.5f-.25f*cos(pi*(fsrc))-.25f*cos(pi*(fdst)));
+}
+
+template<class T>
+inline T cfInterpolationB(T src, T dst) {
+ using namespace Arithmetic;
+
+ return cfInterpolation(cfInterpolation(src,dst),cfInterpolation(src,dst));
+}
+
+
+template<class T>
+inline T cfPenumbraB(T src, T dst) {
+ using namespace Arithmetic;
+
+ if (dst == unitValue<T>()) {
+ return unitValue<T>();
+ }
+ if (dst + src < unitValue<T>()) {
+ return (cfColorDodge(dst,src)/2);
}
+ if (src == zeroValue<T>()) {
+ return zeroValue<T>();
+ }
+
+ return inv(clamp<T>(div(inv(dst),src)/2));
+}
+
+template<class T>
+inline T cfPenumbraD(T src, T dst) {
+ using namespace Arithmetic;
- if(src == unitValue<T>()) {
+ if (dst == unitValue<T>()) {
return unitValue<T>();
}
- return inv(clamp<T>(div(mul(inv(src), inv(src)),dst)));
+ return cfArcTangent(src,inv(dst));
}
template<class T>
-inline T cfFreeze(T src, T dst) {
+inline T cfPenumbraC(T src, T dst) {
+ using namespace Arithmetic;
+
+ return cfPenumbraD(dst,src);
+}
+
+template<class T>
+inline T cfPenumbraA(T src, T dst) {
+ using namespace Arithmetic;
+
+ return (cfPenumbraB(dst,src));
+}
+
+template<class T>
+inline T cfSoftLightIFSIllusions(T src, T dst) {
+ using namespace Arithmetic;
+
+ qreal fsrc = scale<qreal>(src);
+ qreal fdst = scale<qreal>(dst);
+
+ return scale<T>(pow(fdst,pow(2.0,(mul(2.0,.5f-fsrc)))));
+}
+
+template<class T>
+inline T cfSoftLightPegtopDelphi(T src, T dst) {
+ using namespace Arithmetic;
+
+ return clamp<T>(cfAddition(mul(dst,cfScreen(src,dst)),mul(mul(src,dst),inv(dst))));
+}
+
+template<class T>
+inline T cfNegation(T src, T dst) {
+ using namespace Arithmetic;
+ typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
+
+ composite_type unit = unitValue<T>();
+ composite_type a = unit - src - dst;
+ composite_type s = abs(a);
+ composite_type d = unit - s;
+
+ return T(d);
+}
+
+template<class T>
+inline T cfNor(T src, T dst) {
+ using namespace Arithmetic;
+
+ return and(src,dst);
+}
+
+template<class T>
+inline T cfNand(T src, T dst) {
+ using namespace Arithmetic;
+
+ return or(src,dst);
+}
+
+template<class T>
+inline T cfXor(T src, T dst) {
+ using namespace Arithmetic;
+
+ return xor(src,dst);
+}
+
+template<class T>
+inline T cfXnor(T src, T dst) {
+ using namespace Arithmetic;
+
+ return cfXor(src,inv(dst));
+}
+
+template<class T>
+inline T cfAnd(T src, T dst) {
using namespace Arithmetic;
+
+ return cfNor(inv(src),inv(dst));
+}
+
+template<class T>
+inline T cfOr(T src, T dst) {
+ using namespace Arithmetic;
+
+ return cfNand(inv(src),inv(dst));
+}
+
+template<class T>
+inline T cfConverse(T src, T dst) {
+ using namespace Arithmetic;
+
+ return cfOr(inv(src),dst);
+}
+
+template<class T>
+inline T cfNotConverse(T src, T dst) {
+ using namespace Arithmetic;
+
+ return cfAnd(src,inv(dst));
+}
+
+template<class T>
+inline T cfImplies(T src, T dst) {
+ using namespace Arithmetic;
+
+ return cfOr(src,inv(dst));
+}
+
+template<class T>
+inline T cfNotImplies(T src, T dst) {
+ using namespace Arithmetic;
+
+ return cfAnd(inv(src),dst);
+}
+
+template<class T>
+inline T cfPNormA(T src, T dst) {
+ using namespace Arithmetic;
+ //This is also known as P-Norm mode with factor of 2.3333 See IMBLEND image blending mode samples, and please see imblend.m file found on Additional Blending Mode thread at Phabricator. 1/2.3333 is .42875...
+
+ return clamp<T>(pow(pow((float)dst, 2.3333333333333333) + pow((float)src, 2.3333333333333333), 0.428571428571434));
+}
+
+template<class T>
+inline T cfPNormB(T src, T dst) {
+ using namespace Arithmetic;
+ //This is also known as P-Norm mode with factor of 2.3333 See IMBLEND image blending mode samples, and please see imblend.m file found on Additional Blending Mode thread at Phabricator. 1/2.3333 is .42875...
+
+ return clamp<T>(pow(pow(dst,4)+pow(src,4),0.25));
+}
+
+template<class T>
+inline T cfSuperLight(T src, T dst) {
+ using namespace Arithmetic;
+ //4.0 can be adjusted to taste. 4.0 is picked for being the best in terms of contrast and details. See imblend.m file.
+
+ qreal fsrc = scale<qreal>(src);
+ qreal fdst = scale<qreal>(dst);
+
+ if (fsrc < .5) {
+ return scale<T>(inv(pow(pow(inv(fdst),2.875)+pow(inv(2.0*fsrc),2.875),1.0/2.875)));
+ }
+
+ return scale<T>(pow(pow(fdst,2.875)+pow(2.0*fsrc-1.0,2.875),1.0/2.875));
+}
+
+template<class T>
+inline T cfTintIFSIllusions(T src, T dst) {
+ using namespace Arithmetic;
+ //Known as Light Blending mode found in IFS Illusions. Picked this name because it results into a very strong tint, and has better naming convention.
+
+ qreal fsrc = scale<qreal>(src);
+ qreal fdst = scale<qreal>(dst);
+
+ return scale<T>(fsrc*inv(fdst)+sqrt(fdst));
+}
+
+template<class T>
+inline T cfShadeIFSIllusions(T src, T dst) {
+ using namespace Arithmetic;
+ //Known as Shadow Blending mode found in IFS Illusions. Picked this name because it is the opposite of Tint (IFS Illusion Blending mode).
+
+ qreal fsrc = scale<qreal>(src);
+ qreal fdst = scale<qreal>(dst);
+
+ return scale<T>(inv((inv(fdst)*fsrc)+sqrt(inv(fsrc))));
+}
+
+template<class T>
+inline T cfFogLightenIFSIllusions(T src, T dst) {
+ using namespace Arithmetic;
+ //Known as Bright Blending mode found in IFS Illusions. Picked this name because the shading reminds me of fog when overlaying with a gradientt.
+
+ qreal fsrc = scale<qreal>(src);
+ qreal fdst = scale<qreal>(dst);
+
+ if (fsrc < .5) {
+ return scale<T>(inv(inv(fsrc)*fsrc)-inv(fdst)*inv(fsrc));
+ }
+
+ return scale<T>(fsrc-inv(fdst)*inv(fsrc)+pow(inv(fsrc),2));
+}
+
+template<class T>
+inline T cfFogDarkenIFSIllusions(T src, T dst) {
+ using namespace Arithmetic;
+ //Known as Dark Blending mode found in IFS Illusions. Picked this name because the shading reminds me of fog when overlaying with a gradient.
+
+ qreal fsrc = scale<qreal>(src);
+ qreal fdst = scale<qreal>(dst);
+
+ if (fsrc < .5) {
+ return scale<T>(inv(fsrc)*fsrc+fsrc*fdst);
+ }
+
+ return scale<T>(fsrc*fdst+fsrc-pow(fsrc,2));
+}
+
+template<class T>
+inline T cfModulo(T src, T dst) {
+ using namespace Arithmetic;
+
+ return mod(dst,src);
+}
+
+template<class T>
+inline T cfModuloShift(T src, T dst) {
+ using namespace Arithmetic;
+ qreal fsrc = scale<qreal>(src);
+ qreal fdst = scale<qreal>(dst);
+
+ if (fsrc == 1.0 && fdst == 0.0) {
+ return scale<T>(0.0);
+ }
+
- return clamp<T>(cfHeat(dst,src));
+ return scale<T>(mod((fdst+fsrc),1.0000000000));
}
+template<class T>
+inline T cfModuloShiftContinuous(T src, T dst) {
+ using namespace Arithmetic;
+ //This blending mode do not behave like difference/equilavent with destination layer inverted if you use group layer on addition while the content of group layer contains several addition-mode layers, it works as expected on float images. So, no need to change this.
+ qreal fsrc = scale<qreal>(src);
+ qreal fdst = scale<qreal>(dst);
+
+ if (fsrc == 1.0 && fdst == 0.0) {
+ return scale<T>(0.0);
+ }
+
+ return scale<T>((int(ceil(fdst+fsrc)) % 2 != 0) || (fdst == zeroValue<T>()) ? inv(cfModuloShift(fsrc,fdst)) : cfModuloShift(fsrc,fdst));
+}
+template<class T>
+inline T cfDivisiveModulo(T src, T dst) {
+ using namespace Arithmetic;
+ //I have to use 1.00000 as unitValue failed to work for those area.
+
+ qreal fsrc = scale<qreal>(src);
+ qreal fdst = scale<qreal>(dst);
+
+ if (fsrc == zeroValue<T>()) {
+ return scale<T>(mod(((1.0000000000/epsilon<T>()) * fdst),1.0000000000));
+ }
+
+ return scale<T>(mod(((1.0000000000/fsrc) * fdst),1.0000000000));
+}
+
+template<class T>
+inline T cfDivisiveModuloContinuous(T src, T dst) {
+ using namespace Arithmetic;
+
+ qreal fsrc = scale<qreal>(src);
+ qreal fdst = scale<qreal>(dst);
+
+ if (fdst == zeroValue<T>()) {
+ return zeroValue<T>();
+ }
+
+ if (fsrc == zeroValue<T>()) {
+ return cfDivisiveModulo(fsrc,fdst);
+ }
+
+
+ return scale<T>( int(ceil(fdst/fsrc)) % 2 != 0 ? cfDivisiveModulo(fsrc,fdst) : inv(cfDivisiveModulo(fsrc,fdst)));
+}
+
+template<class T>
+inline T cfModuloContinuous(T src, T dst) {
+ using namespace Arithmetic;
+
+ return cfMultiply(cfDivisiveModuloContinuous(src,dst),src);
+}
+
+template<class T>
+inline T cfColorBurnLogarithmic(T src, T dst) {
+ using namespace Arithmetic;
+ //Also known as Darken from EffectBank/Illusions.hu. IFS Illusions had used this blending mode.
+
+ qreal fsrc = scale<qreal>(src);
+ qreal fdst = scale<qreal>(dst);
+
+ if (inv(fdst) == zeroValue<T>()) {
+ return scale<T>(log2(1.0 + abs(fsrc)/abs(inv(.999999))/8));
+ }
+
+ return scale<T>(log2(1.0 + abs(fsrc)/abs(inv(fdst))/8));
+}
+
+template<class T>
+inline T cfColorDodgeLogarithmic(T src, T dst) {
+ using namespace Arithmetic;
+ //Also known as Lighten from EffectBank/Illusions.hu. IFS Illusions had used this blending mode.
+
+ return inv(cfColorBurnLogarithmic(inv(src),inv(dst)));
+}
+
+template<class T>
+inline T cfEasyDodge(T src, T dst) {
+ using namespace Arithmetic;
+ // The 13 divided by 15 can be adjusted to taste. See imgblend.m
+
+ qreal fsrc = scale<qreal>(src);
+ qreal fdst = scale<qreal>(dst);
+
+ if (fsrc == 1.0) {
+ return scale<T>(1.0);}
+
+
+ return scale<T>(pow(fdst,mul(inv(fsrc != 1.0 ? fsrc : .999999999999),1.039999999)));
+}
+
+template<class T>
+inline T cfEasyBurn(T src, T dst) {
+ using namespace Arithmetic;
+ // The 13 divided by 15 can be adjusted to taste. See imgblend.m
+
+ qreal fsrc = scale<qreal>(src);
+ qreal fdst = scale<qreal>(dst);
+
+
+ return scale<T>(inv(pow(inv(fsrc != 1.0 ? fsrc : .999999999999),mul(fdst,1.039999999))));
+}
+
+template<class T>
+inline T cfFlatLight(T src, T dst) {
+ using namespace Arithmetic;
+
+ if (src == zeroValue<T>()) {
+ return zeroValue<T>();
+ }
+
+ return clamp<T>(cfHardMixPhotoshop(inv(src),dst)==unitValue<T>() ? cfPenumbraB(src,dst) : cfPenumbraA(src,dst));
+}
#endif // KOCOMPOSITEOP_FUNCTIONS_H_
diff --git a/libs/pigment/compositeops/KoCompositeOps.h b/libs/pigment/compositeops/KoCompositeOps.h
index 26101bcdda..7dff0ebeb7 100644
--- a/libs/pigment/compositeops/KoCompositeOps.h
+++ b/libs/pigment/compositeops/KoCompositeOps.h
@@ -1,254 +1,315 @@
/*
* Copyright (c) 2007 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2011 Silvio Heinrich <plassy@web.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _KOCOMPOSITEOPS_H_
#define _KOCOMPOSITEOPS_H_
#include <boost/type_traits.hpp>
#include <KoColorSpace.h>
#include <KoColorSpaceTraits.h>
#include <KoColorSpaceMaths.h>
#include "compositeops/KoCompositeOpGeneric.h"
#include "compositeops/KoCompositeOpOver.h"
#include "compositeops/KoCompositeOpCopyChannel.h"
#include "compositeops/KoCompositeOpAlphaDarken.h"
#include "compositeops/KoCompositeOpErase.h"
#include "compositeops/KoCompositeOpCopy2.h"
#include "compositeops/KoCompositeOpDissolve.h"
#include "compositeops/KoCompositeOpBehind.h"
#include "compositeops/KoCompositeOpDestinationIn.h"
#include "compositeops/KoCompositeOpDestinationAtop.h"
#include "compositeops/KoCompositeOpGreater.h"
-
+#include "compositeops/KoAlphaDarkenParamsWrapper.h"
#include "KoOptimizedCompositeOpFactory.h"
namespace _Private {
template<class Traits, bool flag>
struct AddGeneralOps
{
static void add(KoColorSpace* cs) { Q_UNUSED(cs); }
};
template<class Traits>
struct OptimizedOpsSelector
{
static KoCompositeOp* createAlphaDarkenOp(const KoColorSpace *cs) {
- return new KoCompositeOpAlphaDarken<Traits>(cs);
+ if (useCreamyAlphaDarken()) {
+ return new KoCompositeOpAlphaDarken<Traits, KoAlphaDarkenParamsWrapperCreamy>(cs);
+ } else {
+ return new KoCompositeOpAlphaDarken<Traits, KoAlphaDarkenParamsWrapperHard>(cs);
+ }
}
static KoCompositeOp* createOverOp(const KoColorSpace *cs) {
return new KoCompositeOpOver<Traits>(cs);
}
};
template<>
struct OptimizedOpsSelector<KoBgrU8Traits>
{
static KoCompositeOp* createAlphaDarkenOp(const KoColorSpace *cs) {
- return KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs);
+ return useCreamyAlphaDarken() ?
+ KoOptimizedCompositeOpFactory::createAlphaDarkenOpCreamy32(cs) :
+ KoOptimizedCompositeOpFactory::createAlphaDarkenOpHard32(cs);
}
static KoCompositeOp* createOverOp(const KoColorSpace *cs) {
return KoOptimizedCompositeOpFactory::createOverOp32(cs);
}
};
template<>
struct OptimizedOpsSelector<KoLabU8Traits>
{
static KoCompositeOp* createAlphaDarkenOp(const KoColorSpace *cs) {
- return KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(cs);
+ return useCreamyAlphaDarken() ?
+ KoOptimizedCompositeOpFactory::createAlphaDarkenOpCreamy32(cs) :
+ KoOptimizedCompositeOpFactory::createAlphaDarkenOpHard32(cs);
}
static KoCompositeOp* createOverOp(const KoColorSpace *cs) {
return KoOptimizedCompositeOpFactory::createOverOp32(cs);
}
};
template<>
struct OptimizedOpsSelector<KoRgbF32Traits>
{
static KoCompositeOp* createAlphaDarkenOp(const KoColorSpace *cs) {
- return new KoCompositeOpAlphaDarken<KoRgbF32Traits>(cs);
+ return useCreamyAlphaDarken() ?
+ KoOptimizedCompositeOpFactory::createAlphaDarkenOpCreamy128(cs) :
+ KoOptimizedCompositeOpFactory::createAlphaDarkenOpHard128(cs);
}
static KoCompositeOp* createOverOp(const KoColorSpace *cs) {
return KoOptimizedCompositeOpFactory::createOverOp128(cs);
}
};
template<class Traits>
struct AddGeneralOps<Traits, true>
{
typedef typename Traits::channels_type Arg;
typedef Arg (*CompositeFunc)(Arg, Arg);
static const qint32 alpha_pos = Traits::alpha_pos;
template<CompositeFunc func>
static void add(KoColorSpace* cs, const QString& id, const QString& description, const QString& category) {
cs->addCompositeOp(new KoCompositeOpGenericSC<Traits, func>(cs, id, description, category));
}
static void add(KoColorSpace* cs) {
cs->addCompositeOp(OptimizedOpsSelector<Traits>::createOverOp(cs));
cs->addCompositeOp(OptimizedOpsSelector<Traits>::createAlphaDarkenOp(cs));
cs->addCompositeOp(new KoCompositeOpCopy2<Traits>(cs));
cs->addCompositeOp(new KoCompositeOpErase<Traits>(cs));
cs->addCompositeOp(new KoCompositeOpBehind<Traits>(cs));
cs->addCompositeOp(new KoCompositeOpDestinationIn<Traits>(cs));
cs->addCompositeOp(new KoCompositeOpDestinationAtop<Traits>(cs));
cs->addCompositeOp(new KoCompositeOpGreater<Traits>(cs));
add<&cfOverlay<Arg> >(cs, COMPOSITE_OVERLAY , i18n("Overlay") , KoCompositeOp::categoryMix());
add<&cfGrainMerge<Arg> >(cs, COMPOSITE_GRAIN_MERGE , i18n("Grain Merge") , KoCompositeOp::categoryMix());
add<&cfGrainExtract<Arg> >(cs, COMPOSITE_GRAIN_EXTRACT , i18n("Grain Extract") , KoCompositeOp::categoryMix());
add<&cfHardMix<Arg> >(cs, COMPOSITE_HARD_MIX , i18n("Hard Mix") , KoCompositeOp::categoryMix());
add<&cfHardMixPhotoshop<Arg>>(cs, COMPOSITE_HARD_MIX_PHOTOSHOP, i18n("Hard Mix (Photoshop)") , KoCompositeOp::categoryMix());
add<&cfGeometricMean<Arg> >(cs, COMPOSITE_GEOMETRIC_MEAN, i18n("Geometric Mean"), KoCompositeOp::categoryMix());
add<&cfParallel<Arg> >(cs, COMPOSITE_PARALLEL , i18n("Parallel") , KoCompositeOp::categoryMix());
add<&cfAllanon<Arg> >(cs, COMPOSITE_ALLANON , i18n("Allanon") , KoCompositeOp::categoryMix());
add<&cfHardOverlay<Arg> >(cs, COMPOSITE_HARD_OVERLAY , i18n("Hard Overlay") , KoCompositeOp::categoryMix());
+ add<&cfInterpolation<Arg> >(cs, COMPOSITE_INTERPOLATION , i18n("Interpolation") , KoCompositeOp::categoryMix());
+ add<&cfInterpolationB<Arg> >(cs, COMPOSITE_INTERPOLATIONB , i18n("Interpolation - 2X") , KoCompositeOp::categoryMix());
+ add<&cfPenumbraA<Arg> >(cs, COMPOSITE_PENUMBRAA , i18n("Penumbra A") , KoCompositeOp::categoryMix());
+ add<&cfPenumbraB<Arg> >(cs, COMPOSITE_PENUMBRAB , i18n("Penumbra B") , KoCompositeOp::categoryMix());
+ add<&cfPenumbraC<Arg> >(cs, COMPOSITE_PENUMBRAC , i18n("Penumbra C") , KoCompositeOp::categoryMix());
+ add<&cfPenumbraD<Arg> >(cs, COMPOSITE_PENUMBRAD , i18n("Penumbra D") , KoCompositeOp::categoryMix());
add<&cfScreen<Arg> >(cs, COMPOSITE_SCREEN , i18n("Screen") , KoCompositeOp::categoryLight());
add<&cfColorDodge<Arg> >(cs, COMPOSITE_DODGE , i18n("Color Dodge") , KoCompositeOp::categoryLight());
add<&cfAddition<Arg> >(cs, COMPOSITE_LINEAR_DODGE, i18n("Linear Dodge"), KoCompositeOp::categoryLight());
add<&cfLightenOnly<Arg> >(cs, COMPOSITE_LIGHTEN , i18n("Lighten") , KoCompositeOp::categoryLight());
add<&cfHardLight<Arg> >(cs, COMPOSITE_HARD_LIGHT , i18n("Hard Light") , KoCompositeOp::categoryLight());
+ add<&cfSoftLightIFSIllusions<Arg>>(cs, COMPOSITE_SOFT_LIGHT_IFS_ILLUSIONS, i18n("Soft Light (IFS Illusions)") , KoCompositeOp::categoryLight());
+ add<&cfSoftLightPegtopDelphi<Arg>>(cs, COMPOSITE_SOFT_LIGHT_PEGTOP_DELPHI, i18n("Soft Light (Pegtop-Delphi)") , KoCompositeOp::categoryLight());
add<&cfSoftLightSvg<Arg> >(cs, COMPOSITE_SOFT_LIGHT_SVG, i18n("Soft Light (SVG)") , KoCompositeOp::categoryLight());
add<&cfSoftLight<Arg> >(cs, COMPOSITE_SOFT_LIGHT_PHOTOSHOP, i18n("Soft Light (Photoshop)") , KoCompositeOp::categoryLight());
add<&cfGammaLight<Arg> >(cs, COMPOSITE_GAMMA_LIGHT , i18n("Gamma Light") , KoCompositeOp::categoryLight());
+ add<&cfGammaIllumination<Arg> >(cs, COMPOSITE_GAMMA_ILLUMINATION , i18n("Gamma Illumination") , KoCompositeOp::categoryLight());
add<&cfVividLight<Arg> >(cs, COMPOSITE_VIVID_LIGHT , i18n("Vivid Light") , KoCompositeOp::categoryLight());
+ add<&cfFlatLight<Arg> >(cs, COMPOSITE_FLAT_LIGHT , i18n("Flat Light") , KoCompositeOp::categoryLight());
add<&cfPinLight<Arg> >(cs, COMPOSITE_PIN_LIGHT , i18n("Pin Light") , KoCompositeOp::categoryLight());
add<&cfLinearLight<Arg> >(cs, COMPOSITE_LINEAR_LIGHT, i18n("Linear Light"), KoCompositeOp::categoryLight());
+ add<&cfPNormA<Arg> >(cs, COMPOSITE_PNORM_A , i18n("P-Norm A") , KoCompositeOp::categoryLight());
+ add<&cfPNormB<Arg> >(cs, COMPOSITE_PNORM_B , i18n("P-Norm B") , KoCompositeOp::categoryLight());
+ add<&cfSuperLight<Arg> >(cs, COMPOSITE_SUPER_LIGHT , i18n("Super Light") , KoCompositeOp::categoryLight());
+ add<&cfTintIFSIllusions<Arg> >(cs, COMPOSITE_TINT_IFS_ILLUSIONS , i18n("Tint (IFS Illusions)") , KoCompositeOp::categoryLight());
+ add<&cfFogLightenIFSIllusions<Arg> >(cs, COMPOSITE_FOG_LIGHTEN_IFS_ILLUSIONS , i18n("Fog Lighten (IFS Illusions)") , KoCompositeOp::categoryLight());
+ add<&cfColorDodgeLogarithmic<Arg> >(cs, COMPOSITE_DODGE_LOGARITHMIC , i18n("Color Dodge - Logarithmic") , KoCompositeOp::categoryLight());
+ add<&cfEasyDodge<Arg> >(cs, COMPOSITE_EASY_DODGE , i18n("Easy Dodge") , KoCompositeOp::categoryLight());
add<&cfColorBurn<Arg> >(cs, COMPOSITE_BURN , i18n("Color Burn") , KoCompositeOp::categoryDark());
add<&cfLinearBurn<Arg> >(cs, COMPOSITE_LINEAR_BURN , i18n("Linear Burn"), KoCompositeOp::categoryDark());
add<&cfDarkenOnly<Arg> >(cs, COMPOSITE_DARKEN , i18n("Darken") , KoCompositeOp::categoryDark());
add<&cfGammaDark<Arg> >(cs, COMPOSITE_GAMMA_DARK , i18n("Gamma Dark") , KoCompositeOp::categoryDark());
+ add<&cfShadeIFSIllusions<Arg> >(cs, COMPOSITE_SHADE_IFS_ILLUSIONS , i18n("Shade (IFS_Illusions)") , KoCompositeOp::categoryDark());
+ add<&cfFogDarkenIFSIllusions<Arg> >(cs, COMPOSITE_FOG_DARKEN_IFS_ILLUSIONS , i18n("Fog Darken (IFS Illusions)") , KoCompositeOp::categoryDark());
+ add<&cfColorBurnLogarithmic<Arg> >(cs, COMPOSITE_BURN_LOGARITHMIC , i18n("Color Burn - Logarithmic") , KoCompositeOp::categoryDark());
+ add<&cfEasyBurn<Arg> >(cs, COMPOSITE_EASY_BURN , i18n("Easy Burn") , KoCompositeOp::categoryDark());
add<&cfAddition<Arg> >(cs, COMPOSITE_ADD , i18n("Addition") , KoCompositeOp::categoryArithmetic());
add<&cfSubtract<Arg> >(cs, COMPOSITE_SUBTRACT , i18n("Subtract") , KoCompositeOp::categoryArithmetic());
add<&cfInverseSubtract<Arg> >(cs, COMPOSITE_INVERSE_SUBTRACT, i18n("Inversed-Subtract"), KoCompositeOp::categoryArithmetic());
add<&cfMultiply<Arg> >(cs, COMPOSITE_MULT , i18n("Multiply") , KoCompositeOp::categoryArithmetic());
add<&cfDivide<Arg> >(cs, COMPOSITE_DIVIDE , i18n("Divide") , KoCompositeOp::categoryArithmetic());
+ add<&cfModulo<Arg> >(cs, COMPOSITE_MOD , i18n("Modulo") , KoCompositeOp::categoryModulo());
+ add<&cfModuloContinuous<Arg> >(cs, COMPOSITE_MOD_CON , i18n("Modulo - Continuous") , KoCompositeOp::categoryModulo());
+ add<&cfDivisiveModulo<Arg> >(cs, COMPOSITE_DIVISIVE_MOD , i18n("Divisive Modulo") , KoCompositeOp::categoryModulo());
+ add<&cfDivisiveModuloContinuous<Arg> >(cs, COMPOSITE_DIVISIVE_MOD_CON , i18n("Divisive Modulo - Continuous") , KoCompositeOp::categoryModulo());
+ add<&cfModuloShift<Arg> >(cs, COMPOSITE_MODULO_SHIFT , i18n("Modulo Shift") , KoCompositeOp::categoryModulo());
+ add<&cfModuloShiftContinuous<Arg> >(cs, COMPOSITE_MODULO_SHIFT_CON , i18n("Modulo Shift - Continuous") , KoCompositeOp::categoryModulo());
+
add<&cfArcTangent<Arg> >(cs, COMPOSITE_ARC_TANGENT , i18n("Arcus Tangent") , KoCompositeOp::categoryNegative());
add<&cfDifference<Arg> >(cs, COMPOSITE_DIFF , i18n("Difference") , KoCompositeOp::categoryNegative());
add<&cfExclusion<Arg> >(cs, COMPOSITE_EXCLUSION , i18n("Exclusion") , KoCompositeOp::categoryNegative());
add<&cfEquivalence<Arg> >(cs, COMPOSITE_EQUIVALENCE , i18n("Equivalence") , KoCompositeOp::categoryNegative());
add<&cfAdditiveSubtractive<Arg> >(cs, COMPOSITE_ADDITIVE_SUBTRACTIVE , i18n("Additive-Subtractive") , KoCompositeOp::categoryNegative());
+ add<&cfNegation<Arg> >(cs, COMPOSITE_NEGATION , i18n("Negation") , KoCompositeOp::categoryNegative());
- add<&cfReflect<Arg> >(cs, COMPOSITE_REFLECT , i18n("Reflect") , KoCompositeOp::categoryQuadratic());
- add<&cfGlow<Arg> >(cs, COMPOSITE_GLOW , i18n("Glow") , KoCompositeOp::categoryQuadratic());
- add<&cfFreeze<Arg> >(cs, COMPOSITE_FREEZE , i18n("Freeze") , KoCompositeOp::categoryQuadratic());
- add<&cfHeat<Arg> >(cs, COMPOSITE_HEAT , i18n("Heat") , KoCompositeOp::categoryQuadratic());
+ add<&cfXor<Arg> >(cs, COMPOSITE_XOR , i18n("XOR") , KoCompositeOp::categoryBinary());
+ add<&cfOr<Arg> >(cs, COMPOSITE_OR , i18n("OR") , KoCompositeOp::categoryBinary());
+ add<&cfAnd<Arg> >(cs, COMPOSITE_AND , i18n("AND") , KoCompositeOp::categoryBinary());
+ add<&cfNand<Arg> >(cs, COMPOSITE_NAND , i18n("NAND") , KoCompositeOp::categoryBinary());
+ add<&cfNor<Arg> >(cs, COMPOSITE_NOR , i18n("NOR") , KoCompositeOp::categoryBinary());
+ add<&cfXnor<Arg> >(cs, COMPOSITE_XNOR , i18n("XNOR") , KoCompositeOp::categoryBinary());
+ add<&cfImplies<Arg> >(cs, COMPOSITE_IMPLICATION , i18n("IMPLICATION") , KoCompositeOp::categoryBinary());
+ add<&cfNotImplies<Arg> >(cs, COMPOSITE_NOT_IMPLICATION , i18n("NOT IMPLICATION") , KoCompositeOp::categoryBinary());
+ add<&cfConverse<Arg> >(cs, COMPOSITE_CONVERSE , i18n("CONVERSE") , KoCompositeOp::categoryBinary());
+ add<&cfNotConverse<Arg> >(cs, COMPOSITE_NOT_CONVERSE , i18n("NOT CONVERSE") , KoCompositeOp::categoryBinary());
+
+ add<&cfReflect<Arg> >(cs, COMPOSITE_REFLECT , i18n("Reflect") , KoCompositeOp::categoryQuadratic());
+ add<&cfGlow<Arg> >(cs, COMPOSITE_GLOW , i18n("Glow") , KoCompositeOp::categoryQuadratic());
+ add<&cfFreeze<Arg> >(cs, COMPOSITE_FREEZE , i18n("Freeze") , KoCompositeOp::categoryQuadratic());
+ add<&cfHeat<Arg> >(cs, COMPOSITE_HEAT , i18n("Heat") , KoCompositeOp::categoryQuadratic());
+ add<&cfGleat<Arg> >(cs, COMPOSITE_GLEAT , i18n("Glow-Heat") , KoCompositeOp::categoryQuadratic());
+ add<&cfHelow<Arg> >(cs, COMPOSITE_HELOW , i18n("Heat-Glow") , KoCompositeOp::categoryQuadratic());
+ add<&cfReeze<Arg> >(cs, COMPOSITE_REEZE , i18n("Reflect-Freeze") , KoCompositeOp::categoryQuadratic());
+ add<&cfFrect<Arg> >(cs, COMPOSITE_FRECT , i18n("Freeze-Reflect") , KoCompositeOp::categoryQuadratic());
+ add<&cfFhyrd<Arg> >(cs, COMPOSITE_FHYRD , i18n("Heat-Glow & Freeze-Reflect Hybrid") , KoCompositeOp::categoryQuadratic());
cs->addCompositeOp(new KoCompositeOpDissolve<Traits>(cs, KoCompositeOp::categoryMisc()));
}
};
template<class Traits, bool flag>
struct AddRGBOps
{
static void add(KoColorSpace* cs) { Q_UNUSED(cs); }
};
template<class Traits>
struct AddRGBOps<Traits, true>
{
typedef float Arg;
static const qint32 red_pos = Traits::red_pos;
static const qint32 green_pos = Traits::green_pos;
static const qint32 blue_pos = Traits::blue_pos;
template<void compositeFunc(Arg, Arg, Arg, Arg&, Arg&, Arg&)>
static void add(KoColorSpace* cs, const QString& id, const QString& description, const QString& category) {
cs->addCompositeOp(new KoCompositeOpGenericHSL<Traits, compositeFunc>(cs, id, description, category));
}
static void add(KoColorSpace* cs) {
cs->addCompositeOp(new KoCompositeOpCopyChannel<Traits,red_pos >(cs, COMPOSITE_COPY_RED , i18n("Copy Red") , KoCompositeOp::categoryMisc()));
cs->addCompositeOp(new KoCompositeOpCopyChannel<Traits,green_pos>(cs, COMPOSITE_COPY_GREEN, i18n("Copy Green"), KoCompositeOp::categoryMisc()));
cs->addCompositeOp(new KoCompositeOpCopyChannel<Traits,blue_pos >(cs, COMPOSITE_COPY_BLUE , i18n("Copy Blue") , KoCompositeOp::categoryMisc()));
add<&cfTangentNormalmap <HSYType,Arg> >(cs, COMPOSITE_TANGENT_NORMALMAP , i18n("Tangent Normalmap") , KoCompositeOp::categoryMisc());
add<&cfReorientedNormalMapCombine <HSYType, Arg> >(cs, COMPOSITE_COMBINE_NORMAL, i18n("Combine Normal Maps"), KoCompositeOp::categoryMisc());
add<&cfColor <HSYType,Arg> >(cs, COMPOSITE_COLOR , i18n("Color") , KoCompositeOp::categoryHSY());
add<&cfHue <HSYType,Arg> >(cs, COMPOSITE_HUE , i18n("Hue") , KoCompositeOp::categoryHSY());
add<&cfSaturation <HSYType,Arg> >(cs, COMPOSITE_SATURATION , i18n("Saturation") , KoCompositeOp::categoryHSY());
add<&cfIncreaseSaturation<HSYType,Arg> >(cs, COMPOSITE_INC_SATURATION, i18n("Increase Saturation"), KoCompositeOp::categoryHSY());
add<&cfDecreaseSaturation<HSYType,Arg> >(cs, COMPOSITE_DEC_SATURATION, i18n("Decrease Saturation"), KoCompositeOp::categoryHSY());
add<&cfLightness <HSYType,Arg> >(cs, COMPOSITE_LUMINIZE , i18n("Luminosity") , KoCompositeOp::categoryHSY());
add<&cfIncreaseLightness <HSYType,Arg> >(cs, COMPOSITE_INC_LUMINOSITY, i18n("Increase Luminosity"), KoCompositeOp::categoryHSY());
add<&cfDecreaseLightness <HSYType,Arg> >(cs, COMPOSITE_DEC_LUMINOSITY, i18n("Decrease Luminosity"), KoCompositeOp::categoryHSY());
add<&cfDarkerColor <HSYType,Arg> >(cs, COMPOSITE_DARKER_COLOR, i18n("Darker Color"), KoCompositeOp::categoryDark());//darker color as PSD does it//
add<&cfLighterColor <HSYType,Arg> >(cs, COMPOSITE_LIGHTER_COLOR, i18n("Lighter Color"), KoCompositeOp::categoryLight());//lighter color as PSD does it//
add<&cfColor <HSIType,Arg> >(cs, COMPOSITE_COLOR_HSI , i18n("Color HSI") , KoCompositeOp::categoryHSI());
add<&cfHue <HSIType,Arg> >(cs, COMPOSITE_HUE_HSI , i18n("Hue HSI") , KoCompositeOp::categoryHSI());
add<&cfSaturation <HSIType,Arg> >(cs, COMPOSITE_SATURATION_HSI , i18n("Saturation HSI") , KoCompositeOp::categoryHSI());
add<&cfIncreaseSaturation<HSIType,Arg> >(cs, COMPOSITE_INC_SATURATION_HSI, i18n("Increase Saturation HSI"), KoCompositeOp::categoryHSI());
add<&cfDecreaseSaturation<HSIType,Arg> >(cs, COMPOSITE_DEC_SATURATION_HSI, i18n("Decrease Saturation HSI"), KoCompositeOp::categoryHSI());
add<&cfLightness <HSIType,Arg> >(cs, COMPOSITE_INTENSITY , i18n("Intensity") , KoCompositeOp::categoryHSI());
add<&cfIncreaseLightness <HSIType,Arg> >(cs, COMPOSITE_INC_INTENSITY , i18n("Increase Intensity") , KoCompositeOp::categoryHSI());
add<&cfDecreaseLightness <HSIType,Arg> >(cs, COMPOSITE_DEC_INTENSITY , i18n("Decrease Intensity") , KoCompositeOp::categoryHSI());
add<&cfColor <HSLType,Arg> >(cs, COMPOSITE_COLOR_HSL , i18n("Color HSL") , KoCompositeOp::categoryHSL());
add<&cfHue <HSLType,Arg> >(cs, COMPOSITE_HUE_HSL , i18n("Hue HSL") , KoCompositeOp::categoryHSL());
add<&cfSaturation <HSLType,Arg> >(cs, COMPOSITE_SATURATION_HSL , i18n("Saturation HSL") , KoCompositeOp::categoryHSL());
add<&cfIncreaseSaturation<HSLType,Arg> >(cs, COMPOSITE_INC_SATURATION_HSL, i18n("Increase Saturation HSL"), KoCompositeOp::categoryHSL());
add<&cfDecreaseSaturation<HSLType,Arg> >(cs, COMPOSITE_DEC_SATURATION_HSL, i18n("Decrease Saturation HSL"), KoCompositeOp::categoryHSL());
add<&cfLightness <HSLType,Arg> >(cs, COMPOSITE_LIGHTNESS , i18n("Lightness") , KoCompositeOp::categoryHSL());
add<&cfIncreaseLightness <HSLType,Arg> >(cs, COMPOSITE_INC_LIGHTNESS , i18n("Increase Lightness") , KoCompositeOp::categoryHSL());
add<&cfDecreaseLightness <HSLType,Arg> >(cs, COMPOSITE_DEC_LIGHTNESS , i18n("Decrease Lightness") , KoCompositeOp::categoryHSL());
add<&cfColor <HSVType,Arg> >(cs, COMPOSITE_COLOR_HSV , i18n("Color HSV") , KoCompositeOp::categoryHSV());
add<&cfHue <HSVType,Arg> >(cs, COMPOSITE_HUE_HSV , i18n("Hue HSV") , KoCompositeOp::categoryHSV());
add<&cfSaturation <HSVType,Arg> >(cs, COMPOSITE_SATURATION_HSV , i18n("Saturation HSV") , KoCompositeOp::categoryHSV());
add<&cfIncreaseSaturation<HSVType,Arg> >(cs, COMPOSITE_INC_SATURATION_HSV, i18n("Increase Saturation HSV"), KoCompositeOp::categoryHSV());
add<&cfDecreaseSaturation<HSVType,Arg> >(cs, COMPOSITE_DEC_SATURATION_HSV, i18n("Decrease Saturation HSV"), KoCompositeOp::categoryHSV());
add<&cfLightness <HSVType,Arg> >(cs, COMPOSITE_VALUE , i18nc("HSV Value","Value") , KoCompositeOp::categoryHSV());
add<&cfIncreaseLightness <HSVType,Arg> >(cs, COMPOSITE_INC_VALUE , i18n("Increase Value") , KoCompositeOp::categoryHSV());
add<&cfDecreaseLightness <HSVType,Arg> >(cs, COMPOSITE_DEC_VALUE , i18n("Decrease Value") , KoCompositeOp::categoryHSV());
}
};
}
/**
* This function add to the colorspace all the composite ops defined by
* the pigment library.
*/
template<class _Traits_>
void addStandardCompositeOps(KoColorSpace* cs)
{
typedef typename _Traits_::channels_type channels_type;
static const bool useGeneralOps = true;
static const bool useRGBOps = (boost::is_base_of<KoBgrTraits<channels_type>, _Traits_>::value
|| boost::is_base_of<KoRgbTraits<channels_type>, _Traits_>::value);
_Private::AddGeneralOps<_Traits_, useGeneralOps>::add(cs);
_Private::AddRGBOps <_Traits_, useRGBOps >::add(cs);
}
+template<class _Traits_>
+KoCompositeOp* createAlphaDarkenCompositeOp(const KoColorSpace *cs)
+{
+ return _Private::OptimizedOpsSelector<_Traits_>::createAlphaDarkenOp(cs);
+}
+
#endif
diff --git a/libs/pigment/compositeops/KoOptimizedCompositeOpAlphaDarken128.h b/libs/pigment/compositeops/KoOptimizedCompositeOpAlphaDarken128.h
index 4c020aba19..710f9987e9 100644
--- a/libs/pigment/compositeops/KoOptimizedCompositeOpAlphaDarken128.h
+++ b/libs/pigment/compositeops/KoOptimizedCompositeOpAlphaDarken128.h
@@ -1,228 +1,235 @@
/*
* Copyright (c) 2016 Thorsten Zachmann <zachmann@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOOPTIMIZEDCOMPOSITEOPALPHADARKEN128_H
#define KOOPTIMIZEDCOMPOSITEOPALPHADARKEN128_H
#include "KoCompositeOpBase.h"
#include "KoCompositeOpRegistry.h"
#include "KoStreamedMath.h"
+#include "KoAlphaDarkenParamsWrapper.h"
-template<typename channels_type, typename pixel_type>
+template<typename channels_type, typename pixel_type, typename _ParamsWrapper>
struct AlphaDarkenCompositor128 {
- struct OptionalParams {
- OptionalParams(const KoCompositeOp::ParameterInfo& params)
- : flow(params.flow)
- , averageOpacity(*params.lastOpacity * params.flow)
- , premultipliedOpacity(params.opacity * params.flow)
- {
- }
- float flow;
- float averageOpacity;
- float premultipliedOpacity;
- };
+ using ParamsWrapper = _ParamsWrapper;
struct Pixel {
channels_type red;
channels_type green;
channels_type blue;
channels_type alpha;
};
/**
* This is a vector equivalent of compositeOnePixelScalar(). It is considered
* to process Vc::float_v::size() pixels in a single pass.
*
* o the \p haveMask parameter points whether the real (non-null) mask
* pointer is passed to the function.
* o the \p src pointer may be aligned to vector boundary or may be
* not. In case not, it must be pointed with a special parameter
* \p src_aligned.
* o the \p dst pointer must always(!) be aligned to the boundary
* of a streaming vector. Unaligned writes are really expensive.
* o This function is *never* used if HAVE_VC is not present
*/
template<bool haveMask, bool src_aligned, Vc::Implementation _impl>
- static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const OptionalParams &oparams)
+ static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
{
const Pixel *sp = reinterpret_cast<const Pixel*>(src);
Pixel *dp = reinterpret_cast<Pixel*>(dst);
Vc::float_v src_c1;
Vc::float_v src_c2;
Vc::float_v src_c3;
Vc::float_v src_alpha;
const Vc::float_v::IndexType indexes(Vc::IndexesFromZero);
Vc::InterleavedMemoryWrapper<Pixel, Vc::float_v> data(const_cast<Pixel*>(sp));
tie(src_c1, src_c2, src_c3, src_alpha) = data[indexes];
Vc::float_v msk_norm_alpha;
if (haveMask) {
const Vc::float_v uint8Rec1((float)1.0 / 255.0);
Vc::float_v mask_vec = KoStreamedMath<_impl>::fetch_mask_8(mask);
msk_norm_alpha = mask_vec * uint8Rec1 * src_alpha;
}
else {
msk_norm_alpha = src_alpha;
}
// we don't use directly passed value
Q_UNUSED(opacity);
- // instead we should use opacity premultiplied by flow
- opacity = oparams.premultipliedOpacity;
- Vc::float_v opacity_vec(oparams.premultipliedOpacity);
+ // instead we use value calculated by ParamsWrapper
+ opacity = oparams.opacity;
+ Vc::float_v opacity_vec(opacity);
src_alpha = msk_norm_alpha * opacity_vec;
const Vc::float_v zeroValue(KoColorSpaceMathsTraits<channels_type>::zeroValue);
Vc::float_v dst_c1;
Vc::float_v dst_c2;
Vc::float_v dst_c3;
Vc::float_v dst_alpha;
Vc::InterleavedMemoryWrapper<Pixel, Vc::float_v> dataDest(dp);
tie(dst_c1, dst_c2, dst_c3, dst_alpha) = dataDest[indexes];
Vc::float_m empty_dst_pixels_mask = dst_alpha == zeroValue;
if (!empty_dst_pixels_mask.isFull()) {
if (empty_dst_pixels_mask.isEmpty()) {
dst_c1 = (src_c1 - dst_c1) * src_alpha + dst_c1;
dst_c2 = (src_c2 - dst_c2) * src_alpha + dst_c2;
dst_c3 = (src_c3 - dst_c3) * src_alpha + dst_c3;
}
else {
dst_c1(empty_dst_pixels_mask) = src_c1;
dst_c2(empty_dst_pixels_mask) = src_c2;
dst_c3(empty_dst_pixels_mask) = src_c3;
Vc::float_m not_empty_dst_pixels_mask = !empty_dst_pixels_mask;
dst_c1(not_empty_dst_pixels_mask) = (src_c1 - dst_c1) * src_alpha + dst_c1;
dst_c2(not_empty_dst_pixels_mask) = (src_c2 - dst_c2) * src_alpha + dst_c2;
dst_c3(not_empty_dst_pixels_mask) = (src_c3 - dst_c3) * src_alpha + dst_c3;
}
}
else {
dst_c1 = src_c1;
dst_c2 = src_c2;
dst_c3 = src_c3;
}
Vc::float_v fullFlowAlpha(dst_alpha);
if (oparams.averageOpacity > opacity) {
Vc::float_v average_opacity_vec(oparams.averageOpacity);
Vc::float_m fullFlowAlpha_mask = average_opacity_vec > dst_alpha;
fullFlowAlpha(fullFlowAlpha_mask) = (average_opacity_vec - src_alpha) * (dst_alpha / average_opacity_vec) + src_alpha;
}
else {
Vc::float_m fullFlowAlpha_mask = opacity_vec > dst_alpha;
fullFlowAlpha(fullFlowAlpha_mask) = (opacity_vec - dst_alpha) * msk_norm_alpha + dst_alpha;
}
if (oparams.flow == 1.0) {
dst_alpha = fullFlowAlpha;
}
else {
- Vc::float_v zeroFlowAlpha = src_alpha + dst_alpha - src_alpha * dst_alpha;
+ Vc::float_v zeroFlowAlpha = ParamsWrapper::calculateZeroFlowAlpha(src_alpha, dst_alpha);
Vc::float_v flow_norm_vec(oparams.flow);
dst_alpha = (fullFlowAlpha - zeroFlowAlpha) * flow_norm_vec + zeroFlowAlpha;
}
dataDest[indexes] = tie(dst_c1, dst_c2, dst_c3, dst_alpha);
}
/**
* Composes one pixel of the source into the destination
*/
template <bool haveMask, Vc::Implementation _impl>
- static ALWAYS_INLINE void compositeOnePixelScalar(const quint8 *s, quint8 *d, const quint8 *mask, float opacity, const OptionalParams &oparams)
+ static ALWAYS_INLINE void compositeOnePixelScalar(const quint8 *s, quint8 *d, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
{
using namespace Arithmetic;
const qint32 alpha_pos = 3;
const channels_type *src = reinterpret_cast<const channels_type*>(s);
channels_type *dst = reinterpret_cast<channels_type*>(d);
float dstAlphaNorm = dst[alpha_pos];
const float uint8Rec1 = 1.0 / 255.0;
float mskAlphaNorm = haveMask ? float(*mask) * uint8Rec1 * src[alpha_pos] : src[alpha_pos];
Q_UNUSED(opacity);
- opacity = oparams.premultipliedOpacity;
+ opacity = oparams.opacity;
float srcAlphaNorm = mskAlphaNorm * opacity;
if (dstAlphaNorm != 0) {
dst[0] = lerp(dst[0], src[0], srcAlphaNorm);
dst[1] = lerp(dst[1], src[1], srcAlphaNorm);
dst[2] = lerp(dst[2], src[2], srcAlphaNorm);
} else {
const pixel_type *s = reinterpret_cast<const pixel_type*>(src);
pixel_type *d = reinterpret_cast<pixel_type*>(dst);
*d = *s;
}
float flow = oparams.flow;
float averageOpacity = oparams.averageOpacity;
float fullFlowAlpha;
if (averageOpacity > opacity) {
fullFlowAlpha = averageOpacity > dstAlphaNorm ? lerp(srcAlphaNorm, averageOpacity, dstAlphaNorm / averageOpacity) : dstAlphaNorm;
} else {
fullFlowAlpha = opacity > dstAlphaNorm ? lerp(dstAlphaNorm, opacity, mskAlphaNorm) : dstAlphaNorm;
}
if (flow == 1.0) {
dst[alpha_pos] = fullFlowAlpha;
} else {
- float zeroFlowAlpha = unionShapeOpacity(srcAlphaNorm, dstAlphaNorm);
+ float zeroFlowAlpha = ParamsWrapper::calculateZeroFlowAlpha(srcAlphaNorm, dstAlphaNorm);
dst[alpha_pos] = lerp(zeroFlowAlpha, fullFlowAlpha, flow);
}
}
};
/**
* An optimized version of a composite op for the use in 16 byte
* colorspaces with alpha channel placed at the last byte of
* the pixel: C1_C2_C3_A.
*/
-template<Vc::Implementation _impl>
-class KoOptimizedCompositeOpAlphaDarken128 : public KoCompositeOp
+template<Vc::Implementation _impl, typename ParamsWrapper>
+class KoOptimizedCompositeOpAlphaDarken128Impl : public KoCompositeOp
{
public:
- KoOptimizedCompositeOpAlphaDarken128(const KoColorSpace* cs)
+ KoOptimizedCompositeOpAlphaDarken128Impl(const KoColorSpace* cs)
: KoCompositeOp(cs, COMPOSITE_ALPHA_DARKEN, i18n("Alpha darken"), KoCompositeOp::categoryMix()) {}
using KoCompositeOp::composite;
virtual void composite(const KoCompositeOp::ParameterInfo& params) const
{
if(params.maskRowStart) {
- KoStreamedMath<_impl>::template genericComposite128<true, true, AlphaDarkenCompositor128<float, quint32> >(params);
+ KoStreamedMath<_impl>::template genericComposite128<true, true, AlphaDarkenCompositor128<float, quint32, ParamsWrapper> >(params);
} else {
- KoStreamedMath<_impl>::template genericComposite128<false, true, AlphaDarkenCompositor128<float, quint32> >(params);
+ KoStreamedMath<_impl>::template genericComposite128<false, true, AlphaDarkenCompositor128<float, quint32, ParamsWrapper> >(params);
}
}
};
+template<Vc::Implementation _impl>
+struct KoOptimizedCompositeOpAlphaDarkenHard128
+ : public KoOptimizedCompositeOpAlphaDarken128Impl<_impl, KoAlphaDarkenParamsWrapperHard>
+{
+ KoOptimizedCompositeOpAlphaDarkenHard128(const KoColorSpace* cs)
+ : KoOptimizedCompositeOpAlphaDarken128Impl<_impl, KoAlphaDarkenParamsWrapperHard>(cs) {}
+};
+
+template<Vc::Implementation _impl>
+struct KoOptimizedCompositeOpAlphaDarkenCreamy128
+ : public KoOptimizedCompositeOpAlphaDarken128Impl<_impl, KoAlphaDarkenParamsWrapperCreamy>
+{
+ KoOptimizedCompositeOpAlphaDarkenCreamy128(const KoColorSpace* cs)
+ : KoOptimizedCompositeOpAlphaDarken128Impl<_impl, KoAlphaDarkenParamsWrapperCreamy>(cs) {}
+};
+
#endif // KOOPTIMIZEDCOMPOSITEOPALPHADARKEN128_H
diff --git a/libs/pigment/compositeops/KoOptimizedCompositeOpAlphaDarken32.h b/libs/pigment/compositeops/KoOptimizedCompositeOpAlphaDarken32.h
index 41de2b363d..b2dfe28897 100644
--- a/libs/pigment/compositeops/KoOptimizedCompositeOpAlphaDarken32.h
+++ b/libs/pigment/compositeops/KoOptimizedCompositeOpAlphaDarken32.h
@@ -1,269 +1,278 @@
/*
* Copyright (c) 2006 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2011 Silvio Heinrich <plassy@web.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOOPTIMIZEDCOMPOSITEOPALPHADARKEN32_H_
#define KOOPTIMIZEDCOMPOSITEOPALPHADARKEN32_H_
#include "KoCompositeOpBase.h"
#include "KoCompositeOpRegistry.h"
#include <klocalizedstring.h>
#include "KoStreamedMath.h"
+#include <KoAlphaDarkenParamsWrapper.h>
-template<typename channels_type, typename pixel_type>
+template<typename channels_type, typename pixel_type, typename _ParamsWrapper>
struct AlphaDarkenCompositor32 {
- struct OptionalParams {
- OptionalParams(const KoCompositeOp::ParameterInfo& params)
- : flow(params.flow),
- averageOpacity(*params.lastOpacity * params.flow),
- premultipliedOpacity(params.opacity * params.flow)
- {
- }
- float flow;
- float averageOpacity;
- float premultipliedOpacity;
- };
+ using ParamsWrapper = _ParamsWrapper;
/**
* This is a vector equivalent of compositeOnePixelScalar(). It is considered
* to process Vc::float_v::size() pixels in a single pass.
*
* o the \p haveMask parameter points whether the real (non-null) mask
* pointer is passed to the function.
* o the \p src pointer may be aligned to vector boundary or may be
* not. In case not, it must be pointed with a special parameter
* \p src_aligned.
* o the \p dst pointer must always(!) be aligned to the boundary
* of a streaming vector. Unaligned writes are really expensive.
* o This function is *never* used if HAVE_VC is not present
*/
template<bool haveMask, bool src_aligned, Vc::Implementation _impl>
- static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const OptionalParams &oparams)
+ static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
{
Vc::float_v src_alpha;
Vc::float_v dst_alpha;
// we don't use directly passed value
Q_UNUSED(opacity);
- // instead we should use opacity premultiplied by flow
- opacity = oparams.premultipliedOpacity;
- Vc::float_v opacity_vec(255.0 * oparams.premultipliedOpacity);
+ // instead we use value calculated by ParamsWrapper
+ opacity = oparams.opacity;
+ Vc::float_v opacity_vec(255.0 * opacity);
Vc::float_v average_opacity_vec(255.0 * oparams.averageOpacity);
Vc::float_v flow_norm_vec(oparams.flow);
Vc::float_v uint8MaxRec2((float)1.0 / (255.0 * 255.0));
Vc::float_v uint8MaxRec1((float)1.0 / 255.0);
Vc::float_v uint8Max((float)255.0);
Vc::float_v zeroValue(Vc::Zero);
Vc::float_v msk_norm_alpha;
src_alpha = KoStreamedMath<_impl>::template fetch_alpha_32<src_aligned>(src);
if (haveMask) {
Vc::float_v mask_vec = KoStreamedMath<_impl>::fetch_mask_8(mask);
msk_norm_alpha = src_alpha * mask_vec * uint8MaxRec2;
} else {
msk_norm_alpha = src_alpha * uint8MaxRec1;
}
dst_alpha = KoStreamedMath<_impl>::template fetch_alpha_32<true>(dst);
src_alpha = msk_norm_alpha * opacity_vec;
Vc::float_m empty_dst_pixels_mask = dst_alpha == zeroValue;
Vc::float_v src_c1;
Vc::float_v src_c2;
Vc::float_v src_c3;
Vc::float_v dst_c1;
Vc::float_v dst_c2;
Vc::float_v dst_c3;
KoStreamedMath<_impl>::template fetch_colors_32<src_aligned>(src, src_c1, src_c2, src_c3);
bool srcAlphaIsZero = (src_alpha == zeroValue).isFull();
if (srcAlphaIsZero) return;
bool dstAlphaIsZero = empty_dst_pixels_mask.isFull();
Vc::float_v dst_blend = src_alpha * uint8MaxRec1;
bool srcAlphaIsUnit = (src_alpha == uint8Max).isFull();
if (dstAlphaIsZero) {
dst_c1 = src_c1;
dst_c2 = src_c2;
dst_c3 = src_c3;
} else if (srcAlphaIsUnit) {
bool dstAlphaIsUnit = (dst_alpha == uint8Max).isFull();
if (dstAlphaIsUnit) {
memcpy(dst, src, 4 * Vc::float_v::size());
return;
} else {
dst_c1 = src_c1;
dst_c2 = src_c2;
dst_c3 = src_c3;
}
} else if (empty_dst_pixels_mask.isEmpty()) {
KoStreamedMath<_impl>::template fetch_colors_32<true>(dst, dst_c1, dst_c2, dst_c3);
dst_c1 = dst_blend * (src_c1 - dst_c1) + dst_c1;
dst_c2 = dst_blend * (src_c2 - dst_c2) + dst_c2;
dst_c3 = dst_blend * (src_c3 - dst_c3) + dst_c3;
} else {
KoStreamedMath<_impl>::template fetch_colors_32<true>(dst, dst_c1, dst_c2, dst_c3);
dst_c1(empty_dst_pixels_mask) = src_c1;
dst_c2(empty_dst_pixels_mask) = src_c2;
dst_c3(empty_dst_pixels_mask) = src_c3;
Vc::float_m not_empty_dst_pixels_mask = !empty_dst_pixels_mask;
dst_c1(not_empty_dst_pixels_mask) = dst_blend * (src_c1 - dst_c1) + dst_c1;
dst_c2(not_empty_dst_pixels_mask) = dst_blend * (src_c2 - dst_c2) + dst_c2;
dst_c3(not_empty_dst_pixels_mask) = dst_blend * (src_c3 - dst_c3) + dst_c3;
}
Vc::float_v fullFlowAlpha;
if (oparams.averageOpacity > opacity) {
Vc::float_m fullFlowAlpha_mask = average_opacity_vec > dst_alpha;
if (fullFlowAlpha_mask.isEmpty()) {
fullFlowAlpha = dst_alpha;
} else {
Vc::float_v reverse_blend = dst_alpha / average_opacity_vec;
Vc::float_v opt1 = (average_opacity_vec - src_alpha) * reverse_blend + src_alpha;
fullFlowAlpha(!fullFlowAlpha_mask) = dst_alpha;
fullFlowAlpha(fullFlowAlpha_mask) = opt1;
}
} else {
Vc::float_m fullFlowAlpha_mask = opacity_vec > dst_alpha;
if (fullFlowAlpha_mask.isEmpty()) {
fullFlowAlpha = dst_alpha;
} else {
Vc::float_v opt1 = (opacity_vec - dst_alpha) * msk_norm_alpha + dst_alpha;
fullFlowAlpha(!fullFlowAlpha_mask) = dst_alpha;
fullFlowAlpha(fullFlowAlpha_mask) = opt1;
}
}
if (oparams.flow == 1.0) {
dst_alpha = fullFlowAlpha;
} else {
- Vc::float_v zeroFlowAlpha = src_alpha + dst_alpha -
- dst_blend * dst_alpha;
+ Vc::float_v zeroFlowAlpha = ParamsWrapper::calculateZeroFlowAlpha(src_alpha, dst_alpha, uint8MaxRec1);
dst_alpha = (fullFlowAlpha - zeroFlowAlpha) * flow_norm_vec + zeroFlowAlpha;
}
KoStreamedMath<_impl>::write_channels_32(dst, dst_alpha, dst_c1, dst_c2, dst_c3);
}
/**
* Composes one pixel of the source into the destination
*/
template <bool haveMask, Vc::Implementation _impl>
- static ALWAYS_INLINE void compositeOnePixelScalar(const channels_type *src, channels_type *dst, const quint8 *mask, float opacity, const OptionalParams &oparams)
+ static ALWAYS_INLINE void compositeOnePixelScalar(const channels_type *src, channels_type *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
{
using namespace Arithmetic;
const qint32 alpha_pos = 3;
const float uint8Rec1 = 1.0 / 255.0;
const float uint8Rec2 = 1.0 / (255.0 * 255.0);
const float uint8Max = 255.0;
quint8 dstAlphaInt = dst[alpha_pos];
float dstAlphaNorm = dstAlphaInt ? dstAlphaInt * uint8Rec1 : 0.0;
float srcAlphaNorm;
float mskAlphaNorm;
Q_UNUSED(opacity);
- opacity = oparams.premultipliedOpacity;
+ opacity = oparams.opacity;
if (haveMask) {
mskAlphaNorm = float(*mask) * uint8Rec2 * src[alpha_pos];
srcAlphaNorm = mskAlphaNorm * opacity;
} else {
mskAlphaNorm = src[alpha_pos] * uint8Rec1;
srcAlphaNorm = mskAlphaNorm * opacity;
}
if (dstAlphaInt != 0) {
dst[0] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[0], src[0], srcAlphaNorm);
dst[1] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[1], src[1], srcAlphaNorm);
dst[2] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[2], src[2], srcAlphaNorm);
} else {
const pixel_type *s = reinterpret_cast<const pixel_type*>(src);
pixel_type *d = reinterpret_cast<pixel_type*>(dst);
*d = *s;
}
float flow = oparams.flow;
float averageOpacity = oparams.averageOpacity;
float fullFlowAlpha;
if (averageOpacity > opacity) {
fullFlowAlpha = averageOpacity > dstAlphaNorm ? lerp(srcAlphaNorm, averageOpacity, dstAlphaNorm / averageOpacity) : dstAlphaNorm;
} else {
fullFlowAlpha = opacity > dstAlphaNorm ? lerp(dstAlphaNorm, opacity, mskAlphaNorm) : dstAlphaNorm;
}
float dstAlpha;
if (flow == 1.0) {
dstAlpha = fullFlowAlpha * uint8Max;
} else {
- float zeroFlowAlpha = unionShapeOpacity(srcAlphaNorm, dstAlphaNorm);
+ float zeroFlowAlpha = ParamsWrapper::calculateZeroFlowAlpha(srcAlphaNorm, dstAlphaNorm);
dstAlpha = lerp(zeroFlowAlpha, fullFlowAlpha, flow) * uint8Max;
}
dst[alpha_pos] = KoStreamedMath<_impl>::round_float_to_uint(dstAlpha);
}
};
/**
* An optimized version of a composite op for the use in 4 byte
* colorspaces with alpha channel placed at the last byte of
* the pixel: C1_C2_C3_A.
*/
-template<Vc::Implementation _impl>
-class KoOptimizedCompositeOpAlphaDarken32 : public KoCompositeOp
+template<Vc::Implementation _impl, class ParamsWrapper>
+class KoOptimizedCompositeOpAlphaDarken32Impl : public KoCompositeOp
{
public:
- KoOptimizedCompositeOpAlphaDarken32(const KoColorSpace* cs)
+ KoOptimizedCompositeOpAlphaDarken32Impl(const KoColorSpace* cs)
: KoCompositeOp(cs, COMPOSITE_ALPHA_DARKEN, i18n("Alpha darken"), KoCompositeOp::categoryMix()) {}
using KoCompositeOp::composite;
virtual void composite(const KoCompositeOp::ParameterInfo& params) const
{
if(params.maskRowStart) {
- KoStreamedMath<_impl>::template genericComposite32<true, true, AlphaDarkenCompositor32<quint8, quint32> >(params);
+ KoStreamedMath<_impl>::template genericComposite32<true, true, AlphaDarkenCompositor32<quint8, quint32, ParamsWrapper> >(params);
} else {
- KoStreamedMath<_impl>::template genericComposite32<false, true, AlphaDarkenCompositor32<quint8, quint32> >(params);
+ KoStreamedMath<_impl>::template genericComposite32<false, true, AlphaDarkenCompositor32<quint8, quint32, ParamsWrapper> >(params);
}
}
};
+template<Vc::Implementation _impl>
+struct KoOptimizedCompositeOpAlphaDarkenHard32 :
+ public KoOptimizedCompositeOpAlphaDarken32Impl<_impl, KoAlphaDarkenParamsWrapperHard>
+{
+ KoOptimizedCompositeOpAlphaDarkenHard32(const KoColorSpace *cs)
+ : KoOptimizedCompositeOpAlphaDarken32Impl<_impl, KoAlphaDarkenParamsWrapperHard>(cs) {
+ }
+};
+
+template<Vc::Implementation _impl>
+struct KoOptimizedCompositeOpAlphaDarkenCreamy32 :
+ public KoOptimizedCompositeOpAlphaDarken32Impl<_impl, KoAlphaDarkenParamsWrapperCreamy>
+{
+ KoOptimizedCompositeOpAlphaDarkenCreamy32(const KoColorSpace *cs)
+ : KoOptimizedCompositeOpAlphaDarken32Impl<_impl, KoAlphaDarkenParamsWrapperCreamy>(cs) {
+ }
+};
+
+
#endif // KOOPTIMIZEDCOMPOSITEOPALPHADARKEN32_H_
diff --git a/libs/pigment/compositeops/KoOptimizedCompositeOpFactory.cpp b/libs/pigment/compositeops/KoOptimizedCompositeOpFactory.cpp
index 90a8d1f621..9015345fa5 100644
--- a/libs/pigment/compositeops/KoOptimizedCompositeOpFactory.cpp
+++ b/libs/pigment/compositeops/KoOptimizedCompositeOpFactory.cpp
@@ -1,46 +1,65 @@
/*
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoOptimizedCompositeOpFactoryPerArch.h" // vc.h must come first
#include "KoOptimizedCompositeOpFactory.h"
#if defined(__clang__)
#pragma GCC diagnostic ignored "-Wundef"
#endif
-KoCompositeOp* KoOptimizedCompositeOpFactory::createAlphaDarkenOp32(const KoColorSpace *cs)
+KoCompositeOp* KoOptimizedCompositeOpFactory::createAlphaDarkenOpHard32(const KoColorSpace *cs)
{
- return createOptimizedClass<KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarken32> >(cs);
+ return createOptimizedClass<
+ KoOptimizedCompositeOpFactoryPerArch<
+ KoOptimizedCompositeOpAlphaDarkenHard32>>(cs);
+}
+
+KoCompositeOp* KoOptimizedCompositeOpFactory::createAlphaDarkenOpCreamy32(const KoColorSpace *cs)
+{
+ return createOptimizedClass<
+ KoOptimizedCompositeOpFactoryPerArch<
+ KoOptimizedCompositeOpAlphaDarkenCreamy32>>(cs);
}
KoCompositeOp* KoOptimizedCompositeOpFactory::createOverOp32(const KoColorSpace *cs)
{
return createOptimizedClass<KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpOver32> >(cs);
}
-KoCompositeOp* KoOptimizedCompositeOpFactory::createAlphaDarkenOp128(const KoColorSpace *cs)
+KoCompositeOp* KoOptimizedCompositeOpFactory::createAlphaDarkenOpHard128(const KoColorSpace *cs)
{
- return createOptimizedClass<KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarken128> >(cs);
+ return createOptimizedClass<
+ KoOptimizedCompositeOpFactoryPerArch<
+ KoOptimizedCompositeOpAlphaDarkenHard128>>(cs);
}
+KoCompositeOp* KoOptimizedCompositeOpFactory::createAlphaDarkenOpCreamy128(const KoColorSpace *cs)
+{
+ return createOptimizedClass<
+ KoOptimizedCompositeOpFactoryPerArch<
+ KoOptimizedCompositeOpAlphaDarkenCreamy128>>(cs);
+}
+
+
KoCompositeOp* KoOptimizedCompositeOpFactory::createOverOp128(const KoColorSpace *cs)
{
return createOptimizedClass<KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpOver128> >(cs);
}
diff --git a/libs/pigment/compositeops/KoOptimizedCompositeOpFactory.h b/libs/pigment/compositeops/KoOptimizedCompositeOpFactory.h
index b614e402c1..8decc24fd1 100644
--- a/libs/pigment/compositeops/KoOptimizedCompositeOpFactory.h
+++ b/libs/pigment/compositeops/KoOptimizedCompositeOpFactory.h
@@ -1,49 +1,51 @@
/*
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOOPTIMIZEDCOMPOSITEOPFACTORY_H
#define KOOPTIMIZEDCOMPOSITEOPFACTORY_H
#include "kritapigment_export.h"
class KoCompositeOp;
class KoColorSpace;
/**
* The creation of the optimized composite ops is moved into a separate
* objects module for two reasons:
*
* 1) They are not templated, that is they do not need inlining into
* the user's code.
* 2) This removes compilation dependencies.
* 3) (most important!) When the object module is shared with a colorspace
* class, which is quite huge itself, GCC layouts the code somehow badly
* that causes 60% performance degradation.
*/
class KRITAPIGMENT_EXPORT KoOptimizedCompositeOpFactory
{
public:
- static KoCompositeOp* createAlphaDarkenOp32(const KoColorSpace *cs);
+ static KoCompositeOp* createAlphaDarkenOpHard32(const KoColorSpace *cs);
+ static KoCompositeOp* createAlphaDarkenOpCreamy32(const KoColorSpace *cs);
static KoCompositeOp* createOverOp32(const KoColorSpace *cs);
- static KoCompositeOp* createAlphaDarkenOp128(const KoColorSpace *cs);
+ static KoCompositeOp* createAlphaDarkenOpHard128(const KoColorSpace *cs);
+ static KoCompositeOp* createAlphaDarkenOpCreamy128(const KoColorSpace *cs);
static KoCompositeOp* createOverOp128(const KoColorSpace *cs);
};
#endif /* KOOPTIMIZEDCOMPOSITEOPFACTORY_H */
diff --git a/libs/pigment/compositeops/KoOptimizedCompositeOpFactoryPerArch.cpp b/libs/pigment/compositeops/KoOptimizedCompositeOpFactoryPerArch.cpp
index a12679be06..2477ef41f1 100644
--- a/libs/pigment/compositeops/KoOptimizedCompositeOpFactoryPerArch.cpp
+++ b/libs/pigment/compositeops/KoOptimizedCompositeOpFactoryPerArch.cpp
@@ -1,69 +1,87 @@
/*
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#if !defined _MSC_VER
#pragma GCC diagnostic ignored "-Wundef"
#endif
#include "KoOptimizedCompositeOpFactoryPerArch.h"
#include "KoOptimizedCompositeOpAlphaDarken32.h"
#include "KoOptimizedCompositeOpAlphaDarken128.h"
#include "KoOptimizedCompositeOpOver32.h"
#include "KoOptimizedCompositeOpOver128.h"
#include <QString>
#include "DebugPigment.h"
#include <KoCompositeOpRegistry.h>
#if defined(__clang__)
#pragma GCC diagnostic ignored "-Wlocal-type-template-args"
#endif
template<>
template<>
-KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarken32>::ReturnType
-KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarken32>::create<Vc::CurrentImplementation::current()>(ParamType param)
+KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarkenHard32>::ReturnType
+KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarkenHard32>::create<Vc::CurrentImplementation::current()>(ParamType param)
{
- return new KoOptimizedCompositeOpAlphaDarken32<Vc::CurrentImplementation::current()>(param);
+ return new KoOptimizedCompositeOpAlphaDarkenHard32<Vc::CurrentImplementation::current()>(param);
+}
+
+
+template<>
+template<>
+KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarkenCreamy32>::ReturnType
+KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarkenCreamy32>::create<Vc::CurrentImplementation::current()>(ParamType param)
+{
+ return new KoOptimizedCompositeOpAlphaDarkenCreamy32<Vc::CurrentImplementation::current()>(param);
}
template<>
template<>
KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpOver32>::ReturnType
KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpOver32>::create<Vc::CurrentImplementation::current()>(ParamType param)
{
return new KoOptimizedCompositeOpOver32<Vc::CurrentImplementation::current()>(param);
}
template<>
template<>
-KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarken128>::ReturnType
-KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarken128>::create<Vc::CurrentImplementation::current()>(ParamType param)
+KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarkenHard128>::ReturnType
+KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarkenHard128>::create<Vc::CurrentImplementation::current()>(ParamType param)
{
- return new KoOptimizedCompositeOpAlphaDarken128<Vc::CurrentImplementation::current()>(param);
+ return new KoOptimizedCompositeOpAlphaDarkenHard128<Vc::CurrentImplementation::current()>(param);
}
+template<>
+template<>
+KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarkenCreamy128>::ReturnType
+KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarkenCreamy128>::create<Vc::CurrentImplementation::current()>(ParamType param)
+{
+ return new KoOptimizedCompositeOpAlphaDarkenCreamy128<Vc::CurrentImplementation::current()>(param);
+}
+
+
template<>
template<>
KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpOver128>::ReturnType
KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpOver128>::create<Vc::CurrentImplementation::current()>(ParamType param)
{
return new KoOptimizedCompositeOpOver128<Vc::CurrentImplementation::current()>(param);
}
diff --git a/libs/pigment/compositeops/KoOptimizedCompositeOpFactoryPerArch.h b/libs/pigment/compositeops/KoOptimizedCompositeOpFactoryPerArch.h
index 353d525e1f..c80ba4505e 100644
--- a/libs/pigment/compositeops/KoOptimizedCompositeOpFactoryPerArch.h
+++ b/libs/pigment/compositeops/KoOptimizedCompositeOpFactoryPerArch.h
@@ -1,54 +1,60 @@
/*
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOOPTIMIZEDCOMPOSITEOPFACTORYPERARCH_H
#define KOOPTIMIZEDCOMPOSITEOPFACTORYPERARCH_H
#include <compositeops/KoVcMultiArchBuildSupport.h>
class KoCompositeOp;
class KoColorSpace;
template<Vc::Implementation _impl>
-class KoOptimizedCompositeOpAlphaDarken32;
+class KoOptimizedCompositeOpAlphaDarkenCreamy32;
+
+template<Vc::Implementation _impl>
+class KoOptimizedCompositeOpAlphaDarkenHard32;
template<Vc::Implementation _impl>
class KoOptimizedCompositeOpOver32;
template<Vc::Implementation _impl>
-class KoOptimizedCompositeOpAlphaDarken128;
+class KoOptimizedCompositeOpAlphaDarkenHard128;
+
+template<Vc::Implementation _impl>
+class KoOptimizedCompositeOpAlphaDarkenCreamy128;
template<Vc::Implementation _impl>
class KoOptimizedCompositeOpOver128;
template<template<Vc::Implementation I> class CompositeOp>
struct KoOptimizedCompositeOpFactoryPerArch
{
typedef const KoColorSpace* ParamType;
typedef KoCompositeOp* ReturnType;
template<Vc::Implementation _impl>
static ReturnType create(ParamType param);
};
#endif /* KOOPTIMIZEDCOMPOSITEOPFACTORYPERARCH_H */
diff --git a/libs/pigment/compositeops/KoOptimizedCompositeOpFactoryPerArch_Scalar.cpp b/libs/pigment/compositeops/KoOptimizedCompositeOpFactoryPerArch_Scalar.cpp
index 0f8aed551f..b2a6e7e667 100644
--- a/libs/pigment/compositeops/KoOptimizedCompositeOpFactoryPerArch_Scalar.cpp
+++ b/libs/pigment/compositeops/KoOptimizedCompositeOpFactoryPerArch_Scalar.cpp
@@ -1,57 +1,75 @@
/*
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoOptimizedCompositeOpFactoryPerArch.h"
#include "KoColorSpaceTraits.h"
#include "KoCompositeOpAlphaDarken.h"
+#include "KoAlphaDarkenParamsWrapper.h"
#include "KoCompositeOpOver.h"
+#include "KoCompositeOps.h"
+template<>
+template<>
+KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarkenHard32>::ReturnType
+KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarkenHard32>::create<Vc::ScalarImpl>(ParamType param)
+{
+ return new KoCompositeOpAlphaDarken<KoBgrU8Traits, KoAlphaDarkenParamsWrapperHard>(param);
+}
template<>
template<>
-KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarken32>::ReturnType
-KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarken32>::create<Vc::ScalarImpl>(ParamType param)
+KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarkenCreamy32>::ReturnType
+KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarkenCreamy32>::create<Vc::ScalarImpl>(ParamType param)
{
- return new KoCompositeOpAlphaDarken<KoBgrU8Traits>(param);
+ return new KoCompositeOpAlphaDarken<KoBgrU8Traits, KoAlphaDarkenParamsWrapperCreamy>(param);
}
template<>
template<>
KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpOver32>::ReturnType
KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpOver32>::create<Vc::ScalarImpl>(ParamType param)
{
return new KoCompositeOpOver<KoBgrU8Traits>(param);
}
template<>
template<>
-KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarken128>::ReturnType
-KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarken128>::create<Vc::ScalarImpl>(ParamType param)
+KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarkenHard128>::ReturnType
+KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarkenHard128>::create<Vc::ScalarImpl>(ParamType param)
+{
+ return new KoCompositeOpAlphaDarken<KoRgbF32Traits, KoAlphaDarkenParamsWrapperHard>(param);
+}
+
+template<>
+template<>
+KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarkenCreamy128>::ReturnType
+KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpAlphaDarkenCreamy128>::create<Vc::ScalarImpl>(ParamType param)
{
- return new KoCompositeOpAlphaDarken<KoRgbF32Traits>(param);
+ return new KoCompositeOpAlphaDarken<KoRgbF32Traits, KoAlphaDarkenParamsWrapperCreamy>(param);
}
+
template<>
template<>
KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpOver128>::ReturnType
KoOptimizedCompositeOpFactoryPerArch<KoOptimizedCompositeOpOver128>::create<Vc::ScalarImpl>(ParamType param)
{
return new KoCompositeOpOver<KoRgbF32Traits>(param);
}
diff --git a/libs/pigment/compositeops/KoOptimizedCompositeOpOver128.h b/libs/pigment/compositeops/KoOptimizedCompositeOpOver128.h
index 5793204bac..896062343d 100644
--- a/libs/pigment/compositeops/KoOptimizedCompositeOpOver128.h
+++ b/libs/pigment/compositeops/KoOptimizedCompositeOpOver128.h
@@ -1,290 +1,290 @@
/*
* Copyright (c) 2015 Thorsten Zachmann <zachmann@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOOPTIMIZEDCOMPOSITEOPOVER128_H_
#define KOOPTIMIZEDCOMPOSITEOPOVER128_H_
#include "KoCompositeOpBase.h"
#include "KoCompositeOpRegistry.h"
#include "KoStreamedMath.h"
#define NATIVE_OPACITY_OPAQUE KoColorSpaceMathsTraits<channels_type>::unitValue
#define NATIVE_OPACITY_TRANSPARENT KoColorSpaceMathsTraits<channels_type>::zeroValue
#define INFO_DEBUG 0
template<typename channels_type, typename pixel_type, bool alphaLocked, bool allChannelsFlag>
struct OverCompositor128 {
- struct OptionalParams {
- OptionalParams(const KoCompositeOp::ParameterInfo& params)
+ struct ParamsWrapper {
+ ParamsWrapper(const KoCompositeOp::ParameterInfo& params)
: channelFlags(params.channelFlags)
{
}
const QBitArray &channelFlags;
};
struct Pixel {
channels_type red;
channels_type green;
channels_type blue;
channels_type alpha;
};
// \see docs in AlphaDarkenCompositor32
template<bool haveMask, bool src_aligned, Vc::Implementation _impl>
- static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const OptionalParams &oparams)
+ static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
{
#if INFO_DEBUG
static quint32 countTotal = 0;
static quint32 countOne = 0;
static quint32 countTwo = 0;
static quint32 countThree = 0;
static quint32 countFour = 0;
if (++countTotal % 250000 == 0) {
qInfo() << "count" << countOne << countTwo << countThree << countFour << countTotal << opacity;
}
#endif
Q_UNUSED(oparams);
const Pixel *sp = reinterpret_cast<const Pixel*>(src);
Pixel *dp = reinterpret_cast<Pixel*>(dst);
Vc::float_v src_alpha;
Vc::float_v dst_alpha;
Vc::float_v src_c1;
Vc::float_v src_c2;
Vc::float_v src_c3;
const Vc::float_v::IndexType indexes(Vc::IndexesFromZero);
Vc::InterleavedMemoryWrapper<Pixel, Vc::float_v> data(const_cast<Pixel*>(sp));
tie(src_c1, src_c2, src_c3, src_alpha) = data[indexes];
//bool haveOpacity = opacity != 1.0;
const Vc::float_v opacity_norm_vec(opacity);
src_alpha *= opacity_norm_vec;
if (haveMask) {
const Vc::float_v uint8MaxRec1((float)1.0 / 255);
Vc::float_v mask_vec = KoStreamedMath<_impl>::fetch_mask_8(mask);
src_alpha *= mask_vec * uint8MaxRec1;
}
const Vc::float_v zeroValue(NATIVE_OPACITY_TRANSPARENT);
// The source cannot change the colors in the destination,
// since its fully transparent
if ((src_alpha == zeroValue).isFull()) {
#if INFO_DEBUG
countFour++;
#endif
return;
}
Vc::float_v dst_c1;
Vc::float_v dst_c2;
Vc::float_v dst_c3;
Vc::InterleavedMemoryWrapper<Pixel, Vc::float_v> dataDest(dp);
tie(dst_c1, dst_c2, dst_c3, dst_alpha) = dataDest[indexes];
Vc::float_v src_blend;
Vc::float_v new_alpha;
const Vc::float_v oneValue(NATIVE_OPACITY_OPAQUE);
if ((dst_alpha == oneValue).isFull()) {
new_alpha = dst_alpha;
src_blend = src_alpha;
} else if ((dst_alpha == zeroValue).isFull()) {
new_alpha = src_alpha;
src_blend = oneValue;
} else {
/**
* The value of new_alpha can have *some* zero values,
* which will result in NaN values while division.
*/
new_alpha = dst_alpha + (oneValue - dst_alpha) * src_alpha;
Vc::float_m mask = (new_alpha == zeroValue);
src_blend = src_alpha / new_alpha;
src_blend.setZero(mask);
}
if (!(src_blend == oneValue).isFull()) {
#if INFO_DEBUG
++countOne;
#endif
dst_c1 = src_blend * (src_c1 - dst_c1) + dst_c1;
dst_c2 = src_blend * (src_c2 - dst_c2) + dst_c2;
dst_c3 = src_blend * (src_c3 - dst_c3) + dst_c3;
dataDest[indexes] = tie(dst_c1, dst_c2, dst_c3, new_alpha);
} else {
#if INFO_DEBUG
++countTwo;
#endif
dataDest[indexes] = tie(src_c1, src_c2, src_c3, new_alpha);
}
}
template <bool haveMask, Vc::Implementation _impl>
- static ALWAYS_INLINE void compositeOnePixelScalar(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const OptionalParams &oparams)
+ static ALWAYS_INLINE void compositeOnePixelScalar(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
{
using namespace Arithmetic;
const qint32 alpha_pos = 3;
const channels_type *s = reinterpret_cast<const channels_type*>(src);
channels_type *d = reinterpret_cast<channels_type*>(dst);
float srcAlpha = s[alpha_pos];
srcAlpha *= opacity;
if (haveMask) {
const float uint8Rec1 = 1.0 / 255;
srcAlpha *= float(*mask) * uint8Rec1;
}
#if INFO_DEBUG
static int xx = 0;
bool display = xx > 45 && xx < 50;
if (display) {
qInfo() << "O" << s[alpha_pos] << srcAlpha << haveMask << opacity;
}
#endif
if (srcAlpha != NATIVE_OPACITY_TRANSPARENT) {
float dstAlpha = d[alpha_pos];
float srcBlendNorm;
if (dstAlpha == NATIVE_OPACITY_OPAQUE) {
srcBlendNorm = srcAlpha;
} else if (dstAlpha == NATIVE_OPACITY_TRANSPARENT) {
dstAlpha = srcAlpha;
srcBlendNorm = NATIVE_OPACITY_OPAQUE;
if (!allChannelsFlag) {
KoStreamedMathFunctions::clearPixel<16>(dst);
}
} else {
dstAlpha += (NATIVE_OPACITY_OPAQUE - dstAlpha) * srcAlpha;
srcBlendNorm = srcAlpha / dstAlpha;
}
#if INFO_DEBUG
if (display) {
qInfo() << "params" << srcBlendNorm << allChannelsFlag << alphaLocked << dstAlpha << haveMask;
}
#endif
if(allChannelsFlag) {
if (srcBlendNorm == NATIVE_OPACITY_OPAQUE) {
if (!alphaLocked) {
KoStreamedMathFunctions::copyPixel<16>(src, dst);
} else {
d[0] = s[0];
d[1] = s[1];
d[2] = s[2];
}
} else if (srcBlendNorm != 0.0){
#if INFO_DEBUG
if (display) {
qInfo() << "calc" << s[0] << d[0] << srcBlendNorm * (s[0] - d[0]) + d[0] << s[0] - d[0] << srcBlendNorm * (s[0] - d[0]) << srcBlendNorm;
}
#endif
d[0] = srcBlendNorm * (s[0] - d[0]) + d[0];
d[1] = srcBlendNorm * (s[1] - d[1]) + d[1];
d[2] = srcBlendNorm * (s[2] - d[2]) + d[2];
}
} else {
const QBitArray &channelFlags = oparams.channelFlags;
if (srcBlendNorm == NATIVE_OPACITY_OPAQUE) {
if(channelFlags.at(0)) d[0] = s[0];
if(channelFlags.at(1)) d[1] = s[1];
if(channelFlags.at(2)) d[2] = s[2];
} else if (srcBlendNorm != 0.0) {
if(channelFlags.at(0)) d[0] = srcBlendNorm * (s[0] - d[0]) + d[0];
if(channelFlags.at(1)) d[1] = srcBlendNorm * (s[1] - d[1]) + d[1];
if(channelFlags.at(2)) d[2] = srcBlendNorm * (s[2] - d[2]) + d[2];
}
}
if (!alphaLocked) {
d[alpha_pos] = dstAlpha;
}
#if INFO_DEBUG
if (display) {
qInfo() << "result" << d[0] << d[1] << d[2] << d[3];
}
++xx;
#endif
}
}
};
/**
* An optimized version of a composite op for the use in 16 byte
* colorspaces with alpha channel placed at the last byte of
* the pixel: C1_C2_C3_A.
*/
template<Vc::Implementation _impl>
class KoOptimizedCompositeOpOver128 : public KoCompositeOp
{
public:
KoOptimizedCompositeOpOver128(const KoColorSpace* cs)
: KoCompositeOp(cs, COMPOSITE_OVER, i18n("Normal"), KoCompositeOp::categoryMix()) {}
using KoCompositeOp::composite;
virtual void composite(const KoCompositeOp::ParameterInfo& params) const
{
if(params.maskRowStart) {
composite<true>(params);
} else {
composite<false>(params);
}
}
template <bool haveMask>
inline void composite(const KoCompositeOp::ParameterInfo& params) const {
if (params.channelFlags.isEmpty() ||
params.channelFlags == QBitArray(4, true)) {
KoStreamedMath<_impl>::template genericComposite128<haveMask, false, OverCompositor128<float, quint32, false, true> >(params);
} else {
const bool allChannelsFlag =
params.channelFlags.at(0) &&
params.channelFlags.at(1) &&
params.channelFlags.at(2);
const bool alphaLocked =
!params.channelFlags.at(3);
if (allChannelsFlag && alphaLocked) {
KoStreamedMath<_impl>::template genericComposite128_novector<haveMask, false, OverCompositor128<float, quint32, true, true> >(params);
} else if (!allChannelsFlag && !alphaLocked) {
KoStreamedMath<_impl>::template genericComposite128_novector<haveMask, false, OverCompositor128<float, quint32, false, false> >(params);
} else /*if (!allChannelsFlag && alphaLocked) */{
KoStreamedMath<_impl>::template genericComposite128_novector<haveMask, false, OverCompositor128<float, quint32, true, false> >(params);
}
}
}
};
#endif // KOOPTIMIZEDCOMPOSITEOPOVER128_H_
diff --git a/libs/pigment/compositeops/KoOptimizedCompositeOpOver32.h b/libs/pigment/compositeops/KoOptimizedCompositeOpOver32.h
index a1b7861968..7adfbfd4bb 100644
--- a/libs/pigment/compositeops/KoOptimizedCompositeOpOver32.h
+++ b/libs/pigment/compositeops/KoOptimizedCompositeOpOver32.h
@@ -1,287 +1,287 @@
/*
* Copyright (c) 2006 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2011 Silvio Heinrich <plassy@web.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOOPTIMIZEDCOMPOSITEOPOVER32_H_
#define KOOPTIMIZEDCOMPOSITEOPOVER32_H_
#include "KoCompositeOpBase.h"
#include "KoCompositeOpRegistry.h"
#include "KoStreamedMath.h"
template<Vc::Implementation _impl>
struct OptiDiv {
static ALWAYS_INLINE float divScalar(const float& divident, const float& divisor) {
#ifdef __SSE__
float result;
__m128 x = _mm_set_ss(divisor);
__m128 y = _mm_set_ss(divident);
x = _mm_rcp_ss(x);
x = _mm_mul_ss(x, y);
_mm_store_ss(&result, x);
return result;
#else
return divident / divisor;
#endif
}
static ALWAYS_INLINE Vc::float_v divVector(Vc::float_v::AsArg divident, Vc::float_v::AsArg divisor) {
#ifdef __SSE__
return divident * Vc::reciprocal(divisor);
#else
return divident / divisor;
#endif
}
};
template<typename channels_type, typename pixel_type, bool alphaLocked, bool allChannelsFlag>
struct OverCompositor32 {
- struct OptionalParams {
- OptionalParams(const KoCompositeOp::ParameterInfo& params)
+ struct ParamsWrapper {
+ ParamsWrapper(const KoCompositeOp::ParameterInfo& params)
: channelFlags(params.channelFlags)
{
}
const QBitArray &channelFlags;
};
// \see docs in AlphaDarkenCompositor32
template<bool haveMask, bool src_aligned, Vc::Implementation _impl>
- static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const OptionalParams &oparams)
+ static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
{
Q_UNUSED(oparams);
Vc::float_v src_alpha;
Vc::float_v dst_alpha;
src_alpha = KoStreamedMath<_impl>::template fetch_alpha_32<src_aligned>(src);
bool haveOpacity = opacity != 1.0;
Vc::float_v opacity_norm_vec(opacity);
Vc::float_v uint8Max((float)255.0);
Vc::float_v uint8MaxRec1((float)1.0 / 255.0);
Vc::float_v zeroValue(Vc::Zero);
Vc::float_v oneValue(Vc::One);
src_alpha *= opacity_norm_vec;
if (haveMask) {
Vc::float_v mask_vec = KoStreamedMath<_impl>::fetch_mask_8(mask);
src_alpha *= mask_vec * uint8MaxRec1;
}
// The source cannot change the colors in the destination,
// since its fully transparent
if ((src_alpha == zeroValue).isFull()) {
return;
}
dst_alpha = KoStreamedMath<_impl>::template fetch_alpha_32<true>(dst);
Vc::float_v src_c1;
Vc::float_v src_c2;
Vc::float_v src_c3;
Vc::float_v dst_c1;
Vc::float_v dst_c2;
Vc::float_v dst_c3;
KoStreamedMath<_impl>::template fetch_colors_32<src_aligned>(src, src_c1, src_c2, src_c3);
Vc::float_v src_blend;
Vc::float_v new_alpha;
if ((dst_alpha == uint8Max).isFull()) {
new_alpha = dst_alpha;
src_blend = src_alpha * uint8MaxRec1;
} else if ((dst_alpha == zeroValue).isFull()) {
new_alpha = src_alpha;
src_blend = oneValue;
} else {
/**
* The value of new_alpha can have *some* zero values,
* which will result in NaN values while division. But
* when converted to integers these NaN values will
* be converted to zeroes, which is exactly what we need
*/
new_alpha = dst_alpha + (uint8Max - dst_alpha) * src_alpha * uint8MaxRec1;
// Optimized version of:
// src_blend = src_alpha / new_alpha;
src_blend = OptiDiv<_impl>::divVector(src_alpha, new_alpha);
}
if (!(src_blend == oneValue).isFull()) {
KoStreamedMath<_impl>::template fetch_colors_32<true>(dst, dst_c1, dst_c2, dst_c3);
dst_c1 = src_blend * (src_c1 - dst_c1) + dst_c1;
dst_c2 = src_blend * (src_c2 - dst_c2) + dst_c2;
dst_c3 = src_blend * (src_c3 - dst_c3) + dst_c3;
} else {
if (!haveMask && !haveOpacity) {
memcpy(dst, src, 4 * Vc::float_v::size());
return;
} else {
// opacity has changed the alpha of the source,
// so we can't just memcpy the bytes
dst_c1 = src_c1;
dst_c2 = src_c2;
dst_c3 = src_c3;
}
}
KoStreamedMath<_impl>::write_channels_32(dst, new_alpha, dst_c1, dst_c2, dst_c3);
}
template <bool haveMask, Vc::Implementation _impl>
- static ALWAYS_INLINE void compositeOnePixelScalar(const channels_type *src, channels_type *dst, const quint8 *mask, float opacity, const OptionalParams &oparams)
+ static ALWAYS_INLINE void compositeOnePixelScalar(const channels_type *src, channels_type *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
{
using namespace Arithmetic;
const qint32 alpha_pos = 3;
const float uint8Rec1 = 1.0 / 255.0;
const float uint8Max = 255.0;
float srcAlpha = src[alpha_pos];
srcAlpha *= opacity;
if (haveMask) {
srcAlpha *= float(*mask) * uint8Rec1;
}
if (srcAlpha != 0.0) {
float dstAlpha = dst[alpha_pos];
float srcBlendNorm;
if (dstAlpha == uint8Max) {
srcBlendNorm = srcAlpha * uint8Rec1;
} else if (dstAlpha == 0.0) {
dstAlpha = srcAlpha;
srcBlendNorm = 1.0;
if (!allChannelsFlag) {
pixel_type *d = reinterpret_cast<pixel_type*>(dst);
*d = 0; // dstAlpha is already null
}
} else {
dstAlpha += (uint8Max - dstAlpha) * srcAlpha * uint8Rec1;
// Optimized version of:
// srcBlendNorm = srcAlpha / dstAlpha);
srcBlendNorm = OptiDiv<_impl>::divScalar(srcAlpha, dstAlpha);
}
if(allChannelsFlag) {
if (srcBlendNorm == 1.0) {
if (!alphaLocked) {
const pixel_type *s = reinterpret_cast<const pixel_type*>(src);
pixel_type *d = reinterpret_cast<pixel_type*>(dst);
*d = *s;
} else {
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
}
} else if (srcBlendNorm != 0.0){
dst[0] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[0], src[0], srcBlendNorm);
dst[1] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[1], src[1], srcBlendNorm);
dst[2] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[2], src[2], srcBlendNorm);
}
} else {
const QBitArray &channelFlags = oparams.channelFlags;
if (srcBlendNorm == 1.0) {
if(channelFlags.at(0)) dst[0] = src[0];
if(channelFlags.at(1)) dst[1] = src[1];
if(channelFlags.at(2)) dst[2] = src[2];
} else if (srcBlendNorm != 0.0) {
if(channelFlags.at(0)) dst[0] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[0], src[0], srcBlendNorm);
if(channelFlags.at(1)) dst[1] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[1], src[1], srcBlendNorm);
if(channelFlags.at(2)) dst[2] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[2], src[2], srcBlendNorm);
}
}
if (!alphaLocked) {
dst[alpha_pos] = KoStreamedMath<_impl>::round_float_to_uint(dstAlpha);
}
}
}
};
/**
* An optimized version of a composite op for the use in 4 byte
* colorspaces with alpha channel placed at the last byte of
* the pixel: C1_C2_C3_A.
*/
template<Vc::Implementation _impl>
class KoOptimizedCompositeOpOver32 : public KoCompositeOp
{
public:
KoOptimizedCompositeOpOver32(const KoColorSpace* cs)
: KoCompositeOp(cs, COMPOSITE_OVER, i18n("Normal"), KoCompositeOp::categoryMix()) {}
using KoCompositeOp::composite;
virtual void composite(const KoCompositeOp::ParameterInfo& params) const
{
if(params.maskRowStart) {
composite<true>(params);
} else {
composite<false>(params);
}
}
template <bool haveMask>
inline void composite(const KoCompositeOp::ParameterInfo& params) const {
if (params.channelFlags.isEmpty() ||
params.channelFlags == QBitArray(4, true)) {
KoStreamedMath<_impl>::template genericComposite32<haveMask, false, OverCompositor32<quint8, quint32, false, true> >(params);
} else {
const bool allChannelsFlag =
params.channelFlags.at(0) &&
params.channelFlags.at(1) &&
params.channelFlags.at(2);
const bool alphaLocked =
!params.channelFlags.at(3);
if (allChannelsFlag && alphaLocked) {
KoStreamedMath<_impl>::template genericComposite32_novector<haveMask, false, OverCompositor32<quint8, quint32, true, true> >(params);
} else if (!allChannelsFlag && !alphaLocked) {
KoStreamedMath<_impl>::template genericComposite32_novector<haveMask, false, OverCompositor32<quint8, quint32, false, false> >(params);
} else /*if (!allChannelsFlag && alphaLocked) */{
KoStreamedMath<_impl>::template genericComposite32_novector<haveMask, false, OverCompositor32<quint8, quint32, true, false> >(params);
}
}
}
};
#endif // KOOPTIMIZEDCOMPOSITEOPOVER32_H_
diff --git a/libs/pigment/compositeops/KoStreamedMath.h b/libs/pigment/compositeops/KoStreamedMath.h
index c1c8d427c7..21de9686f7 100644
--- a/libs/pigment/compositeops/KoStreamedMath.h
+++ b/libs/pigment/compositeops/KoStreamedMath.h
@@ -1,427 +1,427 @@
/*
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __KOSTREAMED_MATH_H
#define __KOSTREAMED_MATH_H
#if defined _MSC_VER
// Lets shut up the "possible loss of data" and "forcing value to bool 'true' or 'false'
#pragma warning ( push )
#pragma warning ( disable : 4244 )
#pragma warning ( disable : 4800 )
#endif
#include <Vc/Vc>
#include <Vc/IO>
#if defined _MSC_VER
#pragma warning ( pop )
#endif
#include <stdint.h>
#include <KoAlwaysInline.h>
#include <iostream>
#define BLOCKDEBUG 0
#if !defined _MSC_VER
#pragma GCC diagnostic ignored "-Wcast-align"
#endif
template<Vc::Implementation _impl>
struct KoStreamedMath {
using int_v = Vc::SimdArray<int, Vc::float_v::size()>;
using uint_v = Vc::SimdArray<unsigned int, Vc::float_v::size()>;
/**
* Composes src into dst without using vector instructions
*/
template<bool useMask, bool useFlow, class Compositor, int pixelSize>
static void genericComposite_novector(const KoCompositeOp::ParameterInfo& params)
{
using namespace Arithmetic;
const qint32 linearInc = pixelSize;
qint32 srcLinearInc = params.srcRowStride ? pixelSize : 0;
quint8* dstRowStart = params.dstRowStart;
const quint8* maskRowStart = params.maskRowStart;
const quint8* srcRowStart = params.srcRowStart;
- typename Compositor::OptionalParams optionalParams(params);
+ typename Compositor::ParamsWrapper paramsWrapper(params);
for(quint32 r=params.rows; r>0; --r) {
const quint8 *mask = maskRowStart;
const quint8 *src = srcRowStart;
quint8 *dst = dstRowStart;
int blockRest = params.cols;
for(int i = 0; i < blockRest; i++) {
- Compositor::template compositeOnePixelScalar<useMask, _impl>(src, dst, mask, params.opacity, optionalParams);
+ Compositor::template compositeOnePixelScalar<useMask, _impl>(src, dst, mask, params.opacity, paramsWrapper);
src += srcLinearInc;
dst += linearInc;
if (useMask) {
mask++;
}
}
srcRowStart += params.srcRowStride;
dstRowStart += params.dstRowStride;
if (useMask) {
maskRowStart += params.maskRowStride;
}
}
}
template<bool useMask, bool useFlow, class Compositor>
static void genericComposite32_novector(const KoCompositeOp::ParameterInfo& params)
{
genericComposite_novector<useMask, useFlow, Compositor, 4>(params);
}
template<bool useMask, bool useFlow, class Compositor>
static void genericComposite128_novector(const KoCompositeOp::ParameterInfo& params)
{
genericComposite_novector<useMask, useFlow, Compositor, 16>(params);
}
static inline quint8 round_float_to_uint(float value) {
return quint8(value + float(0.5));
}
static inline quint8 lerp_mixed_u8_float(quint8 a, quint8 b, float alpha) {
return round_float_to_uint(qint16(b - a) * alpha + a);
}
/**
* Get a vector containing first Vc::float_v::size() values of mask.
* Each source mask element is considered to be a 8-bit integer
*/
static inline Vc::float_v fetch_mask_8(const quint8 *data) {
uint_v data_i(data);
return Vc::simd_cast<Vc::float_v>(int_v(data_i));
}
/**
* Get an alpha values from Vc::float_v::size() pixels 32-bit each
* (4 channels, 8 bit per channel). The alpha value is considered
* to be stored in the most significant byte of the pixel
*
* \p aligned controls whether the \p data is fetched using aligned
* instruction or not.
* 1) Fetching aligned data with unaligned instruction
* degrades performance.
* 2) Fetching unaligned data with aligned instruction
- * causes #GP (General Protection Exception)
+ * causes \#GP (General Protection Exception)
*/
template <bool aligned>
static inline Vc::float_v fetch_alpha_32(const quint8 *data) {
uint_v data_i;
if (aligned) {
data_i.load((const quint32*)data, Vc::Aligned);
} else {
data_i.load((const quint32*)data, Vc::Unaligned);
}
return Vc::simd_cast<Vc::float_v>(int_v(data_i >> 24));
}
/**
* Get color values from Vc::float_v::size() pixels 32-bit each
* (4 channels, 8 bit per channel). The color data is considered
* to be stored in the 3 least significant bytes of the pixel.
*
* \p aligned controls whether the \p data is fetched using aligned
* instruction or not.
* 1) Fetching aligned data with unaligned instruction
* degrades performance.
* 2) Fetching unaligned data with aligned instruction
- * causes #GP (General Protection Exception)
+ * causes \#GP (General Protection Exception)
*/
template <bool aligned>
static inline void fetch_colors_32(const quint8 *data,
Vc::float_v &c1,
Vc::float_v &c2,
Vc::float_v &c3) {
int_v data_i;
if (aligned) {
data_i.load((const quint32*)data, Vc::Aligned);
} else {
data_i.load((const quint32*)data, Vc::Unaligned);
}
const quint32 lowByteMask = 0xFF;
uint_v mask(lowByteMask);
c1 = Vc::simd_cast<Vc::float_v>(int_v((data_i >> 16) & mask));
c2 = Vc::simd_cast<Vc::float_v>(int_v((data_i >> 8) & mask));
c3 = Vc::simd_cast<Vc::float_v>(int_v( data_i & mask));
}
/**
* Pack color and alpha values to Vc::float_v::size() pixels 32-bit each
* (4 channels, 8 bit per channel). The color data is considered
* to be stored in the 3 least significant bytes of the pixel, alpha -
* in the most significant byte
*
* NOTE: \p data must be aligned pointer!
*/
static inline void write_channels_32(quint8 *data,
Vc::float_v::AsArg alpha,
Vc::float_v::AsArg c1,
Vc::float_v::AsArg c2,
Vc::float_v::AsArg c3) {
/**
* FIXME: make conversion float->int
* use methematical rounding
*/
const quint32 lowByteMask = 0xFF;
// FIXME: Use single-instruction rounding + conversion
// The achieve that we need to implement Vc::iRound()
uint_v mask(lowByteMask);
uint_v v1 = uint_v(int_v(Vc::round(alpha))) << 24;
uint_v v2 = (uint_v(int_v(Vc::round(c1))) & mask) << 16;
uint_v v3 = (uint_v(int_v(Vc::round(c2))) & mask) << 8;
uint_v v4 = uint_v(int_v(Vc::round(c3))) & mask;
v1 = v1 | v2;
v3 = v3 | v4;
(v1 | v3).store((quint32*)data, Vc::Aligned);
}
/**
* Composes src pixels into dst pixles. Is optimized for 32-bit-per-pixel
* colorspaces. Uses \p Compositor strategy parameter for doing actual
* math of the composition
*/
template<bool useMask, bool useFlow, class Compositor, int pixelSize>
static void genericComposite(const KoCompositeOp::ParameterInfo& params)
{
using namespace Arithmetic;
const int vectorSize = Vc::float_v::size();
const qint32 vectorInc = pixelSize * vectorSize;
const qint32 linearInc = pixelSize;
qint32 srcVectorInc = vectorInc;
qint32 srcLinearInc = pixelSize;
quint8* dstRowStart = params.dstRowStart;
const quint8* maskRowStart = params.maskRowStart;
const quint8* srcRowStart = params.srcRowStart;
- typename Compositor::OptionalParams optionalParams(params);
+ typename Compositor::ParamsWrapper paramsWrapper(params);
if (!params.srcRowStride) {
if (pixelSize == 4) {
quint32 *buf = Vc::malloc<quint32, Vc::AlignOnVector>(vectorSize);
*((uint_v*)buf) = uint_v(*((const quint32*)params.srcRowStart));
srcRowStart = reinterpret_cast<quint8*>(buf);
srcLinearInc = 0;
srcVectorInc = 0;
} else {
quint8 *buf = Vc::malloc<quint8, Vc::AlignOnVector>(vectorInc);
quint8 *ptr = buf;
for (int i = 0; i < vectorSize; i++) {
memcpy(ptr, params.srcRowStart, pixelSize);
ptr += pixelSize;
}
srcRowStart = buf;
srcLinearInc = 0;
srcVectorInc = 0;
}
}
#if BLOCKDEBUG
int totalBlockAlign = 0;
int totalBlockAlignedVector = 0;
int totalBlockUnalignedVector = 0;
int totalBlockRest = 0;
#endif
for(quint32 r=params.rows; r>0; --r) {
// Hint: Mask is allowed to be unaligned
const quint8 *mask = maskRowStart;
const quint8 *src = srcRowStart;
quint8 *dst = dstRowStart;
const int pixelsAlignmentMask = vectorSize * sizeof(float) - 1;
uintptr_t srcPtrValue = reinterpret_cast<uintptr_t>(src);
uintptr_t dstPtrValue = reinterpret_cast<uintptr_t>(dst);
uintptr_t srcAlignment = srcPtrValue & pixelsAlignmentMask;
uintptr_t dstAlignment = dstPtrValue & pixelsAlignmentMask;
// Uncomment if facing problems with alignment:
// Q_ASSERT_X(!(dstAlignment & 3), "Compositioning",
// "Pixel data must be aligned on pixels borders!");
int blockAlign = params.cols;
int blockAlignedVector = 0;
int blockUnalignedVector = 0;
int blockRest = 0;
int *vectorBlock =
srcAlignment == dstAlignment || !srcVectorInc ?
&blockAlignedVector : &blockUnalignedVector;
if (!dstAlignment) {
blockAlign = 0;
*vectorBlock = params.cols / vectorSize;
blockRest = params.cols % vectorSize;
} else if (params.cols > 2 * vectorSize) {
blockAlign = (vectorInc - dstAlignment) / pixelSize;
const int restCols = params.cols - blockAlign;
if (restCols > 0) {
*vectorBlock = restCols / vectorSize;
blockRest = restCols % vectorSize;
}
else {
blockAlign = params.cols;
*vectorBlock = 0;
blockRest = 0;
}
}
#if BLOCKDEBUG
totalBlockAlign += blockAlign;
totalBlockAlignedVector += blockAlignedVector;
totalBlockUnalignedVector += blockUnalignedVector;
totalBlockRest += blockRest;
#endif
for(int i = 0; i < blockAlign; i++) {
- Compositor::template compositeOnePixelScalar<useMask, _impl>(src, dst, mask, params.opacity, optionalParams);
+ Compositor::template compositeOnePixelScalar<useMask, _impl>(src, dst, mask, params.opacity, paramsWrapper);
src += srcLinearInc;
dst += linearInc;
if(useMask) {
mask++;
}
}
for (int i = 0; i < blockAlignedVector; i++) {
- Compositor::template compositeVector<useMask, true, _impl>(src, dst, mask, params.opacity, optionalParams);
+ Compositor::template compositeVector<useMask, true, _impl>(src, dst, mask, params.opacity, paramsWrapper);
src += srcVectorInc;
dst += vectorInc;
if (useMask) {
mask += vectorSize;
}
}
for (int i = 0; i < blockUnalignedVector; i++) {
- Compositor::template compositeVector<useMask, false, _impl>(src, dst, mask, params.opacity, optionalParams);
+ Compositor::template compositeVector<useMask, false, _impl>(src, dst, mask, params.opacity, paramsWrapper);
src += srcVectorInc;
dst += vectorInc;
if (useMask) {
mask += vectorSize;
}
}
for(int i = 0; i < blockRest; i++) {
- Compositor::template compositeOnePixelScalar<useMask, _impl>(src, dst, mask, params.opacity, optionalParams);
+ Compositor::template compositeOnePixelScalar<useMask, _impl>(src, dst, mask, params.opacity, paramsWrapper);
src += srcLinearInc;
dst += linearInc;
if (useMask) {
mask++;
}
}
srcRowStart += params.srcRowStride;
dstRowStart += params.dstRowStride;
if (useMask) {
maskRowStart += params.maskRowStride;
}
}
#if BLOCKDEBUG
dbgPigment << "I" << "rows:" << params.rows
<< "\tpad(S):" << totalBlockAlign
<< "\tbav(V):" << totalBlockAlignedVector
<< "\tbuv(V):" << totalBlockUnalignedVector
<< "\tres(S)" << totalBlockRest; // << srcAlignment << dstAlignment;
#endif
if (!params.srcRowStride) {
Vc::free<float>(reinterpret_cast<float*>(const_cast<quint8*>(srcRowStart)));
}
}
template<bool useMask, bool useFlow, class Compositor>
static void genericComposite32(const KoCompositeOp::ParameterInfo& params)
{
genericComposite<useMask, useFlow, Compositor, 4>(params);
}
template<bool useMask, bool useFlow, class Compositor>
static void genericComposite128(const KoCompositeOp::ParameterInfo& params)
{
genericComposite<useMask, useFlow, Compositor, 16>(params);
}
};
namespace KoStreamedMathFunctions {
template<int pixelSize>
ALWAYS_INLINE void clearPixel(quint8* dst);
template<>
ALWAYS_INLINE void clearPixel<4>(quint8* dst)
{
quint32 *d = reinterpret_cast<quint32*>(dst);
*d = 0;
}
template<>
ALWAYS_INLINE void clearPixel<16>(quint8* dst)
{
quint64 *d = reinterpret_cast<quint64*>(dst);
d[0] = 0;
d[1] = 0;
}
template<int pixelSize>
ALWAYS_INLINE void copyPixel(const quint8 *src, quint8* dst);
template<>
ALWAYS_INLINE void copyPixel<4>(const quint8 *src, quint8* dst)
{
const quint32 *s = reinterpret_cast<const quint32*>(src);
quint32 *d = reinterpret_cast<quint32*>(dst);
*d = *s;
}
template<>
ALWAYS_INLINE void copyPixel<16>(const quint8 *src, quint8* dst)
{
const quint64 *s = reinterpret_cast<const quint64*>(src);
quint64 *d = reinterpret_cast<quint64*>(dst);
d[0] = s[0];
d[1] = s[1];
}
}
#endif /* __KOSTREAMED_MATH_H */
diff --git a/libs/pigment/resources/KisSwatchGroup.h b/libs/pigment/resources/KisSwatchGroup.h
index de32624bdd..e98515edea 100644
--- a/libs/pigment/resources/KisSwatchGroup.h
+++ b/libs/pigment/resources/KisSwatchGroup.h
@@ -1,127 +1,127 @@
/* This file is part of the KDE project
Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
Copyright (c) 2016 L. E. Segovia <leo.segovia@siggraph.org>
Copyright (c) 2018 Michael Zhou <simerixh@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef KISSWATCHGROUP_H
#define KISSWATCHGROUP_H
#include "KisSwatch.h"
#include "kritapigment_export.h"
#include <QVector>
#include <QList>
#include <QMap>
#include <QScopedPointer>
/**
* @brief The KisSwatchGroup class stores a matrix of color swatches
* swatches can accessed using (x, y) coordinates.
* x is the column number from left to right and y is the row number from top
* to bottom.
* Both x and y start at 0
* there could be empty entries, so the checkEntry(int, int) method must used
* whenever you want to get an entry from the matrix
*/
class KRITAPIGMENT_EXPORT KisSwatchGroup
{
public /* struct */:
struct SwatchInfo {
QString group;
KisSwatch swatch;
int row;
int column;
};
public:
KisSwatchGroup();
~KisSwatchGroup();
KisSwatchGroup(const KisSwatchGroup &rhs);
KisSwatchGroup &operator =(const KisSwatchGroup &rhs);
public /* methods */:
void setName(const QString &name);
QString name() const;
void setColumnCount(int columnCount);
int columnCount() const;
void setRowCount(int newRowCount);
int rowCount() const;
int colorCount() const;
QList<SwatchInfo> infoList() const;
/**
* @brief checkEntry
- * checks if position x and y has a valid entry
- * both x and y start from 0
- * @param x
- * @param y
- * @return true if there is a valid entry at position (x, y)
+ * checks if position @p column and @p row has a valid entry
+ * both @p column and @p row start from 0
+ * @param column
+ * @param row
+ * @return true if there is a valid entry at position (column, row)
*/
bool checkEntry(int column, int row) const;
/**
* @brief setEntry
- * sets the entry at position (x, y) to be e
+ * sets the entry at position (@p column, @p row) to be @p e
* @param e
- * @param x
- * @param y
+ * @param column
+ * @param row
*/
void setEntry(const KisSwatch &e, int column, int row);
/**
* @brief getEntry
- * used to get the swatch entry at position (x, y)
+ * used to get the swatch entry at position (@p column, @p row)
* there is an assertion to make sure that this position isn't empty,
* so checkEntry(int, int) must be used before this method to ensure
* a valid entry can be found
- * @param x
- * @param y
- * @return the swatch entry at position (x, y)
+ * @param column
+ * @param row
+ * @return the swatch entry at position (column, row)
*/
KisSwatch getEntry(int column, int row) const;
/**
* @brief removeEntry
- * removes the entry at position (x, y)
- * @param x
- * @param y
- * @return true if these is an entry at (x, y)
+ * removes the entry at position (@p column, @p row)
+ * @param column
+ * @param row
+ * @return true if these is an entry at (column, row)
*/
bool removeEntry(int column, int row);
/**
* @brief addEntry
* adds the entry e to the right of the rightmost entry in the last row
* if the rightmost entry in the last row is in the right most column,
* add e to the leftmost column of a new row
*
* when column is set to 0, resize number of columns to default
* @param e
*/
void addEntry(const KisSwatch &e);
void clear();
private /* member variables */:
struct Private;
QScopedPointer<Private> d;
};
#endif // KISSWATCHGROUP_H
diff --git a/libs/pigment/resources/KoColorSet.h b/libs/pigment/resources/KoColorSet.h
index 60ca58b30a..056c2d34be 100644
--- a/libs/pigment/resources/KoColorSet.h
+++ b/libs/pigment/resources/KoColorSet.h
@@ -1,216 +1,219 @@
/* This file is part of the KDE project
Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
Copyright (c) 2016 L. E. Segovia <leo.segovia@siggraph.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef KOCOLORSET
#define KOCOLORSET
#include <QObject>
#include <QColor>
#include <QVector>
#include <QScopedPointer>
#include <resources/KoResource.h>
#include "KoColor.h"
#include "KisSwatch.h"
#include "KisSwatchGroup.h"
/**
* Also called palette.
* Open Gimp, Photoshop or RIFF palette files. This is a straight port
* from the Gimp.
*/
class KRITAPIGMENT_EXPORT KoColorSet : public QObject, public KoResource
{
Q_OBJECT
public:
static const QString GLOBAL_GROUP_NAME;
static const QString KPL_VERSION_ATTR;
static const QString KPL_GROUP_ROW_COUNT_ATTR;
static const QString KPL_PALETTE_COLUMN_COUNT_ATTR;
static const QString KPL_PALETTE_NAME_ATTR;
static const QString KPL_PALETTE_COMMENT_ATTR;
static const QString KPL_PALETTE_FILENAME_ATTR;
static const QString KPL_PALETTE_READONLY_ATTR;
static const QString KPL_COLOR_MODEL_ID_ATTR;
static const QString KPL_COLOR_DEPTH_ID_ATTR;
static const QString KPL_GROUP_NAME_ATTR;
static const QString KPL_SWATCH_ROW_ATTR;
static const QString KPL_SWATCH_COL_ATTR;
static const QString KPL_SWATCH_NAME_ATTR;
static const QString KPL_SWATCH_SPOT_ATTR;
static const QString KPL_SWATCH_ID_ATTR;
static const QString KPL_SWATCH_BITDEPTH_ATTR;
static const QString KPL_PALETTE_PROFILE_TAG;
static const QString KPL_SWATCH_POS_TAG;
static const QString KPL_SWATCH_TAG;
static const QString KPL_GROUP_TAG;
static const QString KPL_PALETTE_TAG;
public:
enum PaletteType {
UNKNOWN = 0,
GPL, // GIMP
RIFF_PAL, // RIFF
ACT, // Photoshop binary
PSP_PAL, // PaintShop Pro
ACO, // Photoshop Swatches
XML, // XML palette (Scribus)
KPL, // KoColor-based XML palette
SBZ // SwatchBooker
};
/**
* Load a color set from a file. This can be a Gimp
* palette, a RIFF palette, a Photoshop palette,
* a Krita palette,
* a Scribus palette or a SwatchBooker palette.
*/
explicit KoColorSet(const QString &filename = QString());
// Explicit copy constructor (KoResource copy constructor is private)
KoColorSet(const KoColorSet& rhs);
public /* overridden methods */: // KoResource
~KoColorSet() override;
bool load() override;
bool loadFromDevice(QIODevice *dev) override;
bool save() override;
bool saveToDevice(QIODevice* dev) const override;
QString defaultFileExtension() const override;
public /* methods */:
void setColumnCount(int columns);
int columnCount() const;
void setComment(QString comment);
QString comment();
int rowCount() const;
quint32 colorCount() const;
PaletteType paletteType() const;
void setPaletteType(PaletteType paletteType);
/**
* @brief isGlobal
* A global color set is a set stored in the config directory
* Such a color set would be opened every time Krita is launched.
*
* A non-global color set, on contrary, would be stored in a kra file,
* and would only be opened when that file is opened by Krita.
- * @return
+ * @return @c true if the set is global
*/
bool isGlobal() const;
void setIsGlobal(bool);
bool isEditable() const;
void setIsEditable(bool isEditable);
QByteArray toByteArray() const;
bool fromByteArray(QByteArray &data);
/**
- * @brief add Add a color to the palette.
+ * @brief Add a color to the palette.
+ * @param c the swatch
* @param groupName color to add the group to. If empty, it will be added to the unsorted.
*/
void add(const KisSwatch &, const QString &groupName = GLOBAL_GROUP_NAME);
void setEntry(const KisSwatch &e, int x, int y, const QString &groupName = GLOBAL_GROUP_NAME);
/**
* @brief getColorGlobal
- * A function for getting a color based on a global index. Useful for itterating through all color entries.
- * @param globalIndex the global index over the whole palette.
+ * A function for getting a color based on a global index. Useful for iterating through all color entries.
+ * @param x the global x index over the whole palette.
+ * @param y the global y index over the whole palette.
* @return the entry.
*/
KisSwatch getColorGlobal(quint32 x, quint32 y) const;
/**
* @brief getColorGroup
* A function for getting the color from a specific group.
- * @param groupName the name of the group, will give unosrted when not defined.
- * @param index the index within the group.
+ * @param x the x index over the group.
+ * @param y the y index over the group.
+ * @param groupName the name of the group, will give unsorted when not defined.
* @return the entry
*/
KisSwatch getColorGroup(quint32 x, quint32 y, QString groupName);
/**
* @brief getGroupNames
* @return returns a list of group names, excluding the unsorted group.
*/
QStringList getGroupNames();
/**
* @brief getGroup
* @param name
* @return the group with the name given; global group if no parameter is given
* null pointer if not found.
*/
KisSwatchGroup *getGroup(const QString &name);
KisSwatchGroup *getGlobalGroup();
bool changeGroupName(const QString &oldGroupName, const QString &newGroupName);
/**
* @brief addGroup
* Adds a new group.
* @param groupName the name of the new group. When not specified, this will fail.
* @return whether thegroup was made.
*/
bool addGroup(const QString &groupName);
/**
* @brief moveGroup
* Move a group in the internal stringlist.
* @param groupName the groupname to move.
* @param groupNameInsertBefore the groupname to insert before. Empty means it will be added to the end.
* @return
*/
bool moveGroup(const QString &groupName, const QString &groupNameInsertBefore = GLOBAL_GROUP_NAME);
/**
* @brief removeGroup
* Remove a group from the KoColorSet
* @param groupName the name of the group you want to remove.
* @param keepColors Whether you wish to keep the colorsetentries. These will be added to the unsorted.
* @return whether it could find the group to remove.
*/
bool removeGroup(const QString &groupName, bool keepColors = true);
void clear();
/**
* @brief getIndexClosestColor
* function that matches the color to all colors in the colorset, and returns the index
* of the closest match.
- * @param color the color you wish to compare.
+ * @param compare the color you wish to compare.
* @param useGivenColorSpace whether to use the color space of the color given
* when the two colors' colorspaces don't match. Else it'll use the entry's colorspace.
* @return returns the int of the closest match.
*/
KisSwatchGroup::SwatchInfo getClosestColorInfo(KoColor compare, bool useGivenColorSpace = true);
private:
class Private;
const QScopedPointer<Private> d;
};
#endif // KOCOLORSET
diff --git a/libs/pigment/resources/KoStopGradient.cpp b/libs/pigment/resources/KoStopGradient.cpp
index 3d185dff6f..5f109c9dc4 100644
--- a/libs/pigment/resources/KoStopGradient.cpp
+++ b/libs/pigment/resources/KoStopGradient.cpp
@@ -1,730 +1,604 @@
/*
Copyright (C) 2005 Tim Beaulen <tbscope@gmail.org>
Copyright (C) 2007 Jan Hambrecht <jaham@gmx.net>
Copyright (c) 2007 Sven Langkamp <sven.langkamp@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <resources/KoStopGradient.h>
#include <cfloat>
#include <QColor>
#include <QFile>
#include <QDomDocument>
#include <QDomElement>
#include <QBuffer>
#include <klocalizedstring.h>
#include <DebugPigment.h>
#include "KoColorSpaceRegistry.h"
#include "KoMixColorsOp.h"
#include <math.h>
#include <KoColorModelStandardIds.h>
KoStopGradient::KoStopGradient(const QString& filename)
: KoAbstractGradient(filename)
{
}
KoStopGradient::~KoStopGradient()
{
}
bool KoStopGradient::operator==(const KoStopGradient &rhs) const
{
return
*colorSpace() == *rhs.colorSpace() &&
spread() == rhs.spread() &&
type() == rhs.type() &&
m_start == rhs.m_start &&
m_stop == rhs.m_stop &&
m_focalPoint == rhs.m_focalPoint &&
m_stops == rhs.m_stops;
}
KoAbstractGradient* KoStopGradient::clone() const
{
return new KoStopGradient(*this);
}
bool KoStopGradient::load()
{
QFile f(filename());
if (!f.open(QIODevice::ReadOnly)) {
warnPigment << "Can't open file " << filename();
return false;
}
bool res = loadFromDevice(&f);
f.close();
return res;
}
bool KoStopGradient::loadFromDevice(QIODevice *dev)
{
QString strExt;
const int result = filename().lastIndexOf('.');
if (result >= 0) {
strExt = filename().mid(result).toLower();
}
QByteArray ba = dev->readAll();
QBuffer buf(&ba);
- if (strExt == ".kgr") {
- loadKarbonGradient(&buf);
- }
- else if (strExt == ".svg") {
- loadSvgGradient(&buf);
- }
+ loadSvgGradient(&buf);
if (m_stops.count() >= 2) {
setValid(true);
}
updatePreview();
return true;
}
bool KoStopGradient::save()
{
QFile fileOut(filename());
if (! fileOut.open(QIODevice::WriteOnly))
return false;
bool retval = saveToDevice(&fileOut);
fileOut.close();
return retval;
}
QGradient* KoStopGradient::toQGradient() const
{
QGradient* gradient;
switch (type()) {
case QGradient::LinearGradient: {
gradient = new QLinearGradient(m_start, m_stop);
break;
}
case QGradient::RadialGradient: {
QPointF diff = m_stop - m_start;
qreal radius = sqrt(diff.x() * diff.x() + diff.y() * diff.y());
gradient = new QRadialGradient(m_start, radius, m_focalPoint);
break;
}
case QGradient::ConicalGradient: {
qreal angle = atan2(m_start.y(), m_start.x()) * 180.0 / M_PI;
if (angle < 0.0)
angle += 360.0;
gradient = new QConicalGradient(m_start, angle);
break;
}
default:
return 0;
}
QColor color;
for (QList<KoGradientStop>::const_iterator i = m_stops.begin(); i != m_stops.end(); ++i) {
i->second.toQColor(&color);
gradient->setColorAt(i->first , color);
}
gradient->setCoordinateMode(QGradient::ObjectBoundingMode);
gradient->setSpread(this->spread());
return gradient;
}
void KoStopGradient::colorAt(KoColor& dst, qreal t) const
{
KoColor buffer;
if (! m_stops.count())
return;
if (t <= m_stops.first().first || m_stops.count() == 1) {
// we have only one stop or t is before the first stop
// -> use the color of the first stop
dst.fromKoColor(m_stops.first().second);
} else if (t >= m_stops.last().first) {
// t is after the last stop
// -> use the color of the last stop
dst.fromKoColor(m_stops.last().second);
} else {
// we have at least two color stops
// -> find the two stops which frame our t
QList<KoGradientStop>::const_iterator stop = m_stops.begin();
QList<KoGradientStop>::const_iterator lastStop = m_stops.end();
// we already checked the first stop, so we start at the second
for (++stop; stop != lastStop; ++stop) {
// we break at the stop which is just after our t
if (stop->first > t)
break;
}
//if ( *buffer.colorSpace() != *colorSpace()) {
// buffer = KoColor(colorSpace());
//}
//hack to get a color space with the bitdepth of the gradients(8bit), but with the colour profile of the image//
const KoColorSpace* mixSpace = KoColorSpaceRegistry::instance()->rgb8(dst.colorSpace()->profile());
const KoGradientStop& leftStop = *(stop - 1);
const KoGradientStop& rightStop = *(stop);
KoColor startDummy, endDummy;
if (mixSpace){
startDummy = KoColor(leftStop.second, mixSpace);
endDummy = KoColor(rightStop.second, mixSpace);
} else {
startDummy = leftStop.second;
endDummy = rightStop.second;
}
const quint8 *colors[2];
colors[0] = startDummy.data();
colors[1] = endDummy.data();
qreal localT;
qreal stopDistance = rightStop.first - leftStop.first;
if (stopDistance < DBL_EPSILON) {
localT = 0.5;
} else {
localT = (t - leftStop.first) / stopDistance;
}
qint16 colorWeights[2];
colorWeights[0] = static_cast<quint8>((1.0 - localT) * 255 + 0.5);
colorWeights[1] = 255 - colorWeights[0];
//check if our mixspace exists, it doesn't at startup.
if (mixSpace){
if (*buffer.colorSpace() != *mixSpace) {
buffer = KoColor(mixSpace);
}
mixSpace->mixColorsOp()->mixColors(colors, colorWeights, 2, buffer.data());
}
else {
buffer = KoColor(colorSpace());
colorSpace()->mixColorsOp()->mixColors(colors, colorWeights, 2, buffer.data());
}
dst.fromKoColor(buffer);
}
}
KoStopGradient * KoStopGradient::fromQGradient(const QGradient * gradient)
{
if (! gradient)
return 0;
KoStopGradient * newGradient = new KoStopGradient(QString());
newGradient->setType(gradient->type());
newGradient->setSpread(gradient->spread());
switch (gradient->type()) {
case QGradient::LinearGradient: {
const QLinearGradient * g = static_cast<const QLinearGradient*>(gradient);
newGradient->m_start = g->start();
newGradient->m_stop = g->finalStop();
newGradient->m_focalPoint = g->start();
break;
}
case QGradient::RadialGradient: {
const QRadialGradient * g = static_cast<const QRadialGradient*>(gradient);
newGradient->m_start = g->center();
newGradient->m_stop = g->center() + QPointF(g->radius(), 0);
newGradient->m_focalPoint = g->focalPoint();
break;
}
case QGradient::ConicalGradient: {
const QConicalGradient * g = static_cast<const QConicalGradient*>(gradient);
qreal radian = g->angle() * M_PI / 180.0;
newGradient->m_start = g->center();
newGradient->m_stop = QPointF(100.0 * cos(radian), 100.0 * sin(radian));
newGradient->m_focalPoint = g->center();
break;
}
default:
delete newGradient;
return 0;
}
Q_FOREACH (const QGradientStop & stop, gradient->stops()) {
KoColor color(newGradient->colorSpace());
color.fromQColor(stop.second);
newGradient->m_stops.append(KoGradientStop(stop.first, color));
}
newGradient->setValid(true);
return newGradient;
}
void KoStopGradient::setStops(QList< KoGradientStop > stops)
{
m_stops.clear();
KoColor color;
Q_FOREACH (const KoGradientStop & stop, stops) {
color = stop.second;
color.convertTo(colorSpace());
m_stops.append(KoGradientStop(stop.first, color));
}
updatePreview();
}
QList<KoGradientStop> KoStopGradient::stops() const
{
return m_stops;
}
-void KoStopGradient::loadKarbonGradient(QIODevice *file)
-{
- QDomDocument doc;
-
- if (!(doc.setContent(file))) {
- file->close();
- setValid(false);
- return;
- }
-
- QDomElement e;
- QDomNode n = doc.documentElement().firstChild();
-
- if (!n.isNull()) {
- e = n.toElement();
- if (!e.isNull() && e.tagName() == "GRADIENT") {
- parseKarbonGradient(e);
- }
- }
-}
-
void KoStopGradient::loadSvgGradient(QIODevice *file)
{
QDomDocument doc;
if (!(doc.setContent(file)))
file->close();
else {
for (QDomNode n = doc.documentElement().firstChild(); !n.isNull(); n = n.nextSibling()) {
QDomElement e = n.toElement();
if (e.isNull()) continue;
if (e.tagName() == "linearGradient" || e.tagName() == "radialGradient") {
parseSvgGradient(e);
return;
}
// Inkscape gradients are in another defs
if (e.tagName() == "defs") {
for (QDomNode defnode = e.firstChild(); !defnode.isNull(); defnode = defnode.nextSibling()) {
QDomElement defelement = defnode.toElement();
if (defelement.isNull()) continue;
if (defelement.tagName() == "linearGradient" || defelement.tagName() == "radialGradient") {
parseSvgGradient(defelement);
return;
}
}
}
}
}
}
-void KoStopGradient::parseKarbonGradient(const QDomElement& element)
-{
- m_start = QPointF(element.attribute("originX", "0.0").toDouble(), element.attribute("originY", "0.0").toDouble());
- m_focalPoint = QPointF(element.attribute("focalX", "0.0").toDouble(), element.attribute("focalY", "0.0").toDouble());
- m_stop = QPointF(element.attribute("vectorX", "0.0").toDouble(), element.attribute("vectorY", "0.0").toDouble());
-
- setType((QGradient::Type)element.attribute("type", 0).toInt());
- setSpread((QGradient::Spread)element.attribute("repeatMethod", 0).toInt());
-
- m_stops.clear();
-
- qreal color1, color2, color3, color4, opacity;
- KoColor color;
- // load stops
- QDomNodeList list = element.childNodes();
- for (int i = 0; i < list.count(); ++i) {
-
- if (list.item(i).isElement()) {
- QDomElement colorstop = list.item(i).toElement();
-
- if (colorstop.tagName() == "COLORSTOP") {
- QDomElement e = colorstop.firstChild().toElement();
-
- opacity = e.attribute("opacity", "1.0").toFloat();
-
- QColor tmpColor;
- const KoColorSpace* stopColorSpace;
- switch (e.attribute("colorSpace").toUShort()) {
- case 1: // cmyk
- color1 = e.attribute("v1", "0.0").toFloat();
- color2 = e.attribute("v2", "0.0").toFloat();
- color3 = e.attribute("v3", "0.0").toFloat();
- color4 = e.attribute("v4", "0.0").toFloat();
-
- stopColorSpace = KoColorSpaceRegistry::instance()->colorSpace( CMYKAColorModelID.id(), Integer8BitsColorDepthID.id(), QString());
- if (stopColorSpace) {
- quint8 data[5];
- data[0] = static_cast<quint8>(color1 * 255 + 0.5);
- data[1] = static_cast<quint8>(color2 * 255 + 0.5);
- data[2] = static_cast<quint8>(color3 * 255 + 0.5);
- data[3] = static_cast<quint8>(color4 * 255 + 0.5);
- data[4] = static_cast<quint8>(opacity * OPACITY_OPAQUE_U8 + 0.5);
- color.setColor(data, stopColorSpace);
- } else {
- // cmyk colorspace not found fallback to rgb
- color.convertTo(KoColorSpaceRegistry::instance()->rgb8());
- tmpColor.setCmykF(color1, color2, color3, color4);
- tmpColor.setAlpha(static_cast<quint8>(opacity * OPACITY_OPAQUE_U8 + 0.5));
- color.fromQColor(tmpColor);
- }
- break;
- case 2: // hsv
- color1 = e.attribute("v1", "0.0").toFloat();
- color2 = e.attribute("v2", "0.0").toFloat();
- color3 = e.attribute("v3", "0.0").toFloat();
-
- color.convertTo(KoColorSpaceRegistry::instance()->rgb8());
- tmpColor.setHsvF(color1, color2, color3);
- tmpColor.setAlpha(static_cast<quint8>(opacity * OPACITY_OPAQUE_U8 + 0.5));
- color.fromQColor(tmpColor);
- break;
- case 3: // gray
- color1 = e.attribute("v1", "0.0").toFloat();
- stopColorSpace = KoColorSpaceRegistry::instance()->colorSpace( GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), QString());
- if (stopColorSpace) {
- quint8 data[2];
- data[0] = static_cast<quint8>(color1 * 255 + 0.5);
- data[1] = static_cast<quint8>(opacity * OPACITY_OPAQUE_U8 + 0.5);
- color.setColor(data, stopColorSpace);
- } else {
- // gray colorspace not found fallback to rgb
- color.convertTo(KoColorSpaceRegistry::instance()->rgb8());
- tmpColor.setRgbF(color1, color1, color1);
- tmpColor.setAlpha(static_cast<quint8>(opacity * OPACITY_OPAQUE_U8 + 0.5));
- color.fromQColor(tmpColor);
- }
- break;
- default: // rgb
- color1 = e.attribute("v1", "0.0").toFloat();
- color2 = e.attribute("v2", "0.0").toFloat();
- color3 = e.attribute("v3", "0.0").toFloat();
- stopColorSpace = KoColorSpaceRegistry::instance()->rgb8();
-
- quint8 data[4];
- data[2] = static_cast<quint8>(color1 * 255 + 0.5);
- data[1] = static_cast<quint8>(color2 * 255 + 0.5);
- data[0] = static_cast<quint8>(color3 * 255 + 0.5);
- data[3] = static_cast<quint8>(opacity * OPACITY_OPAQUE_U8 + 0.5);
-
- color.setColor(data, stopColorSpace);
- }
-
- qreal offset = colorstop.attribute("ramppoint", "0.0").toFloat();
-// midpoint = colorstop.attribute("midpoint", "0.5").toFloat();
-
- m_stops.append(KoGradientStop(offset, color));
- }
- }
- }
-}
void KoStopGradient::parseSvgGradient(const QDomElement& element)
{
m_stops.clear();
setSpread(QGradient::PadSpread);
/*QString href = e.attribute( "xlink:href" ).mid( 1 );
if( !href.isEmpty() )
{
}*/
setName(element.attribute("id", i18n("SVG Gradient")));
const KoColorSpace* rgbColorSpace = KoColorSpaceRegistry::instance()->rgb8();
bool bbox = element.attribute("gradientUnits") != "userSpaceOnUse";
if (element.tagName() == "linearGradient") {
if (bbox) {
QString s;
s = element.attribute("x1", "0%");
qreal xOrigin;
if (s.endsWith('%'))
xOrigin = s.remove('%').toDouble();
else
xOrigin = s.toDouble() * 100.0;
s = element.attribute("y1", "0%");
qreal yOrigin;
if (s.endsWith('%'))
yOrigin = s.remove('%').toDouble();
else
yOrigin = s.toDouble() * 100.0;
s = element.attribute("x2", "100%");
qreal xVector;
if (s.endsWith('%'))
xVector = s.remove('%').toDouble();
else
xVector = s.toDouble() * 100.0;
s = element.attribute("y2", "0%");
qreal yVector;
if (s.endsWith('%'))
yVector = s.remove('%').toDouble();
else
yVector = s.toDouble() * 100.0;
m_start = QPointF(xOrigin, yOrigin);
m_stop = QPointF(xVector, yVector);
} else {
m_start = QPointF(element.attribute("x1").toDouble(), element.attribute("y1").toDouble());
m_stop = QPointF(element.attribute("x2").toDouble(), element.attribute("y2").toDouble());
}
setType(QGradient::LinearGradient);
} else {
if (bbox) {
QString s;
s = element.attribute("cx", "50%");
qreal xOrigin;
if (s.endsWith('%'))
xOrigin = s.remove('%').toDouble();
else
xOrigin = s.toDouble() * 100.0;
s = element.attribute("cy", "50%");
qreal yOrigin;
if (s.endsWith('%'))
yOrigin = s.remove('%').toDouble();
else
yOrigin = s.toDouble() * 100.0;
s = element.attribute("cx", "50%");
qreal xVector;
if (s.endsWith('%'))
xVector = s.remove('%').toDouble();
else
xVector = s.toDouble() * 100.0;
s = element.attribute("r", "50%");
if (s.endsWith('%'))
xVector += s.remove('%').toDouble();
else
xVector += s.toDouble() * 100.0;
s = element.attribute("cy", "50%");
qreal yVector;
if (s.endsWith('%'))
yVector = s.remove('%').toDouble();
else
yVector = s.toDouble() * 100.0;
s = element.attribute("fx", "50%");
qreal xFocal;
if (s.endsWith('%'))
xFocal = s.remove('%').toDouble();
else
xFocal = s.toDouble() * 100.0;
s = element.attribute("fy", "50%");
qreal yFocal;
if (s.endsWith('%'))
yFocal = s.remove('%').toDouble();
else
yFocal = s.toDouble() * 100.0;
m_start = QPointF(xOrigin, yOrigin);
m_stop = QPointF(xVector, yVector);
m_focalPoint = QPointF(xFocal, yFocal);
} else {
m_start = QPointF(element.attribute("cx").toDouble(), element.attribute("cy").toDouble());
m_stop = QPointF(element.attribute("cx").toDouble() + element.attribute("r").toDouble(),
element.attribute("cy").toDouble());
m_focalPoint = QPointF(element.attribute("fx").toDouble(), element.attribute("fy").toDouble());
}
setType(QGradient::RadialGradient);
}
// handle spread method
QString spreadMethod = element.attribute("spreadMethod");
if (!spreadMethod.isEmpty()) {
if (spreadMethod == "reflect")
setSpread(QGradient::ReflectSpread);
else if (spreadMethod == "repeat")
setSpread(QGradient::RepeatSpread);
}
for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) {
QDomElement colorstop = n.toElement();
if (colorstop.tagName() == "stop") {
qreal opacity = 0.0;
QColor c;
float off;
QString temp = colorstop.attribute("offset");
if (temp.contains('%')) {
temp = temp.left(temp.length() - 1);
off = temp.toFloat() / 100.0;
} else
off = temp.toFloat();
if (!colorstop.attribute("stop-color").isEmpty())
parseSvgColor(c, colorstop.attribute("stop-color"));
else {
// try style attr
QString style = colorstop.attribute("style").simplified();
QStringList substyles = style.split(';', QString::SkipEmptyParts);
Q_FOREACH (const QString & s, substyles) {
QStringList substyle = s.split(':');
QString command = substyle[0].trimmed();
QString params = substyle[1].trimmed();
if (command == "stop-color")
parseSvgColor(c, params);
if (command == "stop-opacity")
opacity = params.toDouble();
}
}
if (!colorstop.attribute("stop-opacity").isEmpty())
opacity = colorstop.attribute("stop-opacity").toDouble();
KoColor color(rgbColorSpace);
color.fromQColor(c);
color.setOpacity(static_cast<quint8>(opacity * OPACITY_OPAQUE_U8 + 0.5));
//According to the SVG spec each gradient offset has to be equal to or greater than the previous one
//if not it needs to be adjusted to be equal
if (m_stops.count() > 0 && m_stops.last().first >= off) {
off = m_stops.last().first;
}
m_stops.append(KoGradientStop(off, color));
}
}
}
void KoStopGradient::parseSvgColor(QColor &color, const QString &s)
{
if (s.startsWith("rgb(")) {
QString parse = s.trimmed();
QStringList colors = parse.split(',');
QString r = colors[0].right((colors[0].length() - 4));
QString g = colors[1];
QString b = colors[2].left((colors[2].length() - 1));
if (r.contains('%')) {
r = r.left(r.length() - 1);
r = QString::number(int((qreal(255 * r.toDouble()) / 100.0)));
}
if (g.contains('%')) {
g = g.left(g.length() - 1);
g = QString::number(int((qreal(255 * g.toDouble()) / 100.0)));
}
if (b.contains('%')) {
b = b.left(b.length() - 1);
b = QString::number(int((qreal(255 * b.toDouble()) / 100.0)));
}
color = QColor(r.toInt(), g.toInt(), b.toInt());
} else {
QString rgbColor = s.trimmed();
QColor c;
if (rgbColor.startsWith('#'))
c.setNamedColor(rgbColor);
else {
c = QColor(rgbColor);
}
color = c;
}
}
QString KoStopGradient::defaultFileExtension() const
{
return QString(".svg");
}
void KoStopGradient::toXML(QDomDocument &doc, QDomElement &gradientElt) const
{
gradientElt.setAttribute("type", "stop");
for (int s = 0; s<m_stops.size(); s++) {
KoGradientStop stop = m_stops.at(s);
QDomElement stopElt = doc.createElement("stop");
stopElt.setAttribute("offset", stop.first);
stopElt.setAttribute("bitdepth", stop.second.colorSpace()->colorDepthId().id());
stopElt.setAttribute("alpha", stop.second.opacityF());
stop.second.toXML(doc, stopElt);
gradientElt.appendChild(stopElt);
}
}
KoStopGradient KoStopGradient::fromXML(const QDomElement &elt)
{
KoStopGradient gradient;
QList<KoGradientStop> stops;
QDomElement stopElt = elt.firstChildElement("stop");
while (!stopElt.isNull()) {
qreal offset = stopElt.attribute("offset", "0").toDouble();
QString bitDepth = stopElt.attribute("bitdepth", Integer8BitsColorDepthID.id());
KoColor color = KoColor::fromXML(stopElt.firstChildElement(), bitDepth);
color.setOpacity(stopElt.attribute("alpha", "1.0").toDouble());
stops.append(KoGradientStop(offset, color));
stopElt = stopElt.nextSiblingElement("stop");
}
gradient.setStops(stops);
return gradient;
}
bool KoStopGradient::saveToDevice(QIODevice *dev) const
{
QTextStream stream(dev);
const QString spreadMethod[3] = {
QString("spreadMethod=\"pad\" "),
QString("spreadMethod=\"reflect\" "),
QString("spreadMethod=\"repeat\" ")
};
const QString indent = " ";
stream << "<svg>" << endl;
stream << indent;
stream << "<linearGradient id=\"" << name() << "\" ";
stream << "gradientUnits=\"objectBoundingBox\" ";
stream << spreadMethod[spread()];
stream << ">" << endl;
QColor color;
// color stops
Q_FOREACH (const KoGradientStop & stop, m_stops) {
stop.second.toQColor(&color);
stream << indent << indent;
stream << "<stop stop-color=\"";
stream << color.name();
stream << "\" offset=\"" << QString().setNum(stop.first);
stream << "\" stop-opacity=\"" << static_cast<float>(color.alpha()) / 255.0f << "\"" << " />" << endl;
}
stream << indent;
stream << "</linearGradient>" << endl;
stream << "</svg>" << endl;
KoResource::saveToDevice(dev);
return true;
}
diff --git a/libs/pigment/resources/KoStopGradient.h b/libs/pigment/resources/KoStopGradient.h
index 966703267d..b7161166e8 100644
--- a/libs/pigment/resources/KoStopGradient.h
+++ b/libs/pigment/resources/KoStopGradient.h
@@ -1,97 +1,94 @@
/*
Copyright (c) 2007 Sven Langkamp <sven.langkamp@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef KOSTOPGRADIENT_H
#define KOSTOPGRADIENT_H
#include <QPair>
#include <QGradient>
#include "KoColor.h"
#include <resources/KoAbstractGradient.h>
#include <resources/KoResource.h>
#include <kritapigment_export.h>
#include <boost/operators.hpp>
typedef QPair<qreal, KoColor> KoGradientStop;
/**
- * Resource for colorstop based gradients like Karbon gradients and SVG gradients
+ * Resource for colorstop based gradients like SVG gradients
*/
class KRITAPIGMENT_EXPORT KoStopGradient : public KoAbstractGradient, public boost::equality_comparable<KoStopGradient>
{
public:
explicit KoStopGradient(const QString &filename = QString());
~KoStopGradient() override;
bool operator==(const KoStopGradient &rhs) const;
KoAbstractGradient* clone() const override;
bool load() override;
bool loadFromDevice(QIODevice *dev) override;
bool save() override;
bool saveToDevice(QIODevice* dev) const override;
/// reimplemented
QGradient* toQGradient() const override;
/// reimplemented
void colorAt(KoColor&, qreal t) const override;
/// Creates KoStopGradient from a QGradient
static KoStopGradient * fromQGradient(const QGradient * gradient);
/// Sets the gradient stops
void setStops(QList<KoGradientStop> stops);
QList<KoGradientStop> stops() const;
/// reimplemented
QString defaultFileExtension() const override;
/**
* @brief toXML
* Convert the gradient to an XML string.
*/
void toXML(QDomDocument& doc, QDomElement& gradientElt) const;
/**
* @brief fromXML
* convert a gradient from xml.
* @return a gradient.
*/
static KoStopGradient fromXML(const QDomElement& elt);
protected:
QList<KoGradientStop> m_stops;
QPointF m_start;
QPointF m_stop;
QPointF m_focalPoint;
private:
- void loadKarbonGradient(QIODevice *file);
- void parseKarbonGradient(const QDomElement& element);
-
void loadSvgGradient(QIODevice *file);
void parseSvgGradient(const QDomElement& element);
void parseSvgColor(QColor &color, const QString &s);
};
#endif // KOSTOPGRADIENT_H
diff --git a/libs/store/CMakeLists.txt b/libs/store/CMakeLists.txt
index 719fe00a79..b7c42996bd 100644
--- a/libs/store/CMakeLists.txt
+++ b/libs/store/CMakeLists.txt
@@ -1,24 +1,34 @@
+include_directories(${QUAZIP_INCLUDE_DIRS})
+
add_subdirectory(tests)
set(kritastore_LIB_SRCS
KoDirectoryStore.cpp
KoStoreDevice.cpp
KoLZF.cpp
KoStore.cpp
KoXmlNS.cpp
KoXmlReader.cpp
KoXmlWriter.cpp
- KoZipStore.cpp
+ KoQuaZipStore.cpp
StoreDebug.cpp
)
add_library(kritastore SHARED ${kritastore_LIB_SRCS})
generate_export_header(kritastore BASE_NAME kritastore)
-target_link_libraries(kritastore kritaversion kritaglobal Qt5::Xml Qt5::Gui KF5::Archive)
+target_link_libraries(kritastore
+ PRIVATE
+ kritaversion
+ kritaglobal
+ KF5::ConfigCore
+ Qt5::Xml
+ Qt5::Gui
+ ${QUAZIP_LIBRARIES}
+)
set_target_properties(kritastore PROPERTIES
VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION}
)
install(TARGETS kritastore ${INSTALL_TARGETS_DEFAULT_ARGS} )
diff --git a/libs/store/KoQuaZipStore.cpp b/libs/store/KoQuaZipStore.cpp
new file mode 100644
index 0000000000..c751995fc8
--- /dev/null
+++ b/libs/store/KoQuaZipStore.cpp
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2019 Boudewijn Rempt <boud@valdyas.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#include "KoQuaZipStore.h"
+#include "KoStore_p.h"
+
+#include <StoreDebug.h>
+
+#include <zlib.h>
+#include <quazip.h>
+#include <quazipfile.h>
+#include <quazipdir.h>
+#include <quazipfileinfo.h>
+#include <quazipnewinfo.h>
+
+#include <QTemporaryFile>
+#include <QTextCodec>
+#include <QByteArray>
+#include <QBuffer>
+
+#include <KConfig>
+#include <KSharedConfig>
+#include <KConfigGroup>
+
+struct KoQuaZipStore::Private {
+
+ Private() {}
+ ~Private() {}
+
+ QuaZip *archive {0};
+ QuaZipFile *currentFile {0};
+ int compressionLevel {Z_DEFAULT_COMPRESSION};
+ bool usingSaveFile {false};
+ QByteArray cache;
+ QBuffer buffer;
+};
+
+
+KoQuaZipStore::KoQuaZipStore(const QString &_filename, KoStore::Mode _mode, const QByteArray &appIdentification, bool writeMimetype)
+ : KoStore(_mode, writeMimetype)
+ , dd(new Private())
+{
+ Q_D(KoStore);
+ debugStore << "KoQuaZipStore" << _filename;
+ d->localFileName = _filename;
+ dd->archive = new QuaZip(_filename);
+ init(appIdentification);
+
+}
+
+KoQuaZipStore::KoQuaZipStore(QIODevice *dev, KoStore::Mode _mode, const QByteArray &appIdentification, bool writeMimetype)
+ : KoStore(_mode, writeMimetype)
+ , dd(new Private())
+{
+ Q_D(KoStore);
+ debugStore << "KoQuaZipStore" << dev;
+ dd->archive = new QuaZip(dev);
+ init(appIdentification);
+}
+
+KoQuaZipStore::~KoQuaZipStore()
+{
+ Q_D(KoStore);
+
+ if (dd->currentFile && dd->currentFile->isOpen()) {
+ dd->currentFile->close();
+ }
+
+ if (!d->finalized) {
+ finalize();
+ }
+
+ delete dd->archive;
+ delete dd->currentFile;
+}
+
+void KoQuaZipStore::setCompressionEnabled(bool enabled)
+{
+
+ if (enabled) {
+ dd->compressionLevel = Z_BEST_COMPRESSION;
+ }
+ else {
+ dd->compressionLevel = Z_NO_COMPRESSION;
+ }
+}
+
+qint64 KoQuaZipStore::write(const char *_data, qint64 _len)
+{
+ Q_D(KoStore);
+ if (_len == 0) return 0;
+
+ if (!d->isOpen) {
+ errorStore << "KoStore: You must open before writing" << endl;
+ return 0;
+ }
+
+ if (d->mode != Write) {
+ errorStore << "KoStore: Can not write to store that is opened for reading" << endl;
+ return 0;
+ }
+
+ d->size += _len;
+ if (dd->buffer.write(_data, _len)) { // writeData returns a bool!
+ return _len;
+ }
+ return 0;
+}
+
+QStringList KoQuaZipStore::directoryList() const
+{
+ debugStore << dd->archive->getFileNameList();
+ return dd->archive->getFileNameList();
+}
+
+void KoQuaZipStore::init(const QByteArray &appIdentification)
+{
+ Q_D(KoStore);
+
+ bool enableZip64 = false;
+ if (appIdentification == "application/x-krita") {
+ enableZip64 = KSharedConfig::openConfig()->group("").readEntry<bool>("UseZip64", false);
+ }
+ dd->archive->setZip64Enabled(enableZip64);
+ dd->archive->setFileNameCodec("UTF-8");
+ dd->usingSaveFile = dd->archive->getIoDevice() && dd->archive->getIoDevice()->inherits("QSaveFile");
+ dd->archive->setAutoClose(!dd->usingSaveFile);
+
+ d->good = dd->archive->open(d->mode == Write ? QuaZip::mdCreate : QuaZip::mdUnzip);
+
+ if (!d->good) {
+ return;
+ }
+
+ if (d->mode == Write) {
+ if (d->writeMimetype) {
+ QuaZipFile f(dd->archive);
+ QuaZipNewInfo newInfo("mimetype");
+ newInfo.setPermissions(QFileDevice::ReadOwner | QFileDevice::ReadGroup | QFileDevice::ReadOther);
+ if (!f.open(QIODevice::WriteOnly, newInfo, 0, 0, Z_DEFLATED, Z_NO_COMPRESSION)) {
+ d->good = false;
+ return;
+ }
+ f.write(appIdentification);
+ f.close();
+ }
+ }
+ else {
+ debugStore << dd->archive->getEntriesCount() << dd->archive->getFileNameList();
+ d->good = dd->archive->getEntriesCount();
+ }
+}
+
+bool KoQuaZipStore::doFinalize()
+{
+ Q_D(KoStore);
+
+ d->stream = 0;
+ if (!dd->usingSaveFile) {
+ dd->archive->close();
+ }
+ return dd->archive->getZipError() == ZIP_OK;
+
+}
+
+bool KoQuaZipStore::openWrite(const QString &name)
+{
+ Q_D(KoStore);
+ QString fixedPath = name;
+ fixedPath.replace("//", "/");
+
+ delete d->stream;
+ d->stream = 0; // Not used when writing
+
+ delete dd->currentFile;
+ dd->currentFile = new QuaZipFile(dd->archive);
+ QuaZipNewInfo newInfo(fixedPath);
+ newInfo.setPermissions(QFileDevice::ReadOwner | QFileDevice::ReadGroup | QFileDevice::ReadOther);
+ bool r = dd->currentFile->open(QIODevice::WriteOnly, newInfo, 0, 0, Z_DEFLATED, dd->compressionLevel);
+ if (!r) {
+ qWarning() << "Could not open" << name << dd->currentFile->getZipError();
+ }
+
+ dd->cache = QByteArray();
+ dd->buffer.setBuffer(&dd->cache);
+ dd->buffer.open(QBuffer::WriteOnly);
+
+ return r;
+}
+
+bool KoQuaZipStore::openRead(const QString &name)
+{
+ Q_D(KoStore);
+
+ QString fixedPath = name;
+ fixedPath.replace("//", "/");
+
+ delete d->stream;
+ d->stream = 0;
+ delete dd->currentFile;
+ dd->currentFile = 0;
+
+ if (!currentPath().isEmpty() && !fixedPath.startsWith(currentPath())) {
+ fixedPath = currentPath() + '/' + fixedPath;
+ }
+
+ debugStore << "openRead" << name << fixedPath << currentPath();
+
+ if (!dd->archive->setCurrentFile(fixedPath)) {
+ //qWarning() << "\t\tCould not set current file" << dd->archive->getZipError() << fixedPath;
+ return false;
+ }
+
+ dd->currentFile = new QuaZipFile(dd->archive);
+ if (!dd->currentFile->open(QIODevice::ReadOnly)) {
+ qWarning() << "\t\t\tBut could not open!!!" << dd->archive->getZipError();
+ return false;
+ }
+ d->stream = dd->currentFile;
+ d->size = dd->currentFile->size();
+ return true;
+}
+
+bool KoQuaZipStore::closeWrite()
+{
+ Q_D(KoStore);
+
+ bool r = true;
+ if (!dd->currentFile->write(dd->cache)) {
+ qWarning() << "Could not write buffer to the file";
+ r = false;
+ }
+ dd->buffer.close();
+ dd->currentFile->close();
+ d->stream = 0;
+ return (r && dd->currentFile->getZipError() == ZIP_OK);
+}
+
+bool KoQuaZipStore::closeRead()
+{
+ Q_D(KoStore);
+ d->stream = 0;
+ return true;
+}
+
+bool KoQuaZipStore::enterRelativeDirectory(const QString &path)
+{
+ debugStore << "enterRelativeDirectory()" << path;
+ return true;
+}
+
+bool KoQuaZipStore::enterAbsoluteDirectory(const QString &path)
+{
+ debugStore << "enterAbsoluteDirectory()" << path;
+
+ QString fixedPath = path;
+ fixedPath.replace("//", "/");
+
+ if (fixedPath.isEmpty()) {
+ fixedPath = "/";
+ }
+ QuaZipDir currentDir (dd->archive, fixedPath);
+ return currentDir.exists();
+}
+
+bool KoQuaZipStore::fileExists(const QString &absPath) const
+{
+ QString fixedPath = absPath;
+ fixedPath.replace("//", "/");
+
+ debugStore << "fileExists()" << fixedPath << dd->archive->getFileNameList().contains(fixedPath);
+
+ return dd->archive->getFileNameList().contains(fixedPath);
+}
diff --git a/libs/store/KoQuaZipStore.h b/libs/store/KoQuaZipStore.h
new file mode 100644
index 0000000000..e9fc320aab
--- /dev/null
+++ b/libs/store/KoQuaZipStore.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2019 Boudewijn Rempt <boud@valdyas.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef KoQuaZipStore_h
+#define KoQuaZipStore_h
+
+#include "KoStore.h"
+#include <QScopedPointer>
+
+class QUrl;
+
+class KoQuaZipStore : public KoStore
+{
+public:
+ KoQuaZipStore(const QString & _filename, Mode _mode, const QByteArray & appIdentification,
+ bool writeMimetype = true);
+
+ KoQuaZipStore(QIODevice *dev, Mode mode, const QByteArray & appIdentification,
+ bool writeMimetype = true);
+
+ ~KoQuaZipStore() override;
+
+ void setCompressionEnabled(bool enabled) override;
+ qint64 write(const char* _data, qint64 _len) override;
+
+ QStringList directoryList() const override;
+
+protected:
+ void init(const QByteArray& appIdentification);
+ bool doFinalize() override;
+ bool openWrite(const QString& name) override;
+ bool openRead(const QString& name) override;
+ bool closeWrite() override;
+ bool closeRead() override;
+ bool enterRelativeDirectory(const QString& dirName) override;
+ bool enterAbsoluteDirectory(const QString& path) override;
+ bool fileExists(const QString& absPath) const override;
+
+private:
+ struct Private;
+ const QScopedPointer<Private> dd;
+ Q_DECLARE_PRIVATE(KoStore)
+
+};
+
+#endif
diff --git a/libs/store/KoStore.cpp b/libs/store/KoStore.cpp
index ac6cc53021..d07de3696f 100644
--- a/libs/store/KoStore.cpp
+++ b/libs/store/KoStore.cpp
@@ -1,448 +1,440 @@
/* This file is part of the KDE project
Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
Copyright (C) 2000-2002 David Faure <faure@kde.org>, Werner Trobin <trobin@kde.org>
Copyright (C) 2010 C. Boemann <cbo@boemann.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoStore.h"
#include "KoStore_p.h"
-#include "KoZipStore.h"
+#include "KoQuaZipStore.h"
#include "KoDirectoryStore.h"
#include <QBuffer>
#include <QFileInfo>
#include <QFile>
#include <QUrl>
#include <StoreDebug.h>
#define DefaultFormat KoStore::Zip
static KoStore::Backend determineBackend(QIODevice *dev)
{
unsigned char buf[5];
if (dev->read((char *)buf, 4) < 4)
return DefaultFormat; // will create a "bad" store (bad()==true)
if (buf[0] == 'P' && buf[1] == 'K' && buf[2] == 3 && buf[3] == 4)
return KoStore::Zip;
return DefaultFormat; // fallback
}
KoStore* KoStore::createStore(const QString& fileName, Mode mode, const QByteArray & appIdentification, Backend backend, bool writeMimetype)
{
if (backend == Auto) {
if (mode == KoStore::Write)
backend = DefaultFormat;
else {
QFileInfo inf(fileName);
if (inf.isDir())
backend = Directory;
else {
QFile file(fileName);
if (file.open(QIODevice::ReadOnly))
backend = determineBackend(&file);
else
backend = DefaultFormat; // will create a "bad" store (bad()==true)
}
}
}
switch (backend) {
case Zip:
- return new KoZipStore(fileName, mode, appIdentification, writeMimetype);
+ return new KoQuaZipStore(fileName, mode, appIdentification, writeMimetype);
case Directory:
return new KoDirectoryStore(fileName /* should be a dir name.... */, mode, writeMimetype);
default:
warnStore << "Unsupported backend requested for KoStore : " << backend;
return 0;
}
}
KoStore* KoStore::createStore(QIODevice *device, Mode mode, const QByteArray & appIdentification, Backend backend, bool writeMimetype)
{
if (backend == Auto) {
if (mode == KoStore::Write)
backend = DefaultFormat;
else {
if (device->open(QIODevice::ReadOnly)) {
backend = determineBackend(device);
device->close();
}
}
}
switch (backend) {
case Directory:
errorStore << "Can't create a Directory store for a memory buffer!" << endl;
return 0;
case Zip:
- return new KoZipStore(device, mode, appIdentification, writeMimetype);
+ return new KoQuaZipStore(device, mode, appIdentification, writeMimetype);
default:
warnStore << "Unsupported backend requested for KoStore : " << backend;
return 0;
}
}
-KoStore* KoStore::createStore(const QUrl &url, Mode mode, const QByteArray & appIdentification, Backend backend, bool writeMimetype)
-{
- Q_ASSERT(url.isLocalFile());
- return createStore(url.toLocalFile(), mode, appIdentification, backend, writeMimetype);
-}
-
namespace
{
const char ROOTPART[] = "root";
const char MAINNAME[] = "maindoc.xml";
}
KoStore::KoStore(Mode mode, bool writeMimetype)
: d_ptr(new KoStorePrivate(this, mode, writeMimetype))
{}
KoStore::~KoStore()
{
Q_D(KoStore);
delete d->stream;
delete d_ptr;
}
bool KoStore::open(const QString & _name)
{
Q_D(KoStore);
// This also converts from relative to absolute, i.e. merges the currentPath()
d->fileName = d->toExternalNaming(_name);
+ debugStore << "KOStore" << _name << d->fileName;
+
if (d->isOpen) {
warnStore << "Store is already opened, missing close";
return false;
}
if (d->fileName.length() > 512) {
errorStore << "KoStore: Filename " << d->fileName << " is too long" << endl;
return false;
}
if (d->mode == Write) {
debugStore << "opening for writing" << d->fileName;
if (d->filesList.contains(d->fileName)) {
warnStore << "KoStore: Duplicate filename" << d->fileName;
return false;
}
d->filesList.append(d->fileName);
d->size = 0;
if (!openWrite(d->fileName))
return false;
} else if (d->mode == Read) {
debugStore << "Opening for reading" << d->fileName;
if (!openRead(d->fileName))
return false;
} else
return false;
d->isOpen = true;
return true;
}
bool KoStore::isOpen() const
{
Q_D(const KoStore);
return d->isOpen;
}
bool KoStore::close()
{
Q_D(KoStore);
- debugStore << "Closing";
-
if (!d->isOpen) {
warnStore << "You must open before closing";
return false;
}
bool ret = d->mode == Write ? closeWrite() : closeRead();
-
delete d->stream;
d->stream = 0;
d->isOpen = false;
return ret;
}
QIODevice* KoStore::device() const
{
Q_D(const KoStore);
if (!d->isOpen)
warnStore << "You must open before asking for a device";
if (d->mode != Read)
warnStore << "Can not get device from store that is opened for writing";
return d->stream;
}
QByteArray KoStore::read(qint64 max)
{
Q_D(KoStore);
QByteArray data;
if (!d->isOpen) {
warnStore << "You must open before reading";
return data;
}
if (d->mode != Read) {
errorStore << "KoStore: Can not read from store that is opened for writing" << endl;
return data;
}
return d->stream->read(max);
}
qint64 KoStore::write(const QByteArray& data)
{
return write(data.constData(), data.size()); // see below
}
qint64 KoStore::read(char *_buffer, qint64 _len)
{
Q_D(KoStore);
if (!d->isOpen) {
errorStore << "KoStore: You must open before reading" << endl;
return -1;
}
if (d->mode != Read) {
errorStore << "KoStore: Can not read from store that is opened for writing" << endl;
return -1;
}
return d->stream->read(_buffer, _len);
}
qint64 KoStore::write(const char* _data, qint64 _len)
{
Q_D(KoStore);
if (_len == 0) return 0;
if (!d->isOpen) {
errorStore << "KoStore: You must open before writing" << endl;
return 0;
}
if (d->mode != Write) {
errorStore << "KoStore: Can not write to store that is opened for reading" << endl;
return 0;
}
int nwritten = d->stream->write(_data, _len);
Q_ASSERT(nwritten == (int)_len);
d->size += nwritten;
return nwritten;
}
qint64 KoStore::size() const
{
Q_D(const KoStore);
if (!d->isOpen) {
warnStore << "You must open before asking for a size";
return static_cast<qint64>(-1);
}
if (d->mode != Read) {
warnStore << "Can not get size from store that is opened for writing";
return static_cast<qint64>(-1);
}
return d->size;
}
bool KoStore::enterDirectory(const QString &directory)
{
Q_D(KoStore);
//debugStore <<"enterDirectory" << directory;
int pos;
bool success = true;
QString tmp(directory);
while ((pos = tmp.indexOf('/')) != -1 &&
(success = d->enterDirectoryInternal(tmp.left(pos))))
tmp.remove(0, pos + 1);
if (success && !tmp.isEmpty())
return d->enterDirectoryInternal(tmp);
return success;
}
bool KoStore::leaveDirectory()
{
Q_D(KoStore);
if (d->currentPath.isEmpty())
return false;
d->currentPath.pop_back();
return enterAbsoluteDirectory(currentPath());
}
QString KoStore::currentPath() const
{
Q_D(const KoStore);
QString path;
QStringList::ConstIterator it = d->currentPath.begin();
QStringList::ConstIterator end = d->currentPath.end();
for (; it != end; ++it) {
path += *it;
path += '/';
}
return path;
}
void KoStore::pushDirectory()
{
Q_D(KoStore);
d->directoryStack.push(currentPath());
}
void KoStore::popDirectory()
{
Q_D(KoStore);
d->currentPath.clear();
enterAbsoluteDirectory(QString());
enterDirectory(d->directoryStack.pop());
}
bool KoStore::extractFile(const QString &srcName, QByteArray &data)
{
Q_D(KoStore);
QBuffer buffer(&data);
return d->extractFile(srcName, buffer);
}
bool KoStorePrivate::extractFile(const QString &srcName, QIODevice &buffer)
{
if (!q->open(srcName))
return false;
if (!buffer.open(QIODevice::WriteOnly)) {
q->close();
return false;
}
- // ### This could use KArchive::copy or something, no?
QByteArray data;
data.resize(8 * 1024);
uint total = 0;
for (int block = 0; (block = q->read(data.data(), data.size())) > 0; total += block) {
buffer.write(data.data(), block);
}
if (q->size() != static_cast<qint64>(-1))
Q_ASSERT(total == q->size());
buffer.close();
q->close();
return true;
}
bool KoStore::seek(qint64 pos)
{
Q_D(KoStore);
return d->stream->seek(pos);
}
qint64 KoStore::pos() const
{
Q_D(const KoStore);
return d->stream->pos();
}
bool KoStore::atEnd() const
{
Q_D(const KoStore);
return d->stream->atEnd();
}
// See the specification for details of what this function does.
QString KoStorePrivate::toExternalNaming(const QString & _internalNaming) const
{
if (_internalNaming == ROOTPART)
return q->currentPath() + MAINNAME;
QString intern;
if (_internalNaming.startsWith("tar:/")) // absolute reference
intern = _internalNaming.mid(5); // remove protocol
else
intern = q->currentPath() + _internalNaming;
return intern;
}
bool KoStorePrivate::enterDirectoryInternal(const QString &directory)
{
if (q->enterRelativeDirectory(directory)) {
currentPath.append(directory);
return true;
}
return false;
}
bool KoStore::hasFile(const QString& fileName) const
{
Q_D(const KoStore);
return fileExists(d->toExternalNaming(fileName));
}
bool KoStore::finalize()
{
Q_D(KoStore);
Q_ASSERT(!d->finalized); // call this only once!
d->finalized = true;
return doFinalize();
}
void KoStore::setCompressionEnabled(bool /*e*/)
{
}
bool KoStore::isEncrypted()
{
return false;
}
bool KoStore::setPassword(const QString& /*password*/)
{
return false;
}
QString KoStore::password()
{
return QString();
}
bool KoStore::bad() const
{
Q_D(const KoStore);
return !d->good;
}
KoStore::Mode KoStore::mode() const
{
Q_D(const KoStore);
return d->mode;
}
QStringList KoStore::directoryList() const
{
return QStringList();
}
diff --git a/libs/store/KoStore.h b/libs/store/KoStore.h
index ae5370e46e..56d1065704 100644
--- a/libs/store/KoStore.h
+++ b/libs/store/KoStore.h
@@ -1,351 +1,326 @@
/* This file is part of the KDE project
Copyright (C) 1998, 1999 David Faure <faure@kde.org>
Copyright (C) 2010 C. Boemann <cbo@boemann.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __koStore_h_
#define __koStore_h_
#include <QByteArray>
#include <QIODevice>
#include "kritastore_export.h"
class QWidget;
class QUrl;
class KoStorePrivate;
/**
* Saves and loads Krita documents using various backends. Currently supported
* backends are zip and directory.
* We call a "store" the file on the hard disk (the one the users sees)
* and call a "file" a file inside the store.
*/
class KRITASTORE_EXPORT KoStore
{
public:
enum Mode { Read, Write };
enum Backend { Auto, Zip, Directory };
/**
* Open a store (i.e. the representation on disk of a Krita document).
*
* @param fileName the name of the file to open
* @param mode if KoStore::Read, open an existing store to read it.
* if KoStore::Write, create or replace a store.
* @param backend the backend to use for the data storage.
* Auto means automatically-determined for reading,
* and the current format (now Zip) for writing.
*
* @param appIdentification the application's mimetype,
* to be written in the file for "mime-magic" identification.
* Only meaningful if mode is Write, and if backend!=Directory.
*
* @param writeMimetype If true, some backends (notably the Zip
* store) will write a file called 'mimetype' automatically and
* fill it with data from the appIdentification. This is only
* applicable if Mode is set to Write.
*/
static KoStore *createStore(const QString &fileName, Mode mode,
const QByteArray &appIdentification = QByteArray(),
Backend backend = Auto, bool writeMimetype = true);
/**
* Create a store for any kind of QIODevice: file, memory buffer...
* KoStore will take care of opening the QIODevice.
* This method doesn't support the Directory store!
*/
static KoStore *createStore(QIODevice *device, Mode mode,
const QByteArray &appIdentification = QByteArray(),
Backend backend = Auto, bool writeMimetype = true);
- /**
- * Open a store (i.e. the representation on disk of a Krita document).
- *
- * @param url URL of the file to open
- * @param mode if KoStore::Read, open an existing store to read it.
- * if KoStore::Write, create or replace a store.
- * @param backend the backend to use for the data storage.
- * Auto means automatically-determined for reading,
- * and the current format (now Zip) for writing.
- *
- * @param appIdentification the application's mimetype,
- * to be written in the file for "mime-magic" identification.
- * Only meaningful if mode is Write, and if backend!=Directory.
- *
- * If the file is remote, the backend Directory cannot be used!
- *
- * @param writeMimetype If true, some backends (notably the Zip
- * store) will write a file called 'mimetype' automatically and
- * fill it with data from the appIdentification. This is only
- * applicable if Mode is set to Write.
- *
- * @bug saving not completely implemented (fixed temporary file)
- */
- static KoStore *createStore(const QUrl &url, Mode mode,
- const QByteArray &appIdentification = QByteArray(), Backend backend = Auto, bool writeMimetype = true);
/**
* Destroys the store (i.e. closes the file on the hard disk)
*/
virtual ~KoStore();
/**
* Open a new file inside the store
* @param name The filename, internal representation ("root", "tar:/0"... ).
* If the tar:/ prefix is missing it's assumed to be a relative URI.
* @return true on success.
*/
bool open(const QString &name);
/**
* Check whether a file inside the store is currently opened with open(),
* ready to be read or written.
* @return true if a file is currently opened.
*/
bool isOpen() const;
/**
* Close the file inside the store
* @return true on success.
*/
bool close();
/**
* Get a device for reading a file from the store directly
* (slightly faster than read() calls)
* You need to call @ref open first, and @ref close afterwards.
*/
QIODevice *device() const;
/**
* Read data from the currently opened file. You can also use the streams
* for this.
*/
QByteArray read(qint64 max);
/**
* Write data into the currently opened file. You can also use the streams
* for this.
*/
qint64 write(const QByteArray &data);
/**
* Read data from the currently opened file. You can also use the streams
* for this.
* @return size of data read, -1 on error
*/
qint64 read(char *buffer, qint64 length);
/**
* Write data into the currently opened file. You can also use the streams
* for this.
*/
virtual qint64 write(const char* data, qint64 length);
/**
* @return the size of the currently opened file, -1 on error.
* Can be used as an argument for the read methods, for instance
*/
qint64 size() const;
/**
* @return true if an error occurred
*/
bool bad() const;
/**
* @return the mode used when opening, read or write
*/
Mode mode() const;
/**
* If an store is opened for reading, then the directories
* of the store can be accessed via this function.
*
* @return a stringlist with all directories found
*/
virtual QStringList directoryList() const;
/**
* Enters one or multiple directories. In Read mode this actually
* checks whether the specified directories exist and returns false
* if they don't. In Write mode we don't create the directory, we
* just use the "current directory" to generate the absolute path
* if you pass a relative path (one not starting with tar:/) when
* opening a stream.
* Note: Operates on internal names
*/
bool enterDirectory(const QString &directory);
/**
* Leaves a directory. Equivalent to "cd .."
* @return true on success, false if we were at the root already to
* make it possible to "loop to the root"
*/
bool leaveDirectory();
/**
* Returns the current path including a trailing slash.
* Note: Returns a path in "internal name" style
*/
QString currentPath() const;
/**
* Stacks the current directory. Restore the current path using
* @ref popDirectory .
*/
void pushDirectory();
/**
* Restores the previously pushed directory. No-op if the stack is
* empty.
*/
void popDirectory();
/**
* @return true if the given file exists in the current directory,
* i.e. if open(fileName) will work.
*/
bool hasFile(const QString &fileName) const;
/**
* Extracts a file out of the store to a buffer
* @param sourceName file in the store
* @param data memory buffer
*/
bool extractFile(const QString &sourceName, QByteArray &data);
//@{
/// See QIODevice
bool seek(qint64 pos);
qint64 pos() const;
bool atEnd() const;
//@}
/**
* Call this before destroying the store, to be able to catch errors
* (e.g. from ksavefile)
*/
bool finalize();
/**
* Sets the password to be used for decryption or encryption of the store.
* Use of this function is optional: an encryptable store should make
* a best effort in obtaining a password if it wasn't supplied.
*
* This method only works before opening a file. It might fail when a file
* has already been opened before calling this method.
*
* This method will not function for any store that is not encrypted or
* can't be encrypted when saving.
*
* @param password A non-empty password.
*
* @return True if the password was set.
*/
virtual bool setPassword(const QString &password);
/**
* Retrieves the password used to encrypt or decrypt the store. Note that
* QString() will returned if no password has been given or the store is
* not encrypted.
*
* @return The password this store is encrypted with.
*/
virtual QString password();
/**
* Returns whether a store opened for reading is encrypted or a store opened
* for saving will be encrypted.
*
* @return True if the store is encrypted.
*/
virtual bool isEncrypted();
/**
* Allow to enable or disable compression of the files. Only supported by the
* ZIP backend.
*/
virtual void setCompressionEnabled(bool e);
protected:
KoStore(Mode mode, bool writeMimetype = true);
/**
* Finalize store - called by finalize.
* @return true on success
*/
virtual bool doFinalize() {
return true;
}
/**
* Open the file @p name in the store, for writing
* On success, this method must set m_stream to a stream in which we can write.
* @param name "absolute path" (in the archive) to the file to open
* @return true on success
*/
virtual bool openWrite(const QString &name) = 0;
/**
* Open the file @p name in the store, for reading.
* On success, this method must set m_stream to a stream from which we can read,
* as well as setting m_iSize to the size of the file.
* @param name "absolute path" (in the archive) to the file to open
* @return true on success
*/
virtual bool openRead(const QString &name) = 0;
/**
* @return true on success
*/
virtual bool closeRead() = 0;
/**
* @return true on success
*/
virtual bool closeWrite() = 0;
/**
* Enter a subdirectory of the current directory.
* The directory might not exist yet in Write mode.
*/
virtual bool enterRelativeDirectory(const QString &dirName) = 0;
/**
* Enter a directory where we've been before.
* It is guaranteed to always exist.
*/
virtual bool enterAbsoluteDirectory(const QString &path) = 0;
/**
* Check if a file exists inside the store.
* @param absPath the absolute path inside the store, i.e. not relative to the current directory
*/
virtual bool fileExists(const QString &absPath) const = 0;
protected:
KoStorePrivate *d_ptr;
private:
Q_DECLARE_PRIVATE(KoStore)
private:
KoStore(const KoStore& store); ///< don't copy
KoStore& operator=(const KoStore& store); ///< don't assign
};
#endif
diff --git a/libs/store/KoStore_p.h b/libs/store/KoStore_p.h
index 75b5f6ef58..565f6f4288 100644
--- a/libs/store/KoStore_p.h
+++ b/libs/store/KoStore_p.h
@@ -1,107 +1,103 @@
/* This file is part of the KDE project
Copyright 2004 Nicolas GOUTTE <goutte@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __koStore_p_h_
#define __koStore_p_h_
#include "KoStore.h"
#include <QStringList>
#include <QStack>
#include <QUrl>
class QWidget;
class KoStorePrivate
{
public:
explicit KoStorePrivate(KoStore *qq, KoStore::Mode _mode, bool _writeMimetype)
: q(qq),
window(0),
mode(_mode),
size(0),
stream(0),
isOpen(false),
good(false),
finalized(false),
writeMimetype(_writeMimetype)
{
}
/**
* Conversion routine
- * @param _internalNaming name used internally : "root", "tar:/0", ...
+ * @param internalNaming name used internally : "root", "tar:/0", ...
* @return the name used in the file, more user-friendly ("maindoc.xml",
* "part0/maindoc.xml", ...)
* Examples:
*
* tar:/0 is saved as part0/maindoc.xml
* tar:/0/1 is saved as part0/part1/maindoc.xml
* tar:/0/1/pictures/picture0.png is saved as part0/part1/pictures/picture0.png
*
* see specification (calligra/lib/store/SPEC) for details.
*/
QString toExternalNaming(const QString &internalNaming) const;
/**
* Enter *one* single directory. Nothing like foo/bar/bleh allowed.
* Performs some checking when in Read mode
*/
bool enterDirectoryInternal(const QString &directory);
bool extractFile(const QString &sourceName, QIODevice &buffer);
KoStore *q;
- /**
- * original URL of the remote file
- * (undefined for a local file)
- */
- QUrl url;
+
QString localFileName;
QWidget *window;
KoStore::Mode mode;
/// Store the filenames (with full path inside the archive) when writing, to avoid duplicates
QStringList filesList;
/// The "current directory" (path)
QStringList currentPath;
/// Current filename (between an open() and a close())
QString fileName;
/// Current size of the file named m_sName
qint64 size;
/// The stream for the current read or write operation
QIODevice *stream;
bool isOpen;
/// Must be set by the constructor.
bool good;
bool finalized;
QStack<QString> directoryStack;
bool writeMimetype; ///< true if the backend is allowed to create "mimetype" automatically.
};
#endif
diff --git a/libs/store/KoXmlReader.h b/libs/store/KoXmlReader.h
index aa0f888348..e31d61ccc1 100644
--- a/libs/store/KoXmlReader.h
+++ b/libs/store/KoXmlReader.h
@@ -1,181 +1,183 @@
/* This file is part of the KDE project
Copyright (C) 2005-2006 Ariya Hidayat <ariya@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KO_XMLREADER_H
#define KO_XMLREADER_H
#include "KoXmlReaderForward.h"
#include "kritastore_export.h"
#include <QPair>
#include <QString>
class QIODevice;
/**
* The office-text-content-prelude type.
*/
enum KoXmlNamedItemType {
KoXmlTextContentPrelude ///< office-text-content-prelude
//KoXmlTextContentMain, ///< office-text-content-main
//KoXmlTextContentEpilogue ///< office-text-content-epilogue
};
/**
* This namespace contains a few convenience functions to simplify code using QDom
* (when loading OASIS documents, in particular).
*
* To find the child element with a given name, use KoXml::namedItemNS.
*
* To find all child elements with a given name, use
* QDomElement e;
* forEachElement( e, parent )
* {
* if ( e.localName() == "..." && e.namespaceURI() == KoXmlNS::... )
* {
* ...
* }
* }
* Note that this means you don't ever need to use QDomNode nor toElement anymore!
* Also note that localName is the part without the prefix, this is the whole point
* of namespace-aware methods.
*
* To find the attribute with a given name, use QDomElement::attributeNS.
*
* Do not use getElementsByTagNameNS, it's recursive (which is never needed in Calligra).
* Do not use tagName() or nodeName() or prefix(), since the prefix isn't fixed.
*
* @author David Faure <faure@kde.org>
*/
namespace KoXml
{
/**
* A namespace-aware version of QDomNode::namedItem(),
* which also takes care of casting to a QDomElement.
*
* Use this when a domelement is known to have only *one* child element
* with a given tagname.
*
* Note: do *NOT* use getElementsByTagNameNS, it's recursive!
*/
KRITASTORE_EXPORT KoXmlElement namedItemNS(const KoXmlNode& node,
const QString& nsURI, const QString& localName);
/**
* A namespace-aware version of QDomNode::namedItem().
* which also takes care of casting to a QDomElement.
*
* Use this when you like to return the first or an invalid
* KoXmlElement with a known type.
*
* This is an optimized version of the namedItemNS above to
* give fast access to certain sections of the document using
* the office-text-content-prelude condition as @a KoXmlNamedItemType .
*/
KRITASTORE_EXPORT KoXmlElement namedItemNS(const KoXmlNode& node,
const QString& nsURI, const QString& localName,
KoXmlNamedItemType type);
/**
* Explicitly load child nodes of specified node, up to given depth.
* This function has no effect if QDom is used.
*/
KRITASTORE_EXPORT void load(KoXmlNode& node, int depth = 1);
/**
* Unload child nodes of specified node.
* This function has no effect if QDom is used.
*/
KRITASTORE_EXPORT void unload(KoXmlNode& node);
/**
* Get the number of child nodes of specified node.
*/
KRITASTORE_EXPORT int childNodesCount(const KoXmlNode& node);
/**
* Return the name of all attributes of specified node.
*/
KRITASTORE_EXPORT QStringList attributeNames(const KoXmlNode& node);
/**
* Convert KoXmlNode classes to the corresponding QDom classes, which has
* @p ownerDoc as the owner document (QDomDocument instance).
* The converted @p node (and its children) are added to ownerDoc.
*
* NOTE:
* - If ownerDoc is not empty, this may fail, @see QDomDocument
* - @p node must not be a KoXmlDocument, use asQDomDocument()
*
* @see asQDomDocument, asQDomElement
*/
KRITASTORE_EXPORT void asQDomNode(QDomDocument& ownerDoc, const KoXmlNode& node);
/**
* Convert KoXmlNode classes to the corresponding QDom classes, which has
* @p ownerDoc as the owner document (QDomDocument instance).
* The converted @p element (and its children) is added to ownerDoc.
*
* NOTE: If ownerDoc is not empty, this may fail, @see QDomDocument
*
*/
KRITASTORE_EXPORT void asQDomElement(QDomDocument& ownerDoc, const KoXmlElement& element);
/**
* Converts the whole @p document into a QDomDocument
*/
KRITASTORE_EXPORT QDomDocument asQDomDocument(const KoXmlDocument& document);
/*
* Load an XML document from specified device to a document. You can of
* course use it with QFile (which inherits QIODevice).
* This is much more memory efficient than standard QDomDocument::setContent
* because the data from the device is buffered, unlike
* QDomDocument::setContent which just loads everything in memory.
*
* Note: it is assumed that the XML uses UTF-8 encoding.
*/
KRITASTORE_EXPORT bool setDocument(KoXmlDocument& doc, QIODevice* device,
bool namespaceProcessing, QString* errorMsg = 0,
int* errorLine = 0, int* errorColumn = 0);
}
/**
* \def forEachElement( elem, parent )
- * \brief Loop through all child elements of \parent.
+ * \brief Loop through all child elements of \p parent.
* This convenience macro is used to implement the forEachElement loop.
- * The \elem parameter is a name of a QDomElement variable and the \parent
+ * The \p elem parameter is a name of a QDomElement variable and the \p parent
* is the name of the parent element. For example:
*
+ * \code
* QDomElement e;
* forEachElement( e, parent )
* {
* qDebug() << e.localName() << " element found.";
* ...
* }
+ * \endcode
*/
#define forEachElement( elem, parent ) \
for ( KoXmlNode _node = parent.firstChild(); !_node.isNull(); _node = _node.nextSibling() ) \
if ( ( elem = _node.toElement() ).isNull() ) {} else
#endif // KO_XMLREADER_H
diff --git a/libs/store/KoXmlVector.h b/libs/store/KoXmlVector.h
index a004638760..5e6bc2bd46 100644
--- a/libs/store/KoXmlVector.h
+++ b/libs/store/KoXmlVector.h
@@ -1,176 +1,176 @@
/* This file is part of the KDE project
Copyright (C) 2005-2006 Ariya Hidayat <ariya@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KO_XMLVECTOR_H
#define KO_XMLVECTOR_H
// comment it to test this class without the compression
#define KOXMLVECTOR_USE_LZF
#ifdef KOXMLVECTOR_USE_LZF
#include "KoLZF.h"
#endif
#include <QVector>
#include <QByteArray>
#include <QDataStream>
#include <QBuffer>
/**
* KoXmlVector
*
* similar to QVector, but using LZF compression to save memory space
* this class is however not reentrant
*
* Needs to be used like this, otherwise will crash:
- * <sl>
+ * <ul>
* <li>add content with newItem()</li>
* <li>finish adding content with squeeze()</li>
* <li>just read content with operator[]</li>
- * </sl>
+ * </ul>
*
* @param uncompressedItemCount when number of buffered items reach this,
* compression will start small value will give better memory usage at the
* cost of speed bigger value will be better in term of speed, but use
* more memory
*/
template <typename T, int uncompressedItemCount = 256, int reservedBufferSize = 1024*1024>
class KoXmlVector
{
private:
unsigned m_totalItems;
QVector<unsigned> m_startIndex;
QVector<QByteArray> m_blocks;
mutable unsigned m_bufferStartIndex;
mutable QVector<T> m_bufferItems;
mutable QByteArray m_bufferData;
protected:
/**
* fetch given item index to the buffer
* will INVALIDATE all references to the buffer
*/
void fetchItem(unsigned index) const {
// already in the buffer ?
if (index >= m_bufferStartIndex)
if (index - m_bufferStartIndex < (unsigned)m_bufferItems.count())
return;
// search in the stored blocks
// TODO: binary search to speed up
int loc = m_startIndex.count() - 1;
for (int c = 0; c < m_startIndex.count() - 1; ++c)
if (index >= m_startIndex[c])
if (index < m_startIndex[c+1]) {
loc = c;
break;
}
m_bufferStartIndex = m_startIndex[loc];
#ifdef KOXMLVECTOR_USE_LZF
KoLZF::decompress(m_blocks[loc], m_bufferData);
#else
m_bufferData = m_blocks[loc];
#endif
QBuffer buffer(&m_bufferData);
buffer.open(QIODevice::ReadOnly);
QDataStream in(&buffer);
m_bufferItems.clear();
in >> m_bufferItems;
}
/**
* store data in the buffer to main m_blocks
*/
void storeBuffer() {
QBuffer buffer;
buffer.open(QIODevice::WriteOnly);
QDataStream out(&buffer);
out << m_bufferItems;
m_startIndex.append(m_bufferStartIndex);
#ifdef KOXMLVECTOR_USE_LZF
m_blocks.append(KoLZF::compress(buffer.data()));
#else
m_blocks.append(buffer.data());
#endif
m_bufferStartIndex += m_bufferItems.count();
m_bufferItems.clear();
}
public:
inline KoXmlVector(): m_totalItems(0), m_bufferStartIndex(0) {};
void clear() {
m_totalItems = 0;
m_startIndex.clear();
m_blocks.clear();
m_bufferStartIndex = 0;
m_bufferItems.clear();
m_bufferData.reserve(reservedBufferSize);
}
inline int count() const {
return (int)m_totalItems;
}
inline int size() const {
return (int)m_totalItems;
}
inline bool isEmpty() const {
return m_totalItems == 0;
}
/**
* append a new item
* WARNING: use the return value as soon as possible
* it may be invalid if another function is invoked
*/
T& newItem() {
// buffer full?
if (m_bufferItems.count() >= uncompressedItemCount - 1)
storeBuffer();
++m_totalItems;
m_bufferItems.resize(m_bufferItems.count() + 1);
return m_bufferItems[m_bufferItems.count()-1];
}
/**
* WARNING: use the return value as soon as possible
* it may be invalid if another function is invoked
*/
const T &operator[](int i) const {
fetchItem((unsigned)i);
return m_bufferItems[i - m_bufferStartIndex];
}
/**
* optimize memory usage
* will INVALIDATE all references to the buffer
*/
void squeeze() {
storeBuffer();
}
};
#endif
diff --git a/libs/store/KoZipStore.cpp b/libs/store/KoZipStore.cpp
deleted file mode 100644
index badd197841..0000000000
--- a/libs/store/KoZipStore.cpp
+++ /dev/null
@@ -1,257 +0,0 @@
-/* This file is part of the KDE project
- Copyright (C) 2000-2002 David Faure <faure@kde.org>
- Copyright (C) 2010 C. Boemann <cbo@boemann.dk>
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public License
- along with this library; see the file COPYING.LIB. If not, write to
- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
-*/
-
-#include "KoZipStore.h"
-#include "KoStore_p.h"
-
-#include <QBuffer>
-#include <QByteArray>
-#include <QTemporaryFile>
-
-#include <kzip.h>
-#include <StoreDebug.h>
-
-#include <QUrl>
-
-class SaveZip : public KZip {
-public:
- SaveZip(const QString &filename) : KZip(filename) {}
- SaveZip(QIODevice *dev) : KZip(dev) {}
- ~SaveZip() override {}
- void resetDevice() {
- closeArchive();
- setDevice(0);
- }
-};
-
-KoZipStore::KoZipStore(const QString & _filename, Mode mode, const QByteArray & appIdentification,
- bool writeMimetype)
- : KoStore(mode, writeMimetype)
-{
- Q_D(KoStore);
-
- d->localFileName = _filename;
-
- m_pZip = new SaveZip(_filename);
-
- init(appIdentification); // open the zip file and init some vars
-}
-
-KoZipStore::KoZipStore(QIODevice *dev, Mode mode, const QByteArray & appIdentification,
- bool writeMimetype)
- : KoStore(mode, writeMimetype)
-{
- m_pZip = new SaveZip(dev);
- init(appIdentification);
-}
-
-KoZipStore::KoZipStore(QWidget* window, const QUrl &_url, const QString & _filename, Mode mode,
- const QByteArray & appIdentification, bool writeMimetype)
- : KoStore(mode, writeMimetype)
-{
- debugStore << "KoZipStore Constructor url" << _url.url(QUrl::PreferLocalFile)
- << " filename = " << _filename
- << " mode = " << int(mode)
- << " mimetype = " << appIdentification;
- Q_D(KoStore);
-
- d->url = _url;
- d->window = window;
-
- if (mode == KoStore::Read) {
- d->localFileName = _filename;
- } else {
- QTemporaryFile f("kozip");
- f.open();
- d->localFileName = f.fileName();
- f.close();
- }
-
- m_pZip = new SaveZip(d->localFileName);
- init(appIdentification); // open the zip file and init some vars
-}
-
-KoZipStore::~KoZipStore()
-{
- Q_D(KoStore);
- if (m_pZip->device() && m_pZip->device()->inherits("QSaveFile")) {
- m_pZip->resetDevice(); // otherwise, kzip's destructor will call close(), which aborts on a qsavefile
- }
- else {
- if (!d->finalized) {
- finalize(); // ### no error checking when the app forgot to call finalize itself
- }
- }
- delete m_pZip;
-
- // When writing, we write to a temp file that then gets copied over the original filename
- if (d->mode == Write && (!d->localFileName.isEmpty() && !d->url.isEmpty())) {
- QFile f(d->localFileName);
- if (f.copy(d->url.toLocalFile())) {
- f.remove();
- }
- }
-}
-
-void KoZipStore::init(const QByteArray& appIdentification)
-{
- Q_D(KoStore);
-
- m_currentDir = 0;
- d->good = m_pZip->open(d->mode == Write ? QIODevice::WriteOnly : QIODevice::ReadOnly);
-
- if (!d->good)
- return;
-
- if (d->mode == Write) {
-
- m_pZip->setCompression(KZip::NoCompression);
- m_pZip->setExtraField(KZip::NoExtraField);
-
- // Write identification
- if (d->writeMimetype) {
- (void)m_pZip->writeFile(QLatin1String("mimetype"), appIdentification);
- }
-
- m_pZip->setCompression(KZip::DeflateCompression);
- // We don't need the extra field in Krita - so we leave it as "no extra field".
- } else {
- d->good = m_pZip->directory() != 0;
- }
-}
-
-void KoZipStore::setCompressionEnabled(bool e)
-{
- if (e) {
- m_pZip->setCompression(KZip::DeflateCompression);
- } else {
- m_pZip->setCompression(KZip::NoCompression);
- }
-}
-
-bool KoZipStore::doFinalize()
-{
- if (m_pZip && m_pZip->device() && !m_pZip->device()->inherits("QSaveFile")) {
- return m_pZip->close();
- }
- else {
- return true;
- }
-}
-
-bool KoZipStore::openWrite(const QString& name)
-{
- Q_D(KoStore);
- d->stream = 0; // Don't use!
- return m_pZip->prepareWriting(name, "", "" /*m_pZip->rootDir()->user(), m_pZip->rootDir()->group()*/, 0);
-}
-
-bool KoZipStore::openRead(const QString& name)
-{
- Q_D(KoStore);
- const KArchiveEntry * entry = m_pZip->directory()->entry(name);
- if (entry == 0) {
- return false;
- }
- if (entry->isDirectory()) {
- warnStore << name << " is a directory !";
- return false;
- }
- // Must cast to KZipFileEntry, not only KArchiveFile, because device() isn't virtual!
- const KZipFileEntry * f = static_cast<const KZipFileEntry *>(entry);
- delete d->stream;
- d->stream = f->createDevice();
- d->size = f->size();
- return true;
-}
-
-qint64 KoZipStore::write(const char* _data, qint64 _len)
-{
- Q_D(KoStore);
- if (_len == 0) return 0;
- if (!d->isOpen) {
- errorStore << "KoStore: You must open before writing" << endl;
- return 0;
- }
- if (d->mode != Write) {
- errorStore << "KoStore: Can not write to store that is opened for reading" << endl;
- return 0;
- }
-
- d->size += _len;
- if (m_pZip->writeData(_data, _len)) // writeData returns a bool!
- return _len;
- return 0;
-}
-
-QStringList KoZipStore::directoryList() const
-{
- QStringList retval;
- const KArchiveDirectory *directory = m_pZip->directory();
- Q_FOREACH (const QString &name, directory->entries()) {
- const KArchiveEntry* fileArchiveEntry = m_pZip->directory()->entry(name);
- if (fileArchiveEntry->isDirectory()) {
- retval << name;
- }
- }
- return retval;
-}
-
-bool KoZipStore::closeWrite()
-{
- Q_D(KoStore);
- debugStore << "Wrote file" << d->fileName << " into ZIP archive. size" << d->size;
- return m_pZip->finishWriting(d->size);
-}
-
-bool KoZipStore::enterRelativeDirectory(const QString& dirName)
-{
- Q_D(KoStore);
- if (d->mode == Read) {
- if (!m_currentDir) {
- m_currentDir = m_pZip->directory(); // initialize
- Q_ASSERT(d->currentPath.isEmpty());
- }
- const KArchiveEntry *entry = m_currentDir->entry(dirName);
- if (entry && entry->isDirectory()) {
- m_currentDir = dynamic_cast<const KArchiveDirectory*>(entry);
- return m_currentDir != 0;
- }
- return false;
- } else // Write, no checking here
- return true;
-}
-
-bool KoZipStore::enterAbsoluteDirectory(const QString& path)
-{
- if (path.isEmpty()) {
- m_currentDir = 0;
- return true;
- }
- m_currentDir = dynamic_cast<const KArchiveDirectory*>(m_pZip->directory()->entry(path));
- Q_ASSERT(m_currentDir);
- return m_currentDir != 0;
-}
-
-bool KoZipStore::fileExists(const QString& absPath) const
-{
- const KArchiveEntry *entry = m_pZip->directory()->entry(absPath);
- return entry && entry->isFile();
-}
diff --git a/libs/store/KoZipStore.h b/libs/store/KoZipStore.h
deleted file mode 100644
index b1a4c0409f..0000000000
--- a/libs/store/KoZipStore.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/* This file is part of the KDE project
- Copyright (C) 2002 David Faure <faure@kde.org>
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public License
- along with this library; see the file COPYING.LIB. If not, write to
- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
-*/
-
-#ifndef koZipStore_h
-#define koZipStore_h
-
-#include "KoStore.h"
-
-class SaveZip;
-class KArchiveDirectory;
-class QUrl;
-
-class KoZipStore : public KoStore
-{
-public:
- KoZipStore(const QString & _filename, Mode _mode, const QByteArray & appIdentification,
- bool writeMimetype = true);
- KoZipStore(QIODevice *dev, Mode mode, const QByteArray & appIdentification,
- bool writeMimetype = true);
- /**
- * QUrl-constructor
- * @todo saving not completely implemented (fixed temporary file)
- */
- KoZipStore(QWidget* window, const QUrl &_url, const QString & _filename, Mode _mode,
- const QByteArray & appIdentification, bool writeMimetype = true);
- ~KoZipStore() override;
-
- void setCompressionEnabled(bool e) override;
- qint64 write(const char* _data, qint64 _len) override;
-
- QStringList directoryList() const override;
-
-protected:
- void init(const QByteArray& appIdentification);
- bool doFinalize() override;
- bool openWrite(const QString& name) override;
- bool openRead(const QString& name) override;
- bool closeWrite() override;
- bool closeRead() override {
- return true;
- }
- bool enterRelativeDirectory(const QString& dirName) override;
- bool enterAbsoluteDirectory(const QString& path) override;
- bool fileExists(const QString& absPath) const override;
-
-private:
-
- // The archive
- SaveZip * m_pZip;
-
- // In "Read" mode this pointer is pointing to the current directory in the archive to speed up the verification process
- const KArchiveDirectory* m_currentDir;
-
- Q_DECLARE_PRIVATE(KoStore)
-};
-
-#endif
diff --git a/libs/store/Mainpage.dox b/libs/store/Mainpage.dox
index 3b55a442d3..f2e986a2c0 100644
--- a/libs/store/Mainpage.dox
+++ b/libs/store/Mainpage.dox
@@ -1,8 +1,8 @@
/**
* \mainpage
*
- * KritaStore is a library which contains a wrapper around karchive
+ * KritaStore is a library which contains a wrapper around quazip
*/
// DOXYGEN_SET_PROJECT_NAME = KritaStore
// DOXYGEN_SET_IGNORE_PREFIX = Ko K
diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt
index 5ed17e893b..d195f5f55d 100644
--- a/libs/ui/CMakeLists.txt
+++ b/libs/ui/CMakeLists.txt
@@ -1,596 +1,597 @@
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/qtlockedfile
- ${EXIV2_INCLUDE_DIR}
)
include_directories(SYSTEM
${EIGEN3_INCLUDE_DIR}
${OCIO_INCLUDE_DIR}
)
add_subdirectory( tests )
if (APPLE)
find_library(FOUNDATION_LIBRARY Foundation)
find_library(APPKIT_LIBRARY AppKit)
endif ()
set(kritaui_LIB_SRCS
canvas/kis_canvas_widget_base.cpp
canvas/kis_canvas2.cpp
canvas/kis_canvas_updates_compressor.cpp
canvas/kis_canvas_controller.cpp
canvas/kis_paintop_transformation_connector.cpp
canvas/kis_display_color_converter.cpp
canvas/kis_display_filter.cpp
canvas/kis_exposure_gamma_correction_interface.cpp
canvas/kis_tool_proxy.cpp
canvas/kis_canvas_decoration.cc
canvas/kis_coordinates_converter.cpp
canvas/kis_grid_manager.cpp
canvas/kis_grid_decoration.cpp
canvas/kis_grid_config.cpp
canvas/kis_prescaled_projection.cpp
canvas/kis_qpainter_canvas.cpp
canvas/kis_projection_backend.cpp
canvas/kis_update_info.cpp
canvas/kis_image_patch.cpp
canvas/kis_image_pyramid.cpp
canvas/kis_infinity_manager.cpp
canvas/kis_change_guides_command.cpp
canvas/kis_guides_decoration.cpp
canvas/kis_guides_manager.cpp
canvas/kis_guides_config.cpp
canvas/kis_snap_config.cpp
canvas/kis_snap_line_strategy.cpp
canvas/KisSnapPointStrategy.cpp
dialogs/kis_about_application.cpp
dialogs/kis_dlg_adj_layer_props.cc
dialogs/kis_dlg_adjustment_layer.cc
dialogs/kis_dlg_filter.cpp
dialogs/kis_dlg_generator_layer.cpp
dialogs/kis_dlg_file_layer.cpp
dialogs/kis_dlg_filter.cpp
dialogs/kis_dlg_stroke_selection_properties.cpp
dialogs/kis_dlg_image_properties.cc
dialogs/kis_dlg_layer_properties.cc
dialogs/kis_dlg_preferences.cc
dialogs/slider_and_spin_box_sync.cpp
dialogs/kis_dlg_blacklist_cleanup.cpp
dialogs/kis_dlg_layer_style.cpp
dialogs/kis_dlg_png_import.cpp
dialogs/kis_dlg_import_image_sequence.cpp
dialogs/kis_delayed_save_dialog.cpp
dialogs/KisSessionManagerDialog.cpp
dialogs/KisNewWindowLayoutDialog.cpp
flake/kis_node_dummies_graph.cpp
flake/kis_dummies_facade_base.cpp
flake/kis_dummies_facade.cpp
flake/kis_node_shapes_graph.cpp
flake/kis_node_shape.cpp
flake/kis_shape_controller.cpp
flake/kis_shape_layer.cc
flake/kis_shape_layer_canvas.cpp
flake/kis_shape_selection.cpp
flake/kis_shape_selection_canvas.cpp
flake/kis_shape_selection_model.cpp
flake/kis_take_all_shapes_command.cpp
brushhud/kis_uniform_paintop_property_widget.cpp
brushhud/kis_brush_hud.cpp
brushhud/kis_round_hud_button.cpp
brushhud/kis_dlg_brush_hud_config.cpp
brushhud/kis_brush_hud_properties_list.cpp
brushhud/kis_brush_hud_properties_config.cpp
kis_aspect_ratio_locker.cpp
kis_autogradient.cc
kis_bookmarked_configurations_editor.cc
kis_bookmarked_configurations_model.cc
kis_bookmarked_filter_configurations_model.cc
KisPaintopPropertiesBase.cpp
kis_canvas_resource_provider.cpp
kis_derived_resources.cpp
kis_categories_mapper.cpp
kis_categorized_list_model.cpp
kis_categorized_item_delegate.cpp
kis_clipboard.cc
kis_config.cc
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
kis_histogram_view.cc
KisImageBarrierLockerWithFeedback.cpp
kis_image_manager.cc
kis_image_view_converter.cpp
kis_import_catcher.cc
kis_layer_manager.cc
kis_mask_manager.cc
kis_mimedata.cpp
kis_node_commands_adapter.cpp
kis_node_manager.cpp
kis_node_juggler_compressed.cpp
kis_node_selection_adapter.cpp
kis_node_insertion_adapter.cpp
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
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
+
KisPaletteEditor.cpp
dialogs/KisDlgPaletteEditor.cpp
widgets/KisNewsWidget.cpp
widgets/KisGamutMaskToolbar.cpp
utils/kis_document_aware_spin_box_unit_manager.cpp
input/kis_input_manager.cpp
input/kis_input_manager_p.cpp
input/kis_extended_modifiers_mapper.cpp
input/kis_abstract_input_action.cpp
input/kis_tool_invocation_action.cpp
input/kis_pan_action.cpp
input/kis_alternate_invocation_action.cpp
input/kis_rotate_canvas_action.cpp
input/kis_zoom_action.cpp
input/kis_change_frame_action.cpp
input/kis_gamma_exposure_action.cpp
input/kis_show_palette_action.cpp
input/kis_change_primary_setting_action.cpp
input/kis_abstract_shortcut.cpp
input/kis_native_gesture_shortcut.cpp
input/kis_single_action_shortcut.cpp
input/kis_stroke_shortcut.cpp
input/kis_shortcut_matcher.cpp
input/kis_select_layer_action.cpp
input/KisQtWidgetsTweaker.cpp
input/KisInputActionGroup.cpp
operations/kis_operation.cpp
operations/kis_operation_configuration.cpp
operations/kis_operation_registry.cpp
operations/kis_operation_ui_factory.cpp
operations/kis_operation_ui_widget.cpp
operations/kis_filter_selection_operation.cpp
actions/kis_selection_action_factories.cpp
actions/KisPasteActionFactory.cpp
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
KisNodeDelegate.cpp
kis_node_view_visibility_delegate.cpp
KisNodeToolTip.cpp
KisNodeView.cpp
kis_node_view_color_scheme.cpp
KisImportExportFilter.cpp
KisFilterEntry.cpp
KisImportExportManager.cpp
KisImportExportUtils.cpp
kis_async_action_feedback.cpp
KisMainWindow.cpp
KisOpenPane.cpp
KisPart.cpp
KisPrintJob.cpp
KisTemplate.cpp
KisTemplateCreateDia.cpp
KisTemplateGroup.cpp
KisTemplates.cpp
KisTemplatesPane.cpp
KisTemplateTree.cpp
KisUndoActionsUpdateManager.cpp
KisView.cpp
thememanager.cpp
kis_mainwindow_observer.cpp
KisViewManager.cpp
kis_mirror_manager.cpp
qtlockedfile/qtlockedfile.cpp
qtsingleapplication/qtlocalpeer.cpp
qtsingleapplication/qtsingleapplication.cpp
KisResourceBundle.cpp
KisResourceBundleManifest.cpp
kis_md5_generator.cpp
KisApplicationArguments.cpp
KisNetworkAccessManager.cpp
KisMultiFeedRSSModel.cpp
KisRemoteFileFetcher.cpp
KisSaveGroupVisitor.cpp
KisWindowLayoutResource.cpp
KisWindowLayoutManager.cpp
KisSessionResource.cpp
KisReferenceImagesDecoration.cpp
KisReferenceImage.cpp
flake/KisReferenceImagesLayer.cpp
flake/KisReferenceImagesLayer.h
)
if(WIN32)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
input/wintab/kis_tablet_support_win.cpp
input/wintab/kis_screen_size_choice_dialog.cpp
qtlockedfile/qtlockedfile_win.cpp
input/wintab/kis_tablet_support_win8.cpp
)
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
)
if(NOT USE_QT_XCB)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
input/wintab/kis_tablet_support.cpp
)
endif()
if(NOT APPLE AND NOT USE_QT_XCB)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
input/wintab/qxcbconnection_xi2.cpp
input/wintab/qxcbconnection.cpp
input/wintab/kis_xi2_event_filter.cpp
)
endif()
endif()
if(APPLE)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
osx.mm
)
endif()
ki18n_wrap_ui(kritaui_LIB_SRCS
widgets/KoFillConfigWidget.ui
widgets/KoStrokeConfigWidget.ui
forms/wdgdlgpngimport.ui
forms/wdgfullscreensettings.ui
forms/wdgautogradient.ui
forms/wdggeneralsettings.ui
forms/wdgperformancesettings.ui
forms/wdggenerators.ui
forms/wdgbookmarkedconfigurationseditor.ui
forms/wdgapplyprofile.ui
forms/wdgcustompattern.ui
forms/wdglayerproperties.ui
forms/wdgcolorsettings.ui
forms/wdgtabletsettings.ui
forms/wdgcolorspaceselector.ui
forms/wdgcolorspaceselectoradvanced.ui
forms/wdgdisplaysettings.ui
forms/kis_previewwidgetbase.ui
forms/kis_matrix_widget.ui
forms/wdgselectionoptions.ui
forms/wdggeometryoptions.ui
forms/wdgnewimage.ui
forms/wdgimageproperties.ui
forms/wdgmaskfromselection.ui
forms/wdgmasksource.ui
forms/wdgfilterdialog.ui
forms/wdgmetadatamergestrategychooser.ui
forms/wdgpaintoppresets.ui
forms/wdgpaintopsettings.ui
forms/wdgdlggeneratorlayer.ui
forms/wdgdlgfilelayer.ui
forms/wdgfilterselector.ui
forms/wdgfilternodecreation.ui
forms/wdgmultipliersdoublesliderspinbox.ui
forms/wdgnodequerypatheditor.ui
forms/wdgpresetselectorstrip.ui
forms/wdgsavebrushpreset.ui
forms/wdgpreseticonlibrary.ui
forms/wdgdlgblacklistcleanup.ui
forms/wdgrectangleconstraints.ui
forms/wdgimportimagesequence.ui
forms/wdgstrokeselectionproperties.ui
forms/KisDetailsPaneBase.ui
forms/KisOpenPaneBase.ui
forms/wdgstopgradienteditor.ui
forms/wdgsessionmanager.ui
forms/wdgnewwindowlayout.ui
forms/KisWelcomePage.ui
forms/WdgDlgPaletteEditor.ui
forms/KisNewsPage.ui
forms/wdgGamutMaskToolbar.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
)
QT5_WRAP_CPP(kritaui_HEADERS_MOC KisNodePropertyAction_p.h)
add_library(kritaui SHARED ${kritaui_HEADERS_MOC} ${kritaui_LIB_SRCS} )
generate_export_header(kritaui BASE_NAME kritaui)
target_link_libraries(kritaui KF5::CoreAddons KF5::Completion KF5::I18n KF5::ItemViews Qt5::Network
- kritaimpex kritacolor kritaimage kritalibbrush kritawidgets kritawidgetutils ${PNG_LIBRARIES} ${EXIV2_LIBRARIES}
+ 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
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/canvas>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/flake>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/ora>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/tool>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/utils>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/widgets>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/input/wintab>
)
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/KisApplication.cpp b/libs/ui/KisApplication.cpp
index 25f62753f1..818b563eb8 100644
--- a/libs/ui/KisApplication.cpp
+++ b/libs/ui/KisApplication.cpp
@@ -1,821 +1,828 @@
/*
* Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
* Copyright (C) 2012 Boudewijn Rempt <boud@valdyas.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisApplication.h"
#include <stdlib.h>
#ifdef Q_OS_WIN
#include <windows.h>
#include <tchar.h>
#endif
#ifdef Q_OS_OSX
#include "osx.h"
#endif
#include <QStandardPaths>
#include <QDesktopWidget>
#include <QDir>
#include <QFile>
#include <QLocale>
#include <QMessageBox>
#include <QProcessEnvironment>
#include <QSettings>
#include <QStringList>
#include <QStyle>
#include <QStyleFactory>
#include <QSysInfo>
#include <QTimer>
#include <QWidget>
#include <klocalizedstring.h>
#include <kdesktopfile.h>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <KoColorSpaceRegistry.h>
#include <KoPluginLoader.h>
#include <KoShapeRegistry.h>
#include <KoDpi.h>
#include "KoConfig.h"
#include <resources/KoHashGeneratorProvider.h>
#include <KoResourcePaths.h>
#include <KisMimeDatabase.h>
#include "thememanager.h"
#include "KisPrintJob.h"
#include "KisDocument.h"
#include "KisMainWindow.h"
#include "KisAutoSaveRecoveryDialog.h"
#include "KisPart.h"
#include <kis_icon.h>
#include "kis_md5_generator.h"
#include "kis_splash_screen.h"
#include "kis_config.h"
#include "flake/kis_shape_selection.h"
#include <filter/kis_filter.h>
#include <filter/kis_filter_registry.h>
#include <filter/kis_filter_configuration.h>
#include <generator/kis_generator_registry.h>
#include <generator/kis_generator.h>
#include <brushengine/kis_paintop_registry.h>
#include <kis_meta_data_io_backend.h>
#include "kisexiv2/kis_exiv2.h"
#include "KisApplicationArguments.h"
#include <kis_debug.h>
#include "kis_action_registry.h"
#include <kis_brush_server.h>
#include <KisResourceServerProvider.h>
#include <KoResourceServerProvider.h>
#include "kis_image_barrier_locker.h"
#include "opengl/kis_opengl.h"
#include "kis_spin_box_unit_manager.h"
#include "kis_document_aware_spin_box_unit_manager.h"
#include "KisViewManager.h"
#include "kis_workspace_resource.h"
#include <KritaVersionWrapper.h>
#include <dialogs/KisSessionManagerDialog.h>
#include "widgets/KisScreenColorPicker.h"
#include "KisDlgInternalColorSelector.h"
namespace {
const QTime appStartTime(QTime::currentTime());
}
class KisApplication::Private
{
public:
Private() {}
QPointer<KisSplashScreen> splashScreen;
KisAutoSaveRecoveryDialog *autosaveDialog {0};
QPointer<KisMainWindow> mainWindow; // The first mainwindow we create on startup
bool batchRun {false};
};
class KisApplication::ResetStarting
{
public:
ResetStarting(KisSplashScreen *splash, int fileCount)
: m_splash(splash)
, m_fileCount(fileCount)
{
}
~ResetStarting() {
if (m_splash) {
m_splash->hide();
}
}
QPointer<KisSplashScreen> m_splash;
int m_fileCount;
};
KisApplication::KisApplication(const QString &key, int &argc, char **argv)
: QtSingleApplication(key, argc, argv)
, d(new Private)
{
#ifdef Q_OS_OSX
setMouseCoalescingEnabled(false);
#endif
QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath());
setApplicationDisplayName("Krita");
setApplicationName("krita");
// Note: Qt docs suggest we set this, but if we do, we get resource paths of the form of krita/krita, which is weird.
// setOrganizationName("krita");
setOrganizationDomain("krita.org");
QString version = KritaVersionWrapper::versionString(true);
setApplicationVersion(version);
setWindowIcon(KisIconUtils::loadIcon("calligrakrita"));
if (qgetenv("KRITA_NO_STYLE_OVERRIDE").isEmpty()) {
QStringList styles = QStringList() << "breeze" << "fusion" << "plastique";
if (!styles.contains(style()->objectName().toLower())) {
Q_FOREACH (const QString & style, styles) {
if (!setStyle(style)) {
qDebug() << "No" << style << "available.";
}
else {
qDebug() << "Set style" << style;
break;
}
}
}
}
else {
qDebug() << "Style override disabled, using" << style()->objectName();
}
KisOpenGL::initialize();
}
#if defined(Q_OS_WIN) && defined(ENV32BIT)
typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
LPFN_ISWOW64PROCESS fnIsWow64Process;
BOOL isWow64()
{
BOOL bIsWow64 = FALSE;
//IsWow64Process is not available on all supported versions of Windows.
//Use GetModuleHandle to get a handle to the DLL that contains the function
//and GetProcAddress to get a pointer to the function if available.
fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(
GetModuleHandle(TEXT("kernel32")),"IsWow64Process");
if(0 != fnIsWow64Process)
{
if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64))
{
//handle error
}
}
return bIsWow64;
}
#endif
void KisApplication::initializeGlobals(const KisApplicationArguments &args)
{
int dpiX = args.dpiX();
int dpiY = args.dpiY();
if (dpiX > 0 && dpiY > 0) {
KoDpi::setDPI(dpiX, dpiY);
}
}
void KisApplication::addResourceTypes()
{
// qDebug() << "addResourceTypes();";
// All Krita's resource types
KoResourcePaths::addResourceType("markers", "data", "/styles/");
KoResourcePaths::addResourceType("kis_pics", "data", "/pics/");
KoResourcePaths::addResourceType("kis_images", "data", "/images/");
KoResourcePaths::addResourceType("metadata_schema", "data", "/metadata/schemas/");
KoResourcePaths::addResourceType("kis_brushes", "data", "/brushes/");
KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/");
KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/");
KoResourcePaths::addResourceType("gmic_definitions", "data", "/gmic/");
KoResourcePaths::addResourceType("kis_resourcebundles", "data", "/bundles/");
KoResourcePaths::addResourceType("kis_defaultpresets", "data", "/defaultpresets/");
KoResourcePaths::addResourceType("kis_paintoppresets", "data", "/paintoppresets/");
KoResourcePaths::addResourceType("kis_workspaces", "data", "/workspaces/");
KoResourcePaths::addResourceType("kis_windowlayouts", "data", "/windowlayouts/");
KoResourcePaths::addResourceType("kis_sessions", "data", "/sessions/");
KoResourcePaths::addResourceType("psd_layer_style_collections", "data", "/asl");
KoResourcePaths::addResourceType("ko_patterns", "data", "/patterns/", true);
KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/");
KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/", true);
KoResourcePaths::addResourceType("ko_palettes", "data", "/palettes/", true);
KoResourcePaths::addResourceType("kis_shortcuts", "data", "/shortcuts/");
KoResourcePaths::addResourceType("kis_actions", "data", "/actions");
KoResourcePaths::addResourceType("icc_profiles", "data", "/color/icc");
KoResourcePaths::addResourceType("icc_profiles", "data", "/profiles/");
KoResourcePaths::addResourceType("ko_effects", "data", "/effects/");
KoResourcePaths::addResourceType("tags", "data", "/tags/");
KoResourcePaths::addResourceType("templates", "data", "/templates");
KoResourcePaths::addResourceType("pythonscripts", "data", "/pykrita");
KoResourcePaths::addResourceType("symbols", "data", "/symbols");
KoResourcePaths::addResourceType("preset_icons", "data", "/preset_icons");
KoResourcePaths::addResourceType("ko_gamutmasks", "data", "/gamutmasks/", true);
// // Extra directories to look for create resources. (Does anyone actually use that anymore?)
// KoResourcePaths::addResourceDir("ko_gradients", "/usr/share/create/gradients/gimp");
// KoResourcePaths::addResourceDir("ko_gradients", QDir::homePath() + QString("/.create/gradients/gimp"));
// KoResourcePaths::addResourceDir("ko_patterns", "/usr/share/create/patterns/gimp");
// KoResourcePaths::addResourceDir("ko_patterns", QDir::homePath() + QString("/.create/patterns/gimp"));
// KoResourcePaths::addResourceDir("kis_brushes", "/usr/share/create/brushes/gimp");
// KoResourcePaths::addResourceDir("kis_brushes", QDir::homePath() + QString("/.create/brushes/gimp"));
// KoResourcePaths::addResourceDir("ko_palettes", "/usr/share/create/swatches");
// KoResourcePaths::addResourceDir("ko_palettes", QDir::homePath() + QString("/.create/swatches"));
// Make directories for all resources we can save, and tags
QDir d;
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tags/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/asl/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/bundles/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/brushes/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/gradients/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/paintoppresets/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/palettes/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/patterns/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/taskset/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/workspaces/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/input/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/pykrita/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/symbols/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/color-schemes/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/preset_icons/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/preset_icons/tool_icons/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/preset_icons/emblem_icons/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/gamutmasks/");
}
void KisApplication::loadResources()
{
// qDebug() << "loadResources();";
setSplashScreenLoadingText(i18n("Loading Resources..."));
processEvents();
KoResourceServerProvider::instance();
setSplashScreenLoadingText(i18n("Loading Brush Presets..."));
processEvents();
KisResourceServerProvider::instance();
setSplashScreenLoadingText(i18n("Loading Brushes..."));
processEvents();
KisBrushServer::instance()->brushServer();
setSplashScreenLoadingText(i18n("Loading Bundles..."));
processEvents();
KisResourceBundleServerProvider::instance();
}
void KisApplication::loadResourceTags()
{
// qDebug() << "loadResourceTags()";
KoResourceServerProvider::instance()->patternServer()->loadTags();
KoResourceServerProvider::instance()->gradientServer()->loadTags();
KoResourceServerProvider::instance()->paletteServer()->loadTags();
KoResourceServerProvider::instance()->svgSymbolCollectionServer()->loadTags();
KisBrushServer::instance()->brushServer()->loadTags();
KisResourceServerProvider::instance()->workspaceServer()->loadTags();
KisResourceServerProvider::instance()->layerStyleCollectionServer()->loadTags();
KisResourceBundleServerProvider::instance()->resourceBundleServer()->loadTags();
KisResourceServerProvider::instance()->paintOpPresetServer()->loadTags();
KisResourceServerProvider::instance()->paintOpPresetServer()->clearOldSystemTags();
}
void KisApplication::loadPlugins()
{
// qDebug() << "loadPlugins();";
KoShapeRegistry* r = KoShapeRegistry::instance();
r->add(new KisShapeSelectionFactory());
KisActionRegistry::instance();
KisFilterRegistry::instance();
KisGeneratorRegistry::instance();
KisPaintOpRegistry::instance();
KoColorSpaceRegistry::instance();
}
void KisApplication::loadGuiPlugins()
{
// qDebug() << "loadGuiPlugins();";
// Load the krita-specific tools
setSplashScreenLoadingText(i18n("Loading Plugins for Krita/Tool..."));
processEvents();
// qDebug() << "loading tools";
KoPluginLoader::instance()->load(QString::fromLatin1("Krita/Tool"),
QString::fromLatin1("[X-Krita-Version] == 28"));
// Load dockers
setSplashScreenLoadingText(i18n("Loading Plugins for Krita/Dock..."));
processEvents();
// qDebug() << "loading dockers";
KoPluginLoader::instance()->load(QString::fromLatin1("Krita/Dock"),
QString::fromLatin1("[X-Krita-Version] == 28"));
// XXX_EXIV: make the exiv io backends real plugins
setSplashScreenLoadingText(i18n("Loading Plugins Exiv/IO..."));
processEvents();
// qDebug() << "loading exiv2";
KisExiv2::initialize();
}
bool KisApplication::start(const KisApplicationArguments &args)
{
KisConfig cfg(false);
#if defined(Q_OS_WIN)
#ifdef ENV32BIT
if (isWow64() && !cfg.readEntry("WarnedAbout32Bits", false)) {
QMessageBox::information(0,
i18nc("@title:window", "Krita: Warning"),
i18n("You are running a 32 bits build on a 64 bits Windows.\n"
"This is not recommended.\n"
"Please download and install the x64 build instead."));
cfg.writeEntry("WarnedAbout32Bits", true);
}
#endif
#endif
QString opengl = cfg.canvasState();
if (opengl == "OPENGL_NOT_TRIED" ) {
cfg.setCanvasState("TRY_OPENGL");
}
else if (opengl != "OPENGL_SUCCESS") {
cfg.setCanvasState("OPENGL_FAILED");
}
setSplashScreenLoadingText(i18n("Initializing Globals"));
processEvents();
initializeGlobals(args);
const bool doNewImage = args.doNewImage();
const bool doTemplate = args.doTemplate();
const bool exportAs = args.exportAs();
const QString exportFileName = args.exportFileName();
d->batchRun = (exportAs || !exportFileName.isEmpty());
const bool needsMainWindow = !exportAs;
// only show the mainWindow when no command-line mode option is passed
bool showmainWindow = !exportAs; // would be !batchRun;
const bool showSplashScreen = !d->batchRun && qEnvironmentVariableIsEmpty("NOSPLASH");
if (showSplashScreen && d->splashScreen) {
d->splashScreen->show();
d->splashScreen->repaint();
processEvents();
}
KoHashGeneratorProvider::instance()->setGenerator("MD5", new KisMD5Generator());
KConfigGroup group(KSharedConfig::openConfig(), "theme");
Digikam::ThemeManager themeManager;
themeManager.setCurrentTheme(group.readEntry("Theme", "Krita dark"));
ResetStarting resetStarting(d->splashScreen, args.filenames().count()); // remove the splash when done
Q_UNUSED(resetStarting);
// Make sure we can save resources and tags
setSplashScreenLoadingText(i18n("Adding resource types"));
processEvents();
addResourceTypes();
// Load the plugins
loadPlugins();
// Load all resources
loadResources();
// Load all the tags
loadResourceTags();
// Load the gui plugins
loadGuiPlugins();
KisPart *kisPart = KisPart::instance();
if (needsMainWindow) {
// show a mainWindow asap, if we want that
setSplashScreenLoadingText(i18n("Loading Main Window..."));
processEvents();
bool sessionNeeded = true;
auto sessionMode = cfg.sessionOnStartup();
if (!args.session().isEmpty()) {
sessionNeeded = !kisPart->restoreSession(args.session());
} else if (sessionMode == KisConfig::SOS_ShowSessionManager) {
showmainWindow = false;
sessionNeeded = false;
kisPart->showSessionManager();
} else if (sessionMode == KisConfig::SOS_PreviousSession) {
KConfigGroup sessionCfg = KSharedConfig::openConfig()->group("session");
const QString &sessionName = sessionCfg.readEntry("previousSession");
sessionNeeded = !kisPart->restoreSession(sessionName);
}
if (sessionNeeded) {
kisPart->startBlankSession();
}
if (!args.windowLayout().isEmpty()) {
KoResourceServer<KisWindowLayoutResource> * rserver = KisResourceServerProvider::instance()->windowLayoutServer();
KisWindowLayoutResource* windowLayout = rserver->resourceByName(args.windowLayout());
if (windowLayout) {
windowLayout->applyLayout();
}
}
if (showmainWindow) {
d->mainWindow = kisPart->currentMainwindow();
if (!args.workspace().isEmpty()) {
KoResourceServer<KisWorkspaceResource> * rserver = KisResourceServerProvider::instance()->workspaceServer();
KisWorkspaceResource* workspace = rserver->resourceByName(args.workspace());
if (workspace) {
d->mainWindow->restoreWorkspace(workspace);
}
}
if (args.canvasOnly()) {
d->mainWindow->viewManager()->switchCanvasOnly(true);
}
if (args.fullScreen()) {
d->mainWindow->showFullScreen();
}
} else {
d->mainWindow = kisPart->createMainWindow();
}
}
short int numberOfOpenDocuments = 0; // number of documents open
// Check for autosave files that can be restored, if we're not running a batchrun (test)
if (!d->batchRun) {
checkAutosaveFiles();
}
setSplashScreenLoadingText(QString()); // done loading, so clear out label
processEvents();
//configure the unit manager
KisSpinBoxUnitManagerFactory::setDefaultUnitManagerBuilder(new KisDocumentAwareSpinBoxUnitManagerBuilder());
connect(this, &KisApplication::aboutToQuit, &KisSpinBoxUnitManagerFactory::clearUnitManagerBuilder); //ensure the builder is destroyed when the application leave.
//the new syntax slot syntax allow to connect to a non q_object static method.
// Create a new image, if needed
if (doNewImage) {
KisDocument *doc = args.image();
if (doc) {
kisPart->addDocument(doc);
d->mainWindow->addViewAndNotifyLoadingCompleted(doc);
}
}
// Get the command line arguments which we have to parse
int argsCount = args.filenames().count();
if (argsCount > 0) {
// Loop through arguments
for (int argNumber = 0; argNumber < argsCount; argNumber++) {
QString fileName = args.filenames().at(argNumber);
// are we just trying to open a template?
if (doTemplate) {
// called in mix with batch options? ignore and silently skip
if (d->batchRun) {
continue;
}
if (createNewDocFromTemplate(fileName, d->mainWindow)) {
++numberOfOpenDocuments;
}
// now try to load
}
else {
if (exportAs) {
QString outputMimetype = KisMimeDatabase::mimeTypeForFile(exportFileName, false);
if (outputMimetype == "application/octetstream") {
dbgKrita << i18n("Mimetype not found, try using the -mimetype option") << endl;
return 1;
}
KisDocument *doc = kisPart->createDocument();
doc->setFileBatchMode(d->batchRun);
doc->openUrl(QUrl::fromLocalFile(fileName));
qApp->processEvents(); // For vector layers to be updated
doc->setFileBatchMode(true);
if (!doc->exportDocumentSync(QUrl::fromLocalFile(exportFileName), outputMimetype.toLatin1())) {
dbgKrita << "Could not export " << fileName << "to" << exportFileName << ":" << doc->errorMessage();
}
QTimer::singleShot(0, this, SLOT(quit()));
}
else if (d->mainWindow) {
if (fileName.endsWith(".bundle")) {
d->mainWindow->installBundle(fileName);
}
else {
KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None;
if (d->mainWindow->openDocument(QUrl::fromLocalFile(fileName), flags)) {
// Normal case, success
numberOfOpenDocuments++;
}
}
}
}
}
}
// fixes BUG:369308 - Krita crashing on splash screen when loading.
// trying to open a file before Krita has loaded can cause it to hang and crash
if (d->splashScreen) {
d->splashScreen->displayLinks(true);
d->splashScreen->displayRecentFiles(true);
}
// not calling this before since the program will quit there.
return true;
}
KisApplication::~KisApplication()
{
}
void KisApplication::setSplashScreen(QWidget *splashScreen)
{
d->splashScreen = qobject_cast<KisSplashScreen*>(splashScreen);
}
void KisApplication::setSplashScreenLoadingText(QString textToLoad)
{
if (d->splashScreen) {
//d->splashScreen->loadingLabel->setText(textToLoad);
d->splashScreen->setLoadingText(textToLoad);
d->splashScreen->repaint();
}
}
void KisApplication::hideSplashScreen()
{
if (d->splashScreen) {
// hide the splashscreen to see the dialog
d->splashScreen->hide();
}
}
bool KisApplication::notify(QObject *receiver, QEvent *event)
{
try {
return QApplication::notify(receiver, event);
} catch (std::exception &e) {
qWarning("Error %s sending event %i to object %s",
e.what(), event->type(), qPrintable(receiver->objectName()));
} catch (...) {
qWarning("Error <unknown> sending event %i to object %s",
event->type(), qPrintable(receiver->objectName()));
}
return false;
}
void KisApplication::remoteArguments(QByteArray message, QObject *socket)
{
Q_UNUSED(socket);
// check if we have any mainwindow
KisMainWindow *mw = qobject_cast<KisMainWindow*>(qApp->activeWindow());
if (!mw) {
mw = KisPart::instance()->mainWindows().first();
}
if (!mw) {
return;
}
KisApplicationArguments args = KisApplicationArguments::deserialize(message);
const bool doTemplate = args.doTemplate();
const int argsCount = args.filenames().count();
if (argsCount > 0) {
// Loop through arguments
for (int argNumber = 0; argNumber < argsCount; ++argNumber) {
QString filename = args.filenames().at(argNumber);
// are we just trying to open a template?
if (doTemplate) {
createNewDocFromTemplate(filename, mw);
}
else if (QFile(filename).exists()) {
KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None;
mw->openDocument(QUrl::fromLocalFile(filename), flags);
}
}
}
}
void KisApplication::fileOpenRequested(const QString &url)
{
KisMainWindow *mainWindow = KisPart::instance()->mainWindows().first();
if (mainWindow) {
KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None;
mainWindow->openDocument(QUrl::fromLocalFile(url), flags);
}
}
void KisApplication::checkAutosaveFiles()
{
if (d->batchRun) return;
- // Check for autosave files from a previous run. There can be several, and
- // we want to offer a restore for every one. Including a nice thumbnail!
-
- QStringList filters;
- filters << QString(".krita-*-*-autosave.kra");
-
#ifdef Q_OS_WIN
QDir dir = QDir::temp();
#else
QDir dir = QDir::home();
#endif
+ // Check for autosave files from a previous run. There can be several, and
+ // we want to offer a restore for every one. Including a nice thumbnail!
+
+ // Hidden autosave files
+ QStringList filters = QStringList() << QString(".krita-*-*-autosave.kra");
+
// all autosave files for our application
QStringList autosaveFiles = dir.entryList(filters, QDir::Files | QDir::Hidden);
+ // Visibile autosave files
+ filters = QStringList() << QString("krita-*-*-autosave.kra");
+ autosaveFiles += dir.entryList(filters, QDir::Files);
+
// Allow the user to make their selection
if (autosaveFiles.size() > 0) {
if (d->splashScreen) {
// hide the splashscreen to see the dialog
d->splashScreen->hide();
}
d->autosaveDialog = new KisAutoSaveRecoveryDialog(autosaveFiles, activeWindow());
QDialog::DialogCode result = (QDialog::DialogCode) d->autosaveDialog->exec();
if (result == QDialog::Accepted) {
QStringList filesToRecover = d->autosaveDialog->recoverableFiles();
Q_FOREACH (const QString &autosaveFile, autosaveFiles) {
if (!filesToRecover.contains(autosaveFile)) {
QFile::remove(dir.absolutePath() + "/" + autosaveFile);
}
}
autosaveFiles = filesToRecover;
} else {
autosaveFiles.clear();
}
if (autosaveFiles.size() > 0) {
QList<QUrl> autosaveUrls;
Q_FOREACH (const QString &autoSaveFile, autosaveFiles) {
const QUrl url = QUrl::fromLocalFile(dir.absolutePath() + QLatin1Char('/') + autoSaveFile);
autosaveUrls << url;
}
if (d->mainWindow) {
Q_FOREACH (const QUrl &url, autosaveUrls) {
KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None;
d->mainWindow->openDocument(url, flags | KisMainWindow::RecoveryFile);
}
}
}
// cleanup
delete d->autosaveDialog;
d->autosaveDialog = nullptr;
}
}
bool KisApplication::createNewDocFromTemplate(const QString &fileName, KisMainWindow *mainWindow)
{
QString templatePath;
const QUrl templateUrl = QUrl::fromLocalFile(fileName);
if (QFile::exists(fileName)) {
templatePath = templateUrl.toLocalFile();
dbgUI << "using full path...";
}
else {
QString desktopName(fileName);
const QString templatesResourcePath = QStringLiteral("templates/");
QStringList paths = KoResourcePaths::findAllResources("data", templatesResourcePath + "*/" + desktopName);
if (paths.isEmpty()) {
paths = KoResourcePaths::findAllResources("data", templatesResourcePath + desktopName);
}
if (paths.isEmpty()) {
QMessageBox::critical(0, i18nc("@title:window", "Krita"),
i18n("No template found for: %1", desktopName));
} else if (paths.count() > 1) {
QMessageBox::critical(0, i18nc("@title:window", "Krita"),
i18n("Too many templates found for: %1", desktopName));
} else {
templatePath = paths.at(0);
}
}
if (!templatePath.isEmpty()) {
QUrl templateBase;
templateBase.setPath(templatePath);
KDesktopFile templateInfo(templatePath);
QString templateName = templateInfo.readUrl();
QUrl templateURL;
templateURL.setPath(templateBase.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path() + '/' + templateName);
+ if (templateURL.scheme().isEmpty()) {
+ templateURL.setScheme("file");
+ }
KisMainWindow::OpenFlags batchFlags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None;
if (mainWindow->openDocument(templateURL, KisMainWindow::Import | batchFlags)) {
dbgUI << "Template loaded...";
return true;
}
else {
QMessageBox::critical(0, i18nc("@title:window", "Krita"),
i18n("Template %1 failed to load.", templateURL.toDisplayString()));
}
}
return false;
}
void KisApplication::clearConfig()
{
KIS_ASSERT_RECOVER_RETURN(qApp->thread() == QThread::currentThread());
KSharedConfigPtr config = KSharedConfig::openConfig();
// find user settings file
bool createDir = false;
QString kritarcPath = KoResourcePaths::locateLocal("config", "kritarc", createDir);
QFile configFile(kritarcPath);
if (configFile.exists()) {
// clear file
if (configFile.open(QFile::WriteOnly)) {
configFile.close();
}
else {
QMessageBox::warning(0,
i18nc("@title:window", "Krita"),
i18n("Failed to clear %1\n\n"
"Please make sure no other program is using the file and try again.",
kritarcPath),
QMessageBox::Ok, QMessageBox::Ok);
}
}
// reload from disk; with the user file settings cleared,
// this should load any default configuration files shipping with the program
config->reparseConfiguration();
config->sync();
}
void KisApplication::askClearConfig()
{
Qt::KeyboardModifiers mods = QApplication::queryKeyboardModifiers();
bool askClearConfig = (mods & Qt::ControlModifier) && (mods & Qt::ShiftModifier) && (mods & Qt::AltModifier);
if (askClearConfig) {
bool ok = QMessageBox::question(0,
i18nc("@title:window", "Krita"),
i18n("Do you want to clear the settings file?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes;
if (ok) {
clearConfig();
}
}
}
diff --git a/libs/ui/KisApplicationArguments.cpp b/libs/ui/KisApplicationArguments.cpp
index df58592699..a2029e0f9e 100644
--- a/libs/ui/KisApplicationArguments.cpp
+++ b/libs/ui/KisApplicationArguments.cpp
@@ -1,336 +1,336 @@
/*
* Copyright (c) 2015 Boudewijn Rempt <boud@valdyas.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisApplicationArguments.h"
#include <QCommandLineParser>
#include <QCommandLineOption>
#include <QApplication>
#include <QDir>
#include <QStringList>
#include <QString>
#include <QDebug>
#include <QDataStream>
#include <QBuffer>
#include <klocalizedstring.h>
#include <KisPart.h>
#include <KisDocument.h>
struct Q_DECL_HIDDEN KisApplicationArguments::Private
{
Private()
{
}
QStringList filenames;
int dpiX {72};
int dpiY {72};
bool doTemplate {false};
bool exportAs {false};
QString exportFileName;
QString workspace;
QString windowLayout;
QString session;
bool canvasOnly {false};
bool noSplash {false};
bool fullScreen {false};
bool newImage {false};
QString colorModel {"RGBA"};
QString colorDepth {"U8"};
int width {2000};
int height {5000};
};
KisApplicationArguments::KisApplicationArguments()
: d(new Private)
{
}
KisApplicationArguments::~KisApplicationArguments()
{
}
KisApplicationArguments::KisApplicationArguments(const QApplication &app)
: d(new Private)
{
QCommandLineParser parser;
parser.addVersionOption();
parser.addHelpOption();
parser.addOption(QCommandLineOption(QStringList() << QLatin1String("template"), i18n("Open a new document with a template")));
parser.addOption(QCommandLineOption(QStringList() << QLatin1String("new-image"), i18n("Create a new image on startup.\n"
"Possible colorspace values are:\n"
" * RGBA\n"
" * XYZA\n"
" * LABA\n"
" * CMYKA\n"
" * GRAY\n"
" * YCbCrA\n"
"Possible channel depth arguments are\n"
" * U8 (8 bits integer)\n"
" * U16 (16 bits integer)\n"
" * F16 (16 bits floating point)\n"
" * F32 (32 bits floating point)\n"),
QLatin1String("colorspace,depth,width,height")));
parser.addOption(QCommandLineOption(QStringList() << QLatin1String("workspace"), i18n("The name of the workspace to open Krita with"), QLatin1String("workspace")));
parser.addOption(QCommandLineOption(QStringList() << QLatin1String("windowlayout"), i18n("The name of the window layout to open Krita with"), QLatin1String("windowlayout")));
parser.addOption(QCommandLineOption(QStringList() << QLatin1String("load-session"), i18n("The name of the session to open Krita with"), QLatin1String("load-session"))); // NB: the argument "session" is already used by QGuiApplication
parser.addOption(QCommandLineOption(QStringList() << QLatin1String("canvasonly"), i18n("Start Krita in canvas-only mode")));
parser.addOption(QCommandLineOption(QStringList() << QLatin1String("nosplash"), i18n("Do not show the splash screen")));
parser.addOption(QCommandLineOption(QStringList() << QLatin1String("fullscreen"), i18n("Start Krita in full-screen mode")));
parser.addOption(QCommandLineOption(QStringList() << QLatin1String("dpi"), i18n("Override display DPI"), QLatin1String("dpiX,dpiY")));
parser.addOption(QCommandLineOption(QStringList() << QLatin1String("export"), i18n("Export to the given filename and exit")));
parser.addOption(QCommandLineOption(QStringList() << QLatin1String("export-filename"), i18n("Filename for export"), QLatin1String("filename")));
parser.addPositionalArgument(QLatin1String("[file(s)]"), i18n("File(s) or URL(s) to open"));
parser.process(app);
QString dpiValues = parser.value("dpi");
if (!dpiValues.isEmpty()) {
int sep = dpiValues.indexOf(QRegExp("[x, ]"));
bool ok = true;
if (sep != -1) {
d->dpiY = dpiValues.mid(sep + 1).toInt(&ok);
dpiValues.truncate(sep);
}
if (ok) {
d->dpiX = dpiValues.toInt(&ok);
if (ok) {
if (!d->dpiY)
d->dpiY = d->dpiX;
}
}
}
QString newImageValues = parser.value("new-image");
d->newImage = !newImageValues.isEmpty();
if (d->newImage) {
QStringList v = newImageValues.split(",");
if (v.size() != 4) {
d->newImage = false;
qWarning() << "Cannot create a new image: please specify colormodel, depth, width and height.";
}
d->colorModel = v[0].toUpper();
d->colorDepth = v[1].toUpper();
d->width = v[2].toInt();
d->height = v[3].toInt();
}
d->exportFileName = parser.value("export-filename");
d->workspace = parser.value("workspace");
d->windowLayout = parser.value("windowlayout");
d->session = parser.value("load-session");
d->doTemplate = parser.isSet("template");
d->exportAs = parser.isSet("export");
d->canvasOnly = parser.isSet("canvasonly");
d->noSplash = parser.isSet("nosplash");
d->fullScreen = parser.isSet("fullscreen");
const QDir currentDir = QDir::current();
Q_FOREACH (const QString &filename, parser.positionalArguments()) {
d->filenames << currentDir.absoluteFilePath(filename);
}
}
KisApplicationArguments::KisApplicationArguments(const KisApplicationArguments &rhs)
: d(new Private)
{
d->filenames = rhs.filenames();
d->dpiX = rhs.dpiX();
d->dpiY = rhs.dpiY();
d->doTemplate = rhs.doTemplate();
d->exportAs = rhs.exportAs();
d->exportFileName = rhs.exportFileName();
d->canvasOnly = rhs.canvasOnly();
d->workspace = rhs.workspace();
d->windowLayout = rhs.windowLayout();
d->session = rhs.session();
d->noSplash = rhs.noSplash();
d->fullScreen = rhs.fullScreen();
}
void KisApplicationArguments::operator=(const KisApplicationArguments &rhs)
{
d->filenames = rhs.filenames();
d->dpiX = rhs.dpiX();
d->dpiY = rhs.dpiY();
d->doTemplate = rhs.doTemplate();
d->exportAs = rhs.exportAs();
d->exportFileName = rhs.exportFileName();
d->canvasOnly = rhs.canvasOnly();
d->workspace = rhs.workspace();
d->windowLayout = rhs.windowLayout();
d->session = rhs.session();
d->noSplash = rhs.noSplash();
d->fullScreen = rhs.fullScreen();
}
QByteArray KisApplicationArguments::serialize()
{
QByteArray ba;
QBuffer buf(&ba);
buf.open(QIODevice::WriteOnly);
QDataStream ds(&buf);
ds.setVersion(QDataStream::Qt_5_0);
ds << d->filenames.count();
Q_FOREACH (const QString &filename, d->filenames) {
ds << filename;
}
ds << d->dpiX;
ds << d->dpiY;
ds << d->doTemplate;
ds << d->exportAs;
ds << d->exportFileName;
ds << d->workspace;
ds << d->windowLayout;
ds << d->session;
ds << d->canvasOnly;
ds << d->noSplash;
ds << d->fullScreen;
ds << d->newImage;
ds << d->height;
ds << d->width;
ds << d->height;
ds << d->colorModel;
ds << d->colorDepth;
buf.close();
return ba;
}
KisApplicationArguments KisApplicationArguments::deserialize(QByteArray &serialized)
{
KisApplicationArguments args;
QBuffer buf(&serialized);
buf.open(QIODevice::ReadOnly);
QDataStream ds(&buf);
ds.setVersion(QDataStream::Qt_5_0);
int count;
ds >> count;
for(int i = 0; i < count; ++i) {
QString s;
ds >> s;
args.d->filenames << s;
}
ds >> args.d->dpiX;
ds >> args.d->dpiY;
ds >> args.d->doTemplate;
ds >> args.d->exportAs;
ds >> args.d->exportFileName;
ds >> args.d->workspace;
ds >> args.d->windowLayout;
ds >> args.d->session;
ds >> args.d->canvasOnly;
ds >> args.d->noSplash;
ds >> args.d->fullScreen;
ds >> args.d->newImage;
ds >> args.d->height;
ds >> args.d->width;
ds >> args.d->height;
ds >> args.d->colorModel;
ds >> args.d->colorDepth;
buf.close();
return args;
}
QStringList KisApplicationArguments::filenames() const
{
return d->filenames;
}
int KisApplicationArguments::dpiX() const
{
return d->dpiX;
}
int KisApplicationArguments::dpiY() const
{
return d->dpiY;
}
bool KisApplicationArguments::doTemplate() const
{
return d->doTemplate;
}
bool KisApplicationArguments::exportAs() const
{
return d->exportAs;
}
QString KisApplicationArguments::exportFileName() const
{
return d->exportFileName;
}
QString KisApplicationArguments::workspace() const
{
return d->workspace;
}
QString KisApplicationArguments::windowLayout() const
{
return d->windowLayout;
}
QString KisApplicationArguments::session() const
{
return d->session;
}
bool KisApplicationArguments::canvasOnly() const
{
return d->canvasOnly;
}
bool KisApplicationArguments::noSplash() const
{
return d->noSplash;
}
bool KisApplicationArguments::fullScreen() const
{
return d->fullScreen;
}
bool KisApplicationArguments::doNewImage() const
{
return d->newImage;
}
KisDocument *KisApplicationArguments::image() const
{
KisDocument *doc = KisPart::instance()->createDocument();
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace(d->colorModel, d->colorDepth, "");
if (!cs) {
qWarning() << "Could not create the colorspace for the new image. Check the colorspace and depth arguments.";
return 0;
}
- doc->newImage(i18n("Unnamed"), d->width, d->height, cs, KoColor(QColor(Qt::white), cs), false, 1, "", 100.0);
+ doc->newImage(i18n("Unnamed"), d->width, d->height, cs, KoColor(QColor(Qt::white), cs), KisConfig::CANVAS_COLOR, 1, "", 100.0);
return doc;
}
diff --git a/libs/ui/KisDecorationsManager.cpp b/libs/ui/KisDecorationsManager.cpp
index c7ddcf0ccb..fd6aed8c25 100644
--- a/libs/ui/KisDecorationsManager.cpp
+++ b/libs/ui/KisDecorationsManager.cpp
@@ -1,128 +1,128 @@
/*
* Copyright (c) 2009 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2014 Sven Langkamp <sven.langkamp@gmail.com>
*
* 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 "KisDecorationsManager.h"
#include "KisViewManager.h"
#include "kis_action_manager.h"
#include "kis_action.h"
#include "kis_canvas2.h"
#include <klocalizedstring.h>
#include <kguiitem.h>
#include <ktoggleaction.h>
#include <kactioncollection.h>
KisDecorationsManager::KisDecorationsManager(KisViewManager* view)
: QObject(view)
, m_imageView(0)
{
}
KisDecorationsManager::~KisDecorationsManager()
{
}
void KisDecorationsManager::setup(KisActionManager * actionManager)
{
m_toggleAssistant = actionManager->createAction("view_toggle_painting_assistants");
m_togglePreview = actionManager->createAction("view_toggle_assistant_previews");
m_toggleReferenceImages = actionManager->createAction("view_toggle_reference_images");
updateAction();
}
void KisDecorationsManager::setView(QPointer<KisView> imageView)
{
// set view is called twice when a document is open, so we need to disconnect the original signals
// if m_imageView has already been created. This prevents double signal events firing
if (m_imageView) {
m_toggleAssistant->disconnect();
m_togglePreview->disconnect();
if (assistantsDecoration()) {
assistantsDecoration()->disconnect(this);
}
if (referenceImagesDecoration()) {
referenceImagesDecoration()->disconnect(this);
}
}
m_imageView = imageView;
if (m_imageView && !referenceImagesDecoration()) {
KisReferenceImagesDecoration *deco = new KisReferenceImagesDecoration(m_imageView, imageView->document());
m_imageView->canvasBase()->addDecoration(deco);
}
if (m_imageView && !assistantsDecoration()) {
KisPaintingAssistantsDecoration *deco = new KisPaintingAssistantsDecoration(m_imageView);
m_imageView->canvasBase()->addDecoration(deco);
}
if (m_imageView && assistantsDecoration()) {
connect(m_toggleAssistant, SIGNAL(triggered()), assistantsDecoration(), SLOT(toggleAssistantVisible()));
connect(m_togglePreview, SIGNAL(triggered()), assistantsDecoration(), SLOT(toggleOutlineVisible()));
connect(assistantsDecoration(), SIGNAL(assistantChanged()), SLOT(updateAction()));
}
if (m_imageView && referenceImagesDecoration()) {
- connect(m_toggleReferenceImages, SIGNAL(triggered(bool)), referenceImagesDecoration(), SLOT(setVisible(bool)));
+ connect(m_toggleReferenceImages, SIGNAL(triggered(bool)), referenceImagesDecoration(), SLOT(setVisible(bool)), Qt::UniqueConnection);
}
updateAction();
}
void KisDecorationsManager::updateAction()
{
if (assistantsDecoration()) {
bool enabled = !assistantsDecoration()->assistants().isEmpty();
m_toggleAssistant->setChecked(assistantsDecoration()->visible());
m_toggleAssistant->setEnabled(enabled);
m_togglePreview->setChecked(assistantsDecoration()->outlineVisibility());
m_togglePreview->setEnabled(enabled);
} else {
m_toggleAssistant->setEnabled(false);
}
if (referenceImagesDecoration()) {
m_toggleReferenceImages->setEnabled(referenceImagesDecoration()->documentHasReferenceImages());
m_toggleReferenceImages->setChecked(referenceImagesDecoration()->visible());
}
}
KisPaintingAssistantsDecorationSP KisDecorationsManager::assistantsDecoration() const
{
if (m_imageView) {
return m_imageView->canvasBase()->paintingAssistantsDecoration();
}
return 0;
}
KisReferenceImagesDecorationSP KisDecorationsManager::referenceImagesDecoration() const
{
if (m_imageView) {
return m_imageView->canvasBase()->referenceImagesDecoration();
}
return 0;
}
diff --git a/libs/ui/KisDocument.cpp b/libs/ui/KisDocument.cpp
index 41c2809bb5..03e3c2bf9d 100644
--- a/libs/ui/KisDocument.cpp
+++ b/libs/ui/KisDocument.cpp
@@ -1,1856 +1,1934 @@
/* This file is part of the Krita project
*
* Copyright (C) 2014 Boudewijn Rempt <boud@valdyas.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisMainWindow.h" // XXX: remove
#include <QMessageBox> // XXX: remove
#include <KisMimeDatabase.h>
#include <KoCanvasBase.h>
#include <KoColor.h>
#include <KoColorProfile.h>
#include <KoColorSpaceEngine.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoDocumentInfoDlg.h>
#include <KoDocumentInfo.h>
#include <KoDpi.h>
#include <KoUnit.h>
#include <KoID.h>
#include <KoOdfReadStore.h>
#include <KoProgressProxy.h>
#include <KoProgressUpdater.h>
#include <KoSelection.h>
#include <KoShape.h>
#include <KoShapeController.h>
#include <KoStore.h>
#include <KoUpdater.h>
#include <KoXmlWriter.h>
#include <KoXmlReader.h>
#include <KoStoreDevice.h>
#include <KoDialog.h>
+#include <KisUsageLogger.h>
#include <klocalizedstring.h>
#include <kis_debug.h>
+#include <kis_generator_layer.h>
+#include <kis_generator_registry.h>
#include <kdesktopfile.h>
#include <kconfiggroup.h>
#include <kbackup.h>
#include <QTextBrowser>
#include <QApplication>
#include <QBuffer>
#include <QStandardPaths>
#include <QDir>
#include <QDomDocument>
#include <QDomElement>
#include <QFileInfo>
#include <QImage>
#include <QList>
#include <QPainter>
#include <QRect>
#include <QScopedPointer>
#include <QSize>
#include <QStringList>
#include <QtGlobal>
#include <QTimer>
#include <QWidget>
#include <QFuture>
#include <QFutureWatcher>
// Krita Image
+#include <kis_image_animation_interface.h>
#include <kis_config.h>
#include <flake/kis_shape_layer.h>
#include <kis_group_layer.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <kis_name_server.h>
#include <kis_paint_layer.h>
#include <kis_painter.h>
#include <kis_selection.h>
#include <kis_fill_painter.h>
#include <kis_document_undo_store.h>
#include <kis_idle_watcher.h>
#include <kis_signal_auto_connection.h>
#include <kis_canvas_widget_base.h>
#include "kis_layer_utils.h"
// Local
#include "KisViewManager.h"
#include "kis_clipboard.h"
#include "widgets/kis_custom_image_widget.h"
#include "canvas/kis_canvas2.h"
#include "flake/kis_shape_controller.h"
#include "kis_statusbar.h"
#include "widgets/kis_progress_widget.h"
#include "kis_canvas_resource_provider.h"
#include "KisResourceServerProvider.h"
#include "kis_node_manager.h"
#include "KisPart.h"
#include "KisApplication.h"
#include "KisDocument.h"
#include "KisImportExportManager.h"
#include "KisView.h"
#include "kis_grid_config.h"
#include "kis_guides_config.h"
#include "kis_image_barrier_lock_adapter.h"
#include "KisReferenceImagesLayer.h"
#include <mutex>
#include "kis_config_notifier.h"
#include "kis_async_action_feedback.h"
#include "KisCloneDocumentStroke.h"
// Define the protocol used here for embedded documents' URL
// This used to "store" but QUrl didn't like it,
// so let's simply make it "tar" !
#define STORE_PROTOCOL "tar"
// The internal path is a hack to make QUrl happy and for document children
#define INTERNAL_PROTOCOL "intern"
#define INTERNAL_PREFIX "intern:/"
// Warning, keep it sync in koStore.cc
#include <unistd.h>
using namespace std;
namespace {
constexpr int errorMessageTimeout = 5000;
constexpr int successMessageTimeout = 1000;
}
/**********************************************************
*
* KisDocument
*
**********************************************************/
//static
QString KisDocument::newObjectName()
{
static int s_docIFNumber = 0;
QString name; name.setNum(s_docIFNumber++); name.prepend("document_");
return name;
}
class UndoStack : public KUndo2Stack
{
public:
UndoStack(KisDocument *doc)
: KUndo2Stack(doc),
m_doc(doc)
{
}
void setIndex(int idx) override {
KisImageWSP image = this->image();
image->requestStrokeCancellation();
if(image->tryBarrierLock()) {
KUndo2Stack::setIndex(idx);
image->unlock();
}
}
void notifySetIndexChangedOneCommand() override {
KisImageWSP image = this->image();
image->unlock();
/**
* Some very weird commands may emit blocking signals to
* the GUI (e.g. KisGuiContextCommand). Here is the best thing
* we can do to avoid the deadlock
*/
while(!image->tryBarrierLock()) {
QApplication::processEvents();
}
}
void undo() override {
KisImageWSP image = this->image();
image->requestUndoDuringStroke();
if (image->tryUndoUnfinishedLod0Stroke() == UNDO_OK) {
return;
}
if(image->tryBarrierLock()) {
KUndo2Stack::undo();
image->unlock();
}
}
void redo() override {
KisImageWSP image = this->image();
if(image->tryBarrierLock()) {
KUndo2Stack::redo();
image->unlock();
}
}
private:
KisImageWSP image() {
KisImageWSP currentImage = m_doc->image();
Q_ASSERT(currentImage);
return currentImage;
}
private:
KisDocument *m_doc;
};
class Q_DECL_HIDDEN KisDocument::Private
{
public:
Private(KisDocument *q)
: docInfo(new KoDocumentInfo(q)) // deleted by QObject
, importExportManager(new KisImportExportManager(q)) // deleted manually
, autoSaveTimer(new QTimer(q))
, undoStack(new UndoStack(q)) // deleted by QObject
, m_bAutoDetectedMime(false)
, modified(false)
, readwrite(true)
, firstMod(QDateTime::currentDateTime())
, lastMod(firstMod)
, nserver(new KisNameServer(1))
, imageIdleWatcher(2000 /*ms*/)
, globalAssistantsColor(KisConfig(true).defaultAssistantsColor())
, savingLock(&savingMutex)
, batchMode(false)
{
if (QLocale().measurementSystem() == QLocale::ImperialSystem) {
unit = KoUnit::Inch;
} else {
unit = KoUnit::Centimeter;
}
}
Private(const Private &rhs, KisDocument *q)
: docInfo(new KoDocumentInfo(*rhs.docInfo, q))
, unit(rhs.unit)
, importExportManager(new KisImportExportManager(q))
, mimeType(rhs.mimeType)
, outputMimeType(rhs.outputMimeType)
, autoSaveTimer(new QTimer(q))
, undoStack(new UndoStack(q))
, guidesConfig(rhs.guidesConfig)
, m_bAutoDetectedMime(rhs.m_bAutoDetectedMime)
, m_url(rhs.m_url)
, m_file(rhs.m_file)
, modified(rhs.modified)
, readwrite(rhs.readwrite)
, firstMod(rhs.firstMod)
, lastMod(rhs.lastMod)
, nserver(new KisNameServer(*rhs.nserver))
, preActivatedNode(0) // the node is from another hierarchy!
, imageIdleWatcher(2000 /*ms*/)
, assistants(rhs.assistants) // WARNING: assistants should not store pointers to the document!
, globalAssistantsColor(rhs.globalAssistantsColor)
, paletteList(rhs.paletteList)
, gridConfig(rhs.gridConfig)
, savingLock(&savingMutex)
, batchMode(rhs.batchMode)
{
// TODO: clone assistants
}
~Private() {
// Don't delete m_d->shapeController because it's in a QObject hierarchy.
delete nserver;
}
KoDocumentInfo *docInfo = 0;
KoUnit unit;
KisImportExportManager *importExportManager = 0; // The filter-manager to use when loading/saving [for the options]
QByteArray mimeType; // The actual mimetype of the document
QByteArray outputMimeType; // The mimetype to use when saving
QTimer *autoSaveTimer;
QString lastErrorMessage; // see openFile()
QString lastWarningMessage;
int autoSaveDelay = 300; // in seconds, 0 to disable.
bool modifiedAfterAutosave = false;
bool isAutosaving = false;
bool disregardAutosaveFailure = false;
int autoSaveFailureCount = 0;
KUndo2Stack *undoStack = 0;
KisGuidesConfig guidesConfig;
bool m_bAutoDetectedMime = false; // whether the mimetype in the arguments was detected by the part itself
QUrl m_url; // local url - the one displayed to the user.
QString m_file; // Local file - the only one the part implementation should deal with.
QMutex savingMutex;
bool modified = false;
bool readwrite = false;
QDateTime firstMod;
QDateTime lastMod;
KisNameServer *nserver;
KisImageSP image;
KisImageSP savingImage;
KisNodeWSP preActivatedNode;
KisShapeController* shapeController = 0;
KoShapeController* koShapeController = 0;
KisIdleWatcher imageIdleWatcher;
QScopedPointer<KisSignalAutoConnection> imageIdleConnection;
QList<KisPaintingAssistantSP> assistants;
QColor globalAssistantsColor;
KisSharedPtr<KisReferenceImagesLayer> referenceImagesLayer;
QList<KoColorSet*> paletteList;
KisGridConfig gridConfig;
StdLockableWrapper<QMutex> savingLock;
bool modifiedWhileSaving = false;
QScopedPointer<KisDocument> backgroundSaveDocument;
QPointer<KoUpdater> savingUpdater;
QFuture<KisImportExportFilter::ConversionStatus> childSavingFuture;
KritaUtils::ExportFileJob backgroundSaveJob;
bool isRecovered = false;
bool batchMode { false };
void setImageAndInitIdleWatcher(KisImageSP _image) {
image = _image;
imageIdleWatcher.setTrackedImage(image);
if (image) {
imageIdleConnection.reset(
new KisSignalAutoConnection(
&imageIdleWatcher, SIGNAL(startedIdleMode()),
image.data(), SLOT(explicitRegenerateLevelOfDetail())));
}
}
class StrippedSafeSavingLocker;
};
class KisDocument::Private::StrippedSafeSavingLocker {
public:
StrippedSafeSavingLocker(QMutex *savingMutex, KisImageSP image)
: m_locked(false)
, m_image(image)
, m_savingLock(savingMutex)
, m_imageLock(image, true)
{
/**
* Initial try to lock both objects. Locking the image guards
* us from any image composition threads running in the
* background, while savingMutex guards us from entering the
* saving code twice by autosave and main threads.
*
* Since we are trying to lock multiple objects, so we should
* do it in a safe manner.
*/
m_locked = std::try_lock(m_imageLock, m_savingLock) < 0;
if (!m_locked) {
m_image->requestStrokeEnd();
QApplication::processEvents();
// one more try...
m_locked = std::try_lock(m_imageLock, m_savingLock) < 0;
}
}
~StrippedSafeSavingLocker() {
- if (m_locked) {
- m_imageLock.unlock();
- m_savingLock.unlock();
- }
- }
+ if (m_locked) {
+ m_imageLock.unlock();
+ m_savingLock.unlock();
+ }
+ }
bool successfullyLocked() const {
return m_locked;
}
private:
bool m_locked;
KisImageSP m_image;
StdLockableWrapper<QMutex> m_savingLock;
KisImageBarrierLockAdapter m_imageLock;
};
KisDocument::KisDocument()
: d(new Private(this))
{
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
connect(d->undoStack, SIGNAL(cleanChanged(bool)), this, SLOT(slotUndoStackCleanChanged(bool)));
connect(d->autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave()));
setObjectName(newObjectName());
// preload the krita resources
KisResourceServerProvider::instance();
d->shapeController = new KisShapeController(this, d->nserver),
- d->koShapeController = new KoShapeController(0, d->shapeController),
+ d->koShapeController = new KoShapeController(0, d->shapeController),
- slotConfigChanged();
+ slotConfigChanged();
}
KisDocument::KisDocument(const KisDocument &rhs)
: QObject(),
d(new Private(*rhs.d, this))
{
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
connect(d->undoStack, SIGNAL(cleanChanged(bool)), this, SLOT(slotUndoStackCleanChanged(bool)));
connect(d->autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave()));
setObjectName(rhs.objectName());
d->shapeController = new KisShapeController(this, d->nserver),
- d->koShapeController = new KoShapeController(0, d->shapeController),
+ d->koShapeController = new KoShapeController(0, d->shapeController),
- slotConfigChanged();
+ slotConfigChanged();
// clone the image with keeping the GUIDs of the layers intact
// NOTE: we expect the image to be locked!
setCurrentImage(rhs.image()->clone(true), false);
if (rhs.d->preActivatedNode) {
// since we clone uuid's, we can use them for lacating new
// nodes. Otherwise we would need to use findSymmetricClone()
d->preActivatedNode =
KisLayerUtils::findNodeByUuid(d->image->root(), rhs.d->preActivatedNode->uuid());
}
}
KisDocument::~KisDocument()
{
// wait until all the pending operations are in progress
waitForSavingToComplete();
/**
* Push a timebomb, which will try to release the memory after
* the document has been deleted
*/
KisPaintDevice::createMemoryReleaseObject()->deleteLater();
d->autoSaveTimer->disconnect(this);
d->autoSaveTimer->stop();
delete d->importExportManager;
// Despite being QObject they needs to be deleted before the image
delete d->shapeController;
delete d->koShapeController;
if (d->image) {
d->image->notifyAboutToBeDeleted();
/**
* WARNING: We should wait for all the internal image jobs to
* finish before entering KisImage's destructor. The problem is,
* while execution of KisImage::~KisImage, all the weak shared
* pointers pointing to the image enter an inconsistent
* state(!). The shared counter is already zero and destruction
* has started, but the weak reference doesn't know about it,
* because KisShared::~KisShared hasn't been executed yet. So all
* the threads running in background and having weak pointers will
* enter the KisImage's destructor as well.
*/
d->image->requestStrokeCancellation();
d->image->waitForDone();
// clear undo commands that can still point to the image
d->undoStack->clear();
d->image->waitForDone();
KisImageWSP sanityCheckPointer = d->image;
Q_UNUSED(sanityCheckPointer);
// The following line trigger the deletion of the image
d->image.clear();
// check if the image has actually been deleted
KIS_SAFE_ASSERT_RECOVER_NOOP(!sanityCheckPointer.isValid());
}
delete d;
}
bool KisDocument::reload()
{
// XXX: reimplement!
return false;
}
KisDocument *KisDocument::clone()
{
return new KisDocument(*this);
}
bool KisDocument::exportDocumentImpl(const KritaUtils::ExportFileJob &job, KisPropertiesConfigurationSP exportConfiguration)
{
QFileInfo filePathInfo(job.filePath);
if (filePathInfo.exists() && !filePathInfo.isWritable()) {
slotCompleteSavingDocument(job,
KisImportExportFilter::CreationError,
i18n("%1 cannot be written to. Please save under a different name.", job.filePath));
return false;
}
KisConfig cfg(true);
if (cfg.backupFile() && filePathInfo.exists()) {
- KBackup::backupFile(job.filePath);
+
+ QString backupDir;
+
+ switch(cfg.readEntry<int>("backupfilelocation", 0)) {
+ case 1:
+ backupDir = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
+ break;
+ case 2:
+ backupDir = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
+ break;
+ default:
+ // Do nothing: the empty string is user file location
+ break;
+ }
+
+ int numOfBackupsKept = cfg.readEntry<int>("numberofbackupfiles", 1);
+ QString suffix = cfg.readEntry<QString>("backupfilesuffix", "~");
+
+ if (numOfBackupsKept == 1) {
+ KBackup::simpleBackupFile(job.filePath, backupDir, suffix);
+ }
+ else if (numOfBackupsKept > 2) {
+ KBackup::numberedBackupFile(job.filePath, backupDir, suffix, numOfBackupsKept);
+ }
}
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!job.mimeType.isEmpty(), false);
const QString actionName =
- job.flags & KritaUtils::SaveIsExporting ?
- i18n("Exporting Document...") :
- i18n("Saving Document...");
+ job.flags & KritaUtils::SaveIsExporting ?
+ i18n("Exporting Document...") :
+ i18n("Saving Document...");
bool started =
- initiateSavingInBackground(actionName,
- this, SLOT(slotCompleteSavingDocument(KritaUtils::ExportFileJob,KisImportExportFilter::ConversionStatus,QString)),
- job, exportConfiguration);
+ initiateSavingInBackground(actionName,
+ this, SLOT(slotCompleteSavingDocument(KritaUtils::ExportFileJob,KisImportExportFilter::ConversionStatus,QString)),
+ job, exportConfiguration);
if (!started) {
emit canceled(QString());
}
return started;
}
bool KisDocument::exportDocument(const QUrl &url, const QByteArray &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration)
{
using namespace KritaUtils;
SaveFlags flags = SaveIsExporting;
if (showWarnings) {
flags |= SaveShowWarnings;
}
+ KisUsageLogger::log(QString("Exporting Document: %1 as %2. %3 * %4 pixels, %5 layers, %6 frames, %7 framerate. Export configuration: %8")
+ .arg(url.toLocalFile())
+ .arg(QString::fromLatin1(mimeType))
+ .arg(d->image->width())
+ .arg(d->image->height())
+ .arg(d->image->nlayers())
+ .arg(d->image->animationInterface()->totalLength())
+ .arg(d->image->animationInterface()->framerate())
+ .arg(exportConfiguration ? exportConfiguration->toXML() : "No configuration"));
+
return exportDocumentImpl(KritaUtils::ExportFileJob(url.toLocalFile(),
mimeType,
flags),
exportConfiguration);
}
-bool KisDocument::saveAs(const QUrl &url, const QByteArray &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration)
+bool KisDocument::saveAs(const QUrl &_url, const QByteArray &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration)
{
using namespace KritaUtils;
- return exportDocumentImpl(ExportFileJob(url.toLocalFile(),
+ KisUsageLogger::log(QString("Saving Document %9 as %1 (mime: %2). %3 * %4 pixels, %5 layers. %6 frames, %7 framerate. Export configuration: %8")
+ .arg(_url.toLocalFile())
+ .arg(QString::fromLatin1(mimeType))
+ .arg(d->image->width())
+ .arg(d->image->height())
+ .arg(d->image->nlayers())
+ .arg(d->image->animationInterface()->totalLength())
+ .arg(d->image->animationInterface()->framerate())
+ .arg(exportConfiguration ? exportConfiguration->toXML() : "No configuration")
+ .arg(url().toLocalFile()));
+
+
+ return exportDocumentImpl(ExportFileJob(_url.toLocalFile(),
mimeType,
showWarnings ? SaveShowWarnings : SaveNone),
exportConfiguration);
}
bool KisDocument::save(bool showWarnings, KisPropertiesConfigurationSP exportConfiguration)
{
return saveAs(url(), mimeType(), showWarnings, exportConfiguration);
}
QByteArray KisDocument::serializeToNativeByteArray()
{
QByteArray byteArray;
QBuffer buffer(&byteArray);
QScopedPointer<KisImportExportFilter> filter(KisImportExportManager::filterForMimeType(nativeFormatMimeType(), KisImportExportManager::Export));
filter->setBatchMode(true);
filter->setMimeType(nativeFormatMimeType());
Private::StrippedSafeSavingLocker locker(&d->savingMutex, d->image);
if (!locker.successfullyLocked()) {
return byteArray;
}
d->savingImage = d->image;
if (filter->convert(this, &buffer) != KisImportExportFilter::OK) {
qWarning() << "serializeToByteArray():: Could not export to our native format";
}
return byteArray;
}
void KisDocument::slotCompleteSavingDocument(const KritaUtils::ExportFileJob &job, KisImportExportFilter::ConversionStatus status, const QString &errorMessage)
{
if (status == KisImportExportFilter::UserCancelled)
return;
const QString fileName = QFileInfo(job.filePath).fileName();
if (status != KisImportExportFilter::OK) {
emit statusBarMessage(i18nc("%1 --- failing file name, %2 --- error message",
"Error during saving %1: %2",
fileName,
exportErrorToUserMessage(status, errorMessage)), errorMessageTimeout);
if (!fileBatchMode()) {
const QString filePath = job.filePath;
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not save %1\nReason: %2", filePath, exportErrorToUserMessage(status, errorMessage)));
}
} else {
if (!(job.flags & KritaUtils::SaveIsExporting)) {
const QString existingAutoSaveBaseName = localFilePath();
const bool wasRecovered = isRecovered();
setUrl(QUrl::fromLocalFile(job.filePath));
setLocalFilePath(job.filePath);
setMimeType(job.mimeType);
updateEditingTime(true);
if (!d->modifiedWhileSaving) {
/**
* If undo stack is already clean/empty, it doesn't emit any
* signals, so we might forget update document modified state
* (which was set, e.g. while recovering an autosave file)
*/
if (d->undoStack->isClean()) {
setModified(false);
} else {
d->undoStack->setClean();
}
}
setRecovered(false);
removeAutoSaveFiles(existingAutoSaveBaseName, wasRecovered);
}
emit completed();
emit sigSavingFinished();
emit statusBarMessage(i18n("Finished saving %1", fileName), successMessageTimeout);
}
}
QByteArray KisDocument::mimeType() const
{
return d->mimeType;
}
void KisDocument::setMimeType(const QByteArray & mimeType)
{
d->mimeType = mimeType;
}
bool KisDocument::fileBatchMode() const
{
return d->batchMode;
}
void KisDocument::setFileBatchMode(const bool batchMode)
{
d->batchMode = batchMode;
}
KisDocument* KisDocument::lockAndCloneForSaving()
{
// force update of all the asynchronous nodes before cloning
QApplication::processEvents();
KisLayerUtils::forceAllDelayedNodesUpdate(d->image->root());
KisMainWindow *window = KisPart::instance()->currentMainwindow();
if (window) {
if (window->viewManager()) {
if (!window->viewManager()->blockUntilOperationsFinished(d->image)) {
return 0;
}
}
}
Private::StrippedSafeSavingLocker locker(&d->savingMutex, d->image);
if (!locker.successfullyLocked()) {
return 0;
}
return new KisDocument(*this);
}
bool KisDocument::exportDocumentSync(const QUrl &url, const QByteArray &mimeType, KisPropertiesConfigurationSP exportConfiguration)
{
Private::StrippedSafeSavingLocker locker(&d->savingMutex, d->image);
if (!locker.successfullyLocked()) {
return false;
}
d->savingImage = d->image;
const QString fileName = url.toLocalFile();
KisImportExportFilter::ConversionStatus status =
- d->importExportManager->
+ d->importExportManager->
exportDocument(fileName, fileName, mimeType, false, exportConfiguration);
d->savingImage = 0;
return status == KisImportExportFilter::OK;
}
bool KisDocument::initiateSavingInBackground(const QString actionName,
const QObject *receiverObject, const char *receiverMethod,
const KritaUtils::ExportFileJob &job,
KisPropertiesConfigurationSP exportConfiguration)
{
return initiateSavingInBackground(actionName, receiverObject, receiverMethod,
job, exportConfiguration, std::unique_ptr<KisDocument>());
}
bool KisDocument::initiateSavingInBackground(const QString actionName,
const QObject *receiverObject, const char *receiverMethod,
const KritaUtils::ExportFileJob &job,
KisPropertiesConfigurationSP exportConfiguration,
std::unique_ptr<KisDocument> &&optionalClonedDocument)
{
KIS_ASSERT_RECOVER_RETURN_VALUE(job.isValid(), false);
QScopedPointer<KisDocument> clonedDocument;
if (!optionalClonedDocument) {
clonedDocument.reset(lockAndCloneForSaving());
} else {
clonedDocument.reset(optionalClonedDocument.release());
}
// we block saving until the current saving is finished!
if (!clonedDocument || !d->savingMutex.tryLock()) {
return false;
}
KIS_ASSERT_RECOVER_RETURN_VALUE(!d->backgroundSaveDocument, false);
KIS_ASSERT_RECOVER_RETURN_VALUE(!d->backgroundSaveJob.isValid(), false);
d->backgroundSaveDocument.reset(clonedDocument.take());
d->backgroundSaveJob = job;
d->modifiedWhileSaving = false;
if (d->backgroundSaveJob.flags & KritaUtils::SaveInAutosaveMode) {
d->backgroundSaveDocument->d->isAutosaving = true;
}
connect(d->backgroundSaveDocument.data(),
SIGNAL(sigBackgroundSavingFinished(KisImportExportFilter::ConversionStatus,QString)),
this,
SLOT(slotChildCompletedSavingInBackground(KisImportExportFilter::ConversionStatus,QString)));
connect(this, SIGNAL(sigCompleteBackgroundSaving(KritaUtils::ExportFileJob,KisImportExportFilter::ConversionStatus,QString)),
receiverObject, receiverMethod, Qt::UniqueConnection);
bool started =
- d->backgroundSaveDocument->startExportInBackground(actionName,
- job.filePath,
- job.filePath,
- job.mimeType,
- job.flags & KritaUtils::SaveShowWarnings,
- exportConfiguration);
+ d->backgroundSaveDocument->startExportInBackground(actionName,
+ job.filePath,
+ job.filePath,
+ job.mimeType,
+ job.flags & KritaUtils::SaveShowWarnings,
+ exportConfiguration);
if (!started) {
// the state should have been deinitialized in slotChildCompletedSavingInBackground()
KIS_SAFE_ASSERT_RECOVER (!d->backgroundSaveDocument && !d->backgroundSaveJob.isValid()) {
d->backgroundSaveDocument.take()->deleteLater();
d->savingMutex.unlock();
d->backgroundSaveJob = KritaUtils::ExportFileJob();
}
}
return started;
}
void KisDocument::slotChildCompletedSavingInBackground(KisImportExportFilter::ConversionStatus status, const QString &errorMessage)
{
KIS_SAFE_ASSERT_RECOVER(!d->savingMutex.tryLock()) {
d->savingMutex.unlock();
return;
}
KIS_SAFE_ASSERT_RECOVER_RETURN(d->backgroundSaveDocument);
if (d->backgroundSaveJob.flags & KritaUtils::SaveInAutosaveMode) {
d->backgroundSaveDocument->d->isAutosaving = false;
}
d->backgroundSaveDocument.take()->deleteLater();
d->savingMutex.unlock();
KIS_SAFE_ASSERT_RECOVER_RETURN(d->backgroundSaveJob.isValid());
const KritaUtils::ExportFileJob job = d->backgroundSaveJob;
d->backgroundSaveJob = KritaUtils::ExportFileJob();
+ KisUsageLogger::log(QString("Completed saving %1 (mime: %2). Result: %3")
+ .arg(job.filePath)
+ .arg(QString::fromLatin1(job.mimeType))
+ .arg(status != KisImportExportFilter::OK ? exportErrorToUserMessage(status, errorMessage) : "OK"));
+
emit sigCompleteBackgroundSaving(job, status, errorMessage);
}
void KisDocument::slotAutoSaveImpl(std::unique_ptr<KisDocument> &&optionalClonedDocument)
{
if (!d->modified || !d->modifiedAfterAutosave) return;
const QString autoSaveFileName = generateAutoSaveFileName(localFilePath());
emit statusBarMessage(i18n("Autosaving... %1", autoSaveFileName), successMessageTimeout);
const bool hadClonedDocument = bool(optionalClonedDocument);
bool started = false;
if (d->image->isIdle() || hadClonedDocument) {
started = initiateSavingInBackground(i18n("Autosaving..."),
this, SLOT(slotCompleteAutoSaving(KritaUtils::ExportFileJob,KisImportExportFilter::ConversionStatus,QString)),
KritaUtils::ExportFileJob(autoSaveFileName, nativeFormatMimeType(), KritaUtils::SaveIsExporting | KritaUtils::SaveInAutosaveMode),
0,
std::move(optionalClonedDocument));
} else {
emit statusBarMessage(i18n("Autosaving postponed: document is busy..."), errorMessageTimeout);
}
if (!started && !hadClonedDocument && d->autoSaveFailureCount >= 3) {
KisCloneDocumentStroke *stroke = new KisCloneDocumentStroke(this);
connect(stroke, SIGNAL(sigDocumentCloned(KisDocument*)),
this, SLOT(slotInitiateAsyncAutosaving(KisDocument*)),
Qt::BlockingQueuedConnection);
KisStrokeId strokeId = d->image->startStroke(stroke);
d->image->endStroke(strokeId);
setInfiniteAutoSaveInterval();
} else if (!started) {
setEmergencyAutoSaveInterval();
} else {
d->modifiedAfterAutosave = false;
}
}
void KisDocument::slotAutoSave()
{
slotAutoSaveImpl(std::unique_ptr<KisDocument>());
}
void KisDocument::slotInitiateAsyncAutosaving(KisDocument *clonedDocument)
{
slotAutoSaveImpl(std::unique_ptr<KisDocument>(clonedDocument));
}
void KisDocument::slotCompleteAutoSaving(const KritaUtils::ExportFileJob &job, KisImportExportFilter::ConversionStatus status, const QString &errorMessage)
{
Q_UNUSED(job);
const QString fileName = QFileInfo(job.filePath).fileName();
if (status != KisImportExportFilter::OK) {
setEmergencyAutoSaveInterval();
emit statusBarMessage(i18nc("%1 --- failing file name, %2 --- error message",
"Error during autosaving %1: %2",
fileName,
exportErrorToUserMessage(status, errorMessage)), errorMessageTimeout);
} else {
KisConfig cfg(true);
d->autoSaveDelay = cfg.autoSaveInterval();
if (!d->modifiedWhileSaving) {
d->autoSaveTimer->stop(); // until the next change
d->autoSaveFailureCount = 0;
} else {
setNormalAutoSaveInterval();
}
emit statusBarMessage(i18n("Finished autosaving %1", fileName), successMessageTimeout);
}
}
bool KisDocument::startExportInBackground(const QString &actionName,
const QString &location,
const QString &realLocation,
const QByteArray &mimeType,
bool showWarnings,
KisPropertiesConfigurationSP exportConfiguration)
{
d->savingImage = d->image;
KisMainWindow *window = KisPart::instance()->currentMainwindow();
if (window) {
if (window->viewManager()) {
d->savingUpdater = window->viewManager()->createThreadedUpdater(actionName);
d->importExportManager->setUpdater(d->savingUpdater);
}
}
KisImportExportFilter::ConversionStatus initializationStatus;
d->childSavingFuture =
- d->importExportManager->exportDocumentAsyc(location,
- realLocation,
- mimeType,
- initializationStatus,
- showWarnings,
- exportConfiguration);
+ d->importExportManager->exportDocumentAsyc(location,
+ realLocation,
+ mimeType,
+ initializationStatus,
+ showWarnings,
+ exportConfiguration);
if (initializationStatus != KisImportExportFilter::ConversionStatus::OK) {
if (d->savingUpdater) {
d->savingUpdater->cancel();
}
d->savingImage.clear();
emit sigBackgroundSavingFinished(initializationStatus, this->errorMessage());
return false;
}
typedef QFutureWatcher<KisImportExportFilter::ConversionStatus> StatusWatcher;
StatusWatcher *watcher = new StatusWatcher();
watcher->setFuture(d->childSavingFuture);
connect(watcher, SIGNAL(finished()), SLOT(finishExportInBackground()));
connect(watcher, SIGNAL(finished()), watcher, SLOT(deleteLater()));
return true;
}
void KisDocument::finishExportInBackground()
{
KIS_SAFE_ASSERT_RECOVER(d->childSavingFuture.isFinished()) {
emit sigBackgroundSavingFinished(KisImportExportFilter::InternalError, "");
return;
}
KisImportExportFilter::ConversionStatus status =
- d->childSavingFuture.result();
+ d->childSavingFuture.result();
const QString errorMessage = this->errorMessage();
d->savingImage.clear();
d->childSavingFuture = QFuture<KisImportExportFilter::ConversionStatus>();
d->lastErrorMessage.clear();
if (d->savingUpdater) {
d->savingUpdater->setProgress(100);
}
emit sigBackgroundSavingFinished(status, errorMessage);
}
void KisDocument::setReadWrite(bool readwrite)
{
d->readwrite = readwrite;
setNormalAutoSaveInterval();
Q_FOREACH (KisMainWindow *mainWindow, KisPart::instance()->mainWindows()) {
mainWindow->setReadWrite(readwrite);
}
}
void KisDocument::setAutoSaveDelay(int delay)
{
if (isReadWrite() && delay > 0) {
d->autoSaveTimer->start(delay * 1000);
} else {
d->autoSaveTimer->stop();
}
}
void KisDocument::setNormalAutoSaveInterval()
{
setAutoSaveDelay(d->autoSaveDelay);
d->autoSaveFailureCount = 0;
}
void KisDocument::setEmergencyAutoSaveInterval()
{
const int emergencyAutoSaveInterval = 10; /* sec */
setAutoSaveDelay(emergencyAutoSaveInterval);
d->autoSaveFailureCount++;
}
void KisDocument::setInfiniteAutoSaveInterval()
{
setAutoSaveDelay(-1);
}
KoDocumentInfo *KisDocument::documentInfo() const
{
return d->docInfo;
}
bool KisDocument::isModified() const
{
return d->modified;
}
QPixmap KisDocument::generatePreview(const QSize& size)
{
KisImageSP image = d->image;
if (d->savingImage) image = d->savingImage;
if (image) {
QRect bounds = image->bounds();
QSize newSize = bounds.size();
newSize.scale(size, Qt::KeepAspectRatio);
QPixmap px = QPixmap::fromImage(image->convertToQImage(newSize, 0));
if (px.size() == QSize(0,0)) {
px = QPixmap(newSize);
QPainter gc(&px);
QBrush checkBrush = QBrush(KisCanvasWidgetBase::createCheckersImage(newSize.width() / 5));
gc.fillRect(px.rect(), checkBrush);
gc.end();
}
return px;
}
return QPixmap(size);
}
QString KisDocument::generateAutoSaveFileName(const QString & path) const
{
QString retval;
// Using the extension allows to avoid relying on the mime magic when opening
const QString extension (".kra");
- QRegularExpression autosavePattern("^\\..+-autosave.kra$");
+ QString prefix = KisConfig(true).readEntry<bool>("autosavefileshidden") ? QString(".") : QString();
+ QRegularExpression autosavePattern1("^\\..+-autosave.kra$");
+ QRegularExpression autosavePattern2("^.+-autosave.kra$");
QFileInfo fi(path);
QString dir = fi.absolutePath();
QString filename = fi.fileName();
- if (path.isEmpty() || autosavePattern.match(filename).hasMatch()) {
+ if (path.isEmpty() || autosavePattern1.match(filename).hasMatch() || autosavePattern2.match(filename).hasMatch()) {
// Never saved?
#ifdef Q_OS_WIN
// On Windows, use the temp location (https://bugs.kde.org/show_bug.cgi?id=314921)
- retval = QString("%1%2.%3-%4-%5-autosave%6").arg(QDir::tempPath()).arg(QDir::separator()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension);
+ retval = QString("%1%2%7%3-%4-%5-autosave%6").arg(QDir::tempPath()).arg(QDir::separator()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension).arg(prefix);
#else
// On Linux, use a temp file in $HOME then. Mark it with the pid so two instances don't overwrite each other's autosave file
- retval = QString("%1%2.%3-%4-%5-autosave%6").arg(QDir::homePath()).arg(QDir::separator()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension);
+ retval = QString("%1%2%7%3-%4-%5-autosave%6").arg(QDir::homePath()).arg(QDir::separator()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension).arg(prefix);
#endif
} else {
- retval = QString("%1%2.%3-autosave%4").arg(dir).arg(QDir::separator()).arg(filename).arg(extension);
+ retval = QString("%1%2%5%3-autosave%4").arg(dir).arg(QDir::separator()).arg(filename).arg(extension).arg(prefix);
}
//qDebug() << "generateAutoSaveFileName() for path" << path << ":" << retval;
return retval;
}
bool KisDocument::importDocument(const QUrl &_url)
{
bool ret;
dbgUI << "url=" << _url.url();
// open...
ret = openUrl(_url);
// reset url & m_file (kindly? set by KisParts::openUrl()) to simulate a
// File --> Import
if (ret) {
dbgUI << "success, resetting url";
resetURL();
setTitleModified();
}
return ret;
}
bool KisDocument::openUrl(const QUrl &_url, OpenFlags flags)
{
if (!_url.isLocalFile()) {
return false;
}
dbgUI << "url=" << _url.url();
d->lastErrorMessage.clear();
// Reimplemented, to add a check for autosave files and to improve error reporting
if (!_url.isValid()) {
d->lastErrorMessage = i18n("Malformed URL\n%1", _url.url()); // ## used anywhere ?
return false;
}
QUrl url(_url);
bool autosaveOpened = false;
if (url.isLocalFile() && !fileBatchMode()) {
QString file = url.toLocalFile();
QString asf = generateAutoSaveFileName(file);
if (QFile::exists(asf)) {
KisApplication *kisApp = static_cast<KisApplication*>(qApp);
kisApp->hideSplashScreen();
//dbgUI <<"asf=" << asf;
// ## TODO compare timestamps ?
int res = QMessageBox::warning(0,
i18nc("@title:window", "Krita"),
i18n("An autosaved file exists for this document.\nDo you want to open the autosaved file instead?"),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes);
switch (res) {
case QMessageBox::Yes :
url.setPath(asf);
autosaveOpened = true;
break;
case QMessageBox::No :
QFile::remove(asf);
break;
default: // Cancel
return false;
}
}
}
bool ret = openUrlInternal(url);
if (autosaveOpened || flags & RecoveryFile) {
setReadWrite(true); // enable save button
setModified(true);
setRecovered(true);
}
else {
if (!(flags & DontAddToRecent)) {
KisPart::instance()->addRecentURLToAllMainWindows(_url);
}
if (ret) {
// Detect readonly local-files; remote files are assumed to be writable
QFileInfo fi(url.toLocalFile());
setReadWrite(fi.isWritable());
}
setRecovered(false);
}
return ret;
}
class DlgLoadMessages : public KoDialog {
public:
DlgLoadMessages(const QString &title, const QString &message, const QStringList &warnings) {
setWindowTitle(title);
setWindowIcon(KisIconUtils::loadIcon("warning"));
QWidget *page = new QWidget(this);
QVBoxLayout *layout = new QVBoxLayout(page);
QHBoxLayout *hlayout = new QHBoxLayout();
QLabel *labelWarning= new QLabel();
labelWarning->setPixmap(KisIconUtils::loadIcon("warning").pixmap(32, 32));
hlayout->addWidget(labelWarning);
hlayout->addWidget(new QLabel(message));
layout->addLayout(hlayout);
QTextBrowser *browser = new QTextBrowser();
QString warning = "<html><body><p><b>";
if (warnings.size() == 1) {
warning += "</b> Reason:</p>";
}
else {
warning += "</b> Reasons:</p>";
}
warning += "<p/><ul>";
Q_FOREACH(const QString &w, warnings) {
warning += "\n<li>" + w + "</li>";
}
warning += "</ul>";
browser->setHtml(warning);
browser->setMinimumHeight(200);
browser->setMinimumWidth(400);
layout->addWidget(browser);
setMainWidget(page);
setButtons(KoDialog::Ok);
resize(minimumSize());
}
};
bool KisDocument::openFile()
{
//dbgUI <<"for" << localFilePath();
if (!QFile::exists(localFilePath())) {
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("File %1 does not exist.", localFilePath()));
return false;
}
QString filename = localFilePath();
QString typeName = mimeType();
if (typeName.isEmpty()) {
typeName = KisMimeDatabase::mimeTypeForFile(filename);
}
//qDebug() << "mimetypes 4:" << typeName;
// Allow to open backup files, don't keep the mimetype application/x-trash.
if (typeName == "application/x-trash") {
QString path = filename;
while (path.length() > 0) {
path.chop(1);
typeName = KisMimeDatabase::mimeTypeForFile(path);
//qDebug() << "\t" << path << typeName;
if (!typeName.isEmpty()) {
break;
}
}
//qDebug() << "chopped" << filename << "to" << path << "Was trash, is" << typeName;
}
dbgUI << localFilePath() << "type:" << typeName;
KisMainWindow *window = KisPart::instance()->currentMainwindow();
if (window && window->viewManager()) {
KoUpdaterPtr updater = window->viewManager()->createUnthreadedUpdater(i18n("Opening document"));
d->importExportManager->setUpdater(updater);
}
KisImportExportFilter::ConversionStatus status;
status = d->importExportManager->importDocument(localFilePath(), typeName);
if (status != KisImportExportFilter::OK) {
QString msg = KisImportExportFilter::conversionStatusString(status);
if (!msg.isEmpty()) {
DlgLoadMessages dlg(i18nc("@title:window", "Krita"),
i18n("Could not open %2.\nReason: %1.", msg, prettyPathOrUrl()),
errorMessage().split("\n") + warningMessage().split("\n"));
dlg.exec();
}
return false;
}
else if (!warningMessage().isEmpty()) {
DlgLoadMessages dlg(i18nc("@title:window", "Krita"),
i18n("There were problems opening %1.", prettyPathOrUrl()),
warningMessage().split("\n"));
dlg.exec();
setUrl(QUrl());
}
setMimeTypeAfterLoading(typeName);
emit sigLoadingFinished();
undoStack()->clear();
return true;
}
// shared between openFile and koMainWindow's "create new empty document" code
void KisDocument::setMimeTypeAfterLoading(const QString& mimeType)
{
d->mimeType = mimeType.toLatin1();
d->outputMimeType = d->mimeType;
}
bool KisDocument::loadNativeFormat(const QString & file_)
{
return openUrl(QUrl::fromLocalFile(file_));
}
void KisDocument::setModified(bool mod)
{
if (mod) {
updateEditingTime(false);
}
if (d->isAutosaving) // ignore setModified calls due to autosaving
return;
if ( !d->readwrite && d->modified ) {
errKrita << "Can't set a read-only document to 'modified' !" << endl;
return;
}
//dbgUI<<" url:" << url.path();
//dbgUI<<" mod="<<mod<<" MParts mod="<<KisParts::ReadWritePart::isModified()<<" isModified="<<isModified();
if (mod && !d->autoSaveTimer->isActive()) {
// First change since last autosave -> start the autosave timer
setNormalAutoSaveInterval();
}
d->modifiedAfterAutosave = mod;
d->modifiedWhileSaving = mod;
if (mod == isModified())
return;
d->modified = mod;
if (mod) {
documentInfo()->updateParameters();
}
// This influences the title
setTitleModified();
emit modified(mod);
}
void KisDocument::setRecovered(bool value)
{
d->isRecovered = value;
}
bool KisDocument::isRecovered() const
{
return d->isRecovered;
}
void KisDocument::updateEditingTime(bool forceStoreElapsed)
{
QDateTime now = QDateTime::currentDateTime();
int firstModDelta = d->firstMod.secsTo(now);
int lastModDelta = d->lastMod.secsTo(now);
if (lastModDelta > 30) {
d->docInfo->setAboutInfo("editing-time", QString::number(d->docInfo->aboutInfo("editing-time").toInt() + d->firstMod.secsTo(d->lastMod)));
d->firstMod = now;
} else if (firstModDelta > 60 || forceStoreElapsed) {
d->docInfo->setAboutInfo("editing-time", QString::number(d->docInfo->aboutInfo("editing-time").toInt() + firstModDelta));
d->firstMod = now;
}
d->lastMod = now;
}
QString KisDocument::prettyPathOrUrl() const
{
QString _url(url().toDisplayString());
#ifdef Q_OS_WIN
if (url().isLocalFile()) {
_url = QDir::toNativeSeparators(_url);
}
#endif
return _url;
}
// Get caption from document info (title(), in about page)
QString KisDocument::caption() const
{
QString c;
const QString _url(url().fileName());
// if URL is empty...it is probably an unsaved file
if (_url.isEmpty()) {
c = " [" + i18n("Not Saved") + "] ";
} else {
c = _url; // Fall back to document URL
}
return c;
}
void KisDocument::setTitleModified()
{
emit titleModified(caption(), isModified());
}
QDomDocument KisDocument::createDomDocument(const QString& tagName, const QString& version) const
{
return createDomDocument("krita", tagName, version);
}
//static
QDomDocument KisDocument::createDomDocument(const QString& appName, const QString& tagName, const QString& version)
{
QDomImplementation impl;
QString url = QString("http://www.calligra.org/DTD/%1-%2.dtd").arg(appName).arg(version);
QDomDocumentType dtype = impl.createDocumentType(tagName,
QString("-//KDE//DTD %1 %2//EN").arg(appName).arg(version),
url);
// The namespace URN doesn't need to include the version number.
QString namespaceURN = QString("http://www.calligra.org/DTD/%1").arg(appName);
QDomDocument doc = impl.createDocument(namespaceURN, tagName, dtype);
doc.insertBefore(doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""), doc.documentElement());
return doc;
}
bool KisDocument::isNativeFormat(const QByteArray& mimetype) const
{
if (mimetype == nativeFormatMimeType())
return true;
return extraNativeMimeTypes().contains(mimetype);
}
void KisDocument::setErrorMessage(const QString& errMsg)
{
d->lastErrorMessage = errMsg;
}
QString KisDocument::errorMessage() const
{
return d->lastErrorMessage;
}
void KisDocument::setWarningMessage(const QString& warningMsg)
{
d->lastWarningMessage = warningMsg;
}
QString KisDocument::warningMessage() const
{
return d->lastWarningMessage;
}
void KisDocument::removeAutoSaveFiles(const QString &autosaveBaseName, bool wasRecovered)
{
//qDebug() << "removeAutoSaveFiles";
// Eliminate any auto-save file
QString asf = generateAutoSaveFileName(autosaveBaseName); // the one in the current dir
//qDebug() << "\tfilename:" << asf << "exists:" << QFile::exists(asf);
if (QFile::exists(asf)) {
//qDebug() << "\tremoving autosavefile" << asf;
QFile::remove(asf);
}
asf = generateAutoSaveFileName(QString()); // and the one in $HOME
//qDebug() << "Autsavefile in $home" << asf;
if (QFile::exists(asf)) {
//qDebug() << "\tremoving autsavefile 2" << asf;
QFile::remove(asf);
}
- QRegularExpression autosavePattern("^\\..+-autosave.kra$");
+ QList<QRegularExpression> expressions;
- if (wasRecovered &&
- !autosaveBaseName.isEmpty() &&
- autosavePattern.match(QFileInfo(autosaveBaseName).fileName()).hasMatch() &&
- QFile::exists(autosaveBaseName)) {
+ expressions << QRegularExpression("^\\..+-autosave.kra$")
+ << QRegularExpression("^.+-autosave.kra$");
- QFile::remove(autosaveBaseName);
+ Q_FOREACH(const QRegularExpression &rex, expressions) {
+ if (wasRecovered &&
+ !autosaveBaseName.isEmpty() &&
+ rex.match(QFileInfo(autosaveBaseName).fileName()).hasMatch() &&
+ QFile::exists(autosaveBaseName)) {
+
+ QFile::remove(autosaveBaseName);
+ }
}
}
KoUnit KisDocument::unit() const
{
return d->unit;
}
void KisDocument::setUnit(const KoUnit &unit)
{
if (d->unit != unit) {
d->unit = unit;
emit unitChanged(unit);
}
}
KUndo2Stack *KisDocument::undoStack()
{
return d->undoStack;
}
KisImportExportManager *KisDocument::importExportManager() const
{
return d->importExportManager;
}
void KisDocument::addCommand(KUndo2Command *command)
{
if (command)
d->undoStack->push(command);
}
void KisDocument::beginMacro(const KUndo2MagicString & text)
{
d->undoStack->beginMacro(text);
}
void KisDocument::endMacro()
{
d->undoStack->endMacro();
}
void KisDocument::slotUndoStackCleanChanged(bool value)
{
setModified(!value);
}
void KisDocument::slotConfigChanged()
{
KisConfig cfg(true);
d->undoStack->setUndoLimit(cfg.undoStackLimit());
d->autoSaveDelay = cfg.autoSaveInterval();
setNormalAutoSaveInterval();
}
void KisDocument::clearUndoHistory()
{
d->undoStack->clear();
}
KisGridConfig KisDocument::gridConfig() const
{
return d->gridConfig;
}
void KisDocument::setGridConfig(const KisGridConfig &config)
{
d->gridConfig = config;
}
QList<KoColorSet *> &KisDocument::paletteList()
{
return d->paletteList;
}
void KisDocument::setPaletteList(const QList<KoColorSet *> &paletteList)
{
d->paletteList = paletteList;
}
const KisGuidesConfig& KisDocument::guidesConfig() const
{
return d->guidesConfig;
}
void KisDocument::setGuidesConfig(const KisGuidesConfig &data)
{
if (d->guidesConfig == data) return;
d->guidesConfig = data;
emit sigGuidesConfigChanged(d->guidesConfig);
}
void KisDocument::resetURL() {
setUrl(QUrl());
setLocalFilePath(QString());
}
KoDocumentInfoDlg *KisDocument::createDocumentInfoDialog(QWidget *parent, KoDocumentInfo *docInfo) const
{
return new KoDocumentInfoDlg(parent, docInfo);
}
bool KisDocument::isReadWrite() const
{
return d->readwrite;
}
QUrl KisDocument::url() const
{
return d->m_url;
}
bool KisDocument::closeUrl(bool promptToSave)
{
if (promptToSave) {
if ( isReadWrite() && isModified()) {
Q_FOREACH (KisView *view, KisPart::instance()->views()) {
if (view && view->document() == this) {
if (!view->queryClose()) {
return false;
}
}
}
}
}
// Not modified => ok and delete temp file.
d->mimeType = QByteArray();
// It always succeeds for a read-only part,
// but the return value exists for reimplementations
// (e.g. pressing cancel for a modified read-write part)
return true;
}
void KisDocument::setUrl(const QUrl &url)
{
d->m_url = url;
}
QString KisDocument::localFilePath() const
{
return d->m_file;
}
void KisDocument::setLocalFilePath( const QString &localFilePath )
{
d->m_file = localFilePath;
}
bool KisDocument::openUrlInternal(const QUrl &url)
{
if ( !url.isValid() )
return false;
if (d->m_bAutoDetectedMime) {
d->mimeType = QByteArray();
d->m_bAutoDetectedMime = false;
}
QByteArray mimetype = d->mimeType;
if ( !closeUrl() )
return false;
d->mimeType = mimetype;
setUrl(url);
d->m_file.clear();
if (d->m_url.isLocalFile()) {
d->m_file = d->m_url.toLocalFile();
bool ret;
// set the mimetype only if it was not already set (for example, by the host application)
if (d->mimeType.isEmpty()) {
// get the mimetype of the file
// using findByUrl() to avoid another string -> url conversion
QString mime = KisMimeDatabase::mimeTypeForFile(d->m_url.toLocalFile());
d->mimeType = mime.toLocal8Bit();
d->m_bAutoDetectedMime = true;
}
setUrl(d->m_url);
ret = openFile();
if (ret) {
emit completed();
} else {
emit canceled(QString());
}
return ret;
}
return false;
}
bool KisDocument::newImage(const QString& name,
qint32 width, qint32 height,
const KoColorSpace* cs,
- const KoColor &bgColor, bool backgroundAsLayer,
+ const KoColor &bgColor, KisConfig::BackgroundStyle bgStyle,
int numberOfLayers,
const QString &description, const double imageResolution)
{
Q_ASSERT(cs);
KisImageSP image;
- KisPaintLayerSP layer;
if (!cs) return false;
QApplication::setOverrideCursor(Qt::BusyCursor);
image = new KisImage(createUndoStore(), width, height, cs, name);
Q_CHECK_PTR(image);
connect(image, SIGNAL(sigImageModified()), this, SLOT(setImageModified()), Qt::UniqueConnection);
image->setResolution(imageResolution, imageResolution);
image->assignImageProfile(cs->profile());
documentInfo()->setAboutInfo("title", name);
documentInfo()->setAboutInfo("abstract", description);
- layer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, cs);
- Q_CHECK_PTR(layer);
+ KisLayerSP layer;
+ if (bgStyle == KisConfig::RASTER_LAYER || bgStyle == KisConfig::FILL_LAYER) {
+ KoColor strippedAlpha = bgColor;
+ strippedAlpha.setOpacity(OPACITY_OPAQUE_U8);
+
+ if (bgStyle == KisConfig::RASTER_LAYER) {
+ layer = new KisPaintLayer(image.data(), "Background", OPACITY_OPAQUE_U8, cs);;
+ layer->paintDevice()->setDefaultPixel(strippedAlpha);
+ } else if (bgStyle == KisConfig::FILL_LAYER) {
+ KisFilterConfigurationSP filter_config = KisGeneratorRegistry::instance()->get("color")->defaultConfiguration();
+ filter_config->setProperty("color", strippedAlpha.toQColor());
+ layer = new KisGeneratorLayer(image.data(), "Background Fill", filter_config, image->globalSelection());
+ }
- if (backgroundAsLayer) {
- image->setDefaultProjectionColor(KoColor(cs));
+ layer->setOpacity(bgColor.opacityU8());
- if (bgColor.opacityU8() == OPACITY_OPAQUE_U8) {
- layer->paintDevice()->setDefaultPixel(bgColor);
- } else {
- // Hack: with a semi-transparent background color, the projection isn't composited right if we just set the default pixel
- KisFillPainter painter;
- painter.begin(layer->paintDevice());
- painter.fillRect(0, 0, width, height, bgColor, bgColor.opacityU8());
+ if (numberOfLayers > 1) {
+ //Lock bg layer if others are present.
+ layer->setUserLocked(true);
}
- } else {
+ }
+ else { // KisConfig::CANVAS_COLOR (needs an unlocked starting layer).
image->setDefaultProjectionColor(bgColor);
+ layer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, cs);
}
- layer->setDirty(QRect(0, 0, width, height));
+ Q_CHECK_PTR(layer);
image->addNode(layer.data(), image->rootLayer().data());
+ layer->setDirty(QRect(0, 0, width, height));
+
setCurrentImage(image);
for(int i = 1; i < numberOfLayers; ++i) {
KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), OPACITY_OPAQUE_U8, cs);
image->addNode(layer, image->root(), i);
layer->setDirty(QRect(0, 0, width, height));
}
KisConfig cfg(false);
cfg.defImageWidth(width);
cfg.defImageHeight(height);
cfg.defImageResolution(imageResolution);
cfg.defColorModel(image->colorSpace()->colorModelId().id());
cfg.setDefaultColorDepth(image->colorSpace()->colorDepthId().id());
cfg.defColorProfile(image->colorSpace()->profile()->name());
+ KisUsageLogger::log(i18n("Created image \"%1\", %2 * %3 pixels, %4 dpi. Color model: %6 %5 (%7). Layers: %8"
+ , name
+ , width, height
+ , imageResolution * 72.0
+ , image->colorSpace()->colorModelId().name(), image->colorSpace()->colorDepthId().name()
+ , image->colorSpace()->profile()->name()
+ , numberOfLayers));
+
QApplication::restoreOverrideCursor();
return true;
}
bool KisDocument::isSaving() const
{
const bool result = d->savingMutex.tryLock();
if (result) {
d->savingMutex.unlock();
}
return !result;
}
void KisDocument::waitForSavingToComplete()
{
if (isSaving()) {
KisAsyncActionFeedback f(i18nc("progress dialog message when the user closes the document that is being saved", "Waiting for saving to complete..."), 0);
f.waitForMutex(&d->savingMutex);
}
}
KoShapeControllerBase *KisDocument::shapeController() const
{
return d->shapeController;
}
KoShapeLayer* KisDocument::shapeForNode(KisNodeSP layer) const
{
return d->shapeController->shapeForNode(layer);
}
QList<KisPaintingAssistantSP> KisDocument::assistants() const
{
return d->assistants;
}
void KisDocument::setAssistants(const QList<KisPaintingAssistantSP> &value)
{
d->assistants = value;
}
KisSharedPtr<KisReferenceImagesLayer> KisDocument::referenceImagesLayer() const
{
return d->referenceImagesLayer.data();
}
void KisDocument::setReferenceImagesLayer(KisSharedPtr<KisReferenceImagesLayer> layer, bool updateImage)
{
if (d->referenceImagesLayer) {
d->referenceImagesLayer->disconnect(this);
}
if (updateImage) {
if (layer) {
d->image->addNode(layer);
} else {
d->image->removeNode(d->referenceImagesLayer);
}
}
d->referenceImagesLayer = layer;
if (d->referenceImagesLayer) {
connect(d->referenceImagesLayer, SIGNAL(sigUpdateCanvas(QRectF)),
this, SIGNAL(sigReferenceImagesChanged()));
}
}
void KisDocument::setPreActivatedNode(KisNodeSP activatedNode)
{
d->preActivatedNode = activatedNode;
}
KisNodeSP KisDocument::preActivatedNode() const
{
return d->preActivatedNode;
}
KisImageWSP KisDocument::image() const
{
return d->image;
}
KisImageSP KisDocument::savingImage() const
{
return d->savingImage;
}
void KisDocument::setCurrentImage(KisImageSP image, bool forceInitialUpdate)
{
if (d->image) {
// Disconnect existing sig/slot connections
d->image->setUndoStore(new KisDumbUndoStore());
d->image->disconnect(this);
d->shapeController->setImage(0);
d->image = 0;
}
if (!image) return;
d->setImageAndInitIdleWatcher(image);
d->image->setUndoStore(new KisDocumentUndoStore(this));
d->shapeController->setImage(image);
setModified(false);
connect(d->image, SIGNAL(sigImageModified()), this, SLOT(setImageModified()), Qt::UniqueConnection);
if (forceInitialUpdate) {
d->image->initialRefreshGraph();
}
}
void KisDocument::hackPreliminarySetImage(KisImageSP image)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(!d->image);
d->setImageAndInitIdleWatcher(image);
d->shapeController->setImage(image);
}
void KisDocument::setImageModified()
{
setModified(true);
}
KisUndoStore* KisDocument::createUndoStore()
{
return new KisDocumentUndoStore(this);
}
bool KisDocument::isAutosaving() const
{
return d->isAutosaving;
}
QString KisDocument::exportErrorToUserMessage(KisImportExportFilter::ConversionStatus status, const QString &errorMessage)
{
return errorMessage.isEmpty() ? KisImportExportFilter::conversionStatusString(status) : errorMessage;
}
void KisDocument::setAssistantsGlobalColor(QColor color)
{
d->globalAssistantsColor = color;
}
QColor KisDocument::assistantsGlobalColor()
{
return d->globalAssistantsColor;
}
QRectF KisDocument::documentBounds() const
{
QRectF bounds = d->image->bounds();
if (d->referenceImagesLayer) {
bounds |= d->referenceImagesLayer->boundingImageRect();
}
return bounds;
}
diff --git a/libs/ui/KisDocument.h b/libs/ui/KisDocument.h
index 11f80ba458..eebc152d32 100644
--- a/libs/ui/KisDocument.h
+++ b/libs/ui/KisDocument.h
@@ -1,655 +1,656 @@
/* This file is part of the Krita project
*
* Copyright (C) 2014 Boudewijn Rempt <boud@valdyas.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KISDOCUMENT_H
#define KISDOCUMENT_H
#include <QDateTime>
#include <QTransform>
#include <QList>
#include <klocalizedstring.h>
#include <KoPageLayout.h>
#include <KoDocumentBase.h>
#include <kundo2stack.h>
#include <KoColorSet.h>
#include <kis_image.h>
#include <KisImportExportFilter.h>
#include <kis_properties_configuration.h>
#include <kis_types.h>
#include <kis_painting_assistant.h>
#include <KisReferenceImage.h>
#include <kis_debug.h>
#include <KisImportExportUtils.h>
+#include <kis_config.h>
#include "kritaui_export.h"
#include <memory>
class QString;
class KUndo2Command;
class KoUnit;
class KoColor;
class KoColorSpace;
class KoShapeControllerBase;
class KoShapeLayer;
class KoStore;
class KoOdfReadStore;
class KoDocumentInfo;
class KoDocumentInfoDlg;
class KisImportExportManager;
class KisUndoStore;
class KisPart;
class KisGridConfig;
class KisGuidesConfig;
class QDomDocument;
class KisReferenceImagesLayer;
#define KIS_MIME_TYPE "application/x-krita"
/**
* The %Calligra document class
*
* This class provides some functionality each %Calligra document should have.
*
* @short The %Calligra document class
*/
class KRITAUI_EXPORT KisDocument : public QObject, public KoDocumentBase
{
Q_OBJECT
protected:
explicit KisDocument();
/**
* @brief KisDocument makes a deep copy of the document \p rhs.
* The caller *must* ensure that the image is properly
* locked and is in consistent state before asking for
* cloning.
* @param rhs the source document to copy from
*/
explicit KisDocument(const KisDocument &rhs);
public:
enum OpenFlag {
None = 0,
DontAddToRecent = 0x1,
RecoveryFile = 0x2
};
Q_DECLARE_FLAGS(OpenFlags, OpenFlag)
/**
* Destructor.
*
* The destructor does not delete any attached KisView objects and it does not
* delete the attached widget as returned by widget().
*/
~KisDocument() override;
/**
* @brief reload Reloads the document from the original url
* @return the result of loading the document
*/
bool reload();
/**
* @brief creates a clone of the document and returns it. Please make sure that you
* hold all the necessary locks on the image before asking for a clone!
*/
KisDocument* clone();
/**
* @brief openUrl Open an URL
* @param url The URL to open
* @param flags Control specific behavior
* @return success status
*/
bool openUrl(const QUrl &url, OpenFlags flags = None);
/**
* Opens the document given by @p url, without storing the URL
* in the KisDocument.
* Call this instead of openUrl() to implement KisMainWindow's
* File --> Import feature.
*
* @note This will call openUrl(). To differentiate this from an ordinary
* Open operation (in any reimplementation of openUrl() or openFile())
* call isImporting().
*/
bool importDocument(const QUrl &url);
/**
* Saves the document as @p url without changing the state of the
* KisDocument (URL, modified flag etc.). Call this instead of
* KisParts::ReadWritePart::saveAs() to implement KisMainWindow's
* File --> Export feature.
*/
bool exportDocument(const QUrl &url, const QByteArray &mimeType, bool showWarnings = false, KisPropertiesConfigurationSP exportConfiguration = 0);
bool exportDocumentSync(const QUrl &url, const QByteArray &mimeType, KisPropertiesConfigurationSP exportConfiguration = 0);
private:
bool exportDocumentImpl(const KritaUtils::ExportFileJob &job, KisPropertiesConfigurationSP exportConfiguration);
public:
/**
* @brief Sets whether the document can be edited or is read only.
*
* This recursively applied to all child documents and
* KisView::updateReadWrite is called for every attached
* view.
*/
void setReadWrite(bool readwrite = true);
/**
* To be preferred when a document exists. It is fast when calling
* it multiple times since it caches the result that readNativeFormatMimeType()
* delivers.
* This comes from the X-KDE-NativeMimeType key in the .desktop file.
*/
static QByteArray nativeFormatMimeType() { return KIS_MIME_TYPE; }
/// Checks whether a given mimetype can be handled natively.
bool isNativeFormat(const QByteArray& mimetype) const;
/// Returns a list of the mimetypes considered "native", i.e. which can
/// be saved by KisDocument without a filter, in *addition* to the main one
static QStringList extraNativeMimeTypes() { return QStringList() << KIS_MIME_TYPE; }
/**
* Returns the actual mimetype of the document
*/
QByteArray mimeType() const override;
/**
* @brief Sets the mime type for the document.
*
* When choosing "save as" this is also the mime type
* selected by default.
*/
void setMimeType(const QByteArray & mimeType) override;
/**
* @return true if file operations should inhibit the option dialog
*/
bool fileBatchMode() const;
/**
* @param batchMode if true, do not show the option dialog for file operations.
*/
void setFileBatchMode(const bool batchMode);
/**
* Sets the error message to be shown to the user (use i18n()!)
* when loading or saving fails.
* If you asked the user about something and they chose "Cancel",
*/
void setErrorMessage(const QString& errMsg);
/**
* Return the last error message. Usually KisDocument takes care of
* showing it; this method is mostly provided for non-interactive use.
*/
QString errorMessage() const;
/**
* Sets the warning message to be shown to the user (use i18n()!)
* when loading or saving fails.
*/
void setWarningMessage(const QString& warningMsg);
/**
* Return the last warning message set by loading or saving. Warnings
* mean that the document could not be completely loaded, but the errors
* were not absolutely fatal.
*/
QString warningMessage() const;
/**
* @brief Generates a preview picture of the document
* @note The preview is used in the File Dialog and also to create the Thumbnail
*/
QPixmap generatePreview(const QSize& size);
/**
* Tells the document that its title has been modified, either because
* the modified status changes (this is done by setModified() ) or
* because the URL or the document-info's title changed.
*/
void setTitleModified();
/**
* @brief Sets the document to empty.
*
* Used after loading a template
* (which is not empty, but not the user's input).
*
* @see isEmpty()
*/
void setEmpty(bool empty = true);
/**
* Return a correctly created QDomDocument for this KisDocument,
* including processing instruction, complete DOCTYPE tag (with systemId and publicId), and root element.
* @param tagName the name of the tag for the root element
* @param version the DTD version (usually the application's version).
*/
QDomDocument createDomDocument(const QString& tagName, const QString& version) const;
/**
* Return a correctly created QDomDocument for an old (1.3-style) %Calligra document,
* including processing instruction, complete DOCTYPE tag (with systemId and publicId), and root element.
* This static method can be used e.g. by filters.
* @param appName the app's instance name, e.g. words, kspread, kpresenter etc.
* @param tagName the name of the tag for the root element, e.g. DOC for words/kpresenter.
* @param version the DTD version (usually the application's version).
*/
static QDomDocument createDomDocument(const QString& appName, const QString& tagName, const QString& version);
/**
* Loads a document in the native format from a given URL.
* Reimplement if your native format isn't XML.
*
* @param file the file to load - usually KReadOnlyPart::m_file or the result of a filter
*/
bool loadNativeFormat(const QString & file);
/**
* Set standard autosave interval that is set by a config file
*/
void setNormalAutoSaveInterval();
/**
* Set emergency interval that autosave uses when the image is busy,
* by default it is 10 sec
*/
void setEmergencyAutoSaveInterval();
/**
* Disable autosave
*/
void setInfiniteAutoSaveInterval();
/**
* @return the information concerning this document.
* @see KoDocumentInfo
*/
KoDocumentInfo *documentInfo() const;
/**
* Performs a cleanup of unneeded backup files
*/
void removeAutoSaveFiles(const QString &autosaveBaseName, bool wasRecovered);
/**
* Returns true if this document or any of its internal child documents are modified.
*/
bool isModified() const override;
/**
* @return caption of the document
*
* Caption is of the form "[title] - [url]",
* built out of the document info (title) and pretty-printed
* document URL.
* If the title is not present, only the URL it returned.
*/
QString caption() const;
/**
* Sets the document URL to empty URL
* KParts doesn't allow this, but %Calligra apps have e.g. templates
* After using loadNativeFormat on a template, one wants
* to set the url to QUrl()
*/
void resetURL();
/**
* @internal (public for KisMainWindow)
*/
void setMimeTypeAfterLoading(const QString& mimeType);
/**
* Returns the unit used to display all measures/distances.
*/
KoUnit unit() const;
/**
* Sets the unit used to display all measures/distances.
*/
void setUnit(const KoUnit &unit);
KisGridConfig gridConfig() const;
void setGridConfig(const KisGridConfig &config);
/// returns the guides data for this document.
const KisGuidesConfig& guidesConfig() const;
void setGuidesConfig(const KisGuidesConfig &data);
QList<KoColorSet *> &paletteList();
void setPaletteList(const QList<KoColorSet *> &paletteList);
void clearUndoHistory();
/**
* Sets the modified flag on the document. This means that it has
* to be saved or not before deleting it.
*/
void setModified(bool _mod);
void setRecovered(bool value);
bool isRecovered() const;
void updateEditingTime(bool forceStoreElapsed);
/**
* Returns the global undo stack
*/
KUndo2Stack *undoStack();
/**
* @brief importExportManager gives access to the internal import/export manager
* @return the document's import/export manager
*/
KisImportExportManager *importExportManager() const;
/**
* @brief serializeToNativeByteArray daves the document into a .kra file wtitten
* to a memory-based byte-array
* @return a byte array containing the .kra file
*/
QByteArray serializeToNativeByteArray();
/**
* @brief isInSaving shown if the document has any (background) saving process or not
* @return true if there is some saving in action
*/
bool isInSaving() const;
public Q_SLOTS:
/**
* Adds a command to the undo stack and executes it by calling the redo() function.
* @param command command to add to the undo stack
*/
void addCommand(KUndo2Command *command);
/**
* Begins recording of a macro command. At the end endMacro needs to be called.
* @param text command description
*/
void beginMacro(const KUndo2MagicString &text);
/**
* Ends the recording of a macro command.
*/
void endMacro();
Q_SIGNALS:
/**
* This signal is emitted when the unit is changed by setUnit().
* It is common to connect views to it, in order to change the displayed units
* (e.g. in the rulers)
*/
void unitChanged(const KoUnit &unit);
/**
* Emitted e.g. at the beginning of a save operation
* This is emitted by KisDocument and used by KisView to display a statusbar message
*/
void statusBarMessage(const QString& text, int timeout = 0);
/**
* Emitted e.g. at the end of a save operation
* This is emitted by KisDocument and used by KisView to clear the statusbar message
*/
void clearStatusBarMessage();
/**
* Emitted when the document is modified
*/
void modified(bool);
void titleModified(const QString &caption, bool isModified);
void sigLoadingFinished();
void sigSavingFinished();
void sigGuidesConfigChanged(const KisGuidesConfig &config);
void sigBackgroundSavingFinished(KisImportExportFilter::ConversionStatus status, const QString &errorMessage);
void sigCompleteBackgroundSaving(const KritaUtils::ExportFileJob &job, KisImportExportFilter::ConversionStatus status, const QString &errorMessage);
void sigReferenceImagesChanged();
private Q_SLOTS:
void finishExportInBackground();
void slotChildCompletedSavingInBackground(KisImportExportFilter::ConversionStatus status, const QString &errorMessage);
void slotCompleteAutoSaving(const KritaUtils::ExportFileJob &job, KisImportExportFilter::ConversionStatus status, const QString &errorMessage);
void slotCompleteSavingDocument(const KritaUtils::ExportFileJob &job, KisImportExportFilter::ConversionStatus status, const QString &errorMessage);
void slotInitiateAsyncAutosaving(KisDocument *clonedDocument);
private:
friend class KisPart;
friend class SafeSavingLocker;
bool initiateSavingInBackground(const QString actionName,
const QObject *receiverObject, const char *receiverMethod,
const KritaUtils::ExportFileJob &job,
KisPropertiesConfigurationSP exportConfiguration,
std::unique_ptr<KisDocument> &&optionalClonedDocument);
bool initiateSavingInBackground(const QString actionName,
const QObject *receiverObject, const char *receiverMethod,
const KritaUtils::ExportFileJob &job,
KisPropertiesConfigurationSP exportConfiguration);
bool startExportInBackground(const QString &actionName, const QString &location,
const QString &realLocation,
const QByteArray &mimeType,
bool showWarnings,
KisPropertiesConfigurationSP exportConfiguration);
/**
* Activate/deactivate/configure the autosave feature.
* @param delay in seconds, 0 to disable
*/
void setAutoSaveDelay(int delay);
/**
* Generate a name for the document.
*/
QString newObjectName();
QString generateAutoSaveFileName(const QString & path) const;
/**
* Loads a document
*
* Applies a filter if necessary, and calls loadNativeFormat in any case
* You should not have to reimplement, except for very special cases.
*
* NOTE: this method also creates a new KisView instance!
*
* This method is called from the KReadOnlyPart::openUrl method.
*/
bool openFile();
public:
bool isAutosaving() const override;
public:
QString localFilePath() const override;
void setLocalFilePath( const QString &localFilePath );
KoDocumentInfoDlg* createDocumentInfoDialog(QWidget *parent, KoDocumentInfo *docInfo) const;
bool isReadWrite() const;
QUrl url() const override;
void setUrl(const QUrl &url) override;
bool closeUrl(bool promptToSave = true);
bool saveAs(const QUrl &url, const QByteArray &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfigration = 0);
/**
* Create a new image that has this document as a parent and
* replace the current image with this image.
*/
- bool newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace * cs, const KoColor &bgColor, bool backgroundAsLayer,
+ bool newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace * cs, const KoColor &bgColor, KisConfig::BackgroundStyle bgStyle,
int numberOfLayers, const QString &imageDescription, const double imageResolution);
bool isSaving() const;
void waitForSavingToComplete();
KisImageWSP image() const;
/**
* @brief savingImage provides a detached, shallow copy of the original image that must be used when saving.
* Any strokes in progress will not be applied to this image, so the result might be missing some data. On
* the other hand, it won't block.
*
* @return a shallow copy of the original image, or 0 is saving is not in progress
*/
KisImageSP savingImage() const;
/**
* Set the current image to the specified image and turn undo on.
*/
void setCurrentImage(KisImageSP image, bool forceInitialUpdate = true);
/**
* Set the image of the document preliminary, before the document
* has completed loading. Some of the document items (shapes) may want
* to access image properties (bounds and resolution), so we should provide
* it to them even before the entire image is loaded.
*
* Right now, the only use by KoShapeRegistry::createShapeFromOdf(), remove
* after it is deprecated.
*/
void hackPreliminarySetImage(KisImageSP image);
KisUndoStore* createUndoStore();
/**
* The shape controller matches internal krita image layers with
* the flake shape hierarchy.
*/
KoShapeControllerBase * shapeController() const;
KoShapeLayer* shapeForNode(KisNodeSP layer) const;
/**
* Set the list of nodes that was marked as currently active. Used *only*
* for saving loading. Never use it for tools or processing.
*/
void setPreActivatedNode(KisNodeSP activatedNode);
/**
* @return the node that was set as active during loading. Used *only*
* for saving loading. Never use it for tools or processing.
*/
KisNodeSP preActivatedNode() const;
/// @return the list of assistants associated with this document
QList<KisPaintingAssistantSP> assistants() const;
/// @replace the current list of assistants with @param value
void setAssistants(const QList<KisPaintingAssistantSP> &value);
void setAssistantsGlobalColor(QColor color);
QColor assistantsGlobalColor();
/**
* Get existing reference images layer or null if none exists.
*/
KisSharedPtr<KisReferenceImagesLayer> referenceImagesLayer() const;
void setReferenceImagesLayer(KisSharedPtr<KisReferenceImagesLayer> layer, bool updateImage);
bool save(bool showWarnings, KisPropertiesConfigurationSP exportConfiguration);
/**
* Return the bounding box of the image and associated elements (e.g. reference images)
*/
QRectF documentBounds() const;
Q_SIGNALS:
void completed();
void canceled(const QString &);
private Q_SLOTS:
void setImageModified();
void slotAutoSave();
void slotUndoStackCleanChanged(bool value);
void slotConfigChanged();
private:
/**
* @brief try to clone the image. This method handles all the locking for you. If locking
* has failed, no cloning happens
* @return cloned document on success, null otherwise
*/
KisDocument *lockAndCloneForSaving();
QString exportErrorToUserMessage(KisImportExportFilter::ConversionStatus status, const QString &errorMessage);
QString prettyPathOrUrl() const;
bool openUrlInternal(const QUrl &url);
void slotAutoSaveImpl(std::unique_ptr<KisDocument> &&optionalClonedDocument);
class Private;
Private *const d;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(KisDocument::OpenFlags)
Q_DECLARE_METATYPE(KisDocument*)
#endif
diff --git a/libs/ui/KisImportExportManager.cpp b/libs/ui/KisImportExportManager.cpp
index affa7e6893..e5ac96de10 100644
--- a/libs/ui/KisImportExportManager.cpp
+++ b/libs/ui/KisImportExportManager.cpp
@@ -1,680 +1,699 @@
/*
* Copyright (C) 2016 Boudewijn Rempt <boud@valdyas.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisImportExportManager.h"
#include <QFile>
#include <QLabel>
#include <QVBoxLayout>
#include <QList>
#include <QApplication>
#include <QByteArray>
#include <QPluginLoader>
#include <QFileInfo>
#include <QMessageBox>
#include <QJsonObject>
#include <QTextBrowser>
#include <QCheckBox>
#include <QSaveFile>
#include <QGroupBox>
#include <QFuture>
#include <QtConcurrent>
#include <klocalizedstring.h>
#include <ksqueezedtextlabel.h>
#include <kpluginfactory.h>
+#include <KisUsageLogger.h>
#include <KoFileDialog.h>
#include <kis_icon_utils.h>
#include <KoDialog.h>
#include <KoProgressUpdater.h>
#include <KoJsonTrader.h>
#include <KisMimeDatabase.h>
#include <kis_config_widget.h>
#include <kis_debug.h>
#include <KisPreExportChecker.h>
#include <KisPart.h>
#include "kis_config.h"
#include "KisImportExportFilter.h"
#include "KisDocument.h"
#include <kis_image.h>
#include <kis_paint_layer.h>
#include "kis_painter.h"
#include "kis_guides_config.h"
#include "kis_grid_config.h"
#include "kis_popup_button.h"
#include <kis_iterator_ng.h>
#include "kis_async_action_feedback.h"
#include "KisReferenceImagesLayer.h"
// static cache for import and export mimetypes
QStringList KisImportExportManager::m_importMimeTypes;
QStringList KisImportExportManager::m_exportMimeTypes;
class Q_DECL_HIDDEN KisImportExportManager::Private
{
public:
KoUpdaterPtr updater;
QString cachedExportFilterMimeType;
QSharedPointer<KisImportExportFilter> cachedExportFilter;
};
struct KisImportExportManager::ConversionResult {
ConversionResult()
{
}
ConversionResult(const QFuture<KisImportExportFilter::ConversionStatus> &futureStatus)
: m_isAsync(true),
m_futureStatus(futureStatus)
{
}
ConversionResult(KisImportExportFilter::ConversionStatus status)
: m_isAsync(false),
m_status(status)
{
}
bool isAsync() const {
return m_isAsync;
}
QFuture<KisImportExportFilter::ConversionStatus> futureStatus() const {
// if the result is not async, then it means some failure happened,
// just return a cancelled future
KIS_SAFE_ASSERT_RECOVER_NOOP(m_isAsync || m_status != KisImportExportFilter::OK);
return m_futureStatus;
}
KisImportExportFilter::ConversionStatus status() const {
return m_status;
}
void setStatus(KisImportExportFilter::ConversionStatus value) {
m_status = value;
}
private:
bool m_isAsync = false;
QFuture<KisImportExportFilter::ConversionStatus> m_futureStatus;
KisImportExportFilter::ConversionStatus m_status = KisImportExportFilter::UsageError;
};
KisImportExportManager::KisImportExportManager(KisDocument* document)
: m_document(document)
, d(new Private)
{
}
KisImportExportManager::~KisImportExportManager()
{
delete d;
}
KisImportExportFilter::ConversionStatus KisImportExportManager::importDocument(const QString& location, const QString& mimeType)
{
ConversionResult result = convert(Import, location, location, mimeType, false, 0, false);
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!result.isAsync(), KisImportExportFilter::UsageError);
return result.status();
}
KisImportExportFilter::ConversionStatus KisImportExportManager::exportDocument(const QString& location, const QString& realLocation, const QByteArray& mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration)
{
ConversionResult result = convert(Export, location, realLocation, mimeType, showWarnings, exportConfiguration, false);
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!result.isAsync(), KisImportExportFilter::UsageError);
return result.status();
}
QFuture<KisImportExportFilter::ConversionStatus> KisImportExportManager::exportDocumentAsyc(const QString &location, const QString &realLocation, const QByteArray &mimeType, KisImportExportFilter::ConversionStatus &status, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration)
{
ConversionResult result = convert(Export, location, realLocation, mimeType, showWarnings, exportConfiguration, true);
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(result.isAsync() ||
result.status() != KisImportExportFilter::OK, QFuture<KisImportExportFilter::ConversionStatus>());
status = result.status();
return result.futureStatus();
}
// The static method to figure out to which parts of the
// graph this mimetype has a connection to.
QStringList KisImportExportManager::supportedMimeTypes(Direction direction)
{
// Find the right mimetype by the extension
QSet<QString> mimeTypes;
// mimeTypes << KisDocument::nativeFormatMimeType() << "application/x-krita-paintoppreset" << "image/openraster";
if (direction == KisImportExportManager::Import) {
if (m_importMimeTypes.isEmpty()) {
QList<QPluginLoader *>list = KoJsonTrader::instance()->query("Krita/FileFilter", "");
Q_FOREACH(QPluginLoader *loader, list) {
QJsonObject json = loader->metaData().value("MetaData").toObject();
Q_FOREACH(const QString &mimetype, json.value("X-KDE-Import").toString().split(",", QString::SkipEmptyParts)) {
//qDebug() << "Adding import mimetype" << mimetype << KisMimeDatabase::descriptionForMimeType(mimetype) << "from plugin" << loader;
mimeTypes << mimetype;
}
}
qDeleteAll(list);
m_importMimeTypes = mimeTypes.toList();
}
return m_importMimeTypes;
}
else if (direction == KisImportExportManager::Export) {
if (m_exportMimeTypes.isEmpty()) {
QList<QPluginLoader *>list = KoJsonTrader::instance()->query("Krita/FileFilter", "");
Q_FOREACH(QPluginLoader *loader, list) {
QJsonObject json = loader->metaData().value("MetaData").toObject();
Q_FOREACH(const QString &mimetype, json.value("X-KDE-Export").toString().split(",", QString::SkipEmptyParts)) {
//qDebug() << "Adding export mimetype" << mimetype << KisMimeDatabase::descriptionForMimeType(mimetype) << "from plugin" << loader;
mimeTypes << mimetype;
}
}
qDeleteAll(list);
m_exportMimeTypes = mimeTypes.toList();
}
return m_exportMimeTypes;
}
return QStringList();
}
KisImportExportFilter *KisImportExportManager::filterForMimeType(const QString &mimetype, KisImportExportManager::Direction direction)
{
int weight = -1;
KisImportExportFilter *filter = 0;
QList<QPluginLoader *>list = KoJsonTrader::instance()->query("Krita/FileFilter", "");
Q_FOREACH(QPluginLoader *loader, list) {
QJsonObject json = loader->metaData().value("MetaData").toObject();
QString directionKey = direction == Export ? "X-KDE-Export" : "X-KDE-Import";
if (json.value(directionKey).toString().split(",", QString::SkipEmptyParts).contains(mimetype)) {
KLibFactory *factory = qobject_cast<KLibFactory *>(loader->instance());
if (!factory) {
warnUI << loader->errorString();
continue;
}
QObject* obj = factory->create<KisImportExportFilter>(0);
if (!obj || !obj->inherits("KisImportExportFilter")) {
delete obj;
continue;
}
KisImportExportFilter *f = qobject_cast<KisImportExportFilter*>(obj);
if (!f) {
delete obj;
continue;
}
int w = json.value("X-KDE-Weight").toInt();
if (w > weight) {
delete filter;
filter = f;
f->setObjectName(loader->fileName());
weight = w;
}
}
}
qDeleteAll(list);
if (filter) {
filter->setMimeType(mimetype);
}
return filter;
}
bool KisImportExportManager::batchMode(void) const
{
return m_document->fileBatchMode();
}
void KisImportExportManager::setUpdater(KoUpdaterPtr updater)
{
d->updater = updater;
}
QString KisImportExportManager::askForAudioFileName(const QString &defaultDir, QWidget *parent)
{
KoFileDialog dialog(parent, KoFileDialog::ImportFiles, "ImportAudio");
if (!defaultDir.isEmpty()) {
dialog.setDefaultDir(defaultDir);
}
QStringList mimeTypes;
mimeTypes << "audio/mpeg";
mimeTypes << "audio/ogg";
mimeTypes << "audio/vorbis";
mimeTypes << "audio/vnd.wave";
mimeTypes << "audio/flac";
dialog.setMimeTypeFilters(mimeTypes);
dialog.setCaption(i18nc("@titile:window", "Open Audio"));
return dialog.filename();
}
KisImportExportManager::ConversionResult KisImportExportManager::convert(KisImportExportManager::Direction direction, const QString &location, const QString& realLocation, const QString &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration, bool isAsync)
{
// export configuration is supported for export only
KIS_SAFE_ASSERT_RECOVER_NOOP(direction == Export || !bool(exportConfiguration));
-
QString typeName = mimeType;
if (typeName.isEmpty()) {
typeName = KisMimeDatabase::mimeTypeForFile(location, direction == KisImportExportManager::Export ? false : true);
}
QSharedPointer<KisImportExportFilter> filter;
/**
* Fetching a filter from the registry is a really expensive operation,
* because it blocks all the threads. Cache the filter if possible.
*/
if (direction == KisImportExportManager::Export &&
d->cachedExportFilter &&
d->cachedExportFilterMimeType == typeName) {
filter = d->cachedExportFilter;
} else {
filter = toQShared(filterForMimeType(typeName, direction));
if (direction == Export) {
d->cachedExportFilter = filter;
d->cachedExportFilterMimeType = typeName;
}
}
if (!filter) {
return KisImportExportFilter::FilterCreationError;
}
filter->setFilename(location);
filter->setRealFilename(realLocation);
filter->setBatchMode(batchMode());
filter->setMimeType(typeName);
if (!d->updater.isNull()) {
// WARNING: The updater is not guaranteed to be persistent! If you ever want
// to add progress reporting to "Save also as .kra", make sure you create
// a separate KoProgressUpdater for that!
// WARNING2: the failsafe completion of the updater happens in the destructor
// the filter.
filter->setUpdater(d->updater);
}
QByteArray from, to;
if (direction == Export) {
from = m_document->nativeFormatMimeType();
to = typeName.toLatin1();
}
else {
from = typeName.toLatin1();
to = m_document->nativeFormatMimeType();
}
KIS_ASSERT_RECOVER_RETURN_VALUE(
direction == Import || direction == Export,
KisImportExportFilter::BadConversionGraph);
-
-
ConversionResult result = KisImportExportFilter::OK;
if (direction == Import) {
+
+ KisUsageLogger::log(QString("Importing %1 to %2. Location: %3. Real location: %4. Batchmode: %5")
+ .arg(QString::fromLatin1(from))
+ .arg(QString::fromLatin1(to))
+ .arg(location)
+ .arg(realLocation)
+ .arg(batchMode()));
+
+
+
// async importing is not yet supported!
KIS_SAFE_ASSERT_RECOVER_NOOP(!isAsync);
if (0 && !batchMode()) {
KisAsyncActionFeedback f(i18n("Opening document..."), 0);
result = f.runAction(std::bind(&KisImportExportManager::doImport, this, location, filter));
} else {
result = doImport(location, filter);
}
}
else /* if (direction == Export) */ {
if (!exportConfiguration) {
exportConfiguration = filter->lastSavedConfiguration(from, to);
}
if (exportConfiguration) {
fillStaticExportConfigurationProperties(exportConfiguration);
}
bool alsoAsKra = false;
bool askUser = askUserAboutExportConfiguration(filter, exportConfiguration,
from, to,
batchMode(), showWarnings,
&alsoAsKra);
if (!batchMode() && !askUser) {
return KisImportExportFilter::UserCancelled;
}
+ KisUsageLogger::log(QString("Converting from %1 to %2. Location: %3. Real location: %4. Batchmode: %5. Configuration: %6")
+ .arg(QString::fromLatin1(from))
+ .arg(QString::fromLatin1(to))
+ .arg(location)
+ .arg(realLocation)
+ .arg(batchMode())
+ .arg(exportConfiguration ? exportConfiguration->toXML() : "none"));
+
+
+
if (isAsync) {
result = QtConcurrent::run(std::bind(&KisImportExportManager::doExport, this, location, filter, exportConfiguration, alsoAsKra));
// we should explicitly report that the exporting has been initiated
result.setStatus(KisImportExportFilter::OK);
} else if (!batchMode()) {
KisAsyncActionFeedback f(i18n("Saving document..."), 0);
result = f.runAction(std::bind(&KisImportExportManager::doExport, this, location, filter, exportConfiguration, alsoAsKra));
} else {
result = doExport(location, filter, exportConfiguration, alsoAsKra);
}
if (exportConfiguration && !batchMode() && showWarnings) {
KisConfig(false).setExportConfiguration(typeName, exportConfiguration);
}
}
return result;
}
void KisImportExportManager::fillStaticExportConfigurationProperties(KisPropertiesConfigurationSP exportConfiguration, KisImageSP image)
{
KisPaintDeviceSP dev = image->projection();
const KoColorSpace* cs = dev->colorSpace();
const bool isThereAlpha =
KisPainter::checkDeviceHasTransparency(image->projection());
exportConfiguration->setProperty(KisImportExportFilter::ImageContainsTransparencyTag, isThereAlpha);
exportConfiguration->setProperty(KisImportExportFilter::ColorModelIDTag, cs->colorModelId().id());
exportConfiguration->setProperty(KisImportExportFilter::ColorDepthIDTag, cs->colorDepthId().id());
const bool sRGB =
(cs->profile()->name().contains(QLatin1String("srgb"), Qt::CaseInsensitive) &&
!cs->profile()->name().contains(QLatin1String("g10")));
exportConfiguration->setProperty(KisImportExportFilter::sRGBTag, sRGB);
}
void KisImportExportManager::fillStaticExportConfigurationProperties(KisPropertiesConfigurationSP exportConfiguration)
{
return fillStaticExportConfigurationProperties(exportConfiguration, m_document->image());
}
bool KisImportExportManager::askUserAboutExportConfiguration(
QSharedPointer<KisImportExportFilter> filter,
KisPropertiesConfigurationSP exportConfiguration,
const QByteArray &from,
const QByteArray &to,
const bool batchMode,
const bool showWarnings,
bool *alsoAsKra)
{
// prevents the animation renderer from running this code
const QString mimeUserDescription = KisMimeDatabase::descriptionForMimeType(to);
QStringList warnings;
QStringList errors;
{
KisPreExportChecker checker;
checker.check(m_document->image(), filter->exportChecks());
warnings = checker.warnings();
errors = checker.errors();
}
KisConfigWidget *wdg = 0;
if (QThread::currentThread() == qApp->thread()) {
wdg = filter->createConfigurationWidget(0, from, to);
}
// Extra checks that cannot be done by the checker, because the checker only has access to the image.
if (!m_document->assistants().isEmpty() && to != m_document->nativeFormatMimeType()) {
warnings.append(i18nc("image conversion warning", "The image contains <b>assistants</b>. The assistants will not be saved."));
}
if (m_document->referenceImagesLayer() && m_document->referenceImagesLayer()->shapeCount() > 0 && to != m_document->nativeFormatMimeType()) {
warnings.append(i18nc("image conversion warning", "The image contains <b>reference images</b>. The reference images will not be saved."));
}
if (m_document->guidesConfig().hasGuides() && to != m_document->nativeFormatMimeType()) {
warnings.append(i18nc("image conversion warning", "The image contains <b>guides</b>. The guides will not be saved."));
}
if (!m_document->gridConfig().isDefault() && to != m_document->nativeFormatMimeType()) {
warnings.append(i18nc("image conversion warning", "The image contains a <b>custom grid configuration</b>. The configuration will not be saved."));
}
if (!batchMode && !errors.isEmpty()) {
QString error = "<html><body><p><b>"
+ i18n("Error: cannot save this image as a %1.", mimeUserDescription)
+ "</b> Reasons:</p>"
+ "<p/><ul>";
Q_FOREACH(const QString &w, errors) {
error += "\n<li>" + w + "</li>";
}
error += "</ul>";
QMessageBox::critical(KisPart::instance()->currentMainwindow(), i18nc("@title:window", "Krita: Export Error"), error);
return false;
}
if (!batchMode && (wdg || !warnings.isEmpty())) {
KoDialog dlg;
dlg.setButtons(KoDialog::Ok | KoDialog::Cancel);
dlg.setWindowTitle(mimeUserDescription);
QWidget *page = new QWidget(&dlg);
QVBoxLayout *layout = new QVBoxLayout(page);
if (showWarnings && !warnings.isEmpty()) {
QHBoxLayout *hLayout = new QHBoxLayout();
QLabel *labelWarning = new QLabel();
labelWarning->setPixmap(KisIconUtils::loadIcon("warning").pixmap(32, 32));
hLayout->addWidget(labelWarning);
KisPopupButton *bn = new KisPopupButton(0);
bn->setText(i18nc("Keep the extra space at the end of the sentence, please", "Warning: saving as %1 will lose information from your image. ", mimeUserDescription));
hLayout->addWidget(bn);
layout->addLayout(hLayout);
QTextBrowser *browser = new QTextBrowser();
browser->setMinimumWidth(bn->width());
bn->setPopupWidget(browser);
QString warning = "<html><body><p><b>"
+ i18n("You will lose information when saving this image as a %1.", mimeUserDescription);
if (warnings.size() == 1) {
warning += "</b> Reason:</p>";
}
else {
warning += "</b> Reasons:</p>";
}
warning += "<p/><ul>";
Q_FOREACH(const QString &w, warnings) {
warning += "\n<li>" + w + "</li>";
}
warning += "</ul>";
browser->setHtml(warning);
}
if (wdg) {
QGroupBox *box = new QGroupBox(i18n("Options"));
QVBoxLayout *boxLayout = new QVBoxLayout(box);
wdg->setConfiguration(exportConfiguration);
boxLayout->addWidget(wdg);
layout->addWidget(box);
}
QCheckBox *chkAlsoAsKra = 0;
if (showWarnings && !warnings.isEmpty()) {
chkAlsoAsKra = new QCheckBox(i18n("Also save your image as a Krita file."));
chkAlsoAsKra->setChecked(KisConfig(true).readEntry<bool>("AlsoSaveAsKra", false));
layout->addWidget(chkAlsoAsKra);
}
dlg.setMainWidget(page);
dlg.resize(dlg.minimumSize());
if (showWarnings || wdg) {
if (!dlg.exec()) {
return false;
}
}
*alsoAsKra = false;
if (chkAlsoAsKra) {
KisConfig(false).writeEntry<bool>("AlsoSaveAsKra", chkAlsoAsKra->isChecked());
*alsoAsKra = chkAlsoAsKra->isChecked();
}
if (wdg) {
*exportConfiguration = *wdg->configuration();
}
}
return true;
}
KisImportExportFilter::ConversionStatus KisImportExportManager::doImport(const QString &location, QSharedPointer<KisImportExportFilter> filter)
{
QFile file(location);
if (!file.exists()) {
return KisImportExportFilter::FileNotFound;
}
if (filter->supportsIO() && !file.open(QFile::ReadOnly)) {
return KisImportExportFilter::FileNotFound;
}
KisImportExportFilter::ConversionStatus status =
filter->convert(m_document, &file, KisPropertiesConfigurationSP());
if (file.isOpen()) {
file.close();
}
return status;
}
KisImportExportFilter::ConversionStatus KisImportExportManager::doExport(const QString &location, QSharedPointer<KisImportExportFilter> filter, KisPropertiesConfigurationSP exportConfiguration, bool alsoAsKra)
{
KisImportExportFilter::ConversionStatus status =
doExportImpl(location, filter, exportConfiguration);
if (alsoAsKra && status == KisImportExportFilter::OK) {
QString kraLocation = location + ".kra";
QByteArray mime = m_document->nativeFormatMimeType();
QSharedPointer<KisImportExportFilter> filter(
filterForMimeType(QString::fromLatin1(mime), Export));
KIS_SAFE_ASSERT_RECOVER_NOOP(filter);
if (filter) {
filter->setFilename(kraLocation);
KisPropertiesConfigurationSP kraExportConfiguration =
filter->lastSavedConfiguration(mime, mime);
status = doExportImpl(kraLocation, filter, kraExportConfiguration);
} else {
status = KisImportExportFilter::FilterCreationError;
}
}
return status;
}
// Temporary workaround until QTBUG-57299 is fixed.
#ifndef Q_OS_WIN
#define USE_QSAVEFILE
#endif
KisImportExportFilter::ConversionStatus KisImportExportManager::doExportImpl(const QString &location, QSharedPointer<KisImportExportFilter> filter, KisPropertiesConfigurationSP exportConfiguration)
{
#ifdef USE_QSAVEFILE
QSaveFile file(location);
file.setDirectWriteFallback(true);
if (filter->supportsIO() && !file.open(QFile::WriteOnly)) {
#else
QFileInfo fi(location);
QTemporaryFile file(fi.absolutePath() + ".XXXXXX.kra");
if (filter->supportsIO() && !file.open()) {
#endif
QString error = file.errorString();
if (error.isEmpty()) {
error = i18n("Could not open %1 for writing.", location);
}
m_document->setErrorMessage(error);
#ifdef USE_QSAVEFILE
file.cancelWriting();
#endif
return KisImportExportFilter::CreationError;
}
KisImportExportFilter::ConversionStatus status = filter->convert(m_document, &file, exportConfiguration);
if (filter->supportsIO()) {
if (status != KisImportExportFilter::OK) {
#ifdef USE_QSAVEFILE
file.cancelWriting();
#endif
} else {
#ifdef USE_QSAVEFILE
if (!file.commit()) {
+ qWarning() << "Could not commit QSaveFile";
QString error = file.errorString();
if (error.isEmpty()) {
error = i18n("Could not write to %1.", location);
}
if (m_document->errorMessage().isEmpty()) {
m_document->setErrorMessage(error);
}
status = KisImportExportFilter::CreationError;
}
#else
file.flush();
file.close();
QFile target(location);
if (target.exists()) {
// There should already be a .kra~ backup
target.remove();
}
if (!file.copy(location)) {
file.setAutoRemove(false);
m_document->setErrorMessage(i18n("Could not copy %1 to its final location %2", file.fileName(), location));
return KisImportExportFilter::CreationError;
}
#endif
}
}
return status;
}
#include <KisMimeDatabase.h>
diff --git a/libs/ui/KisMainWindow.cpp b/libs/ui/KisMainWindow.cpp
index 3c8b5858fc..57b2ea4271 100644
--- a/libs/ui/KisMainWindow.cpp
+++ b/libs/ui/KisMainWindow.cpp
@@ -1,2667 +1,2681 @@
/* This file is part of the KDE project
Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
Copyright (C) 2000-2006 David Faure <faure@kde.org>
Copyright (C) 2007, 2009 Thomas zander <zander@kde.org>
Copyright (C) 2010 Benjamin Port <port.benjamin@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisMainWindow.h"
#include <KoConfig.h>
// qt includes
#include <QApplication>
#include <QByteArray>
#include <QCloseEvent>
#include <QStandardPaths>
#include <QDesktopServices>
#include <QDesktopWidget>
#include <QDialog>
#include <QDockWidget>
#include <QIcon>
#include <QInputDialog>
#include <QLabel>
#include <QLayout>
#include <QMdiArea>
#include <QMdiSubWindow>
#include <QMutex>
#include <QMutexLocker>
#include <QPointer>
#include <QPrintDialog>
#include <QPrinter>
#include <QPrintPreviewDialog>
#include <QToolButton>
#include <QSignalMapper>
#include <QTabBar>
#include <QMoveEvent>
#include <QUrl>
#include <QMessageBox>
#include <QStatusBar>
#include <QMenu>
#include <QMenuBar>
#include <KisMimeDatabase.h>
#include <QMimeData>
#include <QStackedWidget>
#include <QProxyStyle>
#include <QScreen>
#include <QAction>
#include <kactioncollection.h>
#include <kactionmenu.h>
#include <kis_debug.h>
#include <kedittoolbar.h>
#include <khelpmenu.h>
#include <klocalizedstring.h>
#include <kaboutdata.h>
#include <kis_workspace_resource.h>
#include <input/kis_input_manager.h>
#include "kis_selection_manager.h"
#include "kis_icon_utils.h"
#include <krecentfilesaction.h>
#include <ktoggleaction.h>
#include <ktoolbar.h>
#include <kmainwindow.h>
#include <kxmlguiwindow.h>
#include <kxmlguifactory.h>
#include <kxmlguiclient.h>
#include <kguiitem.h>
#include <kwindowconfig.h>
#include <kformat.h>
#include <KoResourcePaths.h>
#include <KoToolFactoryBase.h>
#include <KoToolRegistry.h>
#include "KoDockFactoryBase.h"
#include "KoDocumentInfoDlg.h"
#include "KoDocumentInfo.h"
#include "KoFileDialog.h"
#include <kis_icon.h>
#include <KoPageLayoutDialog.h>
#include <KoPageLayoutWidget.h>
#include <KoToolManager.h>
#include <KoZoomController.h>
#include "KoToolDocker.h"
#include "KoToolBoxDocker_p.h"
#include <KoToolBoxFactory.h>
#include <KoDockRegistry.h>
#include <KoPluginLoader.h>
#include <KoColorSpaceEngine.h>
#include <KoUpdater.h>
#include <KoResourceModel.h>
#include <brushengine/kis_paintop_settings.h>
#include "dialogs/kis_about_application.h"
#include "dialogs/kis_delayed_save_dialog.h"
#include "dialogs/kis_dlg_preferences.h"
#include "kis_action.h"
#include "kis_action_manager.h"
#include "KisApplication.h"
#include "kis_canvas2.h"
#include "kis_canvas_controller.h"
#include "kis_canvas_resource_provider.h"
#include "kis_clipboard.h"
#include "kis_config.h"
#include "kis_config_notifier.h"
#include "kis_custom_image_widget.h"
#include <KisDocument.h>
#include "kis_group_layer.h"
#include "kis_image_from_clipboard_widget.h"
#include "kis_image.h"
#include <KisImportExportFilter.h>
#include "KisImportExportManager.h"
#include "kis_mainwindow_observer.h"
#include "kis_memory_statistics_server.h"
#include "kis_node.h"
#include "KisOpenPane.h"
#include "kis_paintop_box.h"
#include "KisPart.h"
#include "KisPrintJob.h"
#include "KisResourceServerProvider.h"
#include "kis_signal_compressor_with_param.h"
#include "kis_statusbar.h"
#include "KisView.h"
#include "KisViewManager.h"
#include "thememanager.h"
#include "kis_animation_importer.h"
#include "dialogs/kis_dlg_import_image_sequence.h"
#include <KisImageConfigNotifier.h>
#include "KisWindowLayoutManager.h"
#include <KisUndoActionsUpdateManager.h>
#include "KisWelcomePageWidget.h"
#include <KritaVersionWrapper.h>
#include <kritaversion.h>
#include <mutex>
#ifdef Q_OS_WIN
#include <QtPlatformHeaders/QWindowsWindowFunctions>
#endif
class ToolDockerFactory : public KoDockFactoryBase
{
public:
ToolDockerFactory() : KoDockFactoryBase() { }
QString id() const override {
return "sharedtooldocker";
}
QDockWidget* createDockWidget() override {
KoToolDocker* dockWidget = new KoToolDocker();
return dockWidget;
}
DockPosition defaultDockPosition() const override {
return DockRight;
}
};
class Q_DECL_HIDDEN KisMainWindow::Private
{
public:
Private(KisMainWindow *parent, QUuid id)
: q(parent)
, id(id)
, dockWidgetMenu(new KActionMenu(i18nc("@action:inmenu", "&Dockers"), parent))
, windowMenu(new KActionMenu(i18nc("@action:inmenu", "&Window"), parent))
, documentMenu(new KActionMenu(i18nc("@action:inmenu", "New &View"), parent))
, workspaceMenu(new KActionMenu(i18nc("@action:inmenu", "Wor&kspace"), parent))
, welcomePage(new KisWelcomePageWidget(parent))
, widgetStack(new QStackedWidget(parent))
, mdiArea(new QMdiArea(parent))
, windowMapper(new QSignalMapper(parent))
, documentMapper(new QSignalMapper(parent))
{
if (id.isNull()) this->id = QUuid::createUuid();
widgetStack->addWidget(welcomePage);
widgetStack->addWidget(mdiArea);
mdiArea->setTabsMovable(true);
mdiArea->setActivationOrder(QMdiArea::ActivationHistoryOrder);
}
~Private() {
qDeleteAll(toolbarList);
}
KisMainWindow *q {0};
QUuid id;
KisViewManager *viewManager {0};
QPointer<KisView> activeView;
QList<QAction *> toolbarList;
bool firstTime {true};
bool windowSizeDirty {false};
bool readOnly {false};
KisAction *showDocumentInfo {0};
KisAction *saveAction {0};
KisAction *saveActionAs {0};
// KisAction *printAction;
// KisAction *printActionPreview;
// KisAction *exportPdf {0};
KisAction *importAnimation {0};
KisAction *closeAll {0};
// KisAction *reloadFile;
KisAction *importFile {0};
KisAction *exportFile {0};
KisAction *undo {0};
KisAction *redo {0};
KisAction *newWindow {0};
KisAction *close {0};
KisAction *mdiCascade {0};
KisAction *mdiTile {0};
KisAction *mdiNextWindow {0};
KisAction *mdiPreviousWindow {0};
KisAction *toggleDockers {0};
KisAction *toggleDockerTitleBars {0};
KisAction *fullScreenMode {0};
KisAction *showSessionManager {0};
KisAction *expandingSpacers[2];
KActionMenu *dockWidgetMenu;
KActionMenu *windowMenu;
KActionMenu *documentMenu;
KActionMenu *workspaceMenu;
KHelpMenu *helpMenu {0};
KRecentFilesAction *recentFiles {0};
KoResourceModel *workspacemodel {0};
QScopedPointer<KisUndoActionsUpdateManager> undoActionsUpdateManager;
QString lastExportLocation;
QMap<QString, QDockWidget *> dockWidgetsMap;
QByteArray dockerStateBeforeHiding;
KoToolDocker *toolOptionsDocker {0};
QCloseEvent *deferredClosingEvent {0};
Digikam::ThemeManager *themeManager {0};
KisWelcomePageWidget *welcomePage {0};
QStackedWidget *widgetStack {0};
QMdiArea *mdiArea;
QMdiSubWindow *activeSubWindow {0};
QSignalMapper *windowMapper;
QSignalMapper *documentMapper;
QByteArray lastExportedFormat;
QScopedPointer<KisSignalCompressorWithParam<int> > tabSwitchCompressor;
QMutex savingEntryMutex;
KConfigGroup windowStateConfig;
QUuid workspaceBorrowedBy;
KisSignalAutoConnectionsStore screenConnectionsStore;
KisActionManager * actionManager() {
return viewManager->actionManager();
}
QTabBar* findTabBarHACK() {
QObjectList objects = mdiArea->children();
Q_FOREACH (QObject *object, objects) {
QTabBar *bar = qobject_cast<QTabBar*>(object);
if (bar) {
return bar;
}
}
return 0;
}
};
KisMainWindow::KisMainWindow(QUuid uuid)
: KXmlGuiWindow()
, d(new Private(this, uuid))
{
auto rserver = KisResourceServerProvider::instance()->workspaceServer();
QSharedPointer<KoAbstractResourceServerAdapter> adapter(new KoResourceServerAdapter<KisWorkspaceResource>(rserver));
d->workspacemodel = new KoResourceModel(adapter, this);
connect(d->workspacemodel, &KoResourceModel::afterResourcesLayoutReset, this, [&]() { updateWindowMenu(); });
d->viewManager = new KisViewManager(this, actionCollection());
KConfigGroup group( KSharedConfig::openConfig(), "theme");
d->themeManager = new Digikam::ThemeManager(group.readEntry("Theme", "Krita dark"), this);
d->windowStateConfig = KSharedConfig::openConfig()->group("MainWindow");
setAcceptDrops(true);
setStandardToolBarMenuEnabled(true);
setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North);
setDockNestingEnabled(true);
qApp->setStartDragDistance(25); // 25 px is a distance that works well for Tablet and Mouse events
#ifdef Q_OS_OSX
setUnifiedTitleAndToolBarOnMac(true);
#endif
connect(this, SIGNAL(restoringDone()), this, SLOT(forceDockTabFonts()));
connect(this, SIGNAL(themeChanged()), d->viewManager, SLOT(updateIcons()));
connect(KisPart::instance(), SIGNAL(documentClosed(QString)), SLOT(updateWindowMenu()));
connect(KisPart::instance(), SIGNAL(documentOpened(QString)), SLOT(updateWindowMenu()));
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), this, SLOT(configChanged()));
actionCollection()->addAssociatedWidget(this);
KoPluginLoader::instance()->load("Krita/ViewPlugin", "Type == 'Service' and ([X-Krita-Version] == 28)", KoPluginLoader::PluginsConfig(), d->viewManager, false);
// Load the per-application plugins (Right now, only Python) We do this only once, when the first mainwindow is being created.
KoPluginLoader::instance()->load("Krita/ApplicationPlugin", "Type == 'Service' and ([X-Krita-Version] == 28)", KoPluginLoader::PluginsConfig(), qApp, true);
KoToolBoxFactory toolBoxFactory;
QDockWidget *toolbox = createDockWidget(&toolBoxFactory);
toolbox->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetClosable);
KisConfig cfg(true);
if (cfg.toolOptionsInDocker()) {
ToolDockerFactory toolDockerFactory;
d->toolOptionsDocker = qobject_cast<KoToolDocker*>(createDockWidget(&toolDockerFactory));
d->toolOptionsDocker->toggleViewAction()->setEnabled(true);
}
QMap<QString, QAction*> dockwidgetActions;
dockwidgetActions[toolbox->toggleViewAction()->text()] = toolbox->toggleViewAction();
Q_FOREACH (const QString & docker, KoDockRegistry::instance()->keys()) {
KoDockFactoryBase *factory = KoDockRegistry::instance()->value(docker);
QDockWidget *dw = createDockWidget(factory);
dockwidgetActions[dw->toggleViewAction()->text()] = dw->toggleViewAction();
}
if (d->toolOptionsDocker) {
dockwidgetActions[d->toolOptionsDocker->toggleViewAction()->text()] = d->toolOptionsDocker->toggleViewAction();
}
connect(KoToolManager::instance(), SIGNAL(toolOptionWidgetsChanged(KoCanvasController*,QList<QPointer<QWidget> >)), this, SLOT(newOptionWidgets(KoCanvasController*,QList<QPointer<QWidget> >)));
Q_FOREACH (QString title, dockwidgetActions.keys()) {
d->dockWidgetMenu->addAction(dockwidgetActions[title]);
}
Q_FOREACH (QDockWidget *wdg, dockWidgets()) {
if ((wdg->features() & QDockWidget::DockWidgetClosable) == 0) {
wdg->setVisible(true);
}
}
Q_FOREACH (KoCanvasObserverBase* observer, canvasObservers()) {
observer->setObservedCanvas(0);
KisMainwindowObserver* mainwindowObserver = dynamic_cast<KisMainwindowObserver*>(observer);
if (mainwindowObserver) {
mainwindowObserver->setViewManager(d->viewManager);
}
}
// Load all the actions from the tool plugins
Q_FOREACH(KoToolFactoryBase *toolFactory, KoToolRegistry::instance()->values()) {
toolFactory->createActions(actionCollection());
}
d->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
d->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
d->mdiArea->setTabPosition(QTabWidget::North);
d->mdiArea->setTabsClosable(true);
// Tab close button override
// Windows just has a black X, and Ubuntu has a dark x that is hard to read
// just switch this icon out for all OSs so it is easier to see
d->mdiArea->setStyleSheet("QTabBar::close-button { image: url(:/pics/broken-preset.png) }");
setCentralWidget(d->widgetStack);
d->widgetStack->setCurrentIndex(0);
connect(d->mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(subWindowActivated()));
connect(d->windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*)));
connect(d->documentMapper, SIGNAL(mapped(QObject*)), this, SLOT(newView(QObject*)));
createActions();
// the welcome screen needs to grab actions...so make sure this line goes after the createAction() so they exist
d->welcomePage->setMainWindow(this);
setAutoSaveSettings(d->windowStateConfig, false);
subWindowActivated();
updateWindowMenu();
if (isHelpMenuEnabled() && !d->helpMenu) {
// workaround for KHelpMenu (or rather KAboutData::applicationData()) internally
// not using the Q*Application metadata ATM, which results e.g. in the bugreport wizard
// not having the app version preset
// fixed hopefully in KF5 5.22.0, patch pending
QGuiApplication *app = qApp;
KAboutData aboutData(app->applicationName(), app->applicationDisplayName(), app->applicationVersion());
aboutData.setOrganizationDomain(app->organizationDomain().toUtf8());
d->helpMenu = new KHelpMenu(this, aboutData, false);
// workaround-less version:
// d->helpMenu = new KHelpMenu(this, QString()/*unused*/, false);
// The difference between using KActionCollection->addAction() is that
// these actions do not get tied to the MainWindow. What does this all do?
KActionCollection *actions = d->viewManager->actionCollection();
QAction *helpContentsAction = d->helpMenu->action(KHelpMenu::menuHelpContents);
QAction *whatsThisAction = d->helpMenu->action(KHelpMenu::menuWhatsThis);
QAction *reportBugAction = d->helpMenu->action(KHelpMenu::menuReportBug);
QAction *switchLanguageAction = d->helpMenu->action(KHelpMenu::menuSwitchLanguage);
QAction *aboutAppAction = d->helpMenu->action(KHelpMenu::menuAboutApp);
QAction *aboutKdeAction = d->helpMenu->action(KHelpMenu::menuAboutKDE);
if (helpContentsAction) {
actions->addAction(helpContentsAction->objectName(), helpContentsAction);
}
if (whatsThisAction) {
actions->addAction(whatsThisAction->objectName(), whatsThisAction);
}
if (reportBugAction) {
actions->addAction(reportBugAction->objectName(), reportBugAction);
}
if (switchLanguageAction) {
actions->addAction(switchLanguageAction->objectName(), switchLanguageAction);
}
if (aboutAppAction) {
actions->addAction(aboutAppAction->objectName(), aboutAppAction);
}
if (aboutKdeAction) {
actions->addAction(aboutKdeAction->objectName(), aboutKdeAction);
}
connect(d->helpMenu, SIGNAL(showAboutApplication()), SLOT(showAboutApplication()));
}
// KDE' libs 4''s help contents action is broken outside kde, for some reason... We can handle it just as easily ourselves
QAction *helpAction = actionCollection()->action("help_contents");
helpAction->disconnect();
connect(helpAction, SIGNAL(triggered()), this, SLOT(showManual()));
#if 0
//check for colliding shortcuts
QSet<QKeySequence> existingShortcuts;
Q_FOREACH (QAction* action, actionCollection()->actions()) {
if(action->shortcut() == QKeySequence(0)) {
continue;
}
dbgKrita << "shortcut " << action->text() << " " << action->shortcut();
Q_ASSERT(!existingShortcuts.contains(action->shortcut()));
existingShortcuts.insert(action->shortcut());
}
#endif
configChanged();
// If we have customized the toolbars, load that first
setLocalXMLFile(KoResourcePaths::locateLocal("data", "krita4.xmlgui"));
setXMLFile(":/kxmlgui5/krita4.xmlgui");
guiFactory()->addClient(this);
// Create and plug toolbar list for Settings menu
QList<QAction *> toolbarList;
Q_FOREACH (QWidget* it, guiFactory()->containers("ToolBar")) {
KToolBar * toolBar = ::qobject_cast<KToolBar *>(it);
toolBar->setMovable(KisConfig(true).readEntry<bool>("LockAllDockerPanels", false));
if (toolBar) {
if (toolBar->objectName() == "BrushesAndStuff") {
toolBar->setEnabled(false);
}
KToggleAction* act = new KToggleAction(i18n("Show %1 Toolbar", toolBar->windowTitle()), this);
actionCollection()->addAction(toolBar->objectName().toUtf8(), act);
act->setCheckedState(KGuiItem(i18n("Hide %1 Toolbar", toolBar->windowTitle())));
connect(act, SIGNAL(toggled(bool)), this, SLOT(slotToolbarToggled(bool)));
act->setChecked(!toolBar->isHidden());
toolbarList.append(act);
} else {
warnUI << "Toolbar list contains a " << it->metaObject()->className() << " which is not a toolbar!";
}
}
KToolBar::setToolBarsLocked(KisConfig(true).readEntry<bool>("LockAllDockerPanels", false));
plugActionList("toolbarlist", toolbarList);
d->toolbarList = toolbarList;
applyToolBarLayout();
d->viewManager->updateGUI();
d->viewManager->updateIcons();
#ifdef Q_OS_WIN
auto w = qApp->activeWindow();
if (w) QWindowsWindowFunctions::setHasBorderInFullScreen(w->windowHandle(), true);
#endif
QTimer::singleShot(1000, this, SLOT(checkSanity()));
{
using namespace std::placeholders; // For _1 placeholder
std::function<void (int)> callback(
std::bind(&KisMainWindow::switchTab, this, _1));
d->tabSwitchCompressor.reset(
new KisSignalCompressorWithParam<int>(500, callback, KisSignalCompressor::FIRST_INACTIVE));
}
+
+
+ if (cfg.readEntry("CanvasOnlyActive", false)) {
+ QString currentWorkspace = cfg.readEntry<QString>("CurrentWorkspace", "Default");
+ KoResourceServer<KisWorkspaceResource> * rserver = KisResourceServerProvider::instance()->workspaceServer();
+ KisWorkspaceResource* workspace = rserver->resourceByName(currentWorkspace);
+ if (workspace) {
+ restoreWorkspace(workspace);
+ }
+ cfg.writeEntry("CanvasOnlyActive", false);
+ menuBar()->setVisible(true);
+ }
+
+
}
KisMainWindow::~KisMainWindow()
{
// Q_FOREACH (QAction *ac, actionCollection()->actions()) {
// QAction *action = qobject_cast<QAction*>(ac);
// if (action) {
// qDebug() << "<Action"
// << "\n\tname=" << action->objectName()
// << "\n\ticon=" << action->icon().name()
// << "\n\ttext=" << action->text().replace("&", "&amp;")
// << "\n\twhatsThis=" << action->whatsThis()
// << "\n\ttoolTip=" << action->toolTip().replace("<html>", "").replace("</html>", "")
// << "\n\ticonText=" << action->iconText().replace("&", "&amp;")
// << "\n\tshortcut=" << action->shortcut().toString()
// << "\n\tisCheckable=" << QString((action->isChecked() ? "true" : "false"))
// << "\n\tstatusTip=" << action->statusTip()
// << "\n/>\n" ;
// }
// else {
// dbgKrita << "Got a non-qaction:" << ac->objectName();
// }
// }
// The doc and view might still exist (this is the case when closing the window)
KisPart::instance()->removeMainWindow(this);
delete d->viewManager;
delete d;
}
QUuid KisMainWindow::id() const {
return d->id;
}
void KisMainWindow::addView(KisView *view)
{
if (d->activeView == view) return;
if (d->activeView) {
d->activeView->disconnect(this);
}
// register the newly created view in the input manager
viewManager()->inputManager()->addTrackedCanvas(view->canvasBase());
showView(view);
updateCaption();
emit restoringDone();
if (d->activeView) {
connect(d->activeView, SIGNAL(titleModified(QString,bool)), SLOT(slotDocumentTitleModified()));
connect(d->viewManager->statusBar(), SIGNAL(memoryStatusUpdated()), this, SLOT(updateCaption()));
}
}
void KisMainWindow::notifyChildViewDestroyed(KisView *view)
{
viewManager()->inputManager()->removeTrackedCanvas(view->canvasBase());
if (view->canvasBase() == viewManager()->canvasBase()) {
viewManager()->setCurrentView(0);
}
}
void KisMainWindow::showView(KisView *imageView)
{
if (imageView && activeView() != imageView) {
// XXX: find a better way to initialize this!
imageView->setViewManager(d->viewManager);
imageView->canvasBase()->setFavoriteResourceManager(d->viewManager->paintOpBox()->favoriteResourcesManager());
imageView->slotLoadingFinished();
QMdiSubWindow *subwin = d->mdiArea->addSubWindow(imageView);
imageView->setSubWindow(subwin);
subwin->setAttribute(Qt::WA_DeleteOnClose, true);
connect(subwin, SIGNAL(destroyed()), SLOT(updateWindowMenu()));
KisConfig cfg(true);
subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.readEntry<int>("mdi_rubberband", cfg.useOpenGL()));
subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.readEntry<int>("mdi_rubberband", cfg.useOpenGL()));
subwin->setWindowIcon(qApp->windowIcon());
/**
* Hack alert!
*
* Here we explicitly request KoToolManager to emit all the tool
* activation signals, to reinitialize the tool options docker.
*
* That is needed due to a design flaw we have in the
* initialization procedure. The tool in the KoToolManager is
* initialized in KisView::setViewManager() calls, which
* happens early enough. During this call the tool manager
* requests KoCanvasControllerWidget to emit the signal to
* update the widgets in the tool docker. *But* at that moment
* of time the view is not yet connected to the main window,
* because it happens in KisViewManager::setCurrentView a bit
* later. This fact makes the widgets updating signals be lost
* and never reach the tool docker.
*
* So here we just explicitly call the tool activation stub.
*/
KoToolManager::instance()->initializeCurrentToolForCanvas();
if (d->mdiArea->subWindowList().size() == 1) {
imageView->showMaximized();
}
else {
imageView->show();
}
// No, no, no: do not try to call this _before_ the show() has
// been called on the view; only when that has happened is the
// opengl context active, and very bad things happen if we tell
// the dockers to update themselves with a view if the opengl
// context is not active.
setActiveView(imageView);
updateWindowMenu();
updateCaption();
}
}
void KisMainWindow::slotPreferences()
{
if (KisDlgPreferences::editPreferences()) {
KisConfigNotifier::instance()->notifyConfigChanged();
KisConfigNotifier::instance()->notifyPixelGridModeChanged();
KisImageConfigNotifier::instance()->notifyConfigChanged();
// XXX: should this be changed for the views in other windows as well?
Q_FOREACH (QPointer<KisView> koview, KisPart::instance()->views()) {
KisViewManager *view = qobject_cast<KisViewManager*>(koview);
if (view) {
// Update the settings for all nodes -- they don't query
// KisConfig directly because they need the settings during
// compositing, and they don't connect to the config notifier
// because nodes are not QObjects (because only one base class
// can be a QObject).
KisNode* node = dynamic_cast<KisNode*>(view->image()->rootLayer().data());
node->updateSettings();
}
}
d->viewManager->showHideScrollbars();
}
}
void KisMainWindow::slotThemeChanged()
{
// save theme changes instantly
KConfigGroup group( KSharedConfig::openConfig(), "theme");
group.writeEntry("Theme", d->themeManager->currentThemeName());
// reload action icons!
Q_FOREACH (QAction *action, actionCollection()->actions()) {
KisIconUtils::updateIcon(action);
}
emit themeChanged();
}
void KisMainWindow::updateReloadFileAction(KisDocument *doc)
{
Q_UNUSED(doc);
// d->reloadFile->setEnabled(doc && !doc->url().isEmpty());
}
void KisMainWindow::setReadWrite(bool readwrite)
{
d->saveAction->setEnabled(readwrite);
d->importFile->setEnabled(readwrite);
d->readOnly = !readwrite;
updateCaption();
}
void KisMainWindow::addRecentURL(const QUrl &url)
{
// Add entry to recent documents list
// (call coming from KisDocument because it must work with cmd line, template dlg, file/open, etc.)
if (!url.isEmpty()) {
bool ok = true;
if (url.isLocalFile()) {
QString path = url.adjusted(QUrl::StripTrailingSlash).toLocalFile();
const QStringList tmpDirs = KoResourcePaths::resourceDirs("tmp");
for (QStringList::ConstIterator it = tmpDirs.begin() ; ok && it != tmpDirs.end() ; ++it) {
if (path.contains(*it)) {
ok = false; // it's in the tmp resource
}
}
const QStringList templateDirs = KoResourcePaths::findDirs("templates");
for (QStringList::ConstIterator it = templateDirs.begin() ; ok && it != templateDirs.end() ; ++it) {
if (path.contains(*it)) {
ok = false; // it's in the templates directory.
break;
}
}
}
if (ok) {
d->recentFiles->addUrl(url);
}
saveRecentFiles();
}
}
void KisMainWindow::saveRecentFiles()
{
// Save list of recent files
KSharedConfigPtr config = KSharedConfig::openConfig();
d->recentFiles->saveEntries(config->group("RecentFiles"));
config->sync();
// Tell all windows to reload their list, after saving
// Doesn't work multi-process, but it's a start
Q_FOREACH (KisMainWindow *mw, KisPart::instance()->mainWindows()) {
if (mw != this) {
mw->reloadRecentFileList();
}
}
}
QList<QUrl> KisMainWindow::recentFilesUrls()
{
return d->recentFiles->urls();
}
void KisMainWindow::clearRecentFiles()
{
d->recentFiles->clear();
}
void KisMainWindow::reloadRecentFileList()
{
d->recentFiles->loadEntries(KSharedConfig::openConfig()->group("RecentFiles"));
}
void KisMainWindow::updateCaption()
{
if (!d->mdiArea->activeSubWindow()) {
updateCaption(QString(), false);
}
else if (d->activeView && d->activeView->document() && d->activeView->image()){
KisDocument *doc = d->activeView->document();
QString caption(doc->caption());
if (d->readOnly) {
caption += " [" + i18n("Write Protected") + "] ";
}
if (doc->isRecovered()) {
caption += " [" + i18n("Recovered") + "] ";
}
// show the file size for the document
KisMemoryStatisticsServer::Statistics m_fileSizeStats = KisMemoryStatisticsServer::instance()->fetchMemoryStatistics(d->activeView ? d->activeView->image() : 0);
if (m_fileSizeStats.imageSize) {
caption += QString(" (").append( KFormat().formatByteSize(m_fileSizeStats.imageSize)).append( ")");
}
d->activeView->setWindowTitle(caption);
d->activeView->setWindowModified(doc->isModified());
updateCaption(caption, doc->isModified());
if (!doc->url().fileName().isEmpty()) {
d->saveAction->setToolTip(i18n("Save as %1", doc->url().fileName()));
}
else {
d->saveAction->setToolTip(i18n("Save"));
}
}
}
void KisMainWindow::updateCaption(const QString & caption, bool mod)
{
dbgUI << "KisMainWindow::updateCaption(" << caption << "," << mod << ")";
QString versionString = KritaVersionWrapper::versionString(true);
#if defined(KRITA_ALPHA) || defined (KRITA_BETA) || defined (KRITA_RC)
setCaption(QString("%1: %2").arg(versionString).arg(caption), mod);
return;
#endif
setCaption(caption, mod);
}
KisView *KisMainWindow::activeView() const
{
if (d->activeView) {
return d->activeView;
}
return 0;
}
bool KisMainWindow::openDocument(const QUrl &url, OpenFlags flags)
{
if (!QFile(url.toLocalFile()).exists()) {
if (!(flags & BatchMode)) {
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("The file %1 does not exist.", url.url()));
}
d->recentFiles->removeUrl(url); //remove the file from the recent-opened-file-list
saveRecentFiles();
return false;
}
return openDocumentInternal(url, flags);
}
bool KisMainWindow::openDocumentInternal(const QUrl &url, OpenFlags flags)
{
if (!url.isLocalFile()) {
qWarning() << "KisMainWindow::openDocumentInternal. Not a local file:" << url;
return false;
}
KisDocument *newdoc = KisPart::instance()->createDocument();
if (flags & BatchMode) {
newdoc->setFileBatchMode(true);
}
d->firstTime = true;
connect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted()));
connect(newdoc, SIGNAL(canceled(QString)), this, SLOT(slotLoadCanceled(QString)));
KisDocument::OpenFlags openFlags = KisDocument::None;
if (flags & RecoveryFile) {
openFlags |= KisDocument::RecoveryFile;
}
bool openRet = !(flags & Import) ? newdoc->openUrl(url, openFlags) : newdoc->importDocument(url);
if (!openRet) {
delete newdoc;
return false;
}
KisPart::instance()->addDocument(newdoc);
updateReloadFileAction(newdoc);
if (!QFileInfo(url.toLocalFile()).isWritable()) {
setReadWrite(false);
}
return true;
}
void KisMainWindow::showDocument(KisDocument *document) {
Q_FOREACH(QMdiSubWindow *subwindow, d->mdiArea->subWindowList()) {
KisView *view = qobject_cast<KisView*>(subwindow->widget());
KIS_SAFE_ASSERT_RECOVER_NOOP(view);
if (view) {
if (view->document() == document) {
setActiveSubWindow(subwindow);
return;
}
}
}
addViewAndNotifyLoadingCompleted(document);
}
KisView* KisMainWindow::addViewAndNotifyLoadingCompleted(KisDocument *document)
{
showWelcomeScreen(false); // see workaround in function header
KisView *view = KisPart::instance()->createView(document, resourceManager(), actionCollection(), this);
addView(view);
emit guiLoadingFinished();
return view;
}
QStringList KisMainWindow::showOpenFileDialog(bool isImporting)
{
KoFileDialog dialog(this, KoFileDialog::ImportFiles, "OpenDocument");
dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
dialog.setMimeTypeFilters(KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import));
dialog.setCaption(isImporting ? i18n("Import Images") : i18n("Open Images"));
return dialog.filenames();
}
// Separate from openDocument to handle async loading (remote URLs)
void KisMainWindow::slotLoadCompleted()
{
KisDocument *newdoc = qobject_cast<KisDocument*>(sender());
if (newdoc && newdoc->image()) {
addViewAndNotifyLoadingCompleted(newdoc);
disconnect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted()));
disconnect(newdoc, SIGNAL(canceled(QString)), this, SLOT(slotLoadCanceled(QString)));
emit loadCompleted();
}
}
void KisMainWindow::slotLoadCanceled(const QString & errMsg)
{
dbgUI << "KisMainWindow::slotLoadCanceled";
if (!errMsg.isEmpty()) // empty when canceled by user
QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg);
// ... can't delete the document, it's the one who emitted the signal...
KisDocument* doc = qobject_cast<KisDocument*>(sender());
Q_ASSERT(doc);
disconnect(doc, SIGNAL(completed()), this, SLOT(slotLoadCompleted()));
disconnect(doc, SIGNAL(canceled(QString)), this, SLOT(slotLoadCanceled(QString)));
}
void KisMainWindow::slotSaveCanceled(const QString &errMsg)
{
dbgUI << "KisMainWindow::slotSaveCanceled";
if (!errMsg.isEmpty()) // empty when canceled by user
QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg);
slotSaveCompleted();
}
void KisMainWindow::slotSaveCompleted()
{
dbgUI << "KisMainWindow::slotSaveCompleted";
KisDocument* doc = qobject_cast<KisDocument*>(sender());
Q_ASSERT(doc);
disconnect(doc, SIGNAL(completed()), this, SLOT(slotSaveCompleted()));
disconnect(doc, SIGNAL(canceled(QString)), this, SLOT(slotSaveCanceled(QString)));
if (d->deferredClosingEvent) {
KXmlGuiWindow::closeEvent(d->deferredClosingEvent);
}
}
bool KisMainWindow::hackIsSaving() const
{
StdLockableWrapper<QMutex> wrapper(&d->savingEntryMutex);
std::unique_lock<StdLockableWrapper<QMutex>> l(wrapper, std::try_to_lock);
return !l.owns_lock();
}
bool KisMainWindow::installBundle(const QString &fileName) const
{
QFileInfo from(fileName);
QFileInfo to(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/bundles/" + from.fileName());
if (to.exists()) {
QFile::remove(to.canonicalFilePath());
}
return QFile::copy(fileName, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/bundles/" + from.fileName());
}
bool KisMainWindow::saveDocument(KisDocument *document, bool saveas, bool isExporting)
{
if (!document) {
return true;
}
/**
* Make sure that we cannot enter this method twice!
*
* The lower level functions may call processEvents() so
* double-entry is quite possible to achieve. Here we try to lock
* the mutex, and if it is failed, just cancel saving.
*/
StdLockableWrapper<QMutex> wrapper(&d->savingEntryMutex);
std::unique_lock<StdLockableWrapper<QMutex>> l(wrapper, std::try_to_lock);
if (!l.owns_lock()) return false;
// no busy wait for saving because it is dangerous!
KisDelayedSaveDialog dlg(document->image(), KisDelayedSaveDialog::SaveDialog, 0, this);
dlg.blockIfImageIsBusy();
if (dlg.result() == KisDelayedSaveDialog::Rejected) {
return false;
}
else if (dlg.result() == KisDelayedSaveDialog::Ignored) {
QMessageBox::critical(0,
i18nc("@title:window", "Krita"),
i18n("You are saving a file while the image is "
"still rendering. The saved file may be "
"incomplete or corrupted.\n\n"
"Please select a location where the original "
"file will not be overridden!"));
saveas = true;
}
if (document->isRecovered()) {
saveas = true;
}
if (document->url().isEmpty()) {
saveas = true;
}
connect(document, SIGNAL(completed()), this, SLOT(slotSaveCompleted()));
connect(document, SIGNAL(canceled(QString)), this, SLOT(slotSaveCanceled(QString)));
QByteArray nativeFormat = document->nativeFormatMimeType();
QByteArray oldMimeFormat = document->mimeType();
QUrl suggestedURL = document->url();
QStringList mimeFilter = KisImportExportManager::supportedMimeTypes(KisImportExportManager::Export);
mimeFilter = KisImportExportManager::supportedMimeTypes(KisImportExportManager::Export);
if (!mimeFilter.contains(oldMimeFormat)) {
dbgUI << "KisMainWindow::saveDocument no export filter for" << oldMimeFormat;
// --- don't setOutputMimeType in case the user cancels the Save As
// dialog and then tries to just plain Save ---
// suggest a different filename extension (yes, we fortunately don't all live in a world of magic :))
QString suggestedFilename = QFileInfo(suggestedURL.toLocalFile()).baseName();
if (!suggestedFilename.isEmpty()) { // ".kra" looks strange for a name
suggestedFilename = suggestedFilename + "." + KisMimeDatabase::suffixesForMimeType(KIS_MIME_TYPE).first();
suggestedURL = suggestedURL.adjusted(QUrl::RemoveFilename);
suggestedURL.setPath(suggestedURL.path() + suggestedFilename);
}
// force the user to choose outputMimeType
saveas = true;
}
bool ret = false;
if (document->url().isEmpty() || isExporting || saveas) {
// if you're just File/Save As'ing to change filter options you
// don't want to be reminded about overwriting files etc.
bool justChangingFilterOptions = false;
KoFileDialog dialog(this, KoFileDialog::SaveFile, "SaveAs");
dialog.setCaption(isExporting ? i18n("Exporting") : i18n("Saving As"));
//qDebug() << ">>>>>" << isExporting << d->lastExportLocation << d->lastExportedFormat << QString::fromLatin1(document->mimeType());
if (isExporting && !d->lastExportLocation.isEmpty()) {
// Use the location where we last exported to, if it's set, as the opening location for the file dialog
QString proposedPath = QFileInfo(d->lastExportLocation).absolutePath();
// If the document doesn't have a filename yet, use the title
QString proposedFileName = suggestedURL.isEmpty() ? document->documentInfo()->aboutInfo("title") : QFileInfo(suggestedURL.toLocalFile()).baseName();
// Use the last mimetype we exported to by default
QString proposedMimeType = d->lastExportedFormat.isEmpty() ? "" : d->lastExportedFormat;
QString proposedExtension = KisMimeDatabase::suffixesForMimeType(proposedMimeType).first().remove("*,");
// Set the default dir: this overrides the one loaded from the config file, since we're exporting and the lastExportLocation is not empty
dialog.setDefaultDir(proposedPath + "/" + proposedFileName + "." + proposedExtension, true);
dialog.setMimeTypeFilters(mimeFilter, proposedMimeType);
}
else {
// Get the last used location for saving
KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs");
QString proposedPath = group.readEntry("SaveAs", "");
// if that is empty, get the last used location for loading
if (proposedPath.isEmpty()) {
proposedPath = group.readEntry("OpenDocument", "");
}
// If that is empty, too, use the Pictures location.
if (proposedPath.isEmpty()) {
proposedPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
}
// But only use that if the suggestedUrl, that is, the document's own url is empty, otherwise
// open the location where the document currently is.
dialog.setDefaultDir(suggestedURL.isEmpty() ? proposedPath : suggestedURL.toLocalFile(), true);
// If exporting, default to all supported file types if user is exporting
QByteArray default_mime_type = "";
if (!isExporting) {
// otherwise use the document's mimetype, or if that is empty, kra, which is the savest.
default_mime_type = document->mimeType().isEmpty() ? nativeFormat : document->mimeType();
}
dialog.setMimeTypeFilters(mimeFilter, QString::fromLatin1(default_mime_type));
}
QUrl newURL = QUrl::fromUserInput(dialog.filename());
if (newURL.isLocalFile()) {
QString fn = newURL.toLocalFile();
if (QFileInfo(fn).completeSuffix().isEmpty()) {
fn.append(KisMimeDatabase::suffixesForMimeType(nativeFormat).first());
newURL = QUrl::fromLocalFile(fn);
}
}
if (document->documentInfo()->aboutInfo("title") == i18n("Unnamed")) {
QString fn = newURL.toLocalFile();
QFileInfo info(fn);
document->documentInfo()->setAboutInfo("title", info.baseName());
}
QByteArray outputFormat = nativeFormat;
QString outputFormatString = KisMimeDatabase::mimeTypeForFile(newURL.toLocalFile(), false);
outputFormat = outputFormatString.toLatin1();
if (!isExporting) {
justChangingFilterOptions = (newURL == document->url()) && (outputFormat == document->mimeType());
}
else {
QString path = QFileInfo(d->lastExportLocation).absolutePath();
QString filename = QFileInfo(document->url().toLocalFile()).baseName();
justChangingFilterOptions = (QFileInfo(newURL.toLocalFile()).absolutePath() == path)
&& (QFileInfo(newURL.toLocalFile()).baseName() == filename)
&& (outputFormat == d->lastExportedFormat);
}
bool bOk = true;
if (newURL.isEmpty()) {
bOk = false;
}
if (bOk) {
bool wantToSave = true;
// don't change this line unless you know what you're doing :)
if (!justChangingFilterOptions) {
if (!document->isNativeFormat(outputFormat))
wantToSave = true;
}
if (wantToSave) {
if (!isExporting) { // Save As
ret = document->saveAs(newURL, outputFormat, true);
if (ret) {
dbgUI << "Successful Save As!";
KisPart::instance()->addRecentURLToAllMainWindows(newURL);
setReadWrite(true);
} else {
dbgUI << "Failed Save As!";
}
}
else { // Export
ret = document->exportDocument(newURL, outputFormat);
if (ret) {
d->lastExportLocation = newURL.toLocalFile();
d->lastExportedFormat = outputFormat;
}
}
} // if (wantToSave) {
else
ret = false;
} // if (bOk) {
else
ret = false;
} else { // saving
// We cannot "export" into the currently
// opened document. We are not Gimp.
KIS_ASSERT_RECOVER_NOOP(!isExporting);
// be sure document has the correct outputMimeType!
if (document->isModified()) {
ret = document->save(true, 0);
}
if (!ret) {
dbgUI << "Failed Save!";
}
}
updateReloadFileAction(document);
updateCaption();
return ret;
}
void KisMainWindow::undo()
{
if (activeView()) {
activeView()->document()->undoStack()->undo();
}
}
void KisMainWindow::redo()
{
if (activeView()) {
activeView()->document()->undoStack()->redo();
}
}
void KisMainWindow::closeEvent(QCloseEvent *e)
{
if (!KisPart::instance()->closingSession()) {
QAction *action= d->viewManager->actionCollection()->action("view_show_canvas_only");
if ((action) && (action->isChecked())) {
action->setChecked(false);
}
// Save session when last window is closed
if (KisPart::instance()->mainwindowCount() == 1) {
bool closeAllowed = KisPart::instance()->closeSession();
if (!closeAllowed) {
e->setAccepted(false);
return;
}
}
}
d->mdiArea->closeAllSubWindows();
QList<QMdiSubWindow*> childrenList = d->mdiArea->subWindowList();
if (childrenList.isEmpty()) {
d->deferredClosingEvent = e;
saveWindowState(true);
} else {
e->setAccepted(false);
}
}
void KisMainWindow::saveWindowSettings()
{
KSharedConfigPtr config = KSharedConfig::openConfig();
if (d->windowSizeDirty ) {
dbgUI << "KisMainWindow::saveWindowSettings";
KConfigGroup group = d->windowStateConfig;
KWindowConfig::saveWindowSize(windowHandle(), group);
config->sync();
d->windowSizeDirty = false;
}
if (!d->activeView || d->activeView->document()) {
// Save toolbar position into the config file of the app, under the doc's component name
KConfigGroup group = d->windowStateConfig;
saveMainWindowSettings(group);
// Save collapsible state of dock widgets
for (QMap<QString, QDockWidget*>::const_iterator i = d->dockWidgetsMap.constBegin();
i != d->dockWidgetsMap.constEnd(); ++i) {
if (i.value()->widget()) {
KConfigGroup dockGroup = group.group(QString("DockWidget ") + i.key());
dockGroup.writeEntry("Collapsed", i.value()->widget()->isHidden());
dockGroup.writeEntry("Locked", i.value()->property("Locked").toBool());
dockGroup.writeEntry("DockArea", (int) dockWidgetArea(i.value()));
dockGroup.writeEntry("xPosition", (int) i.value()->widget()->x());
dockGroup.writeEntry("yPosition", (int) i.value()->widget()->y());
dockGroup.writeEntry("width", (int) i.value()->widget()->width());
dockGroup.writeEntry("height", (int) i.value()->widget()->height());
}
}
}
KSharedConfig::openConfig()->sync();
resetAutoSaveSettings(); // Don't let KMainWindow override the good stuff we wrote down
}
void KisMainWindow::resizeEvent(QResizeEvent * e)
{
d->windowSizeDirty = true;
KXmlGuiWindow::resizeEvent(e);
}
void KisMainWindow::setActiveView(KisView* view)
{
d->activeView = view;
updateCaption();
if (d->undoActionsUpdateManager) {
d->undoActionsUpdateManager->setCurrentDocument(view ? view->document() : 0);
}
d->viewManager->setCurrentView(view);
KisWindowLayoutManager::instance()->activeDocumentChanged(view->document());
}
void KisMainWindow::dragEnterEvent(QDragEnterEvent *event)
{
d->welcomePage->showDropAreaIndicator(true);
if (event->mimeData()->hasUrls() ||
event->mimeData()->hasFormat("application/x-krita-node") ||
event->mimeData()->hasFormat("application/x-qt-image")) {
event->accept();
}
}
void KisMainWindow::dropEvent(QDropEvent *event)
{
d->welcomePage->showDropAreaIndicator(false);
if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() > 0) {
Q_FOREACH (const QUrl &url, event->mimeData()->urls()) {
if (url.toLocalFile().endsWith(".bundle")) {
bool r = installBundle(url.toLocalFile());
if (!r) {
qWarning() << "Could not install bundle" << url.toLocalFile();
}
}
else {
openDocument(url, None);
}
}
}
}
void KisMainWindow::dragMoveEvent(QDragMoveEvent * event)
{
QTabBar *tabBar = d->findTabBarHACK();
if (!tabBar && d->mdiArea->viewMode() == QMdiArea::TabbedView) {
qWarning() << "WARNING!!! Cannot find QTabBar in the main window! Looks like Qt has changed behavior. Drag & Drop between multiple tabs might not work properly (tabs will not switch automatically)!";
}
if (tabBar && tabBar->isVisible()) {
QPoint pos = tabBar->mapFromGlobal(mapToGlobal(event->pos()));
if (tabBar->rect().contains(pos)) {
const int tabIndex = tabBar->tabAt(pos);
if (tabIndex >= 0 && tabBar->currentIndex() != tabIndex) {
d->tabSwitchCompressor->start(tabIndex);
}
} else if (d->tabSwitchCompressor->isActive()) {
d->tabSwitchCompressor->stop();
}
}
}
void KisMainWindow::dragLeaveEvent(QDragLeaveEvent * /*event*/)
{
d->welcomePage->showDropAreaIndicator(false);
if (d->tabSwitchCompressor->isActive()) {
d->tabSwitchCompressor->stop();
}
}
void KisMainWindow::switchTab(int index)
{
QTabBar *tabBar = d->findTabBarHACK();
if (!tabBar) return;
tabBar->setCurrentIndex(index);
}
void KisMainWindow::showWelcomeScreen(bool show)
{
d->widgetStack->setCurrentIndex(!show);
}
void KisMainWindow::slotFileNew()
{
const QStringList mimeFilter = KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import);
KisOpenPane *startupWidget = new KisOpenPane(this, mimeFilter, QStringLiteral("templates/"));
startupWidget->setWindowModality(Qt::WindowModal);
startupWidget->setWindowTitle(i18n("Create new document"));
KisConfig cfg(true);
int w = cfg.defImageWidth();
int h = cfg.defImageHeight();
const double resolution = cfg.defImageResolution();
const QString colorModel = cfg.defColorModel();
const QString colorDepth = cfg.defaultColorDepth();
const QString colorProfile = cfg.defColorProfile();
CustomDocumentWidgetItem item;
item.widget = new KisCustomImageWidget(startupWidget,
w,
h,
resolution,
colorModel,
colorDepth,
colorProfile,
i18n("Unnamed"));
item.icon = "document-new";
startupWidget->addCustomDocumentWidget(item.widget, item.title, item.icon);
QSize sz = KisClipboard::instance()->clipSize();
if (sz.isValid() && sz.width() != 0 && sz.height() != 0) {
w = sz.width();
h = sz.height();
}
item.widget = new KisImageFromClipboard(startupWidget,
w,
h,
resolution,
colorModel,
colorDepth,
colorProfile,
i18n("Unnamed"));
item.title = i18n("Create from Clipboard");
item.icon = "tab-new";
startupWidget->addCustomDocumentWidget(item.widget, item.title, item.icon);
// calls deleteLater
connect(startupWidget, SIGNAL(documentSelected(KisDocument*)), KisPart::instance(), SLOT(startCustomDocument(KisDocument*)));
// calls deleteLater
connect(startupWidget, SIGNAL(openTemplate(QUrl)), KisPart::instance(), SLOT(openTemplate(QUrl)));
startupWidget->exec();
// Cancel calls deleteLater...
}
void KisMainWindow::slotImportFile()
{
dbgUI << "slotImportFile()";
slotFileOpen(true);
}
void KisMainWindow::slotFileOpen(bool isImporting)
{
QStringList urls = showOpenFileDialog(isImporting);
if (urls.isEmpty())
return;
Q_FOREACH (const QString& url, urls) {
if (!url.isEmpty()) {
OpenFlags flags = isImporting ? Import : None;
bool res = openDocument(QUrl::fromLocalFile(url), flags);
if (!res) {
warnKrita << "Loading" << url << "failed";
}
}
}
}
void KisMainWindow::slotFileOpenRecent(const QUrl &url)
{
(void) openDocument(QUrl::fromLocalFile(url.toLocalFile()), None);
}
void KisMainWindow::slotFileSave()
{
if (saveDocument(d->activeView->document(), false, false)) {
emit documentSaved();
}
}
void KisMainWindow::slotFileSaveAs()
{
if (saveDocument(d->activeView->document(), true, false)) {
emit documentSaved();
}
}
void KisMainWindow::slotExportFile()
{
if (saveDocument(d->activeView->document(), true, true)) {
emit documentSaved();
}
}
void KisMainWindow::slotShowSessionManager() {
KisPart::instance()->showSessionManager();
}
KoCanvasResourceProvider *KisMainWindow::resourceManager() const
{
return d->viewManager->resourceProvider()->resourceManager();
}
int KisMainWindow::viewCount() const
{
return d->mdiArea->subWindowList().size();
}
const KConfigGroup &KisMainWindow::windowStateConfig() const
{
return d->windowStateConfig;
}
void KisMainWindow::saveWindowState(bool restoreNormalState)
{
if (restoreNormalState) {
QAction *showCanvasOnly = d->viewManager->actionCollection()->action("view_show_canvas_only");
if (showCanvasOnly && showCanvasOnly->isChecked()) {
showCanvasOnly->setChecked(false);
}
d->windowStateConfig.writeEntry("ko_geometry", saveGeometry().toBase64());
d->windowStateConfig.writeEntry("State", saveState().toBase64());
if (!d->dockerStateBeforeHiding.isEmpty()) {
restoreState(d->dockerStateBeforeHiding);
}
statusBar()->setVisible(true);
menuBar()->setVisible(true);
saveWindowSettings();
} else {
saveMainWindowSettings(d->windowStateConfig);
}
}
bool KisMainWindow::restoreWorkspaceState(const QByteArray &state)
{
QByteArray oldState = saveState();
// needed because otherwise the layout isn't correctly restored in some situations
Q_FOREACH (QDockWidget *dock, dockWidgets()) {
dock->toggleViewAction()->setEnabled(true);
dock->hide();
}
bool success = KXmlGuiWindow::restoreState(state);
if (!success) {
KXmlGuiWindow::restoreState(oldState);
return false;
}
return success;
}
bool KisMainWindow::restoreWorkspace(KisWorkspaceResource *workspace)
{
bool success = restoreWorkspaceState(workspace->dockerState());
if (activeKisView()) {
activeKisView()->resourceProvider()->notifyLoadingWorkspace(workspace);
}
return success;
}
QByteArray KisMainWindow::borrowWorkspace(KisMainWindow *other)
{
QByteArray currentWorkspace = saveState();
if (!d->workspaceBorrowedBy.isNull()) {
if (other->id() == d->workspaceBorrowedBy) {
// We're swapping our original workspace back
d->workspaceBorrowedBy = QUuid();
return currentWorkspace;
} else {
// Get our original workspace back before swapping with a third window
KisMainWindow *borrower = KisPart::instance()->windowById(d->workspaceBorrowedBy);
if (borrower) {
QByteArray originalLayout = borrower->borrowWorkspace(this);
borrower->restoreWorkspaceState(currentWorkspace);
d->workspaceBorrowedBy = other->id();
return originalLayout;
}
}
}
d->workspaceBorrowedBy = other->id();
return currentWorkspace;
}
void KisMainWindow::swapWorkspaces(KisMainWindow *a, KisMainWindow *b)
{
QByteArray workspaceA = a->borrowWorkspace(b);
QByteArray workspaceB = b->borrowWorkspace(a);
a->restoreWorkspaceState(workspaceB);
b->restoreWorkspaceState(workspaceA);
}
KisViewManager *KisMainWindow::viewManager() const
{
return d->viewManager;
}
void KisMainWindow::slotDocumentInfo()
{
if (!d->activeView->document())
return;
KoDocumentInfo *docInfo = d->activeView->document()->documentInfo();
if (!docInfo)
return;
KoDocumentInfoDlg *dlg = d->activeView->document()->createDocumentInfoDialog(this, docInfo);
if (dlg->exec()) {
if (dlg->isDocumentSaved()) {
d->activeView->document()->setModified(false);
} else {
d->activeView->document()->setModified(true);
}
d->activeView->document()->setTitleModified();
}
delete dlg;
}
bool KisMainWindow::slotFileCloseAll()
{
Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) {
if (subwin) {
if(!subwin->close())
return false;
}
}
updateCaption();
return true;
}
void KisMainWindow::slotFileQuit()
{
KisPart::instance()->closeSession();
}
void KisMainWindow::slotFilePrint()
{
if (!activeView())
return;
KisPrintJob *printJob = activeView()->createPrintJob();
if (printJob == 0)
return;
applyDefaultSettings(printJob->printer());
QPrintDialog *printDialog = activeView()->createPrintDialog( printJob, this );
if (printDialog && printDialog->exec() == QDialog::Accepted) {
printJob->printer().setPageMargins(0.0, 0.0, 0.0, 0.0, QPrinter::Point);
printJob->printer().setPaperSize(QSizeF(activeView()->image()->width() / (72.0 * activeView()->image()->xRes()),
activeView()->image()->height()/ (72.0 * activeView()->image()->yRes())),
QPrinter::Inch);
printJob->startPrinting(KisPrintJob::DeleteWhenDone);
}
else {
delete printJob;
}
delete printDialog;
}
void KisMainWindow::slotFilePrintPreview()
{
if (!activeView())
return;
KisPrintJob *printJob = activeView()->createPrintJob();
if (printJob == 0)
return;
/* Sets the startPrinting() slot to be blocking.
The Qt print-preview dialog requires the printing to be completely blocking
and only return when the full document has been printed.
By default the KisPrintingDialog is non-blocking and
multithreading, setting blocking to true will allow it to be used in the preview dialog */
printJob->setProperty("blocking", true);
QPrintPreviewDialog *preview = new QPrintPreviewDialog(&printJob->printer(), this);
printJob->setParent(preview); // will take care of deleting the job
connect(preview, SIGNAL(paintRequested(QPrinter*)), printJob, SLOT(startPrinting()));
preview->exec();
delete preview;
}
KisPrintJob* KisMainWindow::exportToPdf(QString pdfFileName)
{
if (!activeView())
return 0;
if (!activeView()->document())
return 0;
KoPageLayout pageLayout;
pageLayout.width = 0;
pageLayout.height = 0;
pageLayout.topMargin = 0;
pageLayout.bottomMargin = 0;
pageLayout.leftMargin = 0;
pageLayout.rightMargin = 0;
if (pdfFileName.isEmpty()) {
KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs");
QString defaultDir = group.readEntry("SavePdfDialog");
if (defaultDir.isEmpty())
defaultDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
QUrl startUrl = QUrl::fromLocalFile(defaultDir);
KisDocument* pDoc = d->activeView->document();
/** if document has a file name, take file name and replace extension with .pdf */
if (pDoc && pDoc->url().isValid()) {
startUrl = pDoc->url();
QString fileName = startUrl.toLocalFile();
fileName = fileName.replace( QRegExp( "\\.\\w{2,5}$", Qt::CaseInsensitive ), ".pdf" );
startUrl = startUrl.adjusted(QUrl::RemoveFilename);
startUrl.setPath(startUrl.path() + fileName );
}
QPointer<KoPageLayoutDialog> layoutDlg(new KoPageLayoutDialog(this, pageLayout));
layoutDlg->setWindowModality(Qt::WindowModal);
if (layoutDlg->exec() != QDialog::Accepted || !layoutDlg) {
delete layoutDlg;
return 0;
}
pageLayout = layoutDlg->pageLayout();
delete layoutDlg;
KoFileDialog dialog(this, KoFileDialog::SaveFile, "OpenDocument");
dialog.setCaption(i18n("Export as PDF"));
dialog.setDefaultDir(startUrl.toLocalFile());
dialog.setMimeTypeFilters(QStringList() << "application/pdf");
QUrl url = QUrl::fromUserInput(dialog.filename());
pdfFileName = url.toLocalFile();
if (pdfFileName.isEmpty())
return 0;
}
KisPrintJob *printJob = activeView()->createPrintJob();
if (printJob == 0)
return 0;
if (isHidden()) {
printJob->setProperty("noprogressdialog", true);
}
applyDefaultSettings(printJob->printer());
// TODO for remote files we have to first save locally and then upload.
printJob->printer().setOutputFileName(pdfFileName);
printJob->printer().setDocName(pdfFileName);
printJob->printer().setColorMode(QPrinter::Color);
if (pageLayout.format == KoPageFormat::CustomSize) {
printJob->printer().setPaperSize(QSizeF(pageLayout.width, pageLayout.height), QPrinter::Millimeter);
} else {
printJob->printer().setPaperSize(KoPageFormat::printerPageSize(pageLayout.format));
}
printJob->printer().setPageMargins(pageLayout.leftMargin, pageLayout.topMargin, pageLayout.rightMargin, pageLayout.bottomMargin, QPrinter::Millimeter);
switch (pageLayout.orientation) {
case KoPageFormat::Portrait:
printJob->printer().setOrientation(QPrinter::Portrait);
break;
case KoPageFormat::Landscape:
printJob->printer().setOrientation(QPrinter::Landscape);
break;
}
//before printing check if the printer can handle printing
if (!printJob->canPrint()) {
QMessageBox::critical(this, i18nc("@title:window", "Krita"), i18n("Cannot export to the specified file"));
}
printJob->startPrinting(KisPrintJob::DeleteWhenDone);
return printJob;
}
void KisMainWindow::importAnimation()
{
if (!activeView()) return;
KisDocument *document = activeView()->document();
if (!document) return;
KisDlgImportImageSequence dlg(this, document);
if (dlg.exec() == QDialog::Accepted) {
QStringList files = dlg.files();
int firstFrame = dlg.firstFrame();
int step = dlg.step();
KoUpdaterPtr updater =
!document->fileBatchMode() ? viewManager()->createUnthreadedUpdater(i18n("Import frames")) : 0;
KisAnimationImporter importer(document->image(), updater);
KisImportExportFilter::ConversionStatus status = importer.import(files, firstFrame, step);
if (status != KisImportExportFilter::OK && status != KisImportExportFilter::InternalError) {
QString msg = KisImportExportFilter::conversionStatusString(status);
if (!msg.isEmpty())
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not finish import animation:\n%1", msg));
}
activeView()->canvasBase()->refetchDataFromImage();
}
}
void KisMainWindow::slotConfigureToolbars()
{
saveWindowState();
KEditToolBar edit(factory(), this);
connect(&edit, SIGNAL(newToolBarConfig()), this, SLOT(slotNewToolbarConfig()));
(void) edit.exec();
applyToolBarLayout();
}
void KisMainWindow::slotNewToolbarConfig()
{
applyMainWindowSettings(d->windowStateConfig);
KXMLGUIFactory *factory = guiFactory();
Q_UNUSED(factory);
// Check if there's an active view
if (!d->activeView)
return;
plugActionList("toolbarlist", d->toolbarList);
applyToolBarLayout();
}
void KisMainWindow::slotToolbarToggled(bool toggle)
{
//dbgUI <<"KisMainWindow::slotToolbarToggled" << sender()->name() <<" toggle=" << true;
// The action (sender) and the toolbar have the same name
KToolBar * bar = toolBar(sender()->objectName());
if (bar) {
if (toggle) {
bar->show();
}
else {
bar->hide();
}
if (d->activeView && d->activeView->document()) {
saveWindowState();
}
} else
warnUI << "slotToolbarToggled : Toolbar " << sender()->objectName() << " not found!";
}
void KisMainWindow::viewFullscreen(bool fullScreen)
{
KisConfig cfg(false);
cfg.setFullscreenMode(fullScreen);
if (fullScreen) {
setWindowState(windowState() | Qt::WindowFullScreen); // set
} else {
setWindowState(windowState() & ~Qt::WindowFullScreen); // reset
}
}
void KisMainWindow::setMaxRecentItems(uint _number)
{
d->recentFiles->setMaxItems(_number);
}
void KisMainWindow::slotReloadFile()
{
KisDocument* document = d->activeView->document();
if (!document || document->url().isEmpty())
return;
if (document->isModified()) {
bool ok = QMessageBox::question(this,
i18nc("@title:window", "Krita"),
i18n("You will lose all changes made since your last save\n"
"Do you want to continue?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes;
if (!ok)
return;
}
QUrl url = document->url();
saveWindowSettings();
if (!document->reload()) {
QMessageBox::critical(this, i18nc("@title:window", "Krita"), i18n("Error: Could not reload this document"));
}
return;
}
QDockWidget* KisMainWindow::createDockWidget(KoDockFactoryBase* factory)
{
QDockWidget* dockWidget = 0;
bool lockAllDockers = KisConfig(true).readEntry<bool>("LockAllDockerPanels", false);
if (!d->dockWidgetsMap.contains(factory->id())) {
dockWidget = factory->createDockWidget();
// It is quite possible that a dock factory cannot create the dock; don't
// do anything in that case.
if (!dockWidget) {
warnKrita << "Could not create docker for" << factory->id();
return 0;
}
dockWidget->setFont(KoDockRegistry::dockFont());
dockWidget->setObjectName(factory->id());
dockWidget->setParent(this);
if (lockAllDockers) {
if (dockWidget->titleBarWidget()) {
dockWidget->titleBarWidget()->setVisible(false);
}
dockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures);
}
if (dockWidget->widget() && dockWidget->widget()->layout())
dockWidget->widget()->layout()->setContentsMargins(1, 1, 1, 1);
Qt::DockWidgetArea side = Qt::RightDockWidgetArea;
bool visible = true;
switch (factory->defaultDockPosition()) {
case KoDockFactoryBase::DockTornOff:
dockWidget->setFloating(true); // position nicely?
break;
case KoDockFactoryBase::DockTop:
side = Qt::TopDockWidgetArea; break;
case KoDockFactoryBase::DockLeft:
side = Qt::LeftDockWidgetArea; break;
case KoDockFactoryBase::DockBottom:
side = Qt::BottomDockWidgetArea; break;
case KoDockFactoryBase::DockRight:
side = Qt::RightDockWidgetArea; break;
case KoDockFactoryBase::DockMinimized:
default:
side = Qt::RightDockWidgetArea;
visible = false;
}
KConfigGroup group = d->windowStateConfig.group("DockWidget " + factory->id());
side = static_cast<Qt::DockWidgetArea>(group.readEntry("DockArea", static_cast<int>(side)));
if (side == Qt::NoDockWidgetArea) side = Qt::RightDockWidgetArea;
addDockWidget(side, dockWidget);
if (!visible) {
dockWidget->hide();
}
d->dockWidgetsMap.insert(factory->id(), dockWidget);
}
else {
dockWidget = d->dockWidgetsMap[factory->id()];
}
#ifdef Q_OS_OSX
dockWidget->setAttribute(Qt::WA_MacSmallSize, true);
#endif
dockWidget->setFont(KoDockRegistry::dockFont());
connect(dockWidget, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(forceDockTabFonts()));
return dockWidget;
}
void KisMainWindow::forceDockTabFonts()
{
Q_FOREACH (QObject *child, children()) {
if (child->inherits("QTabBar")) {
((QTabBar *)child)->setFont(KoDockRegistry::dockFont());
}
}
}
QList<QDockWidget*> KisMainWindow::dockWidgets() const
{
return d->dockWidgetsMap.values();
}
QDockWidget* KisMainWindow::dockWidget(const QString &id)
{
if (!d->dockWidgetsMap.contains(id)) return 0;
return d->dockWidgetsMap[id];
}
QList<KoCanvasObserverBase*> KisMainWindow::canvasObservers() const
{
QList<KoCanvasObserverBase*> observers;
Q_FOREACH (QDockWidget *docker, dockWidgets()) {
KoCanvasObserverBase *observer = dynamic_cast<KoCanvasObserverBase*>(docker);
if (observer) {
observers << observer;
}
else {
warnKrita << docker << "is not a canvas observer";
}
}
return observers;
}
void KisMainWindow::toggleDockersVisibility(bool visible)
{
if (!visible) {
d->dockerStateBeforeHiding = saveState();
Q_FOREACH (QObject* widget, children()) {
if (widget->inherits("QDockWidget")) {
QDockWidget* dw = static_cast<QDockWidget*>(widget);
if (dw->isVisible()) {
dw->hide();
}
}
}
}
else {
restoreState(d->dockerStateBeforeHiding);
}
}
void KisMainWindow::slotDocumentTitleModified()
{
updateCaption();
updateReloadFileAction(d->activeView ? d->activeView->document() : 0);
}
void KisMainWindow::subWindowActivated()
{
bool enabled = (activeKisView() != 0);
d->mdiCascade->setEnabled(enabled);
d->mdiNextWindow->setEnabled(enabled);
d->mdiPreviousWindow->setEnabled(enabled);
d->mdiTile->setEnabled(enabled);
d->close->setEnabled(enabled);
d->closeAll->setEnabled(enabled);
setActiveSubWindow(d->mdiArea->activeSubWindow());
Q_FOREACH (QToolBar *tb, toolBars()) {
if (tb->objectName() == "BrushesAndStuff") {
tb->setEnabled(enabled);
}
}
/**
* Qt has a weirdness, it has hardcoded shortcuts added to an action
* in the window menu. We need to reset the shortcuts for that menu
* to nothing, otherwise the shortcuts cannot be made configurable.
*
* See: https://bugs.kde.org/show_bug.cgi?id=352205
* https://bugs.kde.org/show_bug.cgi?id=375524
* https://bugs.kde.org/show_bug.cgi?id=398729
*/
QMdiSubWindow *subWindow = d->mdiArea->currentSubWindow();
if (subWindow) {
QMenu *menu = subWindow->systemMenu();
if (menu && menu->actions().size() == 8) {
Q_FOREACH (QAction *action, menu->actions()) {
action->setShortcut(QKeySequence());
}
menu->actions().last()->deleteLater();
}
}
updateCaption();
d->actionManager()->updateGUI();
}
void KisMainWindow::windowFocused()
{
/**
* Notify selection manager so that it could update selection mask overlay
*/
if (viewManager() && viewManager()->selectionManager()) {
viewManager()->selectionManager()->selectionChanged();
}
KisPart *kisPart = KisPart::instance();
KisWindowLayoutManager *layoutManager = KisWindowLayoutManager::instance();
if (!layoutManager->primaryWorkspaceFollowsFocus()) return;
QUuid primary = layoutManager->primaryWindowId();
if (primary.isNull()) return;
if (d->id == primary) {
if (!d->workspaceBorrowedBy.isNull()) {
KisMainWindow *borrower = kisPart->windowById(d->workspaceBorrowedBy);
if (!borrower) return;
swapWorkspaces(this, borrower);
}
} else {
if (d->workspaceBorrowedBy == primary) return;
KisMainWindow *primaryWindow = kisPart->windowById(primary);
if (!primaryWindow) return;
swapWorkspaces(this, primaryWindow);
}
}
void KisMainWindow::updateWindowMenu()
{
QMenu *menu = d->windowMenu->menu();
menu->clear();
menu->addAction(d->newWindow);
menu->addAction(d->documentMenu);
QMenu *docMenu = d->documentMenu->menu();
docMenu->clear();
Q_FOREACH (QPointer<KisDocument> doc, KisPart::instance()->documents()) {
if (doc) {
QString title = doc->url().toDisplayString();
if (title.isEmpty() && doc->image()) {
title = doc->image()->objectName();
}
QAction *action = docMenu->addAction(title);
action->setIcon(qApp->windowIcon());
connect(action, SIGNAL(triggered()), d->documentMapper, SLOT(map()));
d->documentMapper->setMapping(action, doc);
}
}
menu->addAction(d->workspaceMenu);
QMenu *workspaceMenu = d->workspaceMenu->menu();
workspaceMenu->clear();
auto workspaces = KisResourceServerProvider::instance()->workspaceServer()->resources();
auto m_this = this;
for (auto &w : workspaces) {
auto action = workspaceMenu->addAction(w->name());
connect(action, &QAction::triggered, this, [=]() {
m_this->restoreWorkspace(w);
});
}
workspaceMenu->addSeparator();
connect(workspaceMenu->addAction(i18nc("@action:inmenu", "&Import Workspace...")),
&QAction::triggered,
this,
[&]() {
QString extensions = d->workspacemodel->extensions();
QStringList mimeTypes;
for(const QString &suffix : extensions.split(":")) {
mimeTypes << KisMimeDatabase::mimeTypeForSuffix(suffix);
}
KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument");
dialog.setMimeTypeFilters(mimeTypes);
dialog.setCaption(i18nc("@title:window", "Choose File to Add"));
QString filename = dialog.filename();
d->workspacemodel->importResourceFile(filename);
});
connect(workspaceMenu->addAction(i18nc("@action:inmenu", "&New Workspace...")),
&QAction::triggered,
[=]() {
QString name = QInputDialog::getText(this, i18nc("@title:window", "New Workspace..."),
i18nc("@label:textbox", "Name:"));
if (name.isEmpty()) return;
auto rserver = KisResourceServerProvider::instance()->workspaceServer();
KisWorkspaceResource* workspace = new KisWorkspaceResource("");
workspace->setDockerState(m_this->saveState());
d->viewManager->resourceProvider()->notifySavingWorkspace(workspace);
workspace->setValid(true);
QString saveLocation = rserver->saveLocation();
bool newName = false;
if(name.isEmpty()) {
newName = true;
name = i18n("Workspace");
}
QFileInfo fileInfo(saveLocation + name + workspace->defaultFileExtension());
int i = 1;
while (fileInfo.exists()) {
fileInfo.setFile(saveLocation + name + QString("%1").arg(i) + workspace->defaultFileExtension());
i++;
}
workspace->setFilename(fileInfo.filePath());
if(newName) {
name = i18n("Workspace %1", i);
}
workspace->setName(name);
rserver->addResource(workspace);
});
// TODO: What to do about delete?
// workspaceMenu->addAction(i18nc("@action:inmenu", "&Delete Workspace..."));
menu->addSeparator();
menu->addAction(d->close);
menu->addAction(d->closeAll);
if (d->mdiArea->viewMode() == QMdiArea::SubWindowView) {
menu->addSeparator();
menu->addAction(d->mdiTile);
menu->addAction(d->mdiCascade);
}
menu->addSeparator();
menu->addAction(d->mdiNextWindow);
menu->addAction(d->mdiPreviousWindow);
menu->addSeparator();
QList<QMdiSubWindow *> windows = d->mdiArea->subWindowList();
for (int i = 0; i < windows.size(); ++i) {
QPointer<KisView>child = qobject_cast<KisView*>(windows.at(i)->widget());
if (child && child->document()) {
QString text;
if (i < 9) {
text = i18n("&%1 %2", i + 1, child->document()->url().toDisplayString());
}
else {
text = i18n("%1 %2", i + 1, child->document()->url().toDisplayString());
}
QAction *action = menu->addAction(text);
action->setIcon(qApp->windowIcon());
action->setCheckable(true);
action->setChecked(child == activeKisView());
connect(action, SIGNAL(triggered()), d->windowMapper, SLOT(map()));
d->windowMapper->setMapping(action, windows.at(i));
}
}
bool showMdiArea = windows.count( ) > 0;
if (!showMdiArea) {
showWelcomeScreen(true); // see workaround in function in header
// keep the recent file list updated when going back to welcome screen
reloadRecentFileList();
d->welcomePage->populateRecentDocuments();
}
// enable/disable the toolbox docker if there are no documents open
Q_FOREACH (QObject* widget, children()) {
if (widget->inherits("QDockWidget")) {
QDockWidget* dw = static_cast<QDockWidget*>(widget);
if ( dw->objectName() == "ToolBox") {
dw->setEnabled(showMdiArea);
}
}
}
updateCaption();
}
void KisMainWindow::setActiveSubWindow(QWidget *window)
{
if (!window) return;
QMdiSubWindow *subwin = qobject_cast<QMdiSubWindow *>(window);
//dbgKrita << "setActiveSubWindow();" << subwin << d->activeSubWindow;
if (subwin && subwin != d->activeSubWindow) {
KisView *view = qobject_cast<KisView *>(subwin->widget());
//dbgKrita << "\t" << view << activeView();
if (view && view != activeView()) {
d->mdiArea->setActiveSubWindow(subwin);
setActiveView(view);
}
d->activeSubWindow = subwin;
}
updateWindowMenu();
d->actionManager()->updateGUI();
}
void KisMainWindow::configChanged()
{
KisConfig cfg(true);
QMdiArea::ViewMode viewMode = (QMdiArea::ViewMode)cfg.readEntry<int>("mdi_viewmode", (int)QMdiArea::TabbedView);
d->mdiArea->setViewMode(viewMode);
Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) {
subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.readEntry<int>("mdi_rubberband", cfg.useOpenGL()));
subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.readEntry<int>("mdi_rubberband", cfg.useOpenGL()));
/**
* Dirty workaround for a bug in Qt (checked on Qt 5.6.1):
*
* If you make a window "Show on top" and then switch to the tabbed mode
* the window will contiue to be painted in its initial "mid-screen"
* position. It will persist here until you explicitly switch to its tab.
*/
if (viewMode == QMdiArea::TabbedView) {
Qt::WindowFlags oldFlags = subwin->windowFlags();
Qt::WindowFlags flags = oldFlags;
flags &= ~Qt::WindowStaysOnTopHint;
flags &= ~Qt::WindowStaysOnBottomHint;
if (flags != oldFlags) {
subwin->setWindowFlags(flags);
subwin->showMaximized();
}
}
}
KConfigGroup group( KSharedConfig::openConfig(), "theme");
d->themeManager->setCurrentTheme(group.readEntry("Theme", "Krita dark"));
d->actionManager()->updateGUI();
QBrush brush(cfg.getMDIBackgroundColor());
d->mdiArea->setBackground(brush);
QString backgroundImage = cfg.getMDIBackgroundImage();
if (backgroundImage != "") {
QImage image(backgroundImage);
QBrush brush(image);
d->mdiArea->setBackground(brush);
}
d->mdiArea->update();
}
KisView* KisMainWindow::newView(QObject *document)
{
KisDocument *doc = qobject_cast<KisDocument*>(document);
KisView *view = addViewAndNotifyLoadingCompleted(doc);
d->actionManager()->updateGUI();
return view;
}
void KisMainWindow::newWindow()
{
KisMainWindow *mainWindow = KisPart::instance()->createMainWindow();
mainWindow->initializeGeometry();
mainWindow->show();
}
void KisMainWindow::closeCurrentWindow()
{
if (d->mdiArea->currentSubWindow()) {
d->mdiArea->currentSubWindow()->close();
d->actionManager()->updateGUI();
}
}
void KisMainWindow::checkSanity()
{
// print error if the lcms engine is not available
if (!KoColorSpaceEngineRegistry::instance()->contains("icc")) {
// need to wait 1 event since exiting here would not work.
m_errorMessage = i18n("The Krita LittleCMS color management plugin is not installed. Krita will quit now.");
m_dieOnError = true;
QTimer::singleShot(0, this, SLOT(showErrorAndDie()));
return;
}
KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer();
if (rserver->resources().isEmpty()) {
m_errorMessage = i18n("Krita cannot find any brush presets! Krita will quit now.");
m_dieOnError = true;
QTimer::singleShot(0, this, SLOT(showErrorAndDie()));
return;
}
}
void KisMainWindow::showErrorAndDie()
{
QMessageBox::critical(0, i18nc("@title:window", "Installation error"), m_errorMessage);
if (m_dieOnError) {
exit(10);
}
}
void KisMainWindow::showAboutApplication()
{
KisAboutApplication dlg(this);
dlg.exec();
}
QPointer<KisView> KisMainWindow::activeKisView()
{
if (!d->mdiArea) return 0;
QMdiSubWindow *activeSubWindow = d->mdiArea->activeSubWindow();
//dbgKrita << "activeKisView" << activeSubWindow;
if (!activeSubWindow) return 0;
return qobject_cast<KisView*>(activeSubWindow->widget());
}
void KisMainWindow::newOptionWidgets(KoCanvasController *controller, const QList<QPointer<QWidget> > &optionWidgetList)
{
KIS_ASSERT_RECOVER_NOOP(controller == KoToolManager::instance()->activeCanvasController());
bool isOurOwnView = false;
Q_FOREACH (QPointer<KisView> view, KisPart::instance()->views()) {
if (view && view->canvasController() == controller) {
isOurOwnView = view->mainWindow() == this;
}
}
if (!isOurOwnView) return;
Q_FOREACH (QWidget *w, optionWidgetList) {
#ifdef Q_OS_OSX
w->setAttribute(Qt::WA_MacSmallSize, true);
#endif
w->setFont(KoDockRegistry::dockFont());
}
if (d->toolOptionsDocker) {
d->toolOptionsDocker->setOptionWidgets(optionWidgetList);
}
else {
d->viewManager->paintOpBox()->newOptionWidgets(optionWidgetList);
}
}
void KisMainWindow::applyDefaultSettings(QPrinter &printer) {
if (!d->activeView) return;
QString title = d->activeView->document()->documentInfo()->aboutInfo("title");
if (title.isEmpty()) {
QFileInfo info(d->activeView->document()->url().fileName());
title = info.baseName();
}
if (title.isEmpty()) {
// #139905
title = i18n("%1 unsaved document (%2)", qApp->applicationDisplayName(),
QLocale().toString(QDate::currentDate(), QLocale::ShortFormat));
}
printer.setDocName(title);
}
void KisMainWindow::createActions()
{
KisActionManager *actionManager = d->actionManager();
actionManager->createStandardAction(KStandardAction::New, this, SLOT(slotFileNew()));
actionManager->createStandardAction(KStandardAction::Open, this, SLOT(slotFileOpen()));
actionManager->createStandardAction(KStandardAction::Quit, this, SLOT(slotFileQuit()));
actionManager->createStandardAction(KStandardAction::ConfigureToolbars, this, SLOT(slotConfigureToolbars()));
d->fullScreenMode = actionManager->createStandardAction(KStandardAction::FullScreen, this, SLOT(viewFullscreen(bool)));
d->recentFiles = KStandardAction::openRecent(this, SLOT(slotFileOpenRecent(QUrl)), actionCollection());
connect(d->recentFiles, SIGNAL(recentListCleared()), this, SLOT(saveRecentFiles()));
KSharedConfigPtr configPtr = KSharedConfig::openConfig();
d->recentFiles->loadEntries(configPtr->group("RecentFiles"));
d->saveAction = actionManager->createStandardAction(KStandardAction::Save, this, SLOT(slotFileSave()));
d->saveAction->setActivationFlags(KisAction::ACTIVE_IMAGE);
d->saveActionAs = actionManager->createStandardAction(KStandardAction::SaveAs, this, SLOT(slotFileSaveAs()));
d->saveActionAs->setActivationFlags(KisAction::ACTIVE_IMAGE);
// d->printAction = actionManager->createStandardAction(KStandardAction::Print, this, SLOT(slotFilePrint()));
// d->printAction->setActivationFlags(KisAction::ACTIVE_IMAGE);
// d->printActionPreview = actionManager->createStandardAction(KStandardAction::PrintPreview, this, SLOT(slotFilePrintPreview()));
// d->printActionPreview->setActivationFlags(KisAction::ACTIVE_IMAGE);
d->undo = actionManager->createStandardAction(KStandardAction::Undo, this, SLOT(undo()));
d->undo->setActivationFlags(KisAction::ACTIVE_IMAGE);
d->redo = actionManager->createStandardAction(KStandardAction::Redo, this, SLOT(redo()));
d->redo->setActivationFlags(KisAction::ACTIVE_IMAGE);
d->undoActionsUpdateManager.reset(new KisUndoActionsUpdateManager(d->undo, d->redo));
d->undoActionsUpdateManager->setCurrentDocument(d->activeView ? d->activeView->document() : 0);
// d->exportPdf = actionManager->createAction("file_export_pdf");
// connect(d->exportPdf, SIGNAL(triggered()), this, SLOT(exportToPdf()));
d->importAnimation = actionManager->createAction("file_import_animation");
connect(d->importAnimation, SIGNAL(triggered()), this, SLOT(importAnimation()));
d->closeAll = actionManager->createAction("file_close_all");
connect(d->closeAll, SIGNAL(triggered()), this, SLOT(slotFileCloseAll()));
// d->reloadFile = actionManager->createAction("file_reload_file");
// d->reloadFile->setActivationFlags(KisAction::CURRENT_IMAGE_MODIFIED);
// connect(d->reloadFile, SIGNAL(triggered(bool)), this, SLOT(slotReloadFile()));
d->importFile = actionManager->createAction("file_import_file");
connect(d->importFile, SIGNAL(triggered(bool)), this, SLOT(slotImportFile()));
d->exportFile = actionManager->createAction("file_export_file");
connect(d->exportFile, SIGNAL(triggered(bool)), this, SLOT(slotExportFile()));
/* The following entry opens the document information dialog. Since the action is named so it
intends to show data this entry should not have a trailing ellipses (...). */
d->showDocumentInfo = actionManager->createAction("file_documentinfo");
connect(d->showDocumentInfo, SIGNAL(triggered(bool)), this, SLOT(slotDocumentInfo()));
d->themeManager->setThemeMenuAction(new KActionMenu(i18nc("@action:inmenu", "&Themes"), this));
d->themeManager->registerThemeActions(actionCollection());
connect(d->themeManager, SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged()));
connect(d->themeManager, SIGNAL(signalThemeChanged()), d->welcomePage, SLOT(slotUpdateThemeColors()));
d->toggleDockers = actionManager->createAction("view_toggledockers");
KisConfig(true).showDockers(true);
d->toggleDockers->setChecked(true);
connect(d->toggleDockers, SIGNAL(toggled(bool)), SLOT(toggleDockersVisibility(bool)));
actionCollection()->addAction("settings_dockers_menu", d->dockWidgetMenu);
actionCollection()->addAction("window", d->windowMenu);
d->mdiCascade = actionManager->createAction("windows_cascade");
connect(d->mdiCascade, SIGNAL(triggered()), d->mdiArea, SLOT(cascadeSubWindows()));
d->mdiTile = actionManager->createAction("windows_tile");
connect(d->mdiTile, SIGNAL(triggered()), d->mdiArea, SLOT(tileSubWindows()));
d->mdiNextWindow = actionManager->createAction("windows_next");
connect(d->mdiNextWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activateNextSubWindow()));
d->mdiPreviousWindow = actionManager->createAction("windows_previous");
connect(d->mdiPreviousWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activatePreviousSubWindow()));
d->newWindow = actionManager->createAction("view_newwindow");
connect(d->newWindow, SIGNAL(triggered(bool)), this, SLOT(newWindow()));
d->close = actionManager->createStandardAction(KStandardAction::Close, this, SLOT(closeCurrentWindow()));
d->showSessionManager = actionManager->createAction("file_sessions");
connect(d->showSessionManager, SIGNAL(triggered(bool)), this, SLOT(slotShowSessionManager()));
actionManager->createStandardAction(KStandardAction::Preferences, this, SLOT(slotPreferences()));
for (int i = 0; i < 2; i++) {
d->expandingSpacers[i] = new KisAction(i18n("Expanding Spacer"));
d->expandingSpacers[i]->setDefaultWidget(new QWidget(this));
d->expandingSpacers[i]->defaultWidget()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
actionManager->addAction(QString("expanding_spacer_%1").arg(i), d->expandingSpacers[i]);
}
}
void KisMainWindow::applyToolBarLayout()
{
const bool isPlastiqueStyle = style()->objectName() == "plastique";
Q_FOREACH (KToolBar *toolBar, toolBars()) {
toolBar->layout()->setSpacing(4);
if (isPlastiqueStyle) {
toolBar->setContentsMargins(0, 0, 0, 2);
}
//Hide text for buttons with an icon in the toolbar
Q_FOREACH (QAction *ac, toolBar->actions()){
if (ac->icon().pixmap(QSize(1,1)).isNull() == false){
ac->setPriority(QAction::LowPriority);
}else {
ac->setIcon(QIcon());
}
}
}
}
void KisMainWindow::initializeGeometry()
{
// if the user didn's specify the geometry on the command line (does anyone do that still?),
// we first figure out some good default size and restore the x,y position. See bug 285804Z.
KConfigGroup cfg = d->windowStateConfig;
QByteArray geom = QByteArray::fromBase64(cfg.readEntry("ko_geometry", QByteArray()));
if (!restoreGeometry(geom)) {
const int scnum = QApplication::desktop()->screenNumber(parentWidget());
QRect desk = QApplication::desktop()->availableGeometry(scnum);
// if the desktop is virtual then use virtual screen size
if (QApplication::desktop()->isVirtualDesktop()) {
desk = QApplication::desktop()->availableGeometry(QApplication::desktop()->screen(scnum));
}
quint32 x = desk.x();
quint32 y = desk.y();
quint32 w = 0;
quint32 h = 0;
// Default size -- maximize on small screens, something useful on big screens
const int deskWidth = desk.width();
if (deskWidth > 1024) {
// a nice width, and slightly less than total available
// height to compensate for the window decs
w = (deskWidth / 3) * 2;
h = (desk.height() / 3) * 2;
}
else {
w = desk.width();
h = desk.height();
}
x += (desk.width() - w) / 2;
y += (desk.height() - h) / 2;
move(x,y);
setGeometry(geometry().x(), geometry().y(), w, h);
}
d->fullScreenMode->setChecked(isFullScreen());
}
void KisMainWindow::showManual()
{
QDesktopServices::openUrl(QUrl("https://docs.krita.org"));
}
void KisMainWindow::moveEvent(QMoveEvent *e)
{
/**
* For checking if the display number has changed or not we should always use
* positional overload, not using QWidget overload. Otherwise we might get
* inconsistency, because screenNumber(widget) can return -1, but screenNumber(pos)
* will always return the nearest screen.
*/
const int oldScreen = qApp->desktop()->screenNumber(e->oldPos());
const int newScreen = qApp->desktop()->screenNumber(e->pos());
if (oldScreen != newScreen) {
emit screenChanged();
}
if (d->screenConnectionsStore.isEmpty() || oldScreen != newScreen) {
d->screenConnectionsStore.clear();
QScreen *newScreenObject = 0;
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
newScreenObject = qApp->screenAt(e->pos());
#else
// TODO: i'm not sure if this pointer already has a correct value
// by the moment we get the event. It might not work on older
// versions of Qt
newScreenObject = qApp->primaryScreen();
#endif
if (newScreenObject) {
d->screenConnectionsStore.addConnection(newScreenObject, SIGNAL(physicalDotsPerInchChanged(qreal)),
this, SIGNAL(screenChanged()));
}
}
}
#include <moc_KisMainWindow.cpp>
diff --git a/libs/ui/KisNodeDelegate.cpp b/libs/ui/KisNodeDelegate.cpp
index c17e0771b5..06796f19a9 100644
--- a/libs/ui/KisNodeDelegate.cpp
+++ b/libs/ui/KisNodeDelegate.cpp
@@ -1,962 +1,1015 @@
/*
Copyright (c) 2006 Gábor Lehel <illissius@gmail.com>
Copyright (c) 2008 Cyrille Berger <cberger@cberger.net>
Copyright (c) 2011 José Luis Vergara <pentalis@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kis_config.h"
#include "KisNodeDelegate.h"
#include "kis_node_model.h"
#include "KisNodeToolTip.h"
#include "KisNodeView.h"
#include "KisPart.h"
#include "input/kis_input_manager.h"
#include <QtDebug>
#include <QApplication>
#include <QKeyEvent>
#include <QLineEdit>
#include <QModelIndex>
#include <QMouseEvent>
#include <QPainter>
#include <QPointer>
#include <QStyle>
#include <QStyleOptionViewItem>
#include <klocalizedstring.h>
#include "kis_node_view_color_scheme.h"
#include "kis_icon_utils.h"
#include "kis_layer_properties_icons.h"
#include "krita_utils.h"
#include "kis_config_notifier.h"
typedef KisBaseNode::Property* OptionalProperty;
#include <kis_base_node.h>
class KisNodeDelegate::Private
{
public:
Private() : view(0), edit(0) { }
KisNodeView *view;
QPointer<QWidget> edit;
KisNodeToolTip tip;
QColor checkersColor1;
QColor checkersColor2;
QList<OptionalProperty> rightmostProperties(const KisBaseNode::PropertyList &props) const;
int numProperties(const QModelIndex &index) const;
OptionalProperty findProperty(KisBaseNode::PropertyList &props, const OptionalProperty &refProp) const;
OptionalProperty findVisibilityProperty(KisBaseNode::PropertyList &props) const;
void toggleProperty(KisBaseNode::PropertyList &props, OptionalProperty prop, bool controlPressed, const QModelIndex &index);
};
KisNodeDelegate::KisNodeDelegate(KisNodeView *view, QObject *parent)
: QAbstractItemDelegate(parent)
, d(new Private)
{
d->view = view;
QApplication::instance()->installEventFilter(this);
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
slotConfigChanged();
}
KisNodeDelegate::~KisNodeDelegate()
{
delete d;
}
QSize KisNodeDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(index);
KisNodeViewColorScheme scm;
return QSize(option.rect.width(), scm.rowHeight());
}
void KisNodeDelegate::paint(QPainter *p, const QStyleOptionViewItem &o, const QModelIndex &index) const
{
p->save();
{
QStyleOptionViewItem option = getOptions(o, index);
QStyle *style = option.widget ? option.widget->style() : QApplication::style();
style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, p, option.widget);
bool shouldGrayOut = index.data(KisNodeModel::ShouldGrayOutRole).toBool();
if (shouldGrayOut) {
option.state &= ~QStyle::State_Enabled;
}
p->setFont(option.font);
-
drawColorLabel(p, option, index);
drawFrame(p, option, index);
drawThumbnail(p, option, index);
- drawText(p, option, index);
+ drawText(p, option, index); // BUG: Creating group moves things around (RTL-layout alignment)
drawIcons(p, option, index);
- drawVisibilityIconHijack(p, option, index);
+ drawVisibilityIconHijack(p, option, index); // TODO hide when dragging
drawDecoration(p, option, index);
drawExpandButton(p, option, index);
drawBranch(p, option, index);
drawProgressBar(p, option, index);
}
p->restore();
}
-void KisNodeDelegate::drawBranch(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const {
- Q_UNUSED(index);
+void KisNodeDelegate::drawBranch(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ QModelIndex tmp = index.parent();
+
+ // there is no indention if we have no parent group, so don't draw a branch
+ if (!tmp.isValid()) return;
KisNodeViewColorScheme scm;
- const QPoint base = scm.relThumbnailRect().translated(option.rect.topLeft()).topLeft() - QPoint( scm.indentation(), 0);
- // there is no indention if we are starting negative, so don't draw a branch
- if (base.x() < 0) {
- return;
+ int rtlNum = (option.direction == Qt::RightToLeft) ? 1 : -1;
+
+ QRect baseRect = scm.relThumbnailRect();
+
+ // Move to current index
+ baseRect.moveTop(option.rect.topLeft().y());
+ // Move to correct location.
+ if (option.direction == Qt::RightToLeft) {
+ baseRect.moveLeft(option.rect.topRight().x());
+ } else {
+ baseRect.moveRight(option.rect.topLeft().x());
}
+ QPoint base = baseRect.adjusted(rtlNum*scm.indentation(), 0,
+ rtlNum*scm.indentation(), 0).center() + QPoint(0, scm.iconSize()/4);
QPen oldPen = p->pen();
const qreal oldOpacity = p->opacity(); // remember previous opacity
p->setOpacity(1.0);
QColor color = scm.gridColor(option, d->view);
QColor bgColor = option.state & QStyle::State_Selected ?
qApp->palette().color(QPalette::Base) :
qApp->palette().color(QPalette::Text);
color = KritaUtils::blendColors(color, bgColor, 0.9);
-
// TODO: if we are a mask type, use dotted lines for the branch style
// p->setPen(QPen(p->pen().color(), 2, Qt::DashLine, Qt::RoundCap, Qt::RoundJoin));
p->setPen(QPen(color, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
- QPoint p2 = base + QPoint(scm.iconSize() - scm.decorationMargin()*2, scm.iconSize()*0.45);
- QPoint p3 = base + QPoint(scm.iconSize() - scm.decorationMargin()*2, scm.iconSize());
- QPoint p4 = base + QPoint(scm.iconSize()*1.4, scm.iconSize());
- p->drawLine(p2, p3);
- p->drawLine(p3, p4);
-
-
+ QPoint p2 = base - QPoint(rtlNum*(scm.iconSize()/2), 0);
+ QPoint p3 = base - QPoint(0, scm.iconSize()/2);
+ p->drawLine(base, p2);
+ p->drawLine(base, p3);
// draw parent lines (keep drawing until x position is less than 0
- QPoint p5 = p2 - QPoint(scm.indentation(), 0);
- QPoint p6 = p3 - QPoint(scm.indentation(), 0);
-
- QPoint parentBase1 = p5;
- QPoint parentBase2 = p6;
+ QPoint parentBase1 = base + QPoint(rtlNum*scm.indentation(), 0);
+ QPoint parentBase2 = p3 + QPoint(rtlNum*scm.indentation(), 0);
// indent lines needs to be very subtle to avoid making the docker busy looking
color = KritaUtils::blendColors(color, bgColor, 0.9); // makes it a little lighter than L lines
p->setPen(QPen(color, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
- while (parentBase1.x() > scm.visibilityColumnWidth()) {
- p->drawLine(parentBase1, parentBase2);
-
- parentBase1 = parentBase1 - QPoint(scm.indentation(), 0);
- parentBase2 = parentBase2 - QPoint(scm.indentation(), 0);
+ if (tmp.isValid()) {
+ tmp = tmp.parent(); // Ignore the first group as it was already painted
}
+ while (tmp.isValid()) {
+ p->drawLine(parentBase1, parentBase2);
+ parentBase1 += QPoint(rtlNum*scm.indentation(), 0);
+ parentBase2 += QPoint(rtlNum*scm.indentation(), 0);
-
+ tmp = tmp.parent();
+ }
p->setPen(oldPen);
p->setOpacity(oldOpacity);
}
void KisNodeDelegate::drawColorLabel(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
KisNodeViewColorScheme scm;
const int label = index.data(KisNodeModel::ColorLabelIndexRole).toInt();
QColor color = scm.colorLabel(label);
if (color.alpha() <= 0) return;
QColor bgColor = qApp->palette().color(QPalette::Base);
- color = KritaUtils::blendColors(color, bgColor, 0.3);
+ if ((option.state & QStyle::State_MouseOver) && !(option.state & QStyle::State_Selected)) {
+ color = KritaUtils::blendColors(color, bgColor, 0.6);
+ } else {
+ color = KritaUtils::blendColors(color, bgColor, 0.3);
+ }
+
+ QRect optionRect = option.rect.adjusted(0, 0, scm.indentation(), 0);
+ if (option.state & QStyle::State_Selected) {
+ optionRect = iconsRect(option, index);
+ }
- const QRect rect = option.state & QStyle::State_Selected ?
- iconsRect(option, index) :
- option.rect.adjusted(-scm.indentation(), 0, 0, 0);
+ if (option.direction == Qt::RightToLeft) {
+ optionRect.moveLeft(option.rect.topLeft().x());
+ } else {
+ optionRect.moveRight(option.rect.topRight().x());
+ }
- p->fillRect(rect, color);
+ p->fillRect(optionRect, color);
}
void KisNodeDelegate::drawFrame(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
KisNodeViewColorScheme scm;
QPen oldPen = p->pen();
p->setPen(scm.gridColor(option, d->view));
- const QPoint base = option.rect.topLeft();
+ const QRect visibilityRect = visibilityClickRect(option, index);
+ const QRect thumbnailRect = thumbnailClickRect(option, index);
+ const QRect decorationRect = decorationClickRect(option, index);
+ const QRect iconsRectR = iconsRect(option, index);
- QPoint p2 = base + QPoint(-scm.indentation() - 1, 0);
- QPoint p3 = base + QPoint(2 * scm.decorationMargin() + scm.decorationSize(), 0);
- QPoint p4 = base + QPoint(-1, 0);
- QPoint p5(iconsRect(option,
- index).left() - 1, base.y());
- QPoint p6(option.rect.right(), base.y());
-
- QPoint v(0, option.rect.height());
+ const float topY = thumbnailRect.topLeft().y();
+ const float bottomY = thumbnailRect.bottomLeft().y();
+ QPoint bottomLeftPoint;
+ QPoint bottomRightPoint;
+ if (option.direction == Qt::RightToLeft) {
+ bottomLeftPoint = iconsRectR.bottomLeft();
+ bottomRightPoint = visibilityRect.bottomRight();
+ } else {
+ bottomLeftPoint = visibilityRect.bottomLeft();
+ bottomRightPoint = iconsRectR.bottomRight();
+ }
- // draw a line that goes the length of the entire frame. one for the
- // top, and one for the bottom
- QPoint pTopLeft(0, option.rect.topLeft().y());
- QPoint pTopRight(option.rect.bottomRight().x(),option.rect.topLeft().y() );
- p->drawLine(pTopLeft, pTopRight);
+ // bottom running horizontal line
+ p->drawLine(bottomLeftPoint.x(), bottomY,
+ bottomRightPoint.x(), bottomY);
- QPoint pBottomLeft(0, option.rect.topLeft().y() + scm.rowHeight());
- QPoint pBottomRight(option.rect.bottomRight().x(),option.rect.topLeft().y() + scm.rowHeight() );
- p->drawLine(pBottomLeft, pBottomRight);
+ // visiblity icon vertical line - left
+ p->drawLine(visibilityRect.topLeft().x()-1, topY,
+ visibilityRect.bottomLeft().x()-1, bottomY);
+ // visiblity icon vertical line - right
+ p->drawLine(visibilityRect.topRight().x()+1, topY,
+ visibilityRect.bottomRight().x()+1, bottomY);
- const bool paintForParent =
- index.parent().isValid() &&
- !index.row();
+ // thumbnail vertical line - left
+ p->drawLine(thumbnailRect.topLeft().x(), topY,
+ thumbnailRect.bottomLeft().x(), bottomY);
- if (paintForParent) {
- QPoint p1(-2 * scm.indentation() - 1, 0);
- p1 += base;
- p->drawLine(p1, p2);
- }
+ // thumbnail vertical line - right
+ p->drawLine(thumbnailRect.topRight().x(), topY,
+ thumbnailRect.bottomRight().x(), bottomY);
+ // decoration vertical line - left
+ p->drawLine(decorationRect.topLeft().x(), topY,
+ decorationRect.bottomLeft().x(), bottomY);
- QPoint k0(0, base.y());
- QPoint k1(1 * scm.border() + 2 * scm.visibilityMargin() + scm.visibilitySize(), base.y());
- p->drawLine(k0, k1);
- p->drawLine(k0 + v, k1 + v);
- p->drawLine(k0, k0 + v);
- p->drawLine(k1, k1 + v);
+ // decoration vertical line - right
+ p->drawLine(decorationRect.topRight().x(), topY,
+ decorationRect.bottomRight().x(), bottomY);
- p->drawLine(p2, p6);
- p->drawLine(p2 + v, p6 + v);
- p->drawLine(p2, p2 + v);
- p->drawLine(p3, p3 + v);
- p->drawLine(p4, p4 + v);
- p->drawLine(p5, p5 + v);
- p->drawLine(p6, p6 + v);
+ // icons' lines are drawn by drawIcons
//// For debugging purposes only
- //p->setPen(Qt::blue);
- //KritaUtils::renderExactRect(p, iconsRect(option, index));
+ p->setPen(Qt::blue);
+ //KritaUtils::renderExactRect(p, iconsRectR);
//KritaUtils::renderExactRect(p, textRect(option, index));
- //KritaUtils::renderExactRect(p, scm.relThumbnailRect().translated(option.rect.topLeft()));
+ //KritaUtils::renderExactRect(p, visibilityRect);
p->setPen(oldPen);
}
QRect KisNodeDelegate::thumbnailClickRect(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(index);
+ KisNodeViewColorScheme scm;
- int steps = 0;
- QModelIndex tmp = index.parent();
- while (tmp.isValid()) {
- steps++;
- tmp = tmp.parent();
+ QRect rc = scm.relThumbnailRect();
+
+ // Move to current index
+ rc.moveTop(option.rect.topLeft().y());
+ // Move to correct location.
+ if (option.direction == Qt::RightToLeft) {
+ rc.moveLeft(option.rect.topRight().x());
+ } else {
+ rc.moveRight(option.rect.topLeft().x());
}
- KisNodeViewColorScheme scm;
- return QRect(scm.border() +
- 2 * scm.visibilityMargin() + scm.visibilitySize() +
- scm.border() + steps * scm.indentation(),
- scm.border() + option.rect.top(),
- 2 * scm.thumbnailMargin() + scm.thumbnailSize(),
- scm.rowHeight() - scm.border());
+ return rc;
}
void KisNodeDelegate::drawThumbnail(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
KisNodeViewColorScheme scm;
const int thumbSize = scm.thumbnailSize();
const qreal oldOpacity = p->opacity(); // remember previous opacity
QImage img = index.data(int(KisNodeModel::BeginThumbnailRole) + thumbSize).value<QImage>();
if (!(option.state & QStyle::State_Enabled)) {
p->setOpacity(0.35);
}
- QRect fitRect = scm.relThumbnailRect().translated(option.rect.topLeft());
-
- QPoint offset;
- offset.setX((fitRect.width() - img.width()) / 2);
- offset.setY((fitRect.height() - img.height()) / 2);
- offset += fitRect.topLeft();
+ QRect fitRect = thumbnailClickRect(option, index);
+ // Shrink to icon rect
+ fitRect = kisGrowRect(fitRect, -(scm.thumbnailMargin()+scm.border()));
// paint in a checkerboard pattern behind the layer contents to represent transparent
const int step = scm.thumbnailSize() / 6;
QImage checkers(2 * step, 2 * step, QImage::Format_ARGB32);
QPainter gc(&checkers);
gc.fillRect(QRect(0, 0, step, step), d->checkersColor1);
gc.fillRect(QRect(step, 0, step, step), d->checkersColor2);
gc.fillRect(QRect(step, step, step, step), d->checkersColor1);
gc.fillRect(QRect(0, step, step, step), d->checkersColor2);
QBrush brush(checkers);
- p->setBrushOrigin(offset);
- p->fillRect(img.rect().translated(offset), brush);
+ p->fillRect(fitRect, brush);
- p->drawImage(offset, img);
+ p->drawImage(fitRect, img);
p->setOpacity(oldOpacity); // restore old opacity
- QRect borderRect = kisGrowRect(img.rect(), 1).translated(offset);
+ QRect borderRect = kisGrowRect(fitRect, 1);
KritaUtils::renderExactRect(p, borderRect, scm.gridColor(option, d->view));
}
QRect KisNodeDelegate::iconsRect(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
KisNodeViewColorScheme scm;
int propCount = d->numProperties(index);
const int iconsWidth =
propCount * (scm.iconSize() + 2 * scm.iconMargin()) +
- (propCount - 1) * scm.border();
-
- const int x = option.rect.x() + option.rect.width()
- - (iconsWidth + scm.border());
- const int y = option.rect.y() + scm.border();
+ (propCount + 1) * scm.border();
+
+ QRect fitRect = QRect(0, 0,
+ iconsWidth, scm.rowHeight() - scm.border());
+ // Move to current index
+ fitRect.moveTop(option.rect.topLeft().y());
+ // Move to correct location.
+ if (option.direction == Qt::RightToLeft) {
+ fitRect.moveLeft(option.rect.topLeft().x());
+ } else {
+ fitRect.moveRight(option.rect.topRight().x());
+ }
- return QRect(x, y,
- iconsWidth,
- scm.rowHeight() - scm.border());
+ return fitRect;
}
QRect KisNodeDelegate::textRect(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
KisNodeViewColorScheme scm;
static QFont f;
static int minbearing = 1337 + 666; //can be 0 or negative, 2003 is less likely
if (minbearing == 2003 || f != option.font) {
f = option.font; //getting your bearings can be expensive, so we cache them
minbearing = option.fontMetrics.minLeftBearing() + option.fontMetrics.minRightBearing();
}
- const int decorationOffset =
- 2 * scm.border() +
- 2 * scm.decorationMargin() +
- scm.decorationSize();
+ const QRect decoRect = decorationClickRect(option, index);
+ const QRect iconRect = iconsRect(option, index);
- const int width =
- iconsRect(option, index).left() - option.rect.x() -
- scm.border() + minbearing - decorationOffset;
+ QRect rc = QRect((option.direction == Qt::RightToLeft) ? iconRect.topRight() : decoRect.topRight(),
+ (option.direction == Qt::RightToLeft) ? decoRect.bottomLeft() : iconRect.bottomLeft());
+ rc.adjust(-(scm.border()+minbearing), 0,
+ (scm.border()+minbearing), 0);
- return QRect(option.rect.x() - minbearing + decorationOffset,
- option.rect.y() + scm.border(),
- width,
- scm.rowHeight() - scm.border());
+ return rc;
}
void KisNodeDelegate::drawText(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
KisNodeViewColorScheme scm;
- const QRect rc = textRect(option, index)
- .adjusted(scm.textMargin(), 0, -scm.textMargin(), 0);
+ const QRect rc = textRect(option, index).adjusted(scm.textMargin(), 0,
+ -scm.textMargin(), 0);
QPen oldPen = p->pen();
const qreal oldOpacity = p->opacity(); // remember previous opacity
p->setPen(option.palette.color(QPalette::Active,QPalette::Text ));
-
-
if (!(option.state & QStyle::State_Enabled)) {
p->setOpacity(0.55);
}
-
const QString text = index.data(Qt::DisplayRole).toString();
- const QString elided = elidedText(p->fontMetrics(), rc.width(), Qt::ElideRight, text);
+ const QString elided = p->fontMetrics().elidedText(text, Qt::ElideRight, rc.width());
p->drawText(rc, Qt::AlignLeft | Qt::AlignVCenter, elided);
p->setPen(oldPen); // restore pen settings
p->setOpacity(oldOpacity);
}
QList<OptionalProperty> KisNodeDelegate::Private::rightmostProperties(const KisBaseNode::PropertyList &props) const
{
QList<OptionalProperty> list;
QList<OptionalProperty> prependList;
list << OptionalProperty(0);
list << OptionalProperty(0);
list << OptionalProperty(0);
KisBaseNode::PropertyList::const_iterator it = props.constBegin();
KisBaseNode::PropertyList::const_iterator end = props.constEnd();
for (; it != end; ++it) {
if (!it->isMutable) continue;
if (it->id == KisLayerPropertiesIcons::visible.id()) {
// noop...
} else if (it->id == KisLayerPropertiesIcons::locked.id()) {
list[0] = OptionalProperty(&(*it));
} else if (it->id == KisLayerPropertiesIcons::inheritAlpha.id()) {
list[1] = OptionalProperty(&(*it));
} else if (it->id == KisLayerPropertiesIcons::alphaLocked.id()) {
list[2] = OptionalProperty(&(*it));
} else {
prependList.prepend(OptionalProperty(&(*it)));
}
}
{
QMutableListIterator<OptionalProperty> i(prependList);
i.toBack();
while (i.hasPrevious()) {
OptionalProperty val = i.previous();
int emptyIndex = list.lastIndexOf(0);
if (emptyIndex < 0) break;
list[emptyIndex] = val;
i.remove();
}
}
return prependList + list;
}
int KisNodeDelegate::Private::numProperties(const QModelIndex &index) const
{
KisBaseNode::PropertyList props = index.data(KisNodeModel::PropertiesRole).value<KisBaseNode::PropertyList>();
QList<OptionalProperty> realProps = rightmostProperties(props);
return realProps.size();
}
OptionalProperty KisNodeDelegate::Private::findProperty(KisBaseNode::PropertyList &props, const OptionalProperty &refProp) const
{
KisBaseNode::PropertyList::iterator it = props.begin();
KisBaseNode::PropertyList::iterator end = props.end();
for (; it != end; ++it) {
if (it->id == refProp->id) {
return &(*it);
}
}
return 0;
}
OptionalProperty KisNodeDelegate::Private::findVisibilityProperty(KisBaseNode::PropertyList &props) const
{
KisBaseNode::PropertyList::iterator it = props.begin();
KisBaseNode::PropertyList::iterator end = props.end();
for (; it != end; ++it) {
if (it->id == KisLayerPropertiesIcons::visible.id()) {
return &(*it);
}
}
return 0;
}
void KisNodeDelegate::drawIcons(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
KisNodeViewColorScheme scm;
- const QRect r = iconsRect(option, index);
+ const QRect rc = iconsRect(option, index);
QTransform oldTransform = p->transform();
QPen oldPen = p->pen();
- p->setTransform(QTransform::fromTranslate(r.x(), r.y()));
+ p->setTransform(QTransform::fromTranslate(rc.x(), rc.y()));
p->setPen(scm.gridColor(option, d->view));
int x = 0;
const int y = (scm.rowHeight() - scm.border() - scm.iconSize()) / 2;
KisBaseNode::PropertyList props = index.data(KisNodeModel::PropertiesRole).value<KisBaseNode::PropertyList>();
QList<OptionalProperty> realProps = d->rightmostProperties(props);
+ if (option.direction == Qt::RightToLeft) {
+ std::reverse(realProps.begin(), realProps.end());
+ }
+
Q_FOREACH (OptionalProperty prop, realProps) {
+ if (option.direction == Qt::LeftToRight)
+ p->drawLine(x, 0, x, scm.rowHeight() - scm.border());
+
x += scm.iconMargin();
if (prop) {
QIcon icon = prop->state.toBool() ? prop->onIcon : prop->offIcon;
bool fullColor = prop->state.toBool() && option.state & QStyle::State_Enabled;
- const qreal oldOpacity = p->opacity(); // remember previous opacity
-
+ const qreal oldOpacity = p->opacity(); // remember previous opacity
if (fullColor) {
p->setOpacity(1.0);
}
else {
p->setOpacity(0.35);
}
p->drawPixmap(x, y, icon.pixmap(scm.iconSize(), QIcon::Normal));
p->setOpacity(oldOpacity); // restore old opacity
-
-
}
x += scm.iconSize() + scm.iconMargin();
- p->drawLine(x, 0, x, scm.rowHeight() - scm.border());
+
+ if (!(option.direction == Qt::LeftToRight))
+ p->drawLine(x, 0, x, scm.rowHeight() - scm.border());
x += scm.border();
}
p->setTransform(oldTransform);
p->setPen(oldPen);
}
QRect KisNodeDelegate::visibilityClickRect(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(index);
KisNodeViewColorScheme scm;
- return QRect(scm.border(), scm.border() + option.rect.top(),
- 2 * scm.visibilityMargin() + scm.visibilitySize(),
- scm.rowHeight() - scm.border());
+
+ QRect rc = scm.relVisibilityRect();
+ rc.setHeight(scm.rowHeight());
+
+ // Move to current index
+ rc.moveCenter(option.rect.center());
+ // Move to correct location.
+ if (option.direction == Qt::RightToLeft) {
+ // HACK: Without the -5, the right edge is outside the view
+ rc.moveRight(d->view->width()-5);
+ } else {
+ rc.moveLeft(0);
+ }
+
+ return rc;
}
QRect KisNodeDelegate::decorationClickRect(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
- Q_UNUSED(option);
Q_UNUSED(index);
KisNodeViewColorScheme scm;
- QRect realVisualRect = d->view->originalVisualRect(index);
+ QRect rc = scm.relDecorationRect();
+
+ // Move to current index
+ rc.moveTop(option.rect.topLeft().y());
+ rc.setHeight(scm.rowHeight());
+ // Move to correct location.
+ if (option.direction == Qt::RightToLeft) {
+ rc.moveRight(option.rect.topRight().x());
+ } else {
+ rc.moveLeft(option.rect.topLeft().x());
+ }
- return QRect(realVisualRect.left(), scm.border() + realVisualRect.top(),
- 2 * scm.decorationMargin() + scm.decorationSize(),
- scm.rowHeight() - scm.border());
+ return rc;
}
void KisNodeDelegate::drawVisibilityIconHijack(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
/**
* Small hack Alert:
*
* Here wepaint over the area that sits basically outside our layer's
* row. Anyway, just update it later...
*/
KisNodeViewColorScheme scm;
KisBaseNode::PropertyList props = index.data(KisNodeModel::PropertiesRole).value<KisBaseNode::PropertyList>();
OptionalProperty prop = d->findVisibilityProperty(props);
if (!prop) return;
- const int x = scm.border() + scm.visibilityMargin();
- const int y = option.rect.top() + (scm.rowHeight() - scm.border() - scm.visibilitySize()) / 2;
+ QRect fitRect = visibilityClickRect(option, index);
+ // Shrink to icon rect
+ fitRect = kisGrowRect(fitRect, -(scm.visibilityMargin()+scm.border()));
QIcon icon = prop->state.toBool() ? prop->onIcon : prop->offIcon;
// if we are not showing the layer, make the icon slightly transparent like other inactive icons
const qreal oldOpacity = p->opacity();
- if (prop->state.toBool() == true) {
- p->setOpacity(1.0);
- }
- else {
+
+ if (!prop->state.toBool()) {
p->setOpacity(0.35);
}
- p->drawPixmap(x, y, icon.pixmap(scm.visibilitySize(), QIcon::Normal));
+ p->drawPixmap(fitRect.x(), fitRect.center().y() - scm.visibilitySize() / 2,
+ icon.pixmap(scm.visibilitySize(), QIcon::Normal));
p->setOpacity(oldOpacity);
//// For debugging purposes only
- // p->save();
- // p->setPen(Qt::blue);
- // KritaUtils::renderExactRect(p, visibilityClickRect(option, index));
- // p->restore();
+// // // p->save();
+// // // p->setPen(Qt::blue);
+// // // KritaUtils::renderExactRect(p, visibilityClickRect(option, index));
+// // // p->restore();
}
void KisNodeDelegate::drawDecoration(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
KisNodeViewColorScheme scm;
QIcon icon = index.data(Qt::DecorationRole).value<QIcon>();
if (!icon.isNull()) {
- QPixmap pixmap =
- icon.pixmap(scm.decorationSize(),
- (option.state & QStyle::State_Enabled) ?
- QIcon::Normal : QIcon::Disabled);
+ QPixmap pixmap = icon.pixmap(scm.decorationSize(),
+ (option.state & QStyle::State_Enabled) ?
+ QIcon::Normal : QIcon::Disabled);
+
+ QRect rc = decorationClickRect(option, index);
+
+ // Shrink to icon rect
+ rc = kisGrowRect(rc, -(scm.decorationMargin()+scm.border()));
- const QRect rc = scm.relDecorationRect().translated(option.rect.topLeft());
const qreal oldOpacity = p->opacity(); // remember previous opacity
if (!(option.state & QStyle::State_Enabled)) {
p->setOpacity(0.35);
}
-
- p->drawPixmap(rc.topLeft(), pixmap);
+ p->drawPixmap(rc.topLeft()-QPoint(0, 1), pixmap);
p->setOpacity(oldOpacity); // restore old opacity
}
}
void KisNodeDelegate::drawExpandButton(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(index);
KisNodeViewColorScheme scm;
- QRect rc = scm.relExpandButtonRect().translated(option.rect.topLeft());
- rc = kisGrowRect(rc, 0);
- if (!(option.state & QStyle::State_Children)) {
- return;
- }
+ QRect rc = decorationClickRect(option, index);
+
+ // Move to current index
+// rc.moveTop(option.rect.topLeft().y());
+ // Shrink to icon rect
+ rc = kisGrowRect(rc, -(scm.decorationMargin()+scm.border()));
+
+ if (!(option.state & QStyle::State_Children)) return;
+
- QString iconName = option.state & QStyle::State_Open ? "arrow-down" : "arrow-right";
+ QString iconName = option.state & QStyle::State_Open ?
+ "arrow-down" : ((option.direction == Qt::RightToLeft) ? "arrow-left" : "arrow-right");
QIcon icon = KisIconUtils::loadIcon(iconName);
QPixmap pixmap = icon.pixmap(rc.width(),
(option.state & QStyle::State_Enabled) ?
QIcon::Normal : QIcon::Disabled);
- p->drawPixmap(rc.topLeft(), pixmap);
+ p->drawPixmap(rc.bottomLeft()-QPoint(0, scm.decorationSize()-1), pixmap);
}
void KisNodeDelegate::Private::toggleProperty(KisBaseNode::PropertyList &props, OptionalProperty clickedProperty, bool controlPressed, const QModelIndex &index)
{
QAbstractItemModel *model = view->model();
// Using Ctrl+click to enter stasis
if (controlPressed && clickedProperty->canHaveStasis) {
// STEP 0: Prepare to Enter or Leave control key stasis
quint16 numberOfLeaves = model->rowCount(index.parent());
QModelIndex eachItem;
// STEP 1: Go.
if (clickedProperty->isInStasis == false) { // Enter
/* Make every leaf of this node go State = False, saving the old property value to stateInStasis */
for (quint16 i = 0; i < numberOfLeaves; ++i) { // Foreach leaf in the node (index.parent())
eachItem = model->index(i, 1, index.parent());
// The entire property list has to be altered because model->setData cannot set individual properties
KisBaseNode::PropertyList eachPropertyList = eachItem.data(KisNodeModel::PropertiesRole).value<KisBaseNode::PropertyList>();
OptionalProperty prop = findProperty(eachPropertyList, clickedProperty);
if (!prop) continue;
prop->stateInStasis = prop->state.toBool();
prop->state = eachItem == index;
prop->isInStasis = true;
model->setData(eachItem, QVariant::fromValue(eachPropertyList), KisNodeModel::PropertiesRole);
}
for (quint16 i = 0; i < numberOfLeaves; ++i) { // Foreach leaf in the node (index.parent())
eachItem = model->index(i, 1, index.parent());
KisBaseNode::PropertyList eachPropertyList = eachItem.data(KisNodeModel::PropertiesRole).value<KisBaseNode::PropertyList>();
OptionalProperty prop = findProperty(eachPropertyList, clickedProperty);
if (!prop) continue;
}
} else { // Leave
/* Make every leaf of this node go State = stateInStasis */
for (quint16 i = 0; i < numberOfLeaves; ++i) {
eachItem = model->index(i, 1, index.parent());
// The entire property list has to be altered because model->setData cannot set individual properties
KisBaseNode::PropertyList eachPropertyList = eachItem.data(KisNodeModel::PropertiesRole).value<KisBaseNode::PropertyList>();
OptionalProperty prop = findProperty(eachPropertyList, clickedProperty);
if (!prop) continue;
prop->state = prop->stateInStasis;
prop->isInStasis = false;
model->setData(eachItem, QVariant::fromValue(eachPropertyList), KisNodeModel::PropertiesRole);
}
}
} else {
clickedProperty->state = !clickedProperty->state.toBool();
model->setData(index, QVariant::fromValue(props), KisNodeModel::PropertiesRole);
}
}
bool KisNodeDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
{
KisNodeViewColorScheme scm;
+ QStyleOptionViewItem newOption = option;
+ newOption.rect = d->view->originalVisualRect(index);
+
if ((event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick)
&& (index.flags() & Qt::ItemIsEnabled))
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
/**
* Small hack Alert:
*
* Here we handle clicking even when it happened outside
* the rectangle of the current index. The point is, we
* use some virtual scroling offset to move the tree to the
* right of the visibility icon. So the icon itself is placed
* in an empty area that doesn't belong to any index. But we still
* handle it.
*/
- const QRect iconsRect = this->iconsRect(option, index);
- const bool iconsClicked = iconsRect.isValid() &&
- iconsRect.contains(mouseEvent->pos());
-
- const QRect visibilityRect = visibilityClickRect(option, index);
+ const QRect visibilityRect = visibilityClickRect(newOption, index);
const bool visibilityClicked = visibilityRect.isValid() &&
visibilityRect.contains(mouseEvent->pos());
- const QRect decorationRect = decorationClickRect(option, index);
+ const QRect thumbnailRect = thumbnailClickRect(newOption, index);
+ const bool thumbnailClicked = thumbnailRect.isValid() &&
+ thumbnailRect.contains(mouseEvent->pos());
+
+ const QRect decorationRect = decorationClickRect(newOption, index);
const bool decorationClicked = decorationRect.isValid() &&
decorationRect.contains(mouseEvent->pos());
- const bool leftButton = mouseEvent->buttons() & Qt::LeftButton;
+ const QRect iconsRect = this->iconsRect(newOption, index);
+ const bool iconsClicked = iconsRect.isValid() &&
+ iconsRect.contains(mouseEvent->pos());
- const QRect thumbnailRect = thumbnailClickRect(option, index);
- const bool thumbnailClicked = thumbnailRect.contains(mouseEvent->pos());
+ const bool leftButton = mouseEvent->buttons() & Qt::LeftButton;
if (leftButton && iconsClicked) {
KisBaseNode::PropertyList props = index.data(KisNodeModel::PropertiesRole).value<KisBaseNode::PropertyList>();
QList<OptionalProperty> realProps = d->rightmostProperties(props);
+ if (newOption.direction == Qt::RightToLeft) {
+ std::reverse(realProps.begin(), realProps.end());
+ }
const int numProps = realProps.size();
const int iconWidth = scm.iconSize() + 2 * scm.iconMargin() + scm.border();
const int xPos = mouseEvent->pos().x() - iconsRect.left();
const int clickedIcon = xPos / iconWidth;
const int distToBorder = qMin(xPos % iconWidth, iconWidth - xPos % iconWidth);
if (iconsClicked &&
clickedIcon >= 0 &&
clickedIcon < numProps &&
distToBorder > scm.iconMargin()) {
OptionalProperty clickedProperty = realProps[clickedIcon];
if (!clickedProperty) return false;
d->toggleProperty(props, clickedProperty, mouseEvent->modifiers() == Qt::ControlModifier, index);
return true;
}
} else if (leftButton && visibilityClicked) {
KisBaseNode::PropertyList props = index.data(KisNodeModel::PropertiesRole).value<KisBaseNode::PropertyList>();
OptionalProperty clickedProperty = d->findVisibilityProperty(props);
if (!clickedProperty) return false;
d->toggleProperty(props, clickedProperty, mouseEvent->modifiers() == Qt::ControlModifier, index);
return true;
} else if (leftButton && decorationClicked) {
bool isExpandable = model->hasChildren(index);
if (isExpandable) {
bool isExpanded = d->view->isExpanded(index);
d->view->setExpanded(index, !isExpanded);
}
return true;
} else if (leftButton && thumbnailClicked) {
bool hasCorrectModifier = false;
SelectionAction action = SELECTION_REPLACE;
if (mouseEvent->modifiers() == Qt::ControlModifier) {
action = SELECTION_REPLACE;
hasCorrectModifier = true;
} else if (mouseEvent->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) {
action = SELECTION_ADD;
hasCorrectModifier = true;
} else if (mouseEvent->modifiers() == (Qt::ControlModifier | Qt::AltModifier)) {
action = SELECTION_SUBTRACT;
hasCorrectModifier = true;
} else if (mouseEvent->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier | Qt::AltModifier)) {
action = SELECTION_INTERSECT;
hasCorrectModifier = true;
}
if (hasCorrectModifier) {
model->setData(index, QVariant(int(action)), KisNodeModel::SelectOpaqueRole);
- return true;
}
+ return true; //If not here then the item is !expanded when reaching return false;
}
if (mouseEvent->button() == Qt::LeftButton &&
mouseEvent->modifiers() == Qt::AltModifier) {
d->view->setCurrentIndex(index);
model->setData(index, true, KisNodeModel::AlternateActiveRole);
return true;
}
}
else if (event->type() == QEvent::ToolTip) {
if (!KisConfig(true).hidePopups()) {
QHelpEvent *helpEvent = static_cast<QHelpEvent*>(event);
- d->tip.showTip(d->view, helpEvent->pos(), option, index);
+ d->tip.showTip(d->view, helpEvent->pos(), newOption, index);
}
return true;
} else if (event->type() == QEvent::Leave) {
d->tip.hide();
}
return false;
}
QWidget *KisNodeDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem&, const QModelIndex&) const
{
d->edit = new QLineEdit(parent);
d->edit->setFocusPolicy(Qt::StrongFocus);
d->edit->installEventFilter(const_cast<KisNodeDelegate*>(this)); //hack?
return d->edit;
}
void KisNodeDelegate::setEditorData(QWidget *widget, const QModelIndex &index) const
{
QLineEdit *edit = qobject_cast<QLineEdit*>(widget);
Q_ASSERT(edit);
edit->setText(index.data(Qt::DisplayRole).toString());
}
void KisNodeDelegate::setModelData(QWidget *widget, QAbstractItemModel *model, const QModelIndex &index) const
{
QLineEdit *edit = qobject_cast<QLineEdit*>(widget);
Q_ASSERT(edit);
model->setData(index, edit->text(), Qt::DisplayRole);
}
void KisNodeDelegate::updateEditorGeometry(QWidget *widget, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(index);
widget->setGeometry(option.rect);
}
// PROTECTED
bool KisNodeDelegate::eventFilter(QObject *object, QEvent *event)
{
switch (event->type()) {
case QEvent::MouseButtonPress: {
if (d->edit) {
QMouseEvent *me = static_cast<QMouseEvent*>(event);
if (!QRect(d->edit->mapToGlobal(QPoint()), d->edit->size()).contains(me->globalPos())) {
emit commitData(d->edit);
emit closeEditor(d->edit);
}
}
} break;
case QEvent::KeyPress: {
QLineEdit *edit = qobject_cast<QLineEdit*>(object);
if (edit && edit == d->edit) {
QKeyEvent *ke = static_cast<QKeyEvent*>(event);
switch (ke->key()) {
case Qt::Key_Escape:
emit closeEditor(edit);
return true;
case Qt::Key_Tab:
emit commitData(edit);
emit closeEditor(edit, EditNextItem);
return true;
case Qt::Key_Backtab:
emit commitData(edit);
emit closeEditor(edit, EditPreviousItem);
return true;
case Qt::Key_Return:
case Qt::Key_Enter:
emit commitData(edit);
emit closeEditor(edit);
return true;
default: break;
}
}
} break;
case QEvent::ShortcutOverride : {
QLineEdit *edit = qobject_cast<QLineEdit*>(object);
if (edit && edit == d->edit){
auto* key = static_cast<QKeyEvent*>(event);
if (key->modifiers() == Qt::NoModifier){
switch (key->key()){
case Qt::Key_Escape:
case Qt::Key_Tab:
case Qt::Key_Backtab:
case Qt::Key_Return:
case Qt::Key_Enter:
event->accept();
return true;
default: break;
}
}
}
} break;
case QEvent::FocusOut : {
QLineEdit *edit = qobject_cast<QLineEdit*>(object);
if (edit && edit == d->edit) {
emit commitData(edit);
emit closeEditor(edit);
}
}
default: break;
}
return QAbstractItemDelegate::eventFilter(object, event);
}
// PRIVATE
QStyleOptionViewItem KisNodeDelegate::getOptions(const QStyleOptionViewItem &o, const QModelIndex &index)
{
QStyleOptionViewItem option = o;
QVariant v = index.data(Qt::FontRole);
if (v.isValid()) {
option.font = v.value<QFont>();
option.fontMetrics = QFontMetrics(option.font);
}
v = index.data(Qt::TextAlignmentRole);
if (v.isValid())
option.displayAlignment = QFlag(v.toInt());
v = index.data(Qt::TextColorRole);
if (v.isValid())
option.palette.setColor(QPalette::Text, v.value<QColor>());
v = index.data(Qt::BackgroundColorRole);
if (v.isValid())
option.palette.setColor(QPalette::Window, v.value<QColor>());
return option;
}
void KisNodeDelegate::drawProgressBar(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QVariant value = index.data(KisNodeModel::ProgressRole);
if (!value.isNull() && (value.toInt() >= 0 && value.toInt() <= 100)) {
/// The progress bar will display under the layer name area. The bars have accurate data, so we
/// probably don't need to also show the actual number for % complete
KisNodeViewColorScheme scm;
- const int width = textRect(option, index).width() + scm.iconSize()*2;
+
+ const QRect thumbnailRect = thumbnailClickRect(option, index);
+ const QRect iconsRectR = iconsRect(option, index);
const int height = 5;
- const QPoint base = option.rect.bottomLeft() - QPoint(0, height );
- const QRect r = QRect(base.x(), base.y(), width, height);
+ const QRect rc = QRect(
+ ((option.direction == Qt::RightToLeft) ? iconsRectR.bottomRight()
+ : thumbnailRect.bottomRight()) - QPoint(0, height),
+ ((option.direction == Qt::RightToLeft) ? thumbnailRect.bottomLeft()
+ : iconsRectR.bottomLeft()));
p->save();
{
- p->setClipRect(r);
+ p->setClipRect(rc);
QStyle* style = QApplication::style();
QStyleOptionProgressBar opt;
opt.minimum = 0;
opt.maximum = 100;
opt.progress = value.toInt();
opt.textVisible = false;
opt.textAlignment = Qt::AlignHCenter;
opt.text = i18n("%1 %", opt.progress);
- opt.rect = r;
opt.orientation = Qt::Horizontal;
opt.state = option.state;
style->drawControl(QStyle::CE_ProgressBar, &opt, p, 0);
}
p->restore();
}
}
void KisNodeDelegate::slotConfigChanged()
{
KisConfig cfg(true);
d->checkersColor1 = cfg.checkersColor1();
d->checkersColor2 = cfg.checkersColor2();
}
void KisNodeDelegate::slotUpdateIcon()
{
KisLayerPropertiesIcons::instance()->updateIcons();
}
diff --git a/libs/ui/KisNodeDelegate.h b/libs/ui/KisNodeDelegate.h
index ad53f443e1..048eee631e 100644
--- a/libs/ui/KisNodeDelegate.h
+++ b/libs/ui/KisNodeDelegate.h
@@ -1,85 +1,89 @@
/*
Copyright (c) 2006 Gábor Lehel <illissius@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KIS_DOCUMENT_SECTION_DELEGATE_H
#define KIS_DOCUMENT_SECTION_DELEGATE_H
#include <QAbstractItemDelegate>
class KisNodeView;
class KisNodeModel;
/**
* See KisNodeModel and KisNodeView.
*
* A delegate provides the gui machinery, using Qt's model/view terminology.
* This class is owned by KisNodeView to do the work of generating the
* graphical representation of each item.
*/
class KisNodeDelegate: public QAbstractItemDelegate
{
Q_OBJECT
public:
explicit KisNodeDelegate(KisNodeView *view, QObject *parent = 0);
~KisNodeDelegate() override;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex& index) const override;
void slotUpdateIcon();
protected:
bool eventFilter(QObject *object, QEvent *event) override;
private:
typedef KisNodeModel Model;
typedef KisNodeView View;
class Private;
Private* const d;
static QStyleOptionViewItem getOptions(const QStyleOptionViewItem &option, const QModelIndex &index);
void drawProgressBar(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawBranch(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawColorLabel(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawFrame(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const;
+
+ QRect thumbnailClickRect(const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawThumbnail(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const;
+
QRect iconsRect(const QStyleOptionViewItem &option, const QModelIndex &index) const;
QRect textRect(const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawText(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawIcons(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const;
+
QRect visibilityClickRect(const QStyleOptionViewItem &option, const QModelIndex &index) const;
- QRect decorationClickRect(const QStyleOptionViewItem &option, const QModelIndex &index) const;
- QRect thumbnailClickRect(const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawVisibilityIconHijack(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const;
+
+ QRect decorationClickRect(const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawDecoration(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawExpandButton(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const;
private Q_SLOTS:
void slotConfigChanged();
};
#endif
diff --git a/libs/ui/KisNodeView.cpp b/libs/ui/KisNodeView.cpp
index af0dcb65d6..26a1ad4aaa 100644
--- a/libs/ui/KisNodeView.cpp
+++ b/libs/ui/KisNodeView.cpp
@@ -1,586 +1,592 @@
/*
Copyright (c) 2006 Gábor Lehel <illissius@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "KisNodeView.h"
#include "KisNodePropertyAction_p.h"
#include "KisNodeDelegate.h"
#include "kis_node_view_visibility_delegate.h"
#include "kis_node_model.h"
#include "kis_signals_blocker.h"
#include <kconfig.h>
#include <kconfiggroup.h>
#include <kis_icon.h>
#include <ksharedconfig.h>
#include <KisKineticScroller.h>
#include <QtDebug>
#include <QContextMenuEvent>
#include <QHeaderView>
#include <QHelpEvent>
#include <QMenu>
#include <QDrag>
#include <QMouseEvent>
#include <QPersistentModelIndex>
#include <QApplication>
#include <QPainter>
#include <QScrollBar>
#include <QScroller>
#include "kis_node_view_color_scheme.h"
#ifdef HAVE_X11
#define DRAG_WHILE_DRAG_WORKAROUND
#endif
#ifdef DRAG_WHILE_DRAG_WORKAROUND
#define DRAG_WHILE_DRAG_WORKAROUND_START() d->isDragging = true
#define DRAG_WHILE_DRAG_WORKAROUND_STOP() d->isDragging = false
#else
#define DRAG_WHILE_DRAG_WORKAROUND_START()
#define DRAG_WHILE_DRAG_WORKAROUND_STOP()
#endif
class Q_DECL_HIDDEN KisNodeView::Private
{
public:
Private(KisNodeView* _q)
: delegate(_q, _q)
, mode(DetailedMode)
#ifdef DRAG_WHILE_DRAG_WORKAROUND
, isDragging(false)
#endif
{
KSharedConfigPtr config = KSharedConfig::openConfig();
KConfigGroup group = config->group("NodeView");
mode = (DisplayMode) group.readEntry("NodeViewMode", (int)MinimalMode);
}
KisNodeDelegate delegate;
DisplayMode mode;
QPersistentModelIndex hovered;
QPoint lastPos;
#ifdef DRAG_WHILE_DRAG_WORKAROUND
bool isDragging;
#endif
};
KisNodeView::KisNodeView(QWidget *parent)
: QTreeView(parent)
, m_draggingFlag(false)
, d(new Private(this))
{
setItemDelegateForColumn(0, &d->delegate);
setMouseTracking(true);
setSelectionBehavior(SelectRows);
setDefaultDropAction(Qt::MoveAction);
setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
setSelectionMode(QAbstractItemView::ExtendedSelection);
header()->hide();
setDragEnabled(true);
setDragDropMode(QAbstractItemView::DragDrop);
setAcceptDrops(true);
setDropIndicatorShown(true);
{
QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(this);
if (scroller) {
connect(scroller, SIGNAL(stateChanged(QScroller::State)),
this, SLOT(slotScrollerStateChanged(QScroller::State)));
}
}
}
KisNodeView::~KisNodeView()
{
delete d;
}
void KisNodeView::setDisplayMode(DisplayMode mode)
{
if (d->mode != mode) {
d->mode = mode;
KSharedConfigPtr config = KSharedConfig::openConfig();
KConfigGroup group = config->group("NodeView");
group.writeEntry("NodeViewMode", (int)mode);
scheduleDelayedItemsLayout();
}
}
KisNodeView::DisplayMode KisNodeView::displayMode() const
{
return d->mode;
}
void KisNodeView::addPropertyActions(QMenu *menu, const QModelIndex &index)
{
KisBaseNode::PropertyList list = index.data(KisNodeModel::PropertiesRole).value<KisBaseNode::PropertyList>();
for (int i = 0, n = list.count(); i < n; ++i) {
if (list.at(i).isMutable) {
PropertyAction *a = new PropertyAction(i, list.at(i), index, menu);
connect(a, SIGNAL(toggled(bool,QPersistentModelIndex,int)),
this, SLOT(slotActionToggled(bool,QPersistentModelIndex,int)));
menu->addAction(a);
}
}
}
void KisNodeView::updateNode(const QModelIndex &index)
{
dataChanged(index, index);
}
QItemSelectionModel::SelectionFlags KisNodeView::selectionCommand(const QModelIndex &index,
const QEvent *event) const
{
/**
* Qt has a bug: when we Ctrl+click on an item, the item's
* selections gets toggled on mouse *press*, whereas usually it is
* done on mouse *release*. Therefore the user cannot do a
* Ctrl+D&D with the default configuration. This code fixes the
* problem by manually returning QItemSelectionModel::NoUpdate
* flag when the user clicks on an item and returning
* QItemSelectionModel::Toggle on release.
*/
if (event &&
(event->type() == QEvent::MouseButtonPress ||
event->type() == QEvent::MouseButtonRelease) &&
index.isValid()) {
const QMouseEvent *mevent = static_cast<const QMouseEvent*>(event);
if (mevent->button() == Qt::RightButton &&
selectionModel()->selectedIndexes().contains(index)) {
// Allow calling context menu for multiple layers
return QItemSelectionModel::NoUpdate;
}
if (event->type() == QEvent::MouseButtonPress &&
(mevent->modifiers() & Qt::ControlModifier)) {
return QItemSelectionModel::NoUpdate;
}
if (event->type() == QEvent::MouseButtonRelease &&
(mevent->modifiers() & Qt::ControlModifier)) {
return QItemSelectionModel::Toggle;
}
}
/**
* Qt 5.6 has a bug: it reads global modifiers, not the ones
* passed from event. So if you paste an item using Ctrl+V it'll
* select multiple layers for you
*/
Qt::KeyboardModifiers globalModifiers = QApplication::keyboardModifiers();
if (!event && globalModifiers != Qt::NoModifier) {
return QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows;
}
return QAbstractItemView::selectionCommand(index, event);
}
+
QRect KisNodeView::visualRect(const QModelIndex &index) const
{
QRect rc = QTreeView::visualRect(index);
- rc.setLeft(0);
+ if (layoutDirection() == Qt::RightToLeft)
+ rc.setRight(width());
+ else
+ rc.setLeft(0);
return rc;
}
QRect KisNodeView::originalVisualRect(const QModelIndex &index) const
{
return QTreeView::visualRect(index);
}
QModelIndex KisNodeView::indexAt(const QPoint &point) const
{
KisNodeViewColorScheme scm;
QModelIndex index = QTreeView::indexAt(point);
- if (!index.isValid() && point.x() < scm.visibilityColumnWidth()) {
- index = QTreeView::indexAt(point + QPoint(scm.visibilityColumnWidth(), 0));
+ if (!index.isValid()) {
+ // Middle is a good position for both LTR and RTL layouts
+ // First reset x, then get the x in the middle
+ index = QTreeView::indexAt(point - QPoint(point.x(), 0) + QPoint(width() / 2, 0));
}
return index;
}
bool KisNodeView::viewportEvent(QEvent *e)
{
if (model()) {
switch(e->type()) {
case QEvent::MouseButtonPress: {
DRAG_WHILE_DRAG_WORKAROUND_STOP();
const QPoint pos = static_cast<QMouseEvent*>(e)->pos();
d->lastPos = pos;
if (!indexAt(pos).isValid()) {
return QTreeView::viewportEvent(e);
}
QModelIndex index = model()->buddy(indexAt(pos));
if (d->delegate.editorEvent(e, model(), optionForIndex(index), index)) {
return true;
}
} break;
case QEvent::Leave: {
QEvent e(QEvent::Leave);
d->delegate.editorEvent(&e, model(), optionForIndex(d->hovered), d->hovered);
d->hovered = QModelIndex();
} break;
case QEvent::MouseMove: {
#ifdef DRAG_WHILE_DRAG_WORKAROUND
if (d->isDragging) {
return false;
}
#endif
const QPoint pos = static_cast<QMouseEvent*>(e)->pos();
QModelIndex hovered = indexAt(pos);
if (hovered != d->hovered) {
if (d->hovered.isValid()) {
QEvent e(QEvent::Leave);
d->delegate.editorEvent(&e, model(), optionForIndex(d->hovered), d->hovered);
}
if (hovered.isValid()) {
QEvent e(QEvent::Enter);
d->delegate.editorEvent(&e, model(), optionForIndex(hovered), hovered);
}
d->hovered = hovered;
}
/* This is a workaround for a bug in QTreeView that immediately begins a dragging action
when the mouse lands on the decoration/icon of a different index and moves 1 pixel or more */
Qt::MouseButtons buttons = static_cast<QMouseEvent*>(e)->buttons();
if ((Qt::LeftButton | Qt::MidButton) & buttons) {
if ((pos - d->lastPos).manhattanLength() > qApp->startDragDistance()) {
return QTreeView::viewportEvent(e);
}
return true;
}
} break;
case QEvent::ToolTip: {
const QPoint pos = static_cast<QHelpEvent*>(e)->pos();
if (!indexAt(pos).isValid()) {
return QTreeView::viewportEvent(e);
}
QModelIndex index = model()->buddy(indexAt(pos));
return d->delegate.editorEvent(e, model(), optionForIndex(index), index);
} break;
case QEvent::Resize: {
scheduleDelayedItemsLayout();
break;
}
default: break;
}
}
return QTreeView::viewportEvent(e);
}
void KisNodeView::contextMenuEvent(QContextMenuEvent *e)
{
QTreeView::contextMenuEvent(e);
QModelIndex i = indexAt(e->pos());
if (model())
i = model()->buddy(i);
showContextMenu(e->globalPos(), i);
}
void KisNodeView::showContextMenu(const QPoint &globalPos, const QModelIndex &index)
{
emit contextMenuRequested(globalPos, index);
}
void KisNodeView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
{
QTreeView::currentChanged(current, previous);
if (current != previous) {
Q_ASSERT(!current.isValid() || current.model() == model());
model()->setData(current, true, KisNodeModel::ActiveRole);
}
}
void KisNodeView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &/*roles*/)
{
QTreeView::dataChanged(topLeft, bottomRight);
for (int x = topLeft.row(); x <= bottomRight.row(); ++x) {
for (int y = topLeft.column(); y <= bottomRight.column(); ++y) {
QModelIndex index = topLeft.sibling(x, y);
if (index.data(KisNodeModel::ActiveRole).toBool()) {
if (currentIndex() != index) {
setCurrentIndex(index);
}
return;
}
}
}
}
void KisNodeView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
{
QTreeView::selectionChanged(selected, deselected);
emit selectionChanged(selectedIndexes());
}
void KisNodeView::slotActionToggled(bool on, const QPersistentModelIndex &index, int num)
{
KisBaseNode::PropertyList list = index.data(KisNodeModel::PropertiesRole).value<KisBaseNode::PropertyList>();
list[num].state = on;
const_cast<QAbstractItemModel*>(index.model())->setData(index, QVariant::fromValue(list), KisNodeModel::PropertiesRole);
}
QStyleOptionViewItem KisNodeView::optionForIndex(const QModelIndex &index) const
{
QStyleOptionViewItem option = viewOptions();
option.rect = visualRect(index);
if (index == currentIndex())
option.state |= QStyle::State_HasFocus;
return option;
}
void KisNodeView::startDrag(Qt::DropActions supportedActions)
{
DRAG_WHILE_DRAG_WORKAROUND_START();
if (displayMode() == KisNodeView::ThumbnailMode) {
const QModelIndexList indexes = selectionModel()->selectedIndexes();
if (!indexes.isEmpty()) {
QMimeData *data = model()->mimeData(indexes);
if (!data) {
return;
}
QDrag *drag = new QDrag(this);
drag->setPixmap(createDragPixmap());
drag->setMimeData(data);
//m_dragSource = this;
drag->exec(supportedActions);
}
}
else {
QTreeView::startDrag(supportedActions);
}
}
QPixmap KisNodeView::createDragPixmap() const
{
const QModelIndexList selectedIndexes = selectionModel()->selectedIndexes();
Q_ASSERT(!selectedIndexes.isEmpty());
const int itemCount = selectedIndexes.count();
// If more than one item is dragged, align the items inside a
// rectangular grid. The maximum grid size is limited to 4 x 4 items.
int xCount = 2;
int size = 96;
if (itemCount > 9) {
xCount = 4;
size = KisIconUtils::SizeLarge;
}
else if (itemCount > 4) {
xCount = 3;
size = KisIconUtils::SizeHuge;
}
else if (itemCount < xCount) {
xCount = itemCount;
}
int yCount = itemCount / xCount;
if (itemCount % xCount != 0) {
++yCount;
}
if (yCount > xCount) {
yCount = xCount;
}
// Draw the selected items into the grid cells
QPixmap dragPixmap(xCount * size + xCount - 1, yCount * size + yCount - 1);
dragPixmap.fill(Qt::transparent);
QPainter painter(&dragPixmap);
int x = 0;
int y = 0;
Q_FOREACH (const QModelIndex &selectedIndex, selectedIndexes) {
const QImage img = selectedIndex.data(int(KisNodeModel::BeginThumbnailRole) + size).value<QImage>();
painter.drawPixmap(x, y, QPixmap().fromImage(img.scaled(QSize(size, size), Qt::KeepAspectRatio, Qt::SmoothTransformation)));
x += size + 1;
if (x >= dragPixmap.width()) {
x = 0;
y += size + 1;
}
if (y >= dragPixmap.height()) {
break;
}
}
return dragPixmap;
}
void KisNodeView::resizeEvent(QResizeEvent * event)
{
KisNodeViewColorScheme scm;
header()->setStretchLastSection(false);
header()->setOffset(-scm.visibilityColumnWidth());
header()->resizeSection(0, event->size().width() - scm.visibilityColumnWidth());
setIndentation(scm.indentation());
QTreeView::resizeEvent(event);
}
void KisNodeView::paintEvent(QPaintEvent *event)
{
event->accept();
QTreeView::paintEvent(event);
// Paint the line where the slide should go
if (isDragging() && (displayMode() == KisNodeView::ThumbnailMode)) {
QSize size(visualRect(model()->index(0, 0, QModelIndex())).width(), visualRect(model()->index(0, 0, QModelIndex())).height());
int numberRow = cursorPageIndex();
int scrollBarValue = verticalScrollBar()->value();
QPoint point1(0, numberRow * size.height() - scrollBarValue);
QPoint point2(size.width(), numberRow * size.height() - scrollBarValue);
QLineF line(point1, point2);
QPainter painter(this->viewport());
QPen pen = QPen(palette().brush(QPalette::Highlight), 8);
pen.setCapStyle(Qt::RoundCap);
painter.setPen(pen);
painter.setOpacity(0.8);
painter.drawLine(line);
}
}
void KisNodeView::drawBranches(QPainter *painter, const QRect &rect,
const QModelIndex &index) const
{
Q_UNUSED(painter);
Q_UNUSED(rect);
Q_UNUSED(index);
/**
* Noop... Everything is going to be painted by KisNodeDelegate.
* So this override basically disables painting of Qt's branch-lines.
*/
}
void KisNodeView::dropEvent(QDropEvent *ev)
{
if (displayMode() == KisNodeView::ThumbnailMode) {
setDraggingFlag(false);
ev->accept();
clearSelection();
if (!model()) {
return;
}
int newIndex = cursorPageIndex();
model()->dropMimeData(ev->mimeData(), ev->dropAction(), newIndex, -1, QModelIndex());
return;
}
QTreeView::dropEvent(ev);
DRAG_WHILE_DRAG_WORKAROUND_STOP();
}
int KisNodeView::cursorPageIndex() const
{
QSize size(visualRect(model()->index(0, 0, QModelIndex())).width(), visualRect(model()->index(0, 0, QModelIndex())).height());
int scrollBarValue = verticalScrollBar()->value();
QPoint cursorPosition = QWidget::mapFromGlobal(QCursor::pos());
int numberRow = (cursorPosition.y() + scrollBarValue) / size.height();
//If cursor is at the half button of the page then the move action is performed after the slide, otherwise it is
//performed before the page
if (abs((cursorPosition.y() + scrollBarValue) - size.height()*numberRow) > (size.height()/2)) {
numberRow++;
}
if (numberRow > model()->rowCount(QModelIndex())) {
numberRow = model()->rowCount(QModelIndex());
}
return numberRow;
}
void KisNodeView::dragEnterEvent(QDragEnterEvent *ev)
{
DRAG_WHILE_DRAG_WORKAROUND_START();
QVariant data = qVariantFromValue(
static_cast<void*>(const_cast<QMimeData*>(ev->mimeData())));
model()->setData(QModelIndex(), data, KisNodeModel::DropEnabled);
QTreeView::dragEnterEvent(ev);
}
void KisNodeView::dragMoveEvent(QDragMoveEvent *ev)
{
DRAG_WHILE_DRAG_WORKAROUND_START();
if (displayMode() == KisNodeView::ThumbnailMode) {
ev->accept();
if (!model()) {
return;
}
QTreeView::dragMoveEvent(ev);
setDraggingFlag();
viewport()->update();
return;
}
QTreeView::dragMoveEvent(ev);
}
void KisNodeView::dragLeaveEvent(QDragLeaveEvent *e)
{
if (displayMode() == KisNodeView::ThumbnailMode) {
setDraggingFlag(false);
} else {
QTreeView::dragLeaveEvent(e);
}
DRAG_WHILE_DRAG_WORKAROUND_STOP();
}
bool KisNodeView::isDragging() const
{
return m_draggingFlag;
}
void KisNodeView::setDraggingFlag(bool flag)
{
m_draggingFlag = flag;
}
void KisNodeView::slotUpdateIcons()
{
d->delegate.slotUpdateIcon();
}
void KisNodeView::slotScrollerStateChanged(QScroller::State state){
KisKineticScroller::updateCursor(this, state);
}
diff --git a/libs/ui/KisOpenPane.h b/libs/ui/KisOpenPane.h
index 79c8d82aba..34e7bbd3b6 100644
--- a/libs/ui/KisOpenPane.h
+++ b/libs/ui/KisOpenPane.h
@@ -1,108 +1,109 @@
/* This file is part of the KDE project
Copyright (C) 2005 Peter Simonsson <psn@linux.se>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KISOPENPANE_H
#define KISOPENPANE_H
#include <QDialog>
#include <QWidget>
#include <QPixmap>
#include <QList>
class KisDetailsPane;
class KisDocument;
class KisOpenPanePrivate;
class KisTemplatesPane;
class QPixmap;
class QString;
class QStringList;
class QTreeWidgetItem;
class QUrl;
/// \internal
class KisOpenPane : public QDialog
{
Q_OBJECT
public:
/**
* Constructor
- * @param parent the parent widget
- * @param templateType the template-type (group) that should be selected on creation.
+ * @param parent the parent widget.
+ * @param mimeFilter the template-type (group) that should be selected on creation.
+ * @param templatesResourcePath the path to the templates.
*/
KisOpenPane(QWidget *parent, const QStringList& mimeFilter, const QString& templatesResourcePath = QString());
~KisOpenPane() override;
QTreeWidgetItem* addPane(const QString &title, const QString &iconName, QWidget *widget, int sortWeight);
QTreeWidgetItem* addPane(const QString& title, const QPixmap& icon, QWidget* widget, int sortWeight);
/**
* If the application has a way to create a document not based on a template, but on user
* provided settings, the widget showing these gets set here.
* @see KisDocument::createCustomDocumentWidget()
* @param widget the widget.
* @param title the title shown in the sidebar
* @param icon the icon shown in the sidebar
*/
void addCustomDocumentWidget(QWidget *widget, const QString& title = QString(), const QString& icon = QString());
Q_SIGNALS:
/// this signal is emitted (as defined by KisDocument) the moment the document is 'ready'
void documentSelected(KisDocument*);
protected Q_SLOTS:
void updateSelectedWidget();
void itemClicked(QTreeWidgetItem* item);
/// Saves the splitter sizes for KisDetailsPaneBase based panes
void saveSplitterSizes(KisDetailsPane* sender, const QList<int>& sizes);
private Q_SLOTS:
/// when clicked "Open Existing Document" button
void openFileDialog();
Q_SIGNALS:
void openExistingFile(const QUrl&);
void openTemplate(const QUrl&);
/// Emitted when the always use template has changed
void alwaysUseChanged(KisTemplatesPane* sender, const QString& alwaysUse);
/// Emitted when one of the detail panes have changed it's splitter
void splitterResized(KisDetailsPane* sender, const QList<int>& sizes);
void cancelButton();
protected:
/**
* Populate the list with all templates the user can choose.
* @param templatesResourcePath the template-type (group) that should be selected on creation.
*/
void initTemplates(const QString& templatesResourcePath);
// QWidget overrides
void dragEnterEvent(QDragEnterEvent * event) override;
void dropEvent(QDropEvent * event) override;
private:
QStringList m_mimeFilter;
KisOpenPanePrivate * const d;
};
#endif //KOOPENPANE_H
diff --git a/libs/ui/KisPaletteEditor.h b/libs/ui/KisPaletteEditor.h
index abda68e712..6573ff7fdd 100644
--- a/libs/ui/KisPaletteEditor.h
+++ b/libs/ui/KisPaletteEditor.h
@@ -1,148 +1,146 @@
/*
* Copyright (c) 2018 Michael Zhou <simeirxh@gmail.com>
*
* 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 KISPALETTEMANAGER_H
#define KISPALETTEMANAGER_H
#include <QObject>
#include <QScopedPointer>
#include <KisSwatch.h>
#include <kritaui_export.h>
class KoColorSet;
class KisPaletteModel;
class KisViewManager;
class KisSwatchGroup;
class KisViewManager;
/**
* @brief The PaletteEditor class
* this class manipulates a KisPaletteModel using GUI elements and communicate
* with KisDocument
*
* Changes made in this class won't be done to the palette if the palette is
* read only (not editable, isEditable() == false)
*/
class KRITAUI_EXPORT KisPaletteEditor : public QObject
{
Q_OBJECT
public:
struct PaletteInfo;
public:
explicit KisPaletteEditor(QObject *parent = Q_NULLPTR);
~KisPaletteEditor();
void setPaletteModel(KisPaletteModel *model);
void setView(KisViewManager *view);
void addPalette();
void importPalette();
void removePalette(KoColorSet *);
/**
* @brief rowNumberOfGroup
* @param oriName the original name of a group at the creation of the instance
* @return newest row number of the group
*/
int rowNumberOfGroup(const QString &oriName) const;
/**
* @brief oldNameFromNewName
* @param newName the current name of a group
* @return the name of the group at the creation of the instance
*/
QString oldNameFromNewName(const QString &newName) const;
/**
* @brief duplicateExistsFilename
- * @param name
+ * @param filename the name of the file
* @param global if this filename is going to be used for a global palette
* @return true if the a palette in the resource system that has filename
* name already exists else false
*/
bool duplicateExistsFilename(const QString &filename, bool global) const;
QString relativePathFromSaveLocation() const;
void rename(const QString &newName);
void changeFilename(const QString &newName);
void changeColCount(int);
/**
* @brief addGroup
- * @param name original group name
- * @param rowNumber
- * @return new group's name if change accpeted, empty string if cancelled
+ * @return new group's name if change accepted, empty string if cancelled
*/
QString addGroup();
/**
* @brief removeGroup
* @param name original group name
* @return true if change accepted, false if cancelled
*/
bool removeGroup(const QString &name);
/**
* @brief renameGroup
* @param oldName
* @return new name if change accpeted, empty string if cancelled
*/
QString renameGroup(const QString &oldName);
void changeGroupRowCount(const QString &name, int newRowCount);
void setGlobal(bool);
void setReadOnly(bool);
void setEntry(const KoColor &color, const QModelIndex &index);
void removeEntry(const QModelIndex &index);
void modifyEntry(const QModelIndex &index);
void addEntry(const KoColor &color);
bool isModified() const;
/**
* @brief getModifiedGroup
* @param originalName name of the group at the creation of the instance
* @return the modified group
*/
const KisSwatchGroup &getModifiedGroup(const QString &originalName) const;
/**
* @brief updatePalette
* MUST be called to make the changes into the resource server
*/
void updatePalette();
private Q_SLOTS:
void slotGroupNameChanged(const QString &newName);
void slotPaletteChanged();
void slotSetDocumentModified();
private:
QString newPaletteFileName(bool isGlobal);
QString newGroupName() const;
void setNonGlobal();
void setGlobal();
bool duplicateExistsGroupName(const QString &name) const;
bool duplicateExistsOriginalGroupName(const QString &name) const;
void uploadPaletteList() const;
QString filenameFromPath(const QString &path) const;
private:
struct Private;
QScopedPointer<Private> m_d;
};
#endif // KISPALETTEMANAGER_H
diff --git a/libs/ui/KisPrintJob.h b/libs/ui/KisPrintJob.h
index d0ab757f04..0698448f15 100644
--- a/libs/ui/KisPrintJob.h
+++ b/libs/ui/KisPrintJob.h
@@ -1,95 +1,95 @@
/* This file is part of the KDE project
* Copyright (C) 2007 Thomas Zander <zander@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KISPRINTJOB_H
#define KISPRINTJOB_H
#include <QObject>
#include <QList>
#include <QAbstractPrintDialog>
#include <QPrinter>
#include "kritaui_export.h"
#include <kis_types.h>
/**
* A print job is an interface that the KisView uses to create an application-specific
* class that can take care of printing.
* The printjob should be able to print again after a print job has been completed,
* using the same QPrinter to allow the user to alter settings on the QPrinter and
* call print again.
* The printjob can thus see startPrinting() called more than once, and the implementation
* of that signal should honor the removePolicy passed to it.
*/
class KRITAUI_EXPORT KisPrintJob : public QObject
{
Q_OBJECT
public:
/**
* Constructor.
- * @param parent the parent qobject that is passed for memory management purposes.
+ * @param image the image that is passed for management purposes.
*/
explicit KisPrintJob(KisImageWSP image);
~KisPrintJob() override;
/// A policy to allow the printjob to delete itself after its done printing.
enum RemovePolicy {
DeleteWhenDone, ///< Delete the job when its done with printing.
DoNotDelete ///< Keep the job around so it can be started again.
};
/// Returns the printer that is used for this print job so others can alter the details of the print-job.
QPrinter &printer() { return m_printer; }
int documentFirstPage() const {
return 1;
}
int documentLastPage() const {
return 1;
}
int documentCurrentPage() const {
return 1;
}
QAbstractPrintDialog::PrintDialogOptions printDialogOptions() const;
/**
*@brief Check if the painter can print to the printer
*@returns true if the print job can print to the given printer
*/
bool canPrint();
public Q_SLOTS:
/**
* This is called every time the job should be executed.
* When called the document should be printed a new painter using the printer
* of this printJob in order to honor the settings the user made on the printer.
* canPrint() should be called before startPrinting to check if the painter can print
* to the printer
* @param removePolicy a policy that should be honored so the caller can make sure
* this job doesn't leak memory after being used.
*/
void startPrinting(RemovePolicy removePolicy = DoNotDelete);
private:
KisImageWSP m_image;
QPrinter m_printer;
};
#endif
diff --git a/libs/ui/KisResourceBundle.h b/libs/ui/KisResourceBundle.h
index 3ffc188b26..bd167bf4e7 100644
--- a/libs/ui/KisResourceBundle.h
+++ b/libs/ui/KisResourceBundle.h
@@ -1,160 +1,163 @@
/*
* Copyright (c) 2014 Victor Lafon metabolic.ewilan@hotmail.fr
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KORESOURCEBUNDLE_H
#define KORESOURCEBUNDLE_H
#include <QSet>
#include <QList>
#include <KoXmlWriter.h>
#include <resources/KoResource.h>
#include "KisResourceBundleManifest.h"
#include "kritaui_export.h"
class KoStore;
/**
* @brief The ResourceBundle class
* @details Describe the resource bundles as KoResources
*/
class KRITAUI_EXPORT KisResourceBundle : public KoResource
{
public:
/**
- * @brief ResourceBundle : Ctor * @param bundlePath the path of the bundle
+ * @brief ResourceBundle : Ctor *
+ * @param fileName the path of the bundle
*/
KisResourceBundle(QString const& fileName);
/**
* @brief ~ResourceBundle : Dtor
*/
~KisResourceBundle() override;
/**
* @brief defaultFileExtension
* @return the default file extension which should be when saving the resource
*/
QString defaultFileExtension() const override;
/**
* @brief load : Load this resource.
* @return true if succeed, false otherwise.
*/
bool load() override;
bool loadFromDevice(QIODevice *dev) override;
/**
* @brief save : Save this resource.
* @return true if succeed, false otherwise.
*/
bool save() override;
bool saveToDevice(QIODevice* dev) const override;
/**
* @brief install : Install the contents of the resource bundle.
*/
bool install();
/**
* @brief uninstall : Uninstall the resource bundle.
*/
bool uninstall();
/**
* @brief addMeta : Add a Metadata to the resource
* @param type type of the metadata
* @param value value of the metadata
*/
void addMeta(const QString &type, const QString &value);
const QString getMeta(const QString &type, const QString &defaultValue = QString()) const;
/**
- * @brief addFile : Add a file to the bundle
+ * @brief Add a file to the bundle
* @param fileType type of the resource file
* @param filePath path of the resource file
+ * @param fileTagList the list of tags
+ * @param md5sum the file MD5 checksum
*/
void addResource(QString fileType, QString filePath, QStringList fileTagList, const QByteArray md5sum);
QList<QString> getTagsList();
/**
* @brief isInstalled
* @return true if the bundle is installed, false otherwise.
*/
bool isInstalled();
/**
* @brief setInstalled
* This allows you to set installed or uninstalled upon loading. This is used with blacklists.
*/
void setInstalled(bool install);
void setThumbnail(QString);
/**
* @brief saveMetadata: saves bundle metadata
* @param store bundle where to save the metadata
*/
void saveMetadata(QScopedPointer<KoStore> &store);
/**
* @brief saveManifest: saves bundle manifest
* @param store bundle where to save the manifest
*/
void saveManifest(QScopedPointer<KoStore> &store);
/**
* @brief recreateBundle
* It recreates the bundle by copying the old bundle information to a new store
* and recalculating the md5 of each resource.
* @param oldStore the old store to be recreated.
*/
void recreateBundle(QScopedPointer<KoStore> &oldStore);
QStringList resourceTypes() const;
QList<KoResource*> resources(const QString &resType = QString()) const;
int resourceCount() const;
private:
void writeMeta(const char *metaTag, const QString &metaKey, KoXmlWriter *writer);
void writeUserDefinedMeta(const QString &metaKey, KoXmlWriter *writer);
private:
QImage m_thumbnail;
KisResourceBundleManifest m_manifest;
QMap<QString, QString> m_metadata;
QSet<QString> m_bundletags;
bool m_installed;
QList<QByteArray> m_gradientsMd5Installed;
QList<QByteArray> m_patternsMd5Installed;
QList<QByteArray> m_brushesMd5Installed;
QList<QByteArray> m_palettesMd5Installed;
QList<QByteArray> m_workspacesMd5Installed;
QList<QByteArray> m_presetsMd5Installed;
QList<QByteArray> m_gamutMasksMd5Installed;
QString m_bundleVersion;
};
#endif // KORESOURCEBUNDLE_H
diff --git a/libs/ui/KisResourceBundleManifest.h b/libs/ui/KisResourceBundleManifest.h
index 3351b99dea..c854f88d51 100644
--- a/libs/ui/KisResourceBundleManifest.h
+++ b/libs/ui/KisResourceBundleManifest.h
@@ -1,100 +1,100 @@
/* This file is part of the KDE project
Copyright (C) 2014, Victor Lafon <metabolic.ewilan@hotmail.fr>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KOXMLRESOURCEBUNDLEMANIFEST_H
#define KOXMLRESOURCEBUNDLEMANIFEST_H
#include <QString>
#include <QPair>
#include <QMap>
#include <QMultiMap>
#include <kritaui_export.h>
class QIODevice;
class KRITAUI_EXPORT KisResourceBundleManifest
{
public:
struct ResourceReference {
ResourceReference(const QString &_resourcePath, const QList<QString> &_tagList, const QString &_fileTypeName, const QByteArray &_md5) {
resourcePath = _resourcePath;
tagList = _tagList;
fileTypeName = _fileTypeName;
md5sum = _md5;
}
QString resourcePath;
QList<QString> tagList;
QString fileTypeName;
QByteArray md5sum;
};
/**
* @brief ResourceBundleManifest : Ctor
- * @param xmlName the name of the XML file to be created
*/
KisResourceBundleManifest();
/**
* @brief ~ResourceBundleManifest : Dtor
*/
virtual ~KisResourceBundleManifest();
/**
* @brief load the ResourceBundleManifest from the given device
*/
bool load(QIODevice *device);
/**
* @brief save the ResourceBundleManifest to the given device
*/
bool save(QIODevice *device);
/**
* @brief addTag : Add a file tag as a child of the fileType tag.
* @param fileType the type of the file to be added
* @param fileName the name of the file to be added
- * @param emptyFile true if the file is empty
+ * @param tagFileList list of the tags
+ * @param md5 MD5 checksum
* @return the element corresponding to the created tag.
*/
void addResource(const QString &fileType, const QString &fileName, const QStringList &tagFileList, const QByteArray &md5);
QStringList types() const;
QStringList tags() const;
QList<ResourceReference> files(const QString &type = QString()) const;
/**
* @brief removeFile : remove a file from the manifest
* @param fileName : the name of the file to be removed
* @return the list of resource tags to be removed from meta file.
*/
void removeFile(QString fileName);
private:
QMap<QString, QMap<QString, ResourceReference> > m_resources;
};
#endif // KOXMLRESOURCEBUNDLEMANIFEST_H
diff --git a/libs/ui/KisSaveGroupVisitor.cpp b/libs/ui/KisSaveGroupVisitor.cpp
index ee3f21cd2e..be54192370 100644
--- a/libs/ui/KisSaveGroupVisitor.cpp
+++ b/libs/ui/KisSaveGroupVisitor.cpp
@@ -1,139 +1,139 @@
/*
* Copyright (C) 2016 Boudewijn Rempt <boud@valdyas.org>
*
* 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 "KisSaveGroupVisitor.h"
#include <KisDocument.h>
#include <kis_painter.h>
#include <kis_paint_layer.h>
#include <KisPart.h>
KisSaveGroupVisitor::KisSaveGroupVisitor(KisImageWSP image,
bool saveInvisible,
bool saveTopLevelOnly,
const QString &path,
const QString &baseName,
const QString &extension,
const QString &mimeFilter)
: m_image(image)
, m_saveInvisible(saveInvisible)
, m_saveTopLevelOnly(saveTopLevelOnly)
, m_path(path)
, m_baseName(baseName)
, m_extension(extension)
, m_mimeFilter(mimeFilter)
{
}
KisSaveGroupVisitor::~KisSaveGroupVisitor()
{
}
bool KisSaveGroupVisitor::visit(KisNode* ) {
return true;
}
bool KisSaveGroupVisitor::visit(KisPaintLayer *) {
return true;
}
bool KisSaveGroupVisitor::visit(KisAdjustmentLayer *) {
return true;
}
bool KisSaveGroupVisitor::visit(KisExternalLayer *) {
return true;
}
bool KisSaveGroupVisitor::visit(KisCloneLayer *) {
return true;
}
bool KisSaveGroupVisitor::visit(KisFilterMask *) {
return true;
}
bool KisSaveGroupVisitor::visit(KisTransformMask *) {
return true;
}
bool KisSaveGroupVisitor::visit(KisTransparencyMask *) {
return true;
}
bool KisSaveGroupVisitor::visit(KisGeneratorLayer * ) {
return true;
}
bool KisSaveGroupVisitor::visit(KisSelectionMask* ) {
return true;
}
bool KisSaveGroupVisitor::visit(KisColorizeMask* ) {
return true;
}
bool KisSaveGroupVisitor::visit(KisGroupLayer *layer)
{
if (layer == m_image->rootLayer()) {
KisLayerSP child = qobject_cast<KisLayer*>(layer->firstChild().data());
while (child) {
child->accept(*this);
child = qobject_cast<KisLayer*>(child->nextSibling().data());
}
}
else if (layer->visible() || m_saveInvisible) {
QRect r = m_image->bounds();
KisDocument *exportDocument = KisPart::instance()->createDocument();
-
- KisImageSP dst = new KisImage(exportDocument->createUndoStore(), r.width(), r.height(), m_image->colorSpace(), layer->name());
- dst->setResolution(m_image->xRes(), m_image->yRes());
- exportDocument->setCurrentImage(dst);
- KisPaintLayer* paintLayer = new KisPaintLayer(dst, "projection", layer->opacity());
- KisPainter gc(paintLayer->paintDevice());
- gc.bitBlt(QPoint(0, 0), layer->projection(), r);
- dst->addNode(paintLayer, dst->rootLayer(), KisLayerSP(0));
-
- dst->refreshGraph();
-
-
+ { // make sure dst is deleted before calling 'delete exportDocument',
+ // since KisDocument checks that its image is properly deref()'d.
+ KisImageSP dst = new KisImage(exportDocument->createUndoStore(), r.width(), r.height(), m_image->colorSpace(), layer->name());
+ dst->setResolution(m_image->xRes(), m_image->yRes());
+ exportDocument->setCurrentImage(dst);
+ KisPaintLayer* paintLayer = new KisPaintLayer(dst, "projection", layer->opacity());
+ KisPainter gc(paintLayer->paintDevice());
+ gc.bitBlt(QPoint(0, 0), layer->projection(), r);
+ dst->addNode(paintLayer, dst->rootLayer(), KisLayerSP(0));
+
+ dst->refreshGraph();
+ }
QString path = m_path + "/" + m_baseName + "_" + layer->name().replace(' ', '_') + '.' + m_extension;
QUrl url = QUrl::fromLocalFile(path);
exportDocument->setFileBatchMode(true);
exportDocument->exportDocumentSync(url, m_mimeFilter.toLatin1());
if (!m_saveTopLevelOnly) {
KisGroupLayerSP child = dynamic_cast<KisGroupLayer*>(layer->firstChild().data());
while (child) {
child->accept(*this);
child = dynamic_cast<KisGroupLayer*>(child->nextSibling().data());
}
}
delete exportDocument;
}
return true;
}
diff --git a/libs/ui/KisSaveGroupVisitor.h b/libs/ui/KisSaveGroupVisitor.h
index 6e4323ec50..47fccb7d53 100644
--- a/libs/ui/KisSaveGroupVisitor.h
+++ b/libs/ui/KisSaveGroupVisitor.h
@@ -1,103 +1,103 @@
/*
* Copyright (C) 2016 Boudewijn Rempt <boud@valdyas.org>
*
* 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 KISSAVEGROUPVISITOR_H
#define KISSAVEGROUPVISITOR_H
#include "kritaui_export.h"
#include <QUrl>
#include <QString>
#include <kis_types.h>
#include <kis_node_visitor.h>
#include <kis_layer.h>
#include <kis_group_layer.h>
#include <kis_node.h>
#include <kis_image.h>
/**
* @brief The KisSaveGroupVisitor class saves the groups in
* a Krita image to separate images.
*/
class KRITAUI_EXPORT KisSaveGroupVisitor : public KisNodeVisitor
{
public:
/**
* Create a KisSaveGroupVisitor
*
* @param image: the image to save
* @param saveInvisible: also save invisible layers
* @param saveTopLevelOnly: if true, only save the toplevel layers, otherwise
* descend into groups and save the bottom-most groups (groups that do
* not contain another group.
- * @param url the base location where the images will be saved
+ * @param path the base location where the images will be saved
* @param baseName the basename of the images
* @param extension the file format extension
* @param mimeFilter the export image type
*/
KisSaveGroupVisitor(KisImageWSP image,
bool saveInvisible,
bool saveTopLevelOnly,
const QString &path,
const QString &baseName,
const QString &extension,
const QString &mimeFilter);
~KisSaveGroupVisitor() override;
public:
bool visit(KisNode* ) override;
bool visit(KisPaintLayer *) override;
bool visit(KisAdjustmentLayer *) override;
bool visit(KisExternalLayer *) override;
bool visit(KisCloneLayer *) override;
bool visit(KisFilterMask *) override;
bool visit(KisTransformMask *) override;
bool visit(KisTransparencyMask *) override;
bool visit(KisGeneratorLayer * ) override;
bool visit(KisSelectionMask* ) override;
bool visit(KisColorizeMask* ) override;
bool visit(KisGroupLayer *layer) override;
private:
KisImageWSP m_image;
bool m_saveInvisible;
bool m_saveTopLevelOnly;
QString m_path;
QString m_baseName;
QString m_extension;
QString m_mimeFilter;
};
#endif // KISSAVEGROUPVISITOR_H
diff --git a/libs/ui/KisTemplatesPane.cpp b/libs/ui/KisTemplatesPane.cpp
index 4c6824d1f2..b971016f89 100644
--- a/libs/ui/KisTemplatesPane.cpp
+++ b/libs/ui/KisTemplatesPane.cpp
@@ -1,197 +1,192 @@
/* This file is part of the KDE project
Copyright (C) 2005-2006 Peter Simonsson <psn@linux.se>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "KisTemplatesPane.h"
#include "KisTemplateGroup.h"
#include "KisTemplate.h"
#include <QFileInfo>
#include <QStandardItemModel>
#include <QUrl>
#include <kguiitem.h>
#include <kconfiggroup.h>
#include <ksharedconfig.h>
class KisTemplatesPanePrivate
{
public:
KisTemplatesPanePrivate()
: m_selected(false) {
}
bool m_selected;
QString m_alwaysUseTemplate;
};
KisTemplatesPane::KisTemplatesPane(QWidget* parent, const QString& header,
KisTemplateGroup *group, KisTemplate* defaultTemplate)
: KisDetailsPane(parent,header)
, d(new KisTemplatesPanePrivate)
{
setFocusProxy(m_documentList);
KGuiItem openGItem(i18n("Use This Template"));
KGuiItem::assign(m_openButton, openGItem);
KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog");
QString fullTemplateName = cfgGrp.readPathEntry("FullTemplateName", QString());
d->m_alwaysUseTemplate = cfgGrp.readPathEntry("AlwaysUseTemplate", QString());
m_alwaysUseCheckBox->setVisible(false);
connect(m_alwaysUseCheckBox, SIGNAL(clicked()), this, SLOT(alwaysUseClicked()));
QStandardItem* selectItem = 0;
QStandardItem* rootItem = model()->invisibleRootItem();
QStandardItem* defaultItem = 0;
QFileInfo templateFileInfo(fullTemplateName);
Q_FOREACH (KisTemplate* t, group->templates()) {
if (t->isHidden())
continue;
QPixmap preview = t->loadPicture();
QImage icon = preview.toImage();
icon = icon.scaled(IconExtent, IconExtent, Qt::KeepAspectRatio, Qt::SmoothTransformation);
icon = icon.convertToFormat(QImage::Format_ARGB32);
icon = icon.copy((icon.width() - IconExtent) / 2, (icon.height() - IconExtent) / 2, IconExtent, IconExtent);
QStandardItem* item = new QStandardItem(QPixmap::fromImage(icon), t->name());
item->setEditable(false);
item->setData(t->description(), Qt::UserRole);
item->setData(t->file(), Qt::UserRole + 1);
item->setData(preview, Qt::UserRole + 2);
rootItem->appendRow(item);
- if (d->m_alwaysUseTemplate == t->file()) {
- selectItem = item;
- }
- else {
- if (templateFileInfo.exists()) {
- if (!selectItem && (t->file() == fullTemplateName)) {
- selectItem = item;
- }
- }
- else {
- if (!selectItem && QFileInfo(t->file()).fileName() == templateFileInfo.fileName()) {
- selectItem = item;
- }
- }
- }
+ if (templateFileInfo.exists()) {
+ if (!selectItem && (t->file() == fullTemplateName)) {
+ selectItem = item;
+ }
+ }
+ else {
+ if (!selectItem && QFileInfo(t->file()).fileName() == templateFileInfo.fileName()) {
+ selectItem = item;
+ }
+ }
if (defaultTemplate && (t->file() == defaultTemplate->file())) {
defaultItem = item;
}
}
QModelIndex selectedIndex;
if (selectItem) {
selectedIndex = model()->indexFromItem(selectItem);
d->m_selected = true;
} else if (defaultItem) {
selectedIndex = model()->indexFromItem(defaultItem);
} else {
selectedIndex = model()->indexFromItem(model()->item(0));
}
m_documentList->selectionModel()->select(selectedIndex, QItemSelectionModel::Select);
m_documentList->selectionModel()->setCurrentIndex(selectedIndex, QItemSelectionModel::Select);
}
KisTemplatesPane::~KisTemplatesPane()
{
delete d;
}
void KisTemplatesPane::selectionChanged(const QModelIndex& index)
{
if (index.isValid()) {
QStandardItem* item = model()->itemFromIndex(index);
m_openButton->setEnabled(true);
m_alwaysUseCheckBox->setEnabled(true);
m_titleLabel->setText(item->data(Qt::DisplayRole).toString());
m_previewLabel->setPixmap(item->data(Qt::UserRole + 2).value<QPixmap>());
m_detailsLabel->setHtml(item->data(Qt::UserRole).toString());
m_alwaysUseCheckBox->setChecked(item->data(Qt::UserRole + 1).toString() == d->m_alwaysUseTemplate);
} else {
m_openButton->setEnabled(false);
m_alwaysUseCheckBox->setEnabled(false);
m_alwaysUseCheckBox->setChecked(false);
m_titleLabel->clear();
m_previewLabel->setPixmap(QPixmap());
m_detailsLabel->clear();
}
}
void KisTemplatesPane::openFile()
{
KisDetailsPane::openFile();
}
void KisTemplatesPane::openFile(const QModelIndex& index)
{
if (index.isValid()) {
QStandardItem* item = model()->itemFromIndex(index);
KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog");
cfgGrp.writePathEntry("FullTemplateName", item->data(Qt::UserRole + 1).toString());
cfgGrp.writeEntry("LastReturnType", "Template");
cfgGrp.writeEntry("AlwaysUseTemplate", d->m_alwaysUseTemplate);
emit openUrl(QUrl::fromLocalFile(item->data(Qt::UserRole + 1).toString()));
}
}
bool KisTemplatesPane::isSelected()
{
return d->m_selected;
}
void KisTemplatesPane::alwaysUseClicked()
{
QStandardItem* item = model()->itemFromIndex(m_documentList->selectionModel()->currentIndex());
if (!m_alwaysUseCheckBox->isChecked()) {
d->m_alwaysUseTemplate.clear();
} else {
d->m_alwaysUseTemplate = item->data(Qt::UserRole + 1).toString();
}
KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog");
cfgGrp.writeEntry("AlwaysUseTemplate", d->m_alwaysUseTemplate);
cfgGrp.sync();
emit alwaysUseChanged(this, d->m_alwaysUseTemplate);
}
void KisTemplatesPane::changeAlwaysUseTemplate(KisTemplatesPane* sender, const QString& alwaysUse)
{
if (this == sender)
return;
QStandardItem* item = model()->itemFromIndex(m_documentList->selectionModel()->currentIndex());
// If the old always use template is selected uncheck the checkbox
if (item && (item->data(Qt::UserRole + 1).toString() == d->m_alwaysUseTemplate)) {
m_alwaysUseCheckBox->setChecked(false);
}
d->m_alwaysUseTemplate = alwaysUse;
}
diff --git a/libs/ui/KisViewManager.cpp b/libs/ui/KisViewManager.cpp
index 6eb6b858bf..2be7964616 100644
--- a/libs/ui/KisViewManager.cpp
+++ b/libs/ui/KisViewManager.cpp
@@ -1,1443 +1,1442 @@
/*
* This file is part of KimageShop^WKrayon^WKrita
*
* Copyright (c) 1999 Matthias Elter <me@kde.org>
* 1999 Michael Koch <koch@kde.org>
* 1999 Carsten Pfeiffer <pfeiffer@kde.org>
* 2002 Patrick Julien <freak@codepimps.org>
* 2003-2011 Boudewijn Rempt <boud@valdyas.org>
* 2004 Clarence Dang <dang@kde.org>
* 2011 José Luis Vergara <pentalis@gmail.com>
* 2017 L. E. Segovia <leo.segovia@siggraph.org>
*
* 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 <stdio.h>
#include "KisViewManager.h"
#include <QPrinter>
#include <QAction>
#include <QApplication>
#include <QBuffer>
#include <QByteArray>
#include <QStandardPaths>
#include <QDesktopWidget>
#include <QDesktopServices>
#include <QGridLayout>
#include <QMainWindow>
#include <QMenu>
#include <QMenuBar>
#include <QMessageBox>
#include <QObject>
#include <QPoint>
#include <QPrintDialog>
#include <QRect>
#include <QScrollBar>
#include <QStatusBar>
#include <QToolBar>
#include <QUrl>
#include <QWidget>
#include <kactioncollection.h>
#include <klocalizedstring.h>
#include <KoResourcePaths.h>
#include <kselectaction.h>
#include <KoCanvasController.h>
#include <KoCompositeOp.h>
#include <KoDockRegistry.h>
#include <KoProperties.h>
#include <KoResourceItemChooserSync.h>
#include <KoSelection.h>
#include <KoStore.h>
#include <KoToolManager.h>
#include <KoToolRegistry.h>
#include <KoViewConverter.h>
#include <KoZoomHandler.h>
#include <KoPluginLoader.h>
#include <KoDocumentInfo.h>
#include <KoColorSpaceRegistry.h>
#include "input/kis_input_manager.h"
#include "canvas/kis_canvas2.h"
#include "canvas/kis_canvas_controller.h"
#include "canvas/kis_grid_manager.h"
#include "dialogs/kis_dlg_blacklist_cleanup.h"
#include "input/kis_input_profile_manager.h"
#include "kis_action_manager.h"
#include "kis_action.h"
#include "kis_canvas_controls_manager.h"
#include "kis_canvas_resource_provider.h"
#include "kis_composite_progress_proxy.h"
#include <KoProgressUpdater.h>
#include "kis_config.h"
#include "kis_config_notifier.h"
#include "kis_control_frame.h"
#include "kis_coordinates_converter.h"
#include "KisDocument.h"
#include "kis_favorite_resource_manager.h"
#include "kis_filter_manager.h"
#include "kis_group_layer.h"
#include <kis_image.h>
#include <kis_image_barrier_locker.h>
#include "kis_image_manager.h"
#include <kis_layer.h>
#include "kis_mainwindow_observer.h"
#include "kis_mask_manager.h"
#include "kis_mimedata.h"
#include "kis_mirror_manager.h"
#include "kis_node_commands_adapter.h"
#include "kis_node.h"
#include "kis_node_manager.h"
#include "KisDecorationsManager.h"
#include <kis_paint_layer.h>
#include "kis_paintop_box.h"
#include <brushengine/kis_paintop_preset.h>
#include "KisPart.h"
#include "KisPrintJob.h"
#include <KoUpdater.h>
#include "KisResourceServerProvider.h"
#include "kis_selection.h"
#include "kis_selection_mask.h"
#include "kis_selection_manager.h"
#include "kis_shape_controller.h"
#include "kis_shape_layer.h"
#include <kis_signal_compressor.h>
#include "kis_statusbar.h"
#include <KisTemplateCreateDia.h>
#include <kis_tool_freehand.h>
#include "kis_tooltip_manager.h"
#include <kis_undo_adapter.h>
#include "KisView.h"
#include "kis_zoom_manager.h"
#include "widgets/kis_floating_message.h"
#include "kis_signal_auto_connection.h"
#include "kis_icon_utils.h"
#include "kis_guides_manager.h"
#include "kis_derived_resources.h"
#include "dialogs/kis_delayed_save_dialog.h"
#include <KisMainWindow.h>
#include "kis_signals_blocker.h"
class BlockingUserInputEventFilter : public QObject
{
bool eventFilter(QObject *watched, QEvent *event) override
{
Q_UNUSED(watched);
if(dynamic_cast<QWheelEvent*>(event)
|| dynamic_cast<QKeyEvent*>(event)
|| dynamic_cast<QMouseEvent*>(event)) {
return true;
}
else {
return false;
}
}
};
class KisViewManager::KisViewManagerPrivate
{
public:
KisViewManagerPrivate(KisViewManager *_q, KActionCollection *_actionCollection, QWidget *_q_parent)
: filterManager(_q)
, createTemplate(0)
, saveIncremental(0)
, saveIncrementalBackup(0)
, openResourcesDirectory(0)
, rotateCanvasRight(0)
, rotateCanvasLeft(0)
, resetCanvasRotation(0)
, wrapAroundAction(0)
, levelOfDetailAction(0)
, showRulersAction(0)
, rulersTrackMouseAction(0)
, zoomTo100pct(0)
, zoomIn(0)
, zoomOut(0)
, selectionManager(_q)
, statusBar(_q)
, controlFrame(_q, _q_parent)
, nodeManager(_q)
, imageManager(_q)
, gridManager(_q)
, canvasControlsManager(_q)
, paintingAssistantsManager(_q)
, actionManager(_q, _actionCollection)
, mainWindow(0)
, showFloatingMessage(true)
, currentImageView(0)
, canvasResourceProvider(_q)
, canvasResourceManager()
, guiUpdateCompressor(30, KisSignalCompressor::POSTPONE, _q)
, actionCollection(_actionCollection)
, mirrorManager(_q)
, inputManager(_q)
, actionAuthor(0)
, showPixelGrid(0)
{
KisViewManager::initializeResourceManager(&canvasResourceManager);
}
public:
KisFilterManager filterManager;
KisAction *createTemplate;
KisAction *createCopy;
KisAction *saveIncremental;
KisAction *saveIncrementalBackup;
KisAction *openResourcesDirectory;
KisAction *rotateCanvasRight;
KisAction *rotateCanvasLeft;
KisAction *resetCanvasRotation;
KisAction *wrapAroundAction;
KisAction *levelOfDetailAction;
KisAction *showRulersAction;
KisAction *rulersTrackMouseAction;
KisAction *zoomTo100pct;
KisAction *zoomIn;
KisAction *zoomOut;
KisAction *softProof;
KisAction *gamutCheck;
KisAction *toggleFgBg;
KisAction *resetFgBg;
KisSelectionManager selectionManager;
KisGuidesManager guidesManager;
KisStatusBar statusBar;
QPointer<KoUpdater> persistentImageProgressUpdater;
QScopedPointer<KoProgressUpdater> persistentUnthreadedProgressUpdaterRouter;
QPointer<KoUpdater> persistentUnthreadedProgressUpdater;
KisControlFrame controlFrame;
KisNodeManager nodeManager;
KisImageManager imageManager;
KisGridManager gridManager;
KisCanvasControlsManager canvasControlsManager;
KisDecorationsManager paintingAssistantsManager;
BlockingUserInputEventFilter blockingEventFilter;
KisActionManager actionManager;
QMainWindow* mainWindow;
QPointer<KisFloatingMessage> savedFloatingMessage;
bool showFloatingMessage;
QPointer<KisView> currentImageView;
KisCanvasResourceProvider canvasResourceProvider;
KoCanvasResourceProvider canvasResourceManager;
KisSignalCompressor guiUpdateCompressor;
KActionCollection *actionCollection;
KisMirrorManager mirrorManager;
KisInputManager inputManager;
KisSignalAutoConnectionsStore viewConnections;
KSelectAction *actionAuthor; // Select action for author profile.
KisAction *showPixelGrid;
QByteArray canvasState;
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
QFlags<Qt::WindowState> windowFlags;
#endif
bool blockUntilOperationsFinishedImpl(KisImageSP image, bool force);
};
KisViewManager::KisViewManager(QWidget *parent, KActionCollection *_actionCollection)
: d(new KisViewManagerPrivate(this, _actionCollection, parent))
{
d->actionCollection = _actionCollection;
d->mainWindow = dynamic_cast<QMainWindow*>(parent);
d->canvasResourceProvider.setResourceManager(&d->canvasResourceManager);
connect(&d->guiUpdateCompressor, SIGNAL(timeout()), this, SLOT(guiUpdateTimeout()));
createActions();
setupManagers();
// These initialization functions must wait until KisViewManager ctor is complete.
d->statusBar.setup();
d->persistentImageProgressUpdater =
d->statusBar.progressUpdater()->startSubtask(1, "", true);
// reset state to "completed"
d->persistentImageProgressUpdater->setRange(0,100);
d->persistentImageProgressUpdater->setValue(100);
d->persistentUnthreadedProgressUpdater =
d->statusBar.progressUpdater()->startSubtask(1, "", true);
// reset state to "completed"
d->persistentUnthreadedProgressUpdater->setRange(0,100);
d->persistentUnthreadedProgressUpdater->setValue(100);
d->persistentUnthreadedProgressUpdaterRouter.reset(
new KoProgressUpdater(d->persistentUnthreadedProgressUpdater,
KoProgressUpdater::Unthreaded));
d->persistentUnthreadedProgressUpdaterRouter->setAutoNestNames(true);
d->controlFrame.setup(parent);
//Check to draw scrollbars after "Canvas only mode" toggle is created.
this->showHideScrollbars();
QScopedPointer<KoDummyCanvasController> dummy(new KoDummyCanvasController(actionCollection()));
KoToolManager::instance()->registerToolActions(actionCollection(), dummy.data());
QTimer::singleShot(0, this, SLOT(initializeStatusBarVisibility()));
connect(KoToolManager::instance(), SIGNAL(inputDeviceChanged(KoInputDevice)),
d->controlFrame.paintopBox(), SLOT(slotInputDeviceChanged(KoInputDevice)));
connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*,int)),
d->controlFrame.paintopBox(), SLOT(slotToolChanged(KoCanvasController*,int)));
connect(&d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)),
resourceProvider(), SLOT(slotNodeActivated(KisNodeSP)));
connect(KisPart::instance(), SIGNAL(sigViewAdded(KisView*)), SLOT(slotViewAdded(KisView*)));
connect(KisPart::instance(), SIGNAL(sigViewRemoved(KisView*)), SLOT(slotViewRemoved(KisView*)));
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotUpdateAuthorProfileActions()));
connect(KisConfigNotifier::instance(), SIGNAL(pixelGridModeChanged()), SLOT(slotUpdatePixelGridAction()));
KisInputProfileManager::instance()->loadProfiles();
KisConfig cfg(true);
d->showFloatingMessage = cfg.showCanvasMessages();
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
KoColor foreground(Qt::black, cs);
d->canvasResourceProvider.setFGColor(cfg.readKoColor("LastForeGroundColor",foreground));
KoColor background(Qt::white, cs);
d->canvasResourceProvider.setBGColor(cfg.readKoColor("LastBackGroundColor",background));
-
-
-
}
KisViewManager::~KisViewManager()
{
KisConfig cfg(false);
if (resourceProvider() && resourceProvider()->currentPreset()) {
cfg.writeKoColor("LastForeGroundColor",resourceProvider()->fgColor());
cfg.writeKoColor("LastBackGroundColor",resourceProvider()->bgColor());
}
cfg.writeEntry("baseLength", KoResourceItemChooserSync::instance()->baseLength());
-
+ cfg.writeEntry("CanvasOnlyActive", false); // We never restart in CavnasOnlyMode
delete d;
}
void KisViewManager::initializeResourceManager(KoCanvasResourceProvider *resourceManager)
{
resourceManager->addDerivedResourceConverter(toQShared(new KisCompositeOpResourceConverter));
resourceManager->addDerivedResourceConverter(toQShared(new KisEffectiveCompositeOpResourceConverter));
resourceManager->addDerivedResourceConverter(toQShared(new KisOpacityResourceConverter));
resourceManager->addDerivedResourceConverter(toQShared(new KisFlowResourceConverter));
resourceManager->addDerivedResourceConverter(toQShared(new KisSizeResourceConverter));
resourceManager->addDerivedResourceConverter(toQShared(new KisLodAvailabilityResourceConverter));
resourceManager->addDerivedResourceConverter(toQShared(new KisLodSizeThresholdResourceConverter));
resourceManager->addDerivedResourceConverter(toQShared(new KisLodSizeThresholdSupportedResourceConverter));
resourceManager->addDerivedResourceConverter(toQShared(new KisEraserModeResourceConverter));
resourceManager->addResourceUpdateMediator(toQShared(new KisPresetUpdateMediator));
}
KActionCollection *KisViewManager::actionCollection() const
{
return d->actionCollection;
}
void KisViewManager::slotViewAdded(KisView *view)
{
// WARNING: this slot is called even when a view from another main windows is added!
// Don't expect \p view be a child of this view manager!
Q_UNUSED(view);
if (viewCount() == 0) {
d->statusBar.showAllStatusBarItems();
}
}
void KisViewManager::slotViewRemoved(KisView *view)
{
// WARNING: this slot is called even when a view from another main windows is removed!
// Don't expect \p view be a child of this view manager!
Q_UNUSED(view);
if (viewCount() == 0) {
d->statusBar.hideAllStatusBarItems();
}
KisConfig cfg(false);
if (resourceProvider() && resourceProvider()->currentPreset()) {
cfg.writeEntry("LastPreset", resourceProvider()->currentPreset()->name());
}
}
void KisViewManager::setCurrentView(KisView *view)
{
bool first = true;
if (d->currentImageView) {
d->currentImageView->notifyCurrentStateChanged(false);
d->currentImageView->canvasBase()->setCursor(QCursor(Qt::ArrowCursor));
first = false;
KisDocument* doc = d->currentImageView->document();
if (doc) {
doc->image()->compositeProgressProxy()->removeProxy(d->persistentImageProgressUpdater);
doc->disconnect(this);
}
d->currentImageView->canvasController()->proxyObject->disconnect(&d->statusBar);
d->viewConnections.clear();
}
QPointer<KisView> imageView = qobject_cast<KisView*>(view);
d->currentImageView = imageView;
if (imageView) {
d->softProof->setChecked(imageView->softProofing());
d->gamutCheck->setChecked(imageView->gamutCheck());
// Wait for the async image to have loaded
KisDocument* doc = view->document();
if (KisConfig(true).readEntry<bool>("EnablePositionLabel", false)) {
connect(d->currentImageView->canvasController()->proxyObject,
SIGNAL(documentMousePositionChanged(QPointF)),
&d->statusBar,
SLOT(documentMousePositionChanged(QPointF)));
}
// Restore the last used brush preset, color and background color.
if (first) {
KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer();
QString defaultPresetName = "basic_tip_default";
bool foundTip = false;
for (int i=0; i<rserver->resourceCount(); i++) {
KisPaintOpPresetSP resource = rserver->resources().at(i);
if (resource->name().toLower().contains("basic_tip_default")) {
defaultPresetName = resource->name();
foundTip = true;
} else if (foundTip == false && (resource->name().toLower().contains("default") ||
resource->filename().toLower().contains("default"))) {
defaultPresetName = resource->name();
foundTip = true;
}
}
KisConfig cfg(true);
QString lastPreset = cfg.readEntry("LastPreset", defaultPresetName);
KisPaintOpPresetSP preset = rserver->resourceByName(lastPreset);
if (!preset) {
preset = rserver->resourceByName(defaultPresetName);
}
if (!preset && !rserver->resources().isEmpty()) {
preset = rserver->resources().first();
}
if (preset) {
paintOpBox()->restoreResource(preset.data());
}
}
KisCanvasController *canvasController = dynamic_cast<KisCanvasController*>(d->currentImageView->canvasController());
d->viewConnections.addUniqueConnection(&d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)), doc->image(), SLOT(requestStrokeEndActiveNode()));
d->viewConnections.addUniqueConnection(d->rotateCanvasRight, SIGNAL(triggered()), canvasController, SLOT(rotateCanvasRight15()));
d->viewConnections.addUniqueConnection(d->rotateCanvasLeft, SIGNAL(triggered()),canvasController, SLOT(rotateCanvasLeft15()));
d->viewConnections.addUniqueConnection(d->resetCanvasRotation, SIGNAL(triggered()),canvasController, SLOT(resetCanvasRotation()));
d->viewConnections.addUniqueConnection(d->wrapAroundAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleWrapAroundMode(bool)));
d->wrapAroundAction->setChecked(canvasController->wrapAroundMode());
d->viewConnections.addUniqueConnection(d->levelOfDetailAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleLevelOfDetailMode(bool)));
d->levelOfDetailAction->setChecked(canvasController->levelOfDetailMode());
d->viewConnections.addUniqueConnection(d->currentImageView->image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), d->controlFrame.paintopBox(), SLOT(slotColorSpaceChanged(const KoColorSpace*)));
d->viewConnections.addUniqueConnection(d->showRulersAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(setShowRulers(bool)));
d->viewConnections.addUniqueConnection(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(setRulersTrackMouse(bool)));
d->viewConnections.addUniqueConnection(d->zoomTo100pct, SIGNAL(triggered()), imageView->zoomManager(), SLOT(zoomTo100()));
d->viewConnections.addUniqueConnection(d->zoomIn, SIGNAL(triggered()), imageView->zoomController()->zoomAction(), SLOT(zoomIn()));
d->viewConnections.addUniqueConnection(d->zoomOut, SIGNAL(triggered()), imageView->zoomController()->zoomAction(), SLOT(zoomOut()));
d->viewConnections.addUniqueConnection(d->softProof, SIGNAL(toggled(bool)), view, SLOT(slotSoftProofing(bool)) );
d->viewConnections.addUniqueConnection(d->gamutCheck, SIGNAL(toggled(bool)), view, SLOT(slotGamutCheck(bool)) );
// set up progrress reporting
doc->image()->compositeProgressProxy()->addProxy(d->persistentImageProgressUpdater);
d->viewConnections.addUniqueConnection(&d->statusBar, SIGNAL(sigCancellationRequested()), doc->image(), SLOT(requestStrokeCancellation()));
d->viewConnections.addUniqueConnection(d->showPixelGrid, SIGNAL(toggled(bool)), canvasController, SLOT(slotTogglePixelGrid(bool)));
imageView->zoomManager()->setShowRulers(d->showRulersAction->isChecked());
imageView->zoomManager()->setRulersTrackMouse(d->rulersTrackMouseAction->isChecked());
showHideScrollbars();
}
d->filterManager.setView(imageView);
d->selectionManager.setView(imageView);
d->guidesManager.setView(imageView);
d->nodeManager.setView(imageView);
d->imageManager.setView(imageView);
d->canvasControlsManager.setView(imageView);
d->actionManager.setView(imageView);
d->gridManager.setView(imageView);
d->statusBar.setView(imageView);
d->paintingAssistantsManager.setView(imageView);
d->mirrorManager.setView(imageView);
if (d->currentImageView) {
d->currentImageView->notifyCurrentStateChanged(true);
d->currentImageView->canvasController()->activate();
d->currentImageView->canvasController()->setFocus();
d->viewConnections.addUniqueConnection(
image(), SIGNAL(sigSizeChanged(QPointF,QPointF)),
resourceProvider(), SLOT(slotImageSizeChanged()));
d->viewConnections.addUniqueConnection(
image(), SIGNAL(sigResolutionChanged(double,double)),
resourceProvider(), SLOT(slotOnScreenResolutionChanged()));
d->viewConnections.addUniqueConnection(
image(), SIGNAL(sigNodeChanged(KisNodeSP)),
this, SLOT(updateGUI()));
d->viewConnections.addUniqueConnection(
d->currentImageView->zoomManager()->zoomController(),
SIGNAL(zoomChanged(KoZoomMode::Mode,qreal)),
resourceProvider(), SLOT(slotOnScreenResolutionChanged()));
}
d->actionManager.updateGUI();
resourceProvider()->slotImageSizeChanged();
resourceProvider()->slotOnScreenResolutionChanged();
Q_EMIT viewChanged();
}
KoZoomController *KisViewManager::zoomController() const
{
if (d->currentImageView) {
return d->currentImageView->zoomController();
}
return 0;
}
KisImageWSP KisViewManager::image() const
{
if (document()) {
return document()->image();
}
return 0;
}
KisCanvasResourceProvider * KisViewManager::resourceProvider()
{
return &d->canvasResourceProvider;
}
KisCanvas2 * KisViewManager::canvasBase() const
{
if (d && d->currentImageView) {
return d->currentImageView->canvasBase();
}
return 0;
}
QWidget* KisViewManager::canvas() const
{
if (d && d->currentImageView && d->currentImageView->canvasBase()->canvasWidget()) {
return d->currentImageView->canvasBase()->canvasWidget();
}
return 0;
}
KisStatusBar * KisViewManager::statusBar() const
{
return &d->statusBar;
}
KisPaintopBox* KisViewManager::paintOpBox() const
{
return d->controlFrame.paintopBox();
}
QPointer<KoUpdater> KisViewManager::createUnthreadedUpdater(const QString &name)
{
return d->persistentUnthreadedProgressUpdaterRouter->startSubtask(1, name, false);
}
QPointer<KoUpdater> KisViewManager::createThreadedUpdater(const QString &name)
{
return d->statusBar.progressUpdater()->startSubtask(1, name, false);
}
KisSelectionManager * KisViewManager::selectionManager()
{
return &d->selectionManager;
}
KisNodeSP KisViewManager::activeNode()
{
return d->nodeManager.activeNode();
}
KisLayerSP KisViewManager::activeLayer()
{
return d->nodeManager.activeLayer();
}
KisPaintDeviceSP KisViewManager::activeDevice()
{
return d->nodeManager.activePaintDevice();
}
KisZoomManager * KisViewManager::zoomManager()
{
if (d->currentImageView) {
return d->currentImageView->zoomManager();
}
return 0;
}
KisFilterManager * KisViewManager::filterManager()
{
return &d->filterManager;
}
KisImageManager * KisViewManager::imageManager()
{
return &d->imageManager;
}
KisInputManager* KisViewManager::inputManager() const
{
return &d->inputManager;
}
KisSelectionSP KisViewManager::selection()
{
if (d->currentImageView) {
return d->currentImageView->selection();
}
return 0;
}
bool KisViewManager::selectionEditable()
{
KisLayerSP layer = activeLayer();
if (layer) {
KisSelectionMaskSP mask = layer->selectionMask();
if (mask) {
return mask->isEditable();
}
}
// global selection is always editable
return true;
}
KisUndoAdapter * KisViewManager::undoAdapter()
{
if (!document()) return 0;
KisImageWSP image = document()->image();
Q_ASSERT(image);
return image->undoAdapter();
}
void KisViewManager::createActions()
{
KisConfig cfg(true);
d->saveIncremental = actionManager()->createAction("save_incremental_version");
connect(d->saveIncremental, SIGNAL(triggered()), this, SLOT(slotSaveIncremental()));
d->saveIncrementalBackup = actionManager()->createAction("save_incremental_backup");
connect(d->saveIncrementalBackup, SIGNAL(triggered()), this, SLOT(slotSaveIncrementalBackup()));
connect(mainWindow(), SIGNAL(documentSaved()), this, SLOT(slotDocumentSaved()));
d->saveIncremental->setEnabled(false);
d->saveIncrementalBackup->setEnabled(false);
KisAction *tabletDebugger = actionManager()->createAction("tablet_debugger");
connect(tabletDebugger, SIGNAL(triggered()), this, SLOT(toggleTabletLogger()));
d->createTemplate = actionManager()->createAction("create_template");
connect(d->createTemplate, SIGNAL(triggered()), this, SLOT(slotCreateTemplate()));
d->createCopy = actionManager()->createAction("create_copy");
connect(d->createCopy, SIGNAL(triggered()), this, SLOT(slotCreateCopy()));
d->openResourcesDirectory = actionManager()->createAction("open_resources_directory");
connect(d->openResourcesDirectory, SIGNAL(triggered()), SLOT(openResourcesDirectory()));
d->rotateCanvasRight = actionManager()->createAction("rotate_canvas_right");
d->rotateCanvasLeft = actionManager()->createAction("rotate_canvas_left");
d->resetCanvasRotation = actionManager()->createAction("reset_canvas_rotation");
d->wrapAroundAction = actionManager()->createAction("wrap_around_mode");
d->levelOfDetailAction = actionManager()->createAction("level_of_detail_mode");
d->softProof = actionManager()->createAction("softProof");
d->gamutCheck = actionManager()->createAction("gamutCheck");
KisAction *tAction = actionManager()->createAction("showStatusBar");
tAction->setChecked(cfg.showStatusBar());
connect(tAction, SIGNAL(toggled(bool)), this, SLOT(showStatusBar(bool)));
tAction = actionManager()->createAction("view_show_canvas_only");
tAction->setChecked(false);
connect(tAction, SIGNAL(toggled(bool)), this, SLOT(switchCanvasOnly(bool)));
//Workaround, by default has the same shortcut as mirrorCanvas
KisAction *a = dynamic_cast<KisAction*>(actionCollection()->action("format_italic"));
if (a) {
a->setDefaultShortcut(QKeySequence());
}
a = actionManager()->createAction("edit_blacklist_cleanup");
connect(a, SIGNAL(triggered()), this, SLOT(slotBlacklistCleanup()));
actionManager()->createAction("ruler_pixel_multiple2");
d->showRulersAction = actionManager()->createAction("view_ruler");
d->showRulersAction->setChecked(cfg.showRulers());
connect(d->showRulersAction, SIGNAL(toggled(bool)), SLOT(slotSaveShowRulersState(bool)));
d->rulersTrackMouseAction = actionManager()->createAction("rulers_track_mouse");
d->rulersTrackMouseAction->setChecked(cfg.rulersTrackMouse());
connect(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), SLOT(slotSaveRulersTrackMouseState(bool)));
d->zoomTo100pct = actionManager()->createAction("zoom_to_100pct");
d->zoomIn = actionManager()->createStandardAction(KStandardAction::ZoomIn, 0, "");
d->zoomOut = actionManager()->createStandardAction(KStandardAction::ZoomOut, 0, "");
d->actionAuthor = new KSelectAction(KisIconUtils::loadIcon("im-user"), i18n("Active Author Profile"), this);
connect(d->actionAuthor, SIGNAL(triggered(QString)), this, SLOT(changeAuthorProfile(QString)));
actionCollection()->addAction("settings_active_author", d->actionAuthor);
slotUpdateAuthorProfileActions();
d->showPixelGrid = actionManager()->createAction("view_pixel_grid");
slotUpdatePixelGridAction();
d->toggleFgBg = actionManager()->createAction("toggle_fg_bg");
connect(d->toggleFgBg, SIGNAL(triggered(bool)), this, SLOT(slotToggleFgBg()));
d->resetFgBg = actionManager()->createAction("reset_fg_bg");
connect(d->resetFgBg, SIGNAL(triggered(bool)), this, SLOT(slotResetFgBg()));
}
void KisViewManager::setupManagers()
{
// Create the managers for filters, selections, layers etc.
// XXX: When the currentlayer changes, call updateGUI on all
// managers
d->filterManager.setup(actionCollection(), actionManager());
d->selectionManager.setup(actionManager());
d->guidesManager.setup(actionManager());
d->nodeManager.setup(actionCollection(), actionManager());
d->imageManager.setup(actionManager());
d->gridManager.setup(actionManager());
d->paintingAssistantsManager.setup(actionManager());
d->canvasControlsManager.setup(actionManager());
d->mirrorManager.setup(actionCollection());
}
void KisViewManager::updateGUI()
{
d->guiUpdateCompressor.start();
}
void KisViewManager::slotBlacklistCleanup()
{
KisDlgBlacklistCleanup dialog;
dialog.exec();
}
KisNodeManager * KisViewManager::nodeManager() const
{
return &d->nodeManager;
}
KisActionManager* KisViewManager::actionManager() const
{
return &d->actionManager;
}
KisGridManager * KisViewManager::gridManager() const
{
return &d->gridManager;
}
KisGuidesManager * KisViewManager::guidesManager() const
{
return &d->guidesManager;
}
KisDocument *KisViewManager::document() const
{
if (d->currentImageView && d->currentImageView->document()) {
return d->currentImageView->document();
}
return 0;
}
int KisViewManager::viewCount() const
{
KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
if (mw) {
return mw->viewCount();
}
return 0;
}
bool KisViewManager::KisViewManagerPrivate::blockUntilOperationsFinishedImpl(KisImageSP image, bool force)
{
const int busyWaitDelay = 1000;
KisDelayedSaveDialog dialog(image, !force ? KisDelayedSaveDialog::GeneralDialog : KisDelayedSaveDialog::ForcedDialog, busyWaitDelay, mainWindow);
dialog.blockIfImageIsBusy();
return dialog.result() == QDialog::Accepted;
}
bool KisViewManager::blockUntilOperationsFinished(KisImageSP image)
{
return d->blockUntilOperationsFinishedImpl(image, false);
}
void KisViewManager::blockUntilOperationsFinishedForced(KisImageSP image)
{
d->blockUntilOperationsFinishedImpl(image, true);
}
void KisViewManager::slotCreateTemplate()
{
if (!document()) return;
KisTemplateCreateDia::createTemplate( QStringLiteral("templates/"), ".kra", document(), mainWindow());
}
void KisViewManager::slotCreateCopy()
{
KisDocument *srcDoc = document();
if (!srcDoc) return;
if (!this->blockUntilOperationsFinished(srcDoc->image())) return;
KisDocument *doc = 0;
{
KisImageBarrierLocker l(srcDoc->image());
doc = srcDoc->clone();
}
KIS_SAFE_ASSERT_RECOVER_RETURN(doc);
QString name = srcDoc->documentInfo()->aboutInfo("name");
if (name.isEmpty()) {
name = document()->url().toLocalFile();
}
name = i18n("%1 (Copy)", name);
doc->documentInfo()->setAboutInfo("title", name);
KisPart::instance()->addDocument(doc);
KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
mw->addViewAndNotifyLoadingCompleted(doc);
}
QMainWindow* KisViewManager::qtMainWindow() const
{
if (d->mainWindow)
return d->mainWindow;
//Fallback for when we have not yet set the main window.
QMainWindow* w = qobject_cast<QMainWindow*>(qApp->activeWindow());
if(w)
return w;
return mainWindow();
}
void KisViewManager::setQtMainWindow(QMainWindow* newMainWindow)
{
d->mainWindow = newMainWindow;
}
void KisViewManager::slotDocumentSaved()
{
d->saveIncremental->setEnabled(true);
d->saveIncrementalBackup->setEnabled(true);
}
void KisViewManager::slotSaveIncremental()
{
if (!document()) return;
if (document()->url().isEmpty()) {
KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
mw->saveDocument(document(), true, false);
return;
}
bool foundVersion;
bool fileAlreadyExists;
bool isBackup;
QString version = "000";
QString newVersion;
QString letter;
QString fileName = document()->localFilePath();
// Find current version filenames
// v v Regexp to find incremental versions in the filename, taking our backup scheme into account as well
// Considering our incremental version and backup scheme, format is filename_001~001.ext
QRegExp regex("_\\d{1,4}[.]|_\\d{1,4}[a-z][.]|_\\d{1,4}[~]|_\\d{1,4}[a-z][~]");
regex.indexIn(fileName); // Perform the search
QStringList matches = regex.capturedTexts();
foundVersion = matches.at(0).isEmpty() ? false : true;
// Ensure compatibility with Save Incremental Backup
// If this regex is not kept separate, the entire algorithm needs modification;
// It's simpler to just add this.
QRegExp regexAux("_\\d{1,4}[~]|_\\d{1,4}[a-z][~]");
regexAux.indexIn(fileName); // Perform the search
QStringList matchesAux = regexAux.capturedTexts();
isBackup = matchesAux.at(0).isEmpty() ? false : true;
// If the filename has a version, prepare it for incrementation
if (foundVersion) {
version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches
if (version.contains(QRegExp("[a-z]"))) {
version.chop(1); // Trim "."
letter = version.right(1); // Save letter
version.chop(1); // Trim letter
} else {
version.chop(1); // Trim "."
}
version.remove(0, 1); // Trim "_"
} else {
// TODO: this will not work with files extensions like jp2
// ...else, simply add a version to it so the next loop works
QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension
regex2.indexIn(fileName);
QStringList matches2 = regex2.capturedTexts();
QString extensionPlusVersion = matches2.at(0);
extensionPlusVersion.prepend(version);
extensionPlusVersion.prepend("_");
fileName.replace(regex2, extensionPlusVersion);
}
// Prepare the base for new version filename
int intVersion = version.toInt(0);
++intVersion;
QString baseNewVersion = QString::number(intVersion);
while (baseNewVersion.length() < version.length()) {
baseNewVersion.prepend("0");
}
// Check if the file exists under the new name and search until options are exhausted (test appending a to z)
do {
newVersion = baseNewVersion;
newVersion.prepend("_");
if (!letter.isNull()) newVersion.append(letter);
if (isBackup) {
newVersion.append("~");
} else {
newVersion.append(".");
}
fileName.replace(regex, newVersion);
fileAlreadyExists = QFile(fileName).exists();
if (fileAlreadyExists) {
if (!letter.isNull()) {
char letterCh = letter.at(0).toLatin1();
++letterCh;
letter = QString(QChar(letterCh));
} else {
letter = 'a';
}
}
} while (fileAlreadyExists && letter != "{"); // x, y, z, {...
if (letter == "{") {
QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental version"), i18n("Alternative names exhausted, try manually saving with a higher number"));
return;
}
document()->setFileBatchMode(true);
document()->saveAs(QUrl::fromUserInput(fileName), document()->mimeType(), true);
document()->setFileBatchMode(false);
if (mainWindow()) {
mainWindow()->updateCaption();
}
}
void KisViewManager::slotSaveIncrementalBackup()
{
if (!document()) return;
if (document()->url().isEmpty()) {
KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
mw->saveDocument(document(), true, false);
return;
}
bool workingOnBackup;
bool fileAlreadyExists;
QString version = "000";
QString newVersion;
QString letter;
QString fileName = document()->localFilePath();
// First, discover if working on a backup file, or a normal file
QRegExp regex("~\\d{1,4}[.]|~\\d{1,4}[a-z][.]");
regex.indexIn(fileName); // Perform the search
QStringList matches = regex.capturedTexts();
workingOnBackup = matches.at(0).isEmpty() ? false : true;
if (workingOnBackup) {
// Try to save incremental version (of backup), use letter for alt versions
version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches
if (version.contains(QRegExp("[a-z]"))) {
version.chop(1); // Trim "."
letter = version.right(1); // Save letter
version.chop(1); // Trim letter
} else {
version.chop(1); // Trim "."
}
version.remove(0, 1); // Trim "~"
// Prepare the base for new version filename
int intVersion = version.toInt(0);
++intVersion;
QString baseNewVersion = QString::number(intVersion);
QString backupFileName = document()->localFilePath();
while (baseNewVersion.length() < version.length()) {
baseNewVersion.prepend("0");
}
// Check if the file exists under the new name and search until options are exhausted (test appending a to z)
do {
newVersion = baseNewVersion;
newVersion.prepend("~");
if (!letter.isNull()) newVersion.append(letter);
newVersion.append(".");
backupFileName.replace(regex, newVersion);
fileAlreadyExists = QFile(backupFileName).exists();
if (fileAlreadyExists) {
if (!letter.isNull()) {
char letterCh = letter.at(0).toLatin1();
++letterCh;
letter = QString(QChar(letterCh));
} else {
letter = 'a';
}
}
} while (fileAlreadyExists && letter != "{"); // x, y, z, {...
if (letter == "{") {
QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental backup"), i18n("Alternative names exhausted, try manually saving with a higher number"));
return;
}
QFile::copy(fileName, backupFileName);
document()->saveAs(QUrl::fromUserInput(fileName), document()->mimeType(), true);
if (mainWindow()) mainWindow()->updateCaption();
}
else { // if NOT working on a backup...
// Navigate directory searching for latest backup version, ignore letters
const quint8 HARDCODED_DIGIT_COUNT = 3;
QString baseNewVersion = "000";
QString backupFileName = document()->localFilePath();
QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension
regex2.indexIn(backupFileName);
QStringList matches2 = regex2.capturedTexts();
QString extensionPlusVersion = matches2.at(0);
extensionPlusVersion.prepend(baseNewVersion);
extensionPlusVersion.prepend("~");
backupFileName.replace(regex2, extensionPlusVersion);
// Save version with 1 number higher than the highest version found ignoring letters
do {
newVersion = baseNewVersion;
newVersion.prepend("~");
newVersion.append(".");
backupFileName.replace(regex, newVersion);
fileAlreadyExists = QFile(backupFileName).exists();
if (fileAlreadyExists) {
// Prepare the base for new version filename, increment by 1
int intVersion = baseNewVersion.toInt(0);
++intVersion;
baseNewVersion = QString::number(intVersion);
while (baseNewVersion.length() < HARDCODED_DIGIT_COUNT) {
baseNewVersion.prepend("0");
}
}
} while (fileAlreadyExists);
// Save both as backup and on current file for interapplication workflow
document()->setFileBatchMode(true);
QFile::copy(fileName, backupFileName);
document()->saveAs(QUrl::fromUserInput(fileName), document()->mimeType(), true);
document()->setFileBatchMode(false);
if (mainWindow()) mainWindow()->updateCaption();
}
}
void KisViewManager::disableControls()
{
// prevents possible crashes, if somebody changes the paintop during dragging by using the mousewheel
// this is for Bug 250944
// the solution blocks all wheel, mouse and key event, while dragging with the freehand tool
// see KisToolFreehand::initPaint() and endPaint()
d->controlFrame.paintopBox()->installEventFilter(&d->blockingEventFilter);
Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) {
child->installEventFilter(&d->blockingEventFilter);
}
}
void KisViewManager::enableControls()
{
d->controlFrame.paintopBox()->removeEventFilter(&d->blockingEventFilter);
Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) {
child->removeEventFilter(&d->blockingEventFilter);
}
}
void KisViewManager::showStatusBar(bool toggled)
{
KisMainWindow *mw = mainWindow();
if(mw && mw->statusBar()) {
mw->statusBar()->setVisible(toggled);
KisConfig cfg(false);
cfg.setShowStatusBar(toggled);
}
}
void KisViewManager::switchCanvasOnly(bool toggled)
{
KisConfig cfg(false);
- KisMainWindow* main = mainWindow();
+ KisMainWindow *main = mainWindow();
if(!main) {
dbgUI << "Unable to switch to canvas-only mode, main window not found";
return;
}
+ cfg.writeEntry("CanvasOnlyActive", toggled);
+
if (toggled) {
d->canvasState = qtMainWindow()->saveState();
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
d->windowFlags = main->windowState();
#endif
}
if (cfg.hideStatusbarFullscreen()) {
if (main->statusBar()) {
if (!toggled) {
if (main->statusBar()->dynamicPropertyNames().contains("wasvisible")) {
if (main->statusBar()->property("wasvisible").toBool()) {
main->statusBar()->setVisible(true);
}
}
}
else {
main->statusBar()->setProperty("wasvisible", main->statusBar()->isVisible());
main->statusBar()->setVisible(false);
}
}
}
if (cfg.hideDockersFullscreen()) {
KisAction* action = qobject_cast<KisAction*>(main->actionCollection()->action("view_toggledockers"));
if (action) {
action->setCheckable(true);
if (toggled) {
if (action->isChecked()) {
cfg.setShowDockers(action->isChecked());
action->setChecked(false);
} else {
cfg.setShowDockers(false);
}
} else {
action->setChecked(cfg.showDockers());
}
}
}
// QT in windows does not return to maximized upon 4th tab in a row
// https://bugreports.qt.io/browse/QTBUG-57882, https://bugreports.qt.io/browse/QTBUG-52555, https://codereview.qt-project.org/#/c/185016/
if (cfg.hideTitlebarFullscreen() && !cfg.fullscreenMode()) {
if(toggled) {
main->setWindowState( main->windowState() | Qt::WindowFullScreen);
} else {
main->setWindowState( main->windowState() & ~Qt::WindowFullScreen);
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
// If window was maximized prior to fullscreen, restore that
if (d->windowFlags & Qt::WindowMaximized) {
main->setWindowState( main->windowState() | Qt::WindowMaximized);
}
#endif
}
}
if (cfg.hideMenuFullscreen()) {
if (!toggled) {
if (main->menuBar()->dynamicPropertyNames().contains("wasvisible")) {
if (main->menuBar()->property("wasvisible").toBool()) {
main->menuBar()->setVisible(true);
}
}
}
else {
main->menuBar()->setProperty("wasvisible", main->menuBar()->isVisible());
main->menuBar()->setVisible(false);
}
}
if (cfg.hideToolbarFullscreen()) {
QList<QToolBar*> toolBars = main->findChildren<QToolBar*>();
Q_FOREACH (QToolBar* toolbar, toolBars) {
if (!toggled) {
if (toolbar->dynamicPropertyNames().contains("wasvisible")) {
if (toolbar->property("wasvisible").toBool()) {
toolbar->setVisible(true);
}
}
}
else {
toolbar->setProperty("wasvisible", toolbar->isVisible());
toolbar->setVisible(false);
}
}
}
showHideScrollbars();
if (toggled) {
// show a fading heads-up display about the shortcut to go back
showFloatingMessage(i18n("Going into Canvas-Only mode.\nPress %1 to go back.",
actionCollection()->action("view_show_canvas_only")->shortcut().toString()), QIcon());
}
else {
main->restoreState(d->canvasState);
}
}
void KisViewManager::toggleTabletLogger()
{
d->inputManager.toggleTabletLogger();
}
void KisViewManager::openResourcesDirectory()
{
QString dir = KoResourcePaths::locateLocal("data", "");
QDesktopServices::openUrl(QUrl::fromLocalFile(dir));
}
void KisViewManager::updateIcons()
{
if (mainWindow()) {
QList<QDockWidget*> dockers = mainWindow()->dockWidgets();
Q_FOREACH (QDockWidget* dock, dockers) {
QObjectList objects;
objects.append(dock);
while (!objects.isEmpty()) {
QObject* object = objects.takeFirst();
objects.append(object->children());
KisIconUtils::updateIconCommon(object);
}
}
}
}
void KisViewManager::initializeStatusBarVisibility()
{
KisConfig cfg(true);
d->mainWindow->statusBar()->setVisible(cfg.showStatusBar());
}
void KisViewManager::guiUpdateTimeout()
{
d->nodeManager.updateGUI();
d->selectionManager.updateGUI();
d->filterManager.updateGUI();
if (zoomManager()) {
zoomManager()->updateGUI();
}
d->gridManager.updateGUI();
d->actionManager.updateGUI();
}
void KisViewManager::showFloatingMessage(const QString &message, const QIcon& icon, int timeout, KisFloatingMessage::Priority priority, int alignment)
{
if (!d->currentImageView) return;
d->currentImageView->showFloatingMessage(message, icon, timeout, priority, alignment);
emit floatingMessageRequested(message, icon.name());
}
KisMainWindow *KisViewManager::mainWindow() const
{
return qobject_cast<KisMainWindow*>(d->mainWindow);
}
void KisViewManager::showHideScrollbars()
{
if (!d->currentImageView) return;
if (!d->currentImageView->canvasController()) return;
KisConfig cfg(true);
bool toggled = actionCollection()->action("view_show_canvas_only")->isChecked();
if ( (toggled && cfg.hideScrollbarsFullscreen()) || (!toggled && cfg.hideScrollbars()) ) {
d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
} else {
d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
}
}
void KisViewManager::slotSaveShowRulersState(bool value)
{
KisConfig cfg(false);
cfg.setShowRulers(value);
}
void KisViewManager::slotSaveRulersTrackMouseState(bool value)
{
KisConfig cfg(false);
cfg.setRulersTrackMouse(value);
}
void KisViewManager::setShowFloatingMessage(bool show)
{
d->showFloatingMessage = show;
}
void KisViewManager::changeAuthorProfile(const QString &profileName)
{
KConfigGroup appAuthorGroup(KSharedConfig::openConfig(), "Author");
if (profileName.isEmpty() || profileName == i18nc("choice for author profile", "Anonymous")) {
appAuthorGroup.writeEntry("active-profile", "");
} else {
appAuthorGroup.writeEntry("active-profile", profileName);
}
appAuthorGroup.sync();
Q_FOREACH (KisDocument *doc, KisPart::instance()->documents()) {
doc->documentInfo()->updateParameters();
}
}
void KisViewManager::slotUpdateAuthorProfileActions()
{
Q_ASSERT(d->actionAuthor);
if (!d->actionAuthor) {
return;
}
d->actionAuthor->clear();
d->actionAuthor->addAction(i18nc("choice for author profile", "Anonymous"));
KConfigGroup authorGroup(KSharedConfig::openConfig(), "Author");
QStringList profiles = authorGroup.readEntry("profile-names", QStringList());
QString authorInfo = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/authorinfo/";
QStringList filters = QStringList() << "*.authorinfo";
QDir dir(authorInfo);
Q_FOREACH(QString entry, dir.entryList(filters)) {
int ln = QString(".authorinfo").size();
entry.chop(ln);
if (!profiles.contains(entry)) {
profiles.append(entry);
}
}
Q_FOREACH (const QString &profile , profiles) {
d->actionAuthor->addAction(profile);
}
KConfigGroup appAuthorGroup(KSharedConfig::openConfig(), "Author");
QString profileName = appAuthorGroup.readEntry("active-profile", "");
if (profileName == "anonymous" || profileName.isEmpty()) {
d->actionAuthor->setCurrentItem(0);
} else if (profiles.contains(profileName)) {
d->actionAuthor->setCurrentAction(profileName);
}
}
void KisViewManager::slotUpdatePixelGridAction()
{
KIS_SAFE_ASSERT_RECOVER_RETURN(d->showPixelGrid);
KisSignalsBlocker b(d->showPixelGrid);
KisConfig cfg(true);
d->showPixelGrid->setChecked(cfg.pixelGridEnabled() && cfg.useOpenGL());
}
void KisViewManager::slotActivateTransformTool()
{
if(KoToolManager::instance()->activeToolId() == "KisToolTransform") {
KoToolBase* tool = KoToolManager::instance()->toolById(canvasBase(), "KisToolTransform");
QSet<KoShape*> dummy;
// Start a new stroke
tool->deactivate();
tool->activate(KoToolBase::DefaultActivation, dummy);
}
KoToolManager::instance()->switchToolRequested("KisToolTransform");
}
void KisViewManager::slotToggleFgBg()
{
KoColor newFg = d->canvasResourceManager.backgroundColor();
KoColor newBg = d->canvasResourceManager.foregroundColor();
/**
* NOTE: Some of color selectors do not differentiate foreground
* and background colors, so if one wants them to end up
* being set up to foreground color, it should be set the
* last.
*/
d->canvasResourceManager.setBackgroundColor(newBg);
d->canvasResourceManager.setForegroundColor(newFg);
}
void KisViewManager::slotResetFgBg()
{
// see a comment in slotToggleFgBg()
d->canvasResourceManager.setBackgroundColor(KoColor(Qt::white, KoColorSpaceRegistry::instance()->rgb8()));
d->canvasResourceManager.setForegroundColor(KoColor(Qt::black, KoColorSpaceRegistry::instance()->rgb8()));
}
diff --git a/libs/ui/KisViewManager.h b/libs/ui/KisViewManager.h
index 030aeac59e..3fc1ca2a54 100644
--- a/libs/ui/KisViewManager.h
+++ b/libs/ui/KisViewManager.h
@@ -1,261 +1,261 @@
/*
* Copyright (c) 2006 Boudewijn Rempt <boud@valdyas.org>
*
* 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_GUI_CLIENT_H
#define KIS_GUI_CLIENT_H
#include <QDockWidget>
#include <QQueue>
#include <QPointer>
#include <KisMainWindow.h>
#include <KoToolManager.h>
#include <kritaui_export.h>
#include <kis_types.h>
#include "kis_floating_message.h"
class QPoint;
class KisView;
class KisCanvas2;
class KisCanvasResourceProvider;
class KisDocument;
class KisFilterManager;
class KisGridManager;
class KisGuidesManager;
class KisImageManager;
class KisNodeManager;
class KisDecorationsManager;
class KisPaintopBox;
class KisSelectionManager;
class KisStatusBar;
class KisUndoAdapter;
class KisZoomManager;
class KisPaintopBox;
class KisActionManager;
class KisInputManager;
class KoUpdater;
class KoProgressUpdater;
/**
* KisViewManager manages the collection of views shown in a single mainwindow.
*/
class KRITAUI_EXPORT KisViewManager : public QObject
{
Q_OBJECT
public:
/**
* Construct a new view on the krita document.
- * @param document the document we show.
* @param parent a parent widget we show ourselves in.
+ * @param actionCollection an action collection.
*/
KisViewManager(QWidget *parent, KActionCollection *actionCollection);
~KisViewManager() override;
/**
* Retrieves the entire action collection.
*/
virtual KActionCollection* actionCollection() const;
public: // Krita specific interfaces
void setCurrentView(KisView *view);
/// Return the image this view is displaying
KisImageWSP image() const;
KoZoomController *zoomController() const;
/// The resource provider contains all per-view settings, such as
/// current color, current paint op etc.
KisCanvasResourceProvider *resourceProvider();
/// Return the canvasbase class
KisCanvas2 *canvasBase() const;
/// Return the actual widget that is displaying the current image
QWidget* canvas() const;
/// Return the wrapper class around the statusbar
KisStatusBar *statusBar() const;
KisPaintopBox* paintOpBox() const;
/// create a new progress updater
QPointer<KoUpdater> createUnthreadedUpdater(const QString &name);
QPointer<KoUpdater> createThreadedUpdater(const QString &name);
/// The selection manager handles everything action related to
/// selections.
KisSelectionManager *selectionManager();
/// The node manager handles everything about nodes
KisNodeManager *nodeManager() const;
KisActionManager *actionManager() const;
/**
* Convenience method to get at the active node, which may be
* a layer or a mask or a selection
*/
KisNodeSP activeNode();
/// Convenience method to get at the active layer
KisLayerSP activeLayer();
/// Convenience method to get at the active paint device
KisPaintDeviceSP activeDevice();
/// The filtermanager handles everything action-related to filters
KisFilterManager *filterManager();
/// The image manager handles everything action-related to the
/// current image
KisImageManager *imageManager();
/// Filters events and sends them to canvas actions
KisInputManager *inputManager() const;
/// Convenience method to get at the active selection (the
/// selection of the current layer, or, if that does not exist,
/// the global selection.
KisSelectionSP selection();
/// Checks if the current global or local selection is editable
bool selectionEditable();
/// The undo adapter is used to add commands to the undo stack
KisUndoAdapter *undoAdapter();
KisDocument *document() const;
int viewCount() const;
/**
* @brief blockUntilOperationsFinished blocks the GUI of the application until execution
* of actions on \p image is finished
* @param image the image which we should wait for
* @return true if the image has finished execution of the actions, false if
* the user cancelled operation
*/
bool blockUntilOperationsFinished(KisImageSP image);
/**
* @brief blockUntilOperationsFinished blocks the GUI of the application until execution
* of actions on \p image is finished. Does *not* provide a "Cancel" button. So the
* user is forced to wait.
* @param image the image which we should wait for
*/
void blockUntilOperationsFinishedForced(KisImageSP image);
public:
KisGridManager * gridManager() const;
KisGuidesManager * guidesManager() const;
/// disable and enable toolbar controls. used for disabling them during painting.
void enableControls();
void disableControls();
/// shows a floating message in the top right corner of the canvas
void showFloatingMessage(const QString &message, const QIcon& icon, int timeout = 4500,
KisFloatingMessage::Priority priority = KisFloatingMessage::Medium,
int alignment = Qt::AlignCenter | Qt::TextWordWrap);
/// @return the KoMaindow this view is in, or 0
KisMainWindow *mainWindow() const;
/// The QMainWindow associated with this view. This is most likely going to be shell(), but
/// when running as Gemini or Sketch, this will be set to the applications' own QMainWindow.
/// This can be checked by qobject_casting to KisMainWindow to check the difference.
QMainWindow* qtMainWindow() const;
/// The mainWindow function will return the shell() value, unless this function is called
/// with a non-null value. To make it return shell() again, simply pass null to this function.
void setQtMainWindow(QMainWindow* newMainWindow);
static void initializeResourceManager(KoCanvasResourceProvider *resourceManager);
public Q_SLOTS:
void switchCanvasOnly(bool toggled);
void setShowFloatingMessage(bool show);
void showHideScrollbars();
/// Visit all managers to update gui elements, e.g. enable / disable actions.
/// This is heavy-duty call, so it uses a compressor.
void updateGUI();
/// Update the style of all the icons
void updateIcons();
void slotViewAdded(KisView *view);
void slotViewRemoved(KisView *view);
void slotActivateTransformTool();
// Change and update author
void changeAuthorProfile(const QString &profileName);
void slotUpdateAuthorProfileActions();
Q_SIGNALS:
void floatingMessageRequested(const QString &message, const QString &iconName);
/**
* @brief viewChanged
* sent out when the view has changed.
*/
void viewChanged();
private Q_SLOTS:
void slotBlacklistCleanup();
void slotCreateTemplate();
void slotCreateCopy();
void slotDocumentSaved();
void slotSaveIncremental();
void slotSaveIncrementalBackup();
void showStatusBar(bool toggled);
void toggleTabletLogger();
void openResourcesDirectory();
void initializeStatusBarVisibility();
void guiUpdateTimeout();
void slotUpdatePixelGridAction();
void slotSaveShowRulersState(bool value);
void slotSaveRulersTrackMouseState(bool value);
void slotToggleFgBg();
void slotResetFgBg();
private:
void createActions();
void setupManagers();
/// The zoommanager handles everything action-related to zooming
KisZoomManager * zoomManager();
private:
class KisViewManagerPrivate;
KisViewManagerPrivate * const d;
};
#endif
diff --git a/libs/ui/KisWelcomePageWidget.cpp b/libs/ui/KisWelcomePageWidget.cpp
index b5f41f2df1..cbc9a8dc62 100644
--- a/libs/ui/KisWelcomePageWidget.cpp
+++ b/libs/ui/KisWelcomePageWidget.cpp
@@ -1,271 +1,276 @@
/* This file is part of the KDE project
* Copyright (C) 2018 Scott Petrovic <scottpetrovic@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisWelcomePageWidget.h"
#include <QDebug>
#include <QDesktopServices>
+#include <QFileInfo>
+
#include "kis_action_manager.h"
#include "kactioncollection.h"
#include "kis_action.h"
#include "KConfigGroup"
#include "KSharedConfig"
#include <QListWidget>
#include <QListWidgetItem>
#include "kis_icon_utils.h"
#include "krita_utils.h"
#include "KoStore.h"
#include "kis_config.h"
KisWelcomePageWidget::KisWelcomePageWidget(QWidget *parent)
: QWidget(parent)
{
setupUi(this);
recentDocumentsListView->setDragEnabled(false);
recentDocumentsListView->viewport()->setAutoFillBackground(false);
recentDocumentsListView->setSpacing(2);
// set up URLs that go to web browser
manualLink->setText(QString("<a href=\"https://docs.krita.org/\">").append(i18n("User Manual")).append("</a>"));
manualLink->setTextFormat(Qt::RichText);
manualLink->setTextInteractionFlags(Qt::TextBrowserInteraction);
manualLink->setOpenExternalLinks(true);
gettingStartedLink->setText(QString("<a href=\"https://docs.krita.org/en/user_manual/getting_started.html\">").append(i18n("Getting Started")).append("</a>"));
gettingStartedLink->setTextFormat(Qt::RichText);
gettingStartedLink->setTextInteractionFlags(Qt::TextBrowserInteraction);
gettingStartedLink->setOpenExternalLinks(true);
supportKritaLink->setText(QString("<a href=\"https://krita.org/en/support-us/donations/\">").append(i18n("Support Krita")).append("</a>"));
supportKritaLink->setTextFormat(Qt::RichText);
supportKritaLink->setTextInteractionFlags(Qt::TextBrowserInteraction);
supportKritaLink->setOpenExternalLinks(true);
userCommunityLink->setText(QString("<a href=\"https://forum.kde.org/viewforum.php?f=136\">").append(i18n("User Community")).append("</a>"));
userCommunityLink->setTextFormat(Qt::RichText);
userCommunityLink->setTextInteractionFlags(Qt::TextBrowserInteraction);
userCommunityLink->setOpenExternalLinks(true);
kritaWebsiteLink->setText(QString("<a href=\"https://www.krita.org\">").append(i18n("Krita Website")).append("</a>"));
kritaWebsiteLink->setTextFormat(Qt::RichText);
kritaWebsiteLink->setTextInteractionFlags(Qt::TextBrowserInteraction);
kritaWebsiteLink->setOpenExternalLinks(true);
sourceCodeLink->setText(QString("<a href=\"https://phabricator.kde.org/source/krita/\">").append(i18n("Source Code")).append("</a>"));
sourceCodeLink->setTextFormat(Qt::RichText);
sourceCodeLink->setTextInteractionFlags(Qt::TextBrowserInteraction);
sourceCodeLink->setOpenExternalLinks(true);
poweredByKDELink->setText(QString("<a href=\"https://userbase.kde.org/What_is_KDE\">").append(i18n("Powered by KDE")).append("</a>"));
poweredByKDELink->setTextFormat(Qt::RichText);
poweredByKDELink->setTextInteractionFlags(Qt::TextBrowserInteraction);
poweredByKDELink->setOpenExternalLinks(true);
kdeIcon->setIconSize(QSize(20, 20));
kdeIcon->setIcon(KisIconUtils::loadIcon(QStringLiteral("kde")).pixmap(20));
connect(chkShowNews, SIGNAL(toggled(bool)), newsWidget, SLOT(toggleNews(bool)));
// configure the News area
KisConfig cfg(true);
bool m_getNews = cfg.readEntry<bool>("FetchNews", false);
chkShowNews->setChecked(m_getNews);
}
KisWelcomePageWidget::~KisWelcomePageWidget()
{
}
void KisWelcomePageWidget::setMainWindow(KisMainWindow* mainWin)
{
if (mainWin) {
m_mainWindow = mainWin;
// set the shortcut links from actions (only if a shortcut exists)
if ( mainWin->viewManager()->actionManager()->actionByName("file_new")->shortcut().toString() != "") {
newFileLinkShortcut->setText(QString("(") + mainWin->viewManager()->actionManager()->actionByName("file_new")->shortcut().toString() + QString(")"));
}
if (mainWin->viewManager()->actionManager()->actionByName("file_open")->shortcut().toString() != "") {
openFileShortcut->setText(QString("(") + mainWin->viewManager()->actionManager()->actionByName("file_open")->shortcut().toString() + QString(")"));
}
connect(recentDocumentsListView, SIGNAL(clicked(QModelIndex)), this, SLOT(recentDocumentClicked(QModelIndex)));
// we need the view manager to actually call actions, so don't create the connections
// until after the view manager is set
connect(newFileLink, SIGNAL(clicked(bool)), this, SLOT(slotNewFileClicked()));
connect(openFileLink, SIGNAL(clicked(bool)), this, SLOT(slotOpenFileClicked()));
connect(clearRecentFilesLink, SIGNAL(clicked(bool)), this, SLOT(slotClearRecentFiles()));
slotUpdateThemeColors();
}
}
void KisWelcomePageWidget::showDropAreaIndicator(bool show)
{
if (!show) {
QString dropFrameStyle = "QFrame#dropAreaIndicator { border: 0px }";
dropFrameBorder->setStyleSheet(dropFrameStyle);
} else {
QColor textColor = qApp->palette().color(QPalette::Text);
QColor backgroundColor = qApp->palette().color(QPalette::Background);
QColor blendedColor = KritaUtils::blendColors(textColor, backgroundColor, 0.8);
// QColor.name() turns it into a hex/web format
QString dropFrameStyle = QString("QFrame#dropAreaIndicator { border: 2px dotted ").append(blendedColor.name()).append(" }") ;
dropFrameBorder->setStyleSheet(dropFrameStyle);
}
}
void KisWelcomePageWidget::slotUpdateThemeColors()
{
QColor textColor = qApp->palette().color(QPalette::Text);
QColor backgroundColor = qApp->palette().color(QPalette::Background);
// make the welcome screen labels a subtle color so it doesn't clash with the main UI elements
QColor blendedColor = KritaUtils::blendColors(textColor, backgroundColor, 0.8);
QString blendedStyle = QString("color: ").append(blendedColor.name());
// what labels to change the color...
startTitleLabel->setStyleSheet(blendedStyle);
recentDocumentsLabel->setStyleSheet(blendedStyle);
helpTitleLabel->setStyleSheet(blendedStyle);
manualLink->setStyleSheet(blendedStyle);
gettingStartedLink->setStyleSheet(blendedStyle);
supportKritaLink->setStyleSheet(blendedStyle);
userCommunityLink->setStyleSheet(blendedStyle);
kritaWebsiteLink->setStyleSheet(blendedStyle);
sourceCodeLink->setStyleSheet(blendedStyle);
newFileLinkShortcut->setStyleSheet(blendedStyle);
openFileShortcut->setStyleSheet(blendedStyle);
clearRecentFilesLink->setStyleSheet(blendedStyle);
poweredByKDELink->setStyleSheet(blendedStyle);
recentDocumentsListView->setStyleSheet(blendedStyle);
newFileLink->setStyleSheet(blendedStyle);
openFileLink->setStyleSheet(blendedStyle);
// giving the drag area messaging a dotted border
QString dottedBorderStyle = QString("border: 2px dotted ").append(blendedColor.name()).append("; color:").append(blendedColor.name()).append( ";");
dragImageHereLabel->setStyleSheet(dottedBorderStyle);
// make drop area QFrame have a dotted line
dropFrameBorder->setObjectName("dropAreaIndicator");
QString dropFrameStyle = QString("QFrame#dropAreaIndicator { border: 4px dotted ").append(blendedColor.name()).append("}");
dropFrameBorder->setStyleSheet(dropFrameStyle);
// only show drop area when we have a document over the empty area
showDropAreaIndicator(false);
// add icons for new and open settings to make them stand out a bit more
openFileLink->setIconSize(QSize(30, 30));
newFileLink->setIconSize(QSize(30, 30));
openFileLink->setIcon(KisIconUtils::loadIcon("document-open"));
newFileLink->setIcon(KisIconUtils::loadIcon("document-new"));
}
void KisWelcomePageWidget::populateRecentDocuments()
{
m_recentFilesModel.clear(); // clear existing data before it gets re-populated
// grab recent files data
int numRecentFiles = m_mainWindow->recentFilesUrls().length() > 5 ? 5 : m_mainWindow->recentFilesUrls().length(); // grab at most 5
for (int i = 0; i < numRecentFiles; i++ ) {
QStandardItem *recentItem = new QStandardItem(1,2); // 1 row, 1 column
recentItem->setIcon(KisIconUtils::loadIcon("document-export"));
- QString recentFileUrlPath = m_mainWindow->recentFilesUrls().at(i).toString();
+ QString recentFileUrlPath = m_mainWindow->recentFilesUrls().at(i).toLocalFile();
QString fileName = recentFileUrlPath.split("/").last();
if (m_thumbnailMap.contains(recentFileUrlPath)) {
recentItem->setIcon(m_thumbnailMap[recentFileUrlPath]);
}
else {
- if (recentFileUrlPath.endsWith("ora") || recentFileUrlPath.endsWith("kra")) {
- QScopedPointer<KoStore> store(KoStore::createStore(QUrl(recentFileUrlPath), KoStore::Read));
- if (store) {
- if (store->open(QString("Thumbnails/thumbnail.png"))
- || store->open(QString("preview.png"))) {
-
- QByteArray bytes = store->read(store->size());
- store->close();
- QImage img;
- img.loadFromData(bytes);
- img.setDevicePixelRatio(devicePixelRatioF());
- recentItem->setIcon(QIcon(QPixmap::fromImage(img)));
+ if (QFileInfo(recentFileUrlPath).exists()) {
+ if (recentFileUrlPath.toLower().endsWith("ora") || recentFileUrlPath.toLower().endsWith("kra")) {
+ QScopedPointer<KoStore> store(KoStore::createStore(recentFileUrlPath, KoStore::Read));
+ if (store) {
+ if (store->open(QString("Thumbnails/thumbnail.png"))
+ || store->open(QString("preview.png"))) {
+
+ QByteArray bytes = store->read(store->size());
+ store->close();
+ QImage img;
+ img.loadFromData(bytes);
+ img.setDevicePixelRatio(devicePixelRatioF());
+ recentItem->setIcon(QIcon(QPixmap::fromImage(img)));
+ }
}
}
- }
- else {
- QImage img(QUrl(recentFileUrlPath).toLocalFile());
- img.setDevicePixelRatio(devicePixelRatioF());
- if (!img.isNull()) {
- recentItem->setIcon(QIcon(QPixmap::fromImage(img.scaledToWidth(48))));
+ else {
+ QImage img;
+ img.setDevicePixelRatio(devicePixelRatioF());
+ img.load(recentFileUrlPath);
+ if (!img.isNull()) {
+ recentItem->setIcon(QIcon(QPixmap::fromImage(img.scaledToWidth(48))));
+ }
}
+ m_thumbnailMap[recentFileUrlPath] = recentItem->icon();
}
- m_thumbnailMap[recentFileUrlPath] = recentItem->icon();
}
// set the recent object with the data
recentItem->setText(fileName); // what to display for the item
recentItem->setToolTip(recentFileUrlPath);
m_recentFilesModel.appendRow(recentItem);
}
// hide clear and Recent files title if there are none
bool hasRecentFiles = m_mainWindow->recentFilesUrls().length() > 0;
recentDocumentsLabel->setVisible(hasRecentFiles);
clearRecentFilesLink->setVisible(hasRecentFiles);
recentDocumentsListView->setIconSize(QSize(48, 48));
recentDocumentsListView->setModel(&m_recentFilesModel);
}
void KisWelcomePageWidget::recentDocumentClicked(QModelIndex index)
{
QString fileUrl = index.data(Qt::ToolTipRole).toString();
- m_mainWindow->openDocument(QUrl(fileUrl), KisMainWindow::None );
+ m_mainWindow->openDocument(QUrl::fromLocalFile(fileUrl), KisMainWindow::None );
}
void KisWelcomePageWidget::slotNewFileClicked()
{
m_mainWindow->slotFileNew();
}
void KisWelcomePageWidget::slotOpenFileClicked()
{
m_mainWindow->slotFileOpen();
}
void KisWelcomePageWidget::slotClearRecentFiles()
{
m_mainWindow->clearRecentFiles();
populateRecentDocuments();
}
diff --git a/libs/ui/brushhud/kis_brush_hud.cpp b/libs/ui/brushhud/kis_brush_hud.cpp
index 93a40399bd..17968359e2 100644
--- a/libs/ui/brushhud/kis_brush_hud.cpp
+++ b/libs/ui/brushhud/kis_brush_hud.cpp
@@ -1,286 +1,287 @@
/*
* Copyright (c) 2016 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_brush_hud.h"
#include <QGuiApplication>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPointer>
#include <QLabel>
#include <QPainter>
#include <QPaintEvent>
#include <QScrollArea>
#include <QEvent>
#include <QToolButton>
#include "kis_uniform_paintop_property.h"
#include "kis_slider_based_paintop_property.h"
#include "kis_uniform_paintop_property_widget.h"
#include "kis_canvas_resource_provider.h"
#include "kis_paintop_preset.h"
#include "kis_paintop_settings.h"
#include "kis_signal_auto_connection.h"
#include "kis_paintop_settings_update_proxy.h"
#include "kis_icon_utils.h"
#include "kis_dlg_brush_hud_config.h"
#include "kis_brush_hud_properties_config.h"
#include "kis_elided_label.h"
#include "kis_debug.h"
struct KisBrushHud::Private
{
QPointer<KisElidedLabel> lblPresetName;
QPointer<QLabel> lblPresetIcon;
QPointer<QWidget> wdgProperties;
QPointer<QScrollArea> wdgPropertiesArea;
QPointer<QVBoxLayout> propertiesLayout;
QPointer<QToolButton> btnConfigure;
KisCanvasResourceProvider *provider;
KisSignalAutoConnectionsStore connections;
KisSignalAutoConnectionsStore presetConnections;
KisPaintOpPresetSP currentPreset;
};
KisBrushHud::KisBrushHud(KisCanvasResourceProvider *provider, QWidget *parent)
: QWidget(parent, Qt::FramelessWindowHint),
m_d(new Private)
{
m_d->provider = provider;
QVBoxLayout *layout = new QVBoxLayout();
QHBoxLayout *labelLayout = new QHBoxLayout();
m_d->lblPresetIcon = new QLabel(this);
const QSize iconSize = QSize(22,22);
m_d->lblPresetIcon->setMinimumSize(iconSize);
m_d->lblPresetIcon->setMaximumSize(iconSize);
m_d->lblPresetIcon->setScaledContents(true);
m_d->lblPresetName = new KisElidedLabel("<Preset Name>", Qt::ElideMiddle, this);
m_d->btnConfigure = new QToolButton(this);
m_d->btnConfigure->setAutoRaise(true);
connect(m_d->btnConfigure, SIGNAL(clicked()), SLOT(slotConfigBrushHud()));
labelLayout->addWidget(m_d->lblPresetIcon);
labelLayout->addWidget(m_d->lblPresetName);
labelLayout->addWidget(m_d->btnConfigure);
layout->addLayout(labelLayout);
m_d->wdgPropertiesArea = new QScrollArea(this);
m_d->wdgPropertiesArea->setAlignment(Qt::AlignLeft | Qt::AlignTop);
m_d->wdgPropertiesArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_d->wdgPropertiesArea->setWidgetResizable(true);
m_d->wdgProperties = new QWidget(this);
m_d->propertiesLayout = new QVBoxLayout(this);
m_d->propertiesLayout->setSpacing(0);
m_d->propertiesLayout->setContentsMargins(0, 0, 22, 0);
m_d->propertiesLayout->setSizeConstraint(QLayout::SetMinimumSize);
// not adding any widgets until explicitly requested
m_d->wdgProperties->setLayout(m_d->propertiesLayout);
m_d->wdgPropertiesArea->setWidget(m_d->wdgProperties);
layout->addWidget(m_d->wdgPropertiesArea);
updateIcons();
setLayout(layout);
setCursor(Qt::ArrowCursor);
// Prevent tablet events from being captured by the canvas
setAttribute(Qt::WA_NoMousePropagation, true);
}
KisBrushHud::~KisBrushHud()
{
}
QSize KisBrushHud::sizeHint() const
{
QSize size = QWidget::sizeHint();
return QSize(size.width(), parentWidget()->height());
}
void KisBrushHud::updateIcons()
{
m_d->btnConfigure->setIcon(KisIconUtils::loadIcon("applications-system"));
}
void KisBrushHud::slotReloadProperties()
{
m_d->presetConnections.clear();
clearProperties();
updateProperties();
}
void KisBrushHud::clearProperties() const
{
while (m_d->propertiesLayout->count()) {
QLayoutItem *item = m_d->propertiesLayout->takeAt(0);
QWidget *w = item->widget();
if (w) {
w->deleteLater();
}
delete item;
}
m_d->currentPreset.clear();
}
void KisBrushHud::updateProperties()
{
KisPaintOpPresetSP preset = m_d->provider->currentPreset();
if (preset == m_d->currentPreset) return;
m_d->presetConnections.clear();
clearProperties();
m_d->currentPreset = preset;
m_d->presetConnections.addConnection(
m_d->currentPreset->updateProxy(), SIGNAL(sigUniformPropertiesChanged()),
this, SLOT(slotReloadProperties()));
m_d->lblPresetIcon->setPixmap(QPixmap::fromImage(preset->image()));
m_d->lblPresetName->setLongText(preset->name());
QList<KisUniformPaintOpPropertySP> properties;
{
QList<KisUniformPaintOpPropertySP> allProperties = preset->uniformProperties();
QList<KisUniformPaintOpPropertySP> discardedProperties;
KisBrushHudPropertiesConfig cfg;
cfg.filterProperties(preset->paintOp().id(),
allProperties,
&properties,
&discardedProperties);
}
Q_FOREACH(auto property, properties) {
QWidget *w = 0;
if (!property->isVisible()) continue;
if (property->type() == KisUniformPaintOpProperty::Int) {
w = new KisUniformPaintOpPropertyIntSlider(property, m_d->wdgProperties);
} else if (property->type() == KisUniformPaintOpProperty::Double) {
w = new KisUniformPaintOpPropertyDoubleSlider(property, m_d->wdgProperties);
} else if (property->type() == KisUniformPaintOpProperty::Bool) {
w = new KisUniformPaintOpPropertyCheckBox(property, m_d->wdgProperties);
} else if (property->type() == KisUniformPaintOpProperty::Combo) {
w = new KisUniformPaintOpPropertyComboBox(property, m_d->wdgProperties);
}
if (w) {
w->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
m_d->propertiesLayout->addWidget(w);
}
}
m_d->propertiesLayout->addStretch();
+ resize(sizeHint());
}
void KisBrushHud::showEvent(QShowEvent *event)
{
m_d->connections.clear();
m_d->connections.addUniqueConnection(
m_d->provider->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)),
this, SLOT(slotCanvasResourceChanged(int,QVariant)));
updateProperties();
QWidget::showEvent(event);
}
void KisBrushHud::hideEvent(QHideEvent *event)
{
m_d->connections.clear();
QWidget::hideEvent(event);
clearProperties();
}
void KisBrushHud::slotCanvasResourceChanged(int key, const QVariant &resource)
{
Q_UNUSED(resource);
if (key == KisCanvasResourceProvider::CurrentPaintOpPreset) {
updateProperties();
}
}
void KisBrushHud::paintEvent(QPaintEvent *event)
{
QColor bgColor = palette().color(QPalette::Window);
QPainter painter(this);
painter.fillRect(rect() & event->rect(), bgColor);
painter.end();
QWidget::paintEvent(event);
}
bool KisBrushHud::event(QEvent *event)
{
switch (event->type()) {
case QEvent::TabletPress:
case QEvent::TabletMove:
case QEvent::TabletRelease:
// Allow the tablet event to be translated to a mouse event on certain platforms
break;
case QEvent::MouseButtonPress:
case QEvent::MouseMove:
case QEvent::MouseButtonRelease:
case QEvent::Wheel:
event->accept();
return true;
default:
break;
}
return QWidget::event(event);
}
void KisBrushHud::slotConfigBrushHud()
{
if (!m_d->currentPreset) return;
KisDlgConfigureBrushHud dlg(m_d->currentPreset);
dlg.exec();
slotReloadProperties();
}
diff --git a/libs/ui/canvas/kis_canvas2.cpp b/libs/ui/canvas/kis_canvas2.cpp
index 6d819cdca2..91ff1c101c 100644
--- a/libs/ui/canvas/kis_canvas2.cpp
+++ b/libs/ui/canvas/kis_canvas2.cpp
@@ -1,1254 +1,1262 @@
/* This file is part of the KDE project
*
* Copyright (C) 2006, 2010 Boudewijn Rempt <boud@valdyas.org>
* Copyright (C) Lukáš Tvrdý <lukast.dev@gmail.com>, (C) 2010
* Copyright (C) 2011 Silvio Heinrich <plassy@web.de>
*
* 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_canvas2.h"
#include <functional>
#include <numeric>
#include <QApplication>
#include <QWidget>
#include <QVBoxLayout>
#include <QTime>
#include <QLabel>
#include <QMouseEvent>
#include <QDesktopWidget>
#include <kis_debug.h>
#include <KoUnit.h>
#include <KoShapeManager.h>
#include <KisSelectedShapesProxy.h>
#include <KoColorProfile.h>
#include <KoCanvasControllerWidget.h>
#include <KisDocument.h>
#include <KoSelection.h>
#include <KoShapeController.h>
#include <kis_lod_transform.h>
#include "kis_tool_proxy.h"
#include "kis_coordinates_converter.h"
#include "kis_prescaled_projection.h"
#include "kis_image.h"
#include "kis_image_barrier_locker.h"
#include "kis_undo_adapter.h"
#include "flake/kis_shape_layer.h"
#include "kis_canvas_resource_provider.h"
#include "KisViewManager.h"
#include "kis_config.h"
#include "kis_config_notifier.h"
#include "kis_abstract_canvas_widget.h"
#include "kis_qpainter_canvas.h"
#include "kis_group_layer.h"
#include "flake/kis_shape_controller.h"
#include "kis_node_manager.h"
#include "kis_selection.h"
#include "kis_selection_component.h"
#include "flake/kis_shape_selection.h"
#include "kis_selection_mask.h"
#include "kis_image_config.h"
#include "kis_infinity_manager.h"
#include "kis_signal_compressor.h"
#include "kis_display_color_converter.h"
#include "kis_exposure_gamma_correction_interface.h"
#include "KisView.h"
#include "kis_canvas_controller.h"
#include "kis_grid_config.h"
#include "kis_animation_player.h"
#include "kis_animation_frame_cache.h"
#include "opengl/kis_opengl_canvas2.h"
#include "opengl/kis_opengl.h"
#include "kis_fps_decoration.h"
#include "KoColorConversionTransformation.h"
#include "KisProofingConfiguration.h"
#include <kis_favorite_resource_manager.h>
#include <kis_popup_palette.h>
#include "input/kis_input_manager.h"
#include "kis_painting_assistants_decoration.h"
#include "kis_canvas_updates_compressor.h"
#include "KoZoomController.h"
#include <KisStrokeSpeedMonitor.h>
#include "opengl/kis_opengl_canvas_debugger.h"
#include "kis_algebra_2d.h"
#include "kis_image_signal_router.h"
class Q_DECL_HIDDEN KisCanvas2::KisCanvas2Private
{
public:
KisCanvas2Private(KoCanvasBase *parent, KisCoordinatesConverter* coordConverter, QPointer<KisView> view, KoCanvasResourceProvider* resourceManager)
: coordinatesConverter(coordConverter)
, view(view)
, shapeManager(parent)
, selectedShapesProxy(&shapeManager)
, toolProxy(parent)
, displayColorConverter(resourceManager, view)
, regionOfInterestUpdateCompressor(100, KisSignalCompressor::FIRST_INACTIVE)
{
}
KisCoordinatesConverter *coordinatesConverter;
QPointer<KisView>view;
KisAbstractCanvasWidget *canvasWidget = 0;
KoShapeManager shapeManager;
KisSelectedShapesProxy selectedShapesProxy;
bool currentCanvasIsOpenGL;
int openGLFilterMode;
KisToolProxy toolProxy;
KisPrescaledProjectionSP prescaledProjection;
bool vastScrolling;
KisSignalCompressor canvasUpdateCompressor;
QRect savedUpdateRect;
QBitArray channelFlags;
KisProofingConfigurationSP proofingConfig;
bool softProofing = false;
bool gamutCheck = false;
bool proofingConfigUpdated = false;
KisPopupPalette *popupPalette = 0;
KisDisplayColorConverter displayColorConverter;
KisCanvasUpdatesCompressor projectionUpdatesCompressor;
KisAnimationPlayer *animationPlayer;
KisAnimationFrameCacheSP frameCache;
bool lodAllowedInImage = false;
bool bootstrapLodBlocked;
QPointer<KoShapeManager> currentlyActiveShapeManager;
KisInputActionGroupsMask inputActionGroupsMask = AllActionGroup;
KisSignalCompressor frameRenderStartCompressor;
KisSignalCompressor regionOfInterestUpdateCompressor;
QRect regionOfInterest;
QRect renderingLimit;
int isBatchUpdateActive = 0;
bool effectiveLodAllowedInImage() {
return lodAllowedInImage && !bootstrapLodBlocked;
}
void setActiveShapeManager(KoShapeManager *shapeManager);
};
namespace {
KoShapeManager* fetchShapeManagerFromNode(KisNodeSP node)
{
KoShapeManager *shapeManager = 0;
KisSelectionSP selection;
if (KisLayer *layer = dynamic_cast<KisLayer*>(node.data())) {
KisShapeLayer *shapeLayer = dynamic_cast<KisShapeLayer*>(layer);
if (shapeLayer) {
shapeManager = shapeLayer->shapeManager();
}
} else if (KisSelectionMask *mask = dynamic_cast<KisSelectionMask*>(node.data())) {
selection = mask->selection();
}
if (!shapeManager && selection && selection->hasShapeSelection()) {
KisShapeSelection *shapeSelection = dynamic_cast<KisShapeSelection*>(selection->shapeSelection());
KIS_ASSERT_RECOVER_RETURN_VALUE(shapeSelection, 0);
shapeManager = shapeSelection->shapeManager();
}
return shapeManager;
}
}
KisCanvas2::KisCanvas2(KisCoordinatesConverter *coordConverter, KoCanvasResourceProvider *resourceManager, KisView *view, KoShapeControllerBase *sc)
: KoCanvasBase(sc, resourceManager)
, m_d(new KisCanvas2Private(this, coordConverter, view, resourceManager))
{
/**
* While loading LoD should be blocked. Only when GUI has finished
* loading and zoom level settled down, LoD is given a green
* light.
*/
m_d->bootstrapLodBlocked = true;
connect(view->mainWindow(), SIGNAL(guiLoadingFinished()), SLOT(bootstrapFinished()));
connect(view->mainWindow(), SIGNAL(screenChanged()), SLOT(slotConfigChanged()));
KisImageConfig config(false);
m_d->canvasUpdateCompressor.setDelay(1000 / config.fpsLimit());
m_d->canvasUpdateCompressor.setMode(KisSignalCompressor::FIRST_ACTIVE);
m_d->frameRenderStartCompressor.setDelay(1000 / config.fpsLimit());
m_d->frameRenderStartCompressor.setMode(KisSignalCompressor::FIRST_ACTIVE);
}
void KisCanvas2::setup()
{
// a bit of duplication from slotConfigChanged()
KisConfig cfg(true);
m_d->vastScrolling = cfg.vastScrolling();
m_d->lodAllowedInImage = cfg.levelOfDetailEnabled();
createCanvas(cfg.useOpenGL());
setLodAllowedInCanvas(m_d->lodAllowedInImage);
m_d->animationPlayer = new KisAnimationPlayer(this);
connect(m_d->view->canvasController()->proxyObject, SIGNAL(moveDocumentOffset(QPoint)), SLOT(documentOffsetMoved(QPoint)));
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
/**
* We switch the shape manager every time vector layer or
* shape selection is activated. Flake does not expect this
* and connects all the signals of the global shape manager
* to the clients in the constructor. To workaround this we
* forward the signals of local shape managers stored in the
* vector layers to the signals of global shape manager. So the
* sequence of signal deliveries is the following:
*
* shapeLayer.m_d.canvas.m_shapeManager.selection() ->
* shapeLayer ->
* shapeController ->
* globalShapeManager.selection()
*/
KisShapeController *kritaShapeController = static_cast<KisShapeController*>(shapeController()->documentBase());
connect(kritaShapeController, SIGNAL(selectionChanged()),
this, SLOT(slotSelectionChanged()));
connect(kritaShapeController, SIGNAL(selectionContentChanged()),
selectedShapesProxy(), SIGNAL(selectionContentChanged()));
connect(kritaShapeController, SIGNAL(currentLayerChanged(const KoShapeLayer*)),
selectedShapesProxy(), SIGNAL(currentLayerChanged(const KoShapeLayer*)));
connect(&m_d->canvasUpdateCompressor, SIGNAL(timeout()), SLOT(slotDoCanvasUpdate()));
connect(this, SIGNAL(sigCanvasCacheUpdated()), &m_d->frameRenderStartCompressor, SLOT(start()));
connect(&m_d->frameRenderStartCompressor, SIGNAL(timeout()), SLOT(updateCanvasProjection()));
connect(this, SIGNAL(sigContinueResizeImage(qint32,qint32)), SLOT(finishResizingImage(qint32,qint32)));
connect(&m_d->regionOfInterestUpdateCompressor, SIGNAL(timeout()), SLOT(slotUpdateRegionOfInterest()));
connect(m_d->view->document(), SIGNAL(sigReferenceImagesChanged()), this, SLOT(slotReferenceImagesChanged()));
initializeFpsDecoration();
}
void KisCanvas2::initializeFpsDecoration()
{
KisConfig cfg(true);
const bool shouldShowDebugOverlay =
(canvasIsOpenGL() && cfg.enableOpenGLFramerateLogging()) ||
cfg.enableBrushSpeedLogging();
if (shouldShowDebugOverlay && !decoration(KisFpsDecoration::idTag)) {
addDecoration(new KisFpsDecoration(imageView()));
if (cfg.enableBrushSpeedLogging()) {
connect(KisStrokeSpeedMonitor::instance(), SIGNAL(sigStatsUpdated()), this, SLOT(updateCanvas()));
}
} else if (!shouldShowDebugOverlay && decoration(KisFpsDecoration::idTag)) {
m_d->canvasWidget->removeDecoration(KisFpsDecoration::idTag);
disconnect(KisStrokeSpeedMonitor::instance(), SIGNAL(sigStatsUpdated()), this, SLOT(updateCanvas()));
}
}
KisCanvas2::~KisCanvas2()
{
if (m_d->animationPlayer->isPlaying()) {
m_d->animationPlayer->forcedStopOnExit();
}
delete m_d;
}
void KisCanvas2::setCanvasWidget(KisAbstractCanvasWidget *widget)
{
if (m_d->popupPalette) {
m_d->popupPalette->setParent(widget->widget());
}
if (m_d->canvasWidget != 0) {
widget->setDecorations(m_d->canvasWidget->decorations());
// Redundant check for the constructor case, see below
if(viewManager() != 0)
viewManager()->inputManager()->removeTrackedCanvas(this);
}
m_d->canvasWidget = widget;
// Either tmp was null or we are being called by KisCanvas2 constructor that is called by KisView
// constructor, so the view manager still doesn't exists.
if(m_d->canvasWidget != 0 && viewManager() != 0)
viewManager()->inputManager()->addTrackedCanvas(this);
if (!m_d->canvasWidget->decoration(INFINITY_DECORATION_ID)) {
KisInfinityManager *manager = new KisInfinityManager(m_d->view, this);
manager->setVisible(true);
m_d->canvasWidget->addDecoration(manager);
}
widget->widget()->setAutoFillBackground(false);
widget->widget()->setAttribute(Qt::WA_OpaquePaintEvent);
widget->widget()->setMouseTracking(true);
widget->widget()->setAcceptDrops(true);
KoCanvasControllerWidget *controller = dynamic_cast<KoCanvasControllerWidget*>(canvasController());
if (controller && controller->canvas() == this) {
controller->changeCanvasWidget(widget->widget());
}
}
bool KisCanvas2::canvasIsOpenGL() const
{
return m_d->currentCanvasIsOpenGL;
}
KisOpenGL::FilterMode KisCanvas2::openGLFilterMode() const
{
return KisOpenGL::FilterMode(m_d->openGLFilterMode);
}
void KisCanvas2::gridSize(QPointF *offset, QSizeF *spacing) const
{
QTransform transform = coordinatesConverter()->imageToDocumentTransform();
const QPoint intSpacing = m_d->view->document()->gridConfig().spacing();
const QPoint intOffset = m_d->view->document()->gridConfig().offset();
QPointF size = transform.map(QPointF(intSpacing));
spacing->rwidth() = size.x();
spacing->rheight() = size.y();
*offset = transform.map(QPointF(intOffset));
}
bool KisCanvas2::snapToGrid() const
{
return m_d->view->document()->gridConfig().snapToGrid();
}
qreal KisCanvas2::rotationAngle() const
{
return m_d->coordinatesConverter->rotationAngle();
}
bool KisCanvas2::xAxisMirrored() const
{
return m_d->coordinatesConverter->xAxisMirrored();
}
bool KisCanvas2::yAxisMirrored() const
{
return m_d->coordinatesConverter->yAxisMirrored();
}
void KisCanvas2::channelSelectionChanged()
{
KisImageSP image = this->image();
m_d->channelFlags = image->rootLayer()->channelFlags();
m_d->view->viewManager()->blockUntilOperationsFinishedForced(image);
image->barrierLock();
m_d->canvasWidget->channelSelectionChanged(m_d->channelFlags);
startUpdateInPatches(image->bounds());
image->unlock();
}
void KisCanvas2::addCommand(KUndo2Command *command)
{
// This method exists to support flake-related operations
m_d->view->document()->addCommand(command);
}
void KisCanvas2::KisCanvas2Private::setActiveShapeManager(KoShapeManager *shapeManager)
{
if (shapeManager != currentlyActiveShapeManager) {
currentlyActiveShapeManager = shapeManager;
selectedShapesProxy.setShapeManager(shapeManager);
}
}
KoShapeManager* KisCanvas2::shapeManager() const
{
KoShapeManager *localShapeManager = this->localShapeManager();
// sanity check for consistency of the local shape manager
KIS_SAFE_ASSERT_RECOVER (localShapeManager == m_d->currentlyActiveShapeManager) {
localShapeManager = globalShapeManager();
}
return localShapeManager ? localShapeManager : globalShapeManager();
}
KoSelectedShapesProxy* KisCanvas2::selectedShapesProxy() const
{
return &m_d->selectedShapesProxy;
}
KoShapeManager* KisCanvas2::globalShapeManager() const
{
return &m_d->shapeManager;
}
KoShapeManager *KisCanvas2::localShapeManager() const
{
KisNodeSP node = m_d->view->currentNode();
KoShapeManager *localShapeManager = fetchShapeManagerFromNode(node);
if (localShapeManager != m_d->currentlyActiveShapeManager) {
m_d->setActiveShapeManager(localShapeManager);
}
return localShapeManager;
}
void KisCanvas2::updateInputMethodInfo()
{
// TODO call (the protected) QWidget::updateMicroFocus() on the proper canvas widget...
}
const KisCoordinatesConverter* KisCanvas2::coordinatesConverter() const
{
return m_d->coordinatesConverter;
}
KoViewConverter* KisCanvas2::viewConverter() const
{
return m_d->coordinatesConverter;
}
KisInputManager* KisCanvas2::globalInputManager() const
{
return m_d->view->globalInputManager();
}
KisInputActionGroupsMask KisCanvas2::inputActionGroupsMask() const
{
return m_d->inputActionGroupsMask;
}
void KisCanvas2::setInputActionGroupsMask(KisInputActionGroupsMask mask)
{
m_d->inputActionGroupsMask = mask;
}
QWidget* KisCanvas2::canvasWidget()
{
return m_d->canvasWidget->widget();
}
const QWidget* KisCanvas2::canvasWidget() const
{
return m_d->canvasWidget->widget();
}
KoUnit KisCanvas2::unit() const
{
KoUnit unit(KoUnit::Pixel);
KisImageWSP image = m_d->view->image();
if (image) {
if (!qFuzzyCompare(image->xRes(), image->yRes())) {
warnKrita << "WARNING: resolution of the image is anisotropic"
<< ppVar(image->xRes())
<< ppVar(image->yRes());
}
const qreal resolution = image->xRes();
unit.setFactor(resolution);
}
return unit;
}
KoToolProxy * KisCanvas2::toolProxy() const
{
return &m_d->toolProxy;
}
void KisCanvas2::createQPainterCanvas()
{
m_d->currentCanvasIsOpenGL = false;
KisQPainterCanvas * canvasWidget = new KisQPainterCanvas(this, m_d->coordinatesConverter, m_d->view);
m_d->prescaledProjection = new KisPrescaledProjection();
m_d->prescaledProjection->setCoordinatesConverter(m_d->coordinatesConverter);
m_d->prescaledProjection->setMonitorProfile(m_d->displayColorConverter.monitorProfile(),
m_d->displayColorConverter.renderingIntent(),
m_d->displayColorConverter.conversionFlags());
m_d->prescaledProjection->setDisplayFilter(m_d->displayColorConverter.displayFilter());
canvasWidget->setPrescaledProjection(m_d->prescaledProjection);
setCanvasWidget(canvasWidget);
}
void KisCanvas2::createOpenGLCanvas()
{
KisConfig cfg(true);
m_d->openGLFilterMode = cfg.openGLFilteringMode();
m_d->currentCanvasIsOpenGL = true;
KisOpenGLCanvas2 *canvasWidget = new KisOpenGLCanvas2(this, m_d->coordinatesConverter, 0, m_d->view->image(), &m_d->displayColorConverter);
m_d->frameCache = KisAnimationFrameCache::getFrameCache(canvasWidget->openGLImageTextures());
setCanvasWidget(canvasWidget);
}
void KisCanvas2::createCanvas(bool useOpenGL)
{
// deinitialize previous canvas structures
m_d->prescaledProjection = 0;
m_d->frameCache = 0;
KisConfig cfg(true);
QDesktopWidget dw;
const KoColorProfile *profile = cfg.displayProfile(dw.screenNumber(imageView()));
m_d->displayColorConverter.notifyOpenGLCanvasIsActive(useOpenGL && KisOpenGL::hasOpenGL());
m_d->displayColorConverter.setMonitorProfile(profile);
if (useOpenGL && !KisOpenGL::hasOpenGL()) {
warnKrita << "Tried to create OpenGL widget when system doesn't have OpenGL\n";
useOpenGL = false;
}
m_d->displayColorConverter.notifyOpenGLCanvasIsActive(useOpenGL);
if (useOpenGL) {
createOpenGLCanvas();
if (cfg.canvasState() == "OPENGL_FAILED") {
// Creating the opengl canvas failed, fall back
warnKrita << "OpenGL Canvas initialization returned OPENGL_FAILED. Falling back to QPainter.";
m_d->displayColorConverter.notifyOpenGLCanvasIsActive(false);
createQPainterCanvas();
}
} else {
createQPainterCanvas();
}
if (m_d->popupPalette) {
m_d->popupPalette->setParent(m_d->canvasWidget->widget());
}
}
void KisCanvas2::initializeImage()
{
KisImageSP image = m_d->view->image();
m_d->displayColorConverter.setImageColorSpace(image->colorSpace());
m_d->coordinatesConverter->setImage(image);
m_d->toolProxy.initializeImage(image);
connect(image, SIGNAL(sigImageUpdated(QRect)), SLOT(startUpdateCanvasProjection(QRect)), Qt::DirectConnection);
connect(image->signalRouter(), SIGNAL(sigNotifyBatchUpdateStarted()), SLOT(slotBeginUpdatesBatch()), Qt::DirectConnection);
connect(image->signalRouter(), SIGNAL(sigNotifyBatchUpdateEnded()), SLOT(slotEndUpdatesBatch()), Qt::DirectConnection);
connect(image->signalRouter(), SIGNAL(sigRequestLodPlanesSyncBlocked(bool)), SLOT(slotSetLodUpdatesBlocked(bool)), Qt::DirectConnection);
connect(image, SIGNAL(sigProofingConfigChanged()), SLOT(slotChangeProofingConfig()));
connect(image, SIGNAL(sigSizeChanged(QPointF,QPointF)), SLOT(startResizingImage()), Qt::DirectConnection);
connect(image->undoAdapter(), SIGNAL(selectionChanged()), SLOT(slotTrySwitchShapeManager()));
connect(image, SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), SLOT(slotImageColorSpaceChanged()));
connect(image, SIGNAL(sigProfileChanged(const KoColorProfile*)), SLOT(slotImageColorSpaceChanged()));
connectCurrentCanvas();
}
void KisCanvas2::connectCurrentCanvas()
{
KisImageWSP image = m_d->view->image();
if (!m_d->currentCanvasIsOpenGL) {
Q_ASSERT(m_d->prescaledProjection);
m_d->prescaledProjection->setImage(image);
}
startResizingImage();
setLodAllowedInCanvas(m_d->lodAllowedInImage);
emit sigCanvasEngineChanged();
}
void KisCanvas2::resetCanvas(bool useOpenGL)
{
// we cannot reset the canvas before it's created, but this method might be called,
// for instance when setting the monitor profile.
if (!m_d->canvasWidget) {
return;
}
KisConfig cfg(true);
bool needReset = (m_d->currentCanvasIsOpenGL != useOpenGL) ||
(m_d->currentCanvasIsOpenGL &&
m_d->openGLFilterMode != cfg.openGLFilteringMode());
if (needReset) {
createCanvas(useOpenGL);
connectCurrentCanvas();
notifyZoomChanged();
}
updateCanvasWidgetImpl();
}
void KisCanvas2::startUpdateInPatches(const QRect &imageRect)
{
/**
* We don't do patched loading for openGL canvas, becasue it loads
* the tiles, which are bascially "patches". Therefore, big chunks
* of memory are never allocated.
*/
if (m_d->currentCanvasIsOpenGL) {
startUpdateCanvasProjection(imageRect);
} else {
KisImageConfig imageConfig(true);
int patchWidth = imageConfig.updatePatchWidth();
int patchHeight = imageConfig.updatePatchHeight();
for (int y = 0; y < imageRect.height(); y += patchHeight) {
for (int x = 0; x < imageRect.width(); x += patchWidth) {
QRect patchRect(x, y, patchWidth, patchHeight);
startUpdateCanvasProjection(patchRect);
}
}
}
}
void KisCanvas2::setDisplayFilter(QSharedPointer<KisDisplayFilter> displayFilter)
{
m_d->displayColorConverter.setDisplayFilter(displayFilter);
KisImageSP image = this->image();
m_d->view->viewManager()->blockUntilOperationsFinishedForced(image);
image->barrierLock();
m_d->canvasWidget->setDisplayFilter(displayFilter);
image->unlock();
}
QSharedPointer<KisDisplayFilter> KisCanvas2::displayFilter() const
{
return m_d->displayColorConverter.displayFilter();
}
void KisCanvas2::slotImageColorSpaceChanged()
{
KisImageSP image = this->image();
m_d->view->viewManager()->blockUntilOperationsFinishedForced(image);
m_d->displayColorConverter.setImageColorSpace(image->colorSpace());
image->barrierLock();
m_d->canvasWidget->notifyImageColorSpaceChanged(image->colorSpace());
image->unlock();
}
KisDisplayColorConverter* KisCanvas2::displayColorConverter() const
{
return &m_d->displayColorConverter;
}
KisExposureGammaCorrectionInterface* KisCanvas2::exposureGammaCorrectionInterface() const
{
QSharedPointer<KisDisplayFilter> displayFilter = m_d->displayColorConverter.displayFilter();
return displayFilter ?
displayFilter->correctionInterface() :
KisDumbExposureGammaCorrectionInterface::instance();
}
void KisCanvas2::setProofingOptions(bool softProof, bool gamutCheck)
{
m_d->proofingConfig = this->image()->proofingConfiguration();
if (!m_d->proofingConfig) {
KisImageConfig cfg(false);
m_d->proofingConfig = cfg.defaultProofingconfiguration();
}
KoColorConversionTransformation::ConversionFlags conversionFlags = m_d->proofingConfig->conversionFlags;
#if QT_VERSION >= 0x050700
if (this->image()->colorSpace()->colorDepthId().id().contains("U")) {
conversionFlags.setFlag(KoColorConversionTransformation::SoftProofing, softProof);
if (softProof) {
conversionFlags.setFlag(KoColorConversionTransformation::GamutCheck, gamutCheck);
}
}
#else
if (this->image()->colorSpace()->colorDepthId().id().contains("U")) {
conversionFlags |= KoColorConversionTransformation::SoftProofing;
} else {
conversionFlags = conversionFlags & ~KoColorConversionTransformation::SoftProofing;
}
if (gamutCheck && softProof && this->image()->colorSpace()->colorDepthId().id().contains("U")) {
conversionFlags |= KoColorConversionTransformation::GamutCheck;
} else {
conversionFlags = conversionFlags & ~KoColorConversionTransformation::GamutCheck;
}
#endif
m_d->proofingConfig->conversionFlags = conversionFlags;
m_d->proofingConfigUpdated = true;
startUpdateInPatches(this->image()->bounds());
}
void KisCanvas2::slotSoftProofing(bool softProofing)
{
m_d->softProofing = softProofing;
setProofingOptions(m_d->softProofing, m_d->gamutCheck);
}
void KisCanvas2::slotGamutCheck(bool gamutCheck)
{
m_d->gamutCheck = gamutCheck;
setProofingOptions(m_d->softProofing, m_d->gamutCheck);
}
void KisCanvas2::slotChangeProofingConfig()
{
setProofingOptions(m_d->softProofing, m_d->gamutCheck);
}
void KisCanvas2::setProofingConfigUpdated(bool updated)
{
m_d->proofingConfigUpdated = updated;
}
bool KisCanvas2::proofingConfigUpdated()
{
return m_d->proofingConfigUpdated;
}
KisProofingConfigurationSP KisCanvas2::proofingConfiguration() const
{
if (!m_d->proofingConfig) {
m_d->proofingConfig = this->image()->proofingConfiguration();
if (!m_d->proofingConfig) {
m_d->proofingConfig = KisImageConfig(true).defaultProofingconfiguration();
}
}
return m_d->proofingConfig;
}
void KisCanvas2::startResizingImage()
{
KisImageWSP image = this->image();
qint32 w = image->width();
qint32 h = image->height();
emit sigContinueResizeImage(w, h);
QRect imageBounds(0, 0, w, h);
startUpdateInPatches(imageBounds);
}
void KisCanvas2::finishResizingImage(qint32 w, qint32 h)
{
m_d->canvasWidget->finishResizingImage(w, h);
}
void KisCanvas2::startUpdateCanvasProjection(const QRect & rc)
{
KisUpdateInfoSP info = m_d->canvasWidget->startUpdateCanvasProjection(rc, m_d->channelFlags);
if (m_d->projectionUpdatesCompressor.putUpdateInfo(info)) {
emit sigCanvasCacheUpdated();
}
}
void KisCanvas2::updateCanvasProjection()
{
auto tryIssueCanvasUpdates = [this](const QRect &vRect) {
if (!m_d->isBatchUpdateActive) {
// TODO: Implement info->dirtyViewportRect() for KisOpenGLCanvas2 to avoid updating whole canvas
if (m_d->currentCanvasIsOpenGL) {
m_d->savedUpdateRect = QRect();
// we already had a compression in frameRenderStartCompressor, so force the update directly
slotDoCanvasUpdate();
} else if (/* !m_d->currentCanvasIsOpenGL && */ !vRect.isEmpty()) {
m_d->savedUpdateRect = m_d->coordinatesConverter->viewportToWidget(vRect).toAlignedRect();
// we already had a compression in frameRenderStartCompressor, so force the update directly
slotDoCanvasUpdate();
}
}
};
auto uploadData = [this, tryIssueCanvasUpdates](const QVector<KisUpdateInfoSP> &infoObjects) {
QVector<QRect> viewportRects = m_d->canvasWidget->updateCanvasProjection(infoObjects);
const QRect vRect = std::accumulate(viewportRects.constBegin(), viewportRects.constEnd(),
QRect(), std::bit_or<QRect>());
tryIssueCanvasUpdates(vRect);
};
bool shouldExplicitlyIssueUpdates = false;
QVector<KisUpdateInfoSP> infoObjects;
- while (KisUpdateInfoSP info = m_d->projectionUpdatesCompressor.takeUpdateInfo()) {
+ KisUpdateInfoList originalInfoObjects;
+ m_d->projectionUpdatesCompressor.takeUpdateInfo(originalInfoObjects);
+
+ for (auto it = originalInfoObjects.constBegin();
+ it != originalInfoObjects.constEnd();
+ ++it) {
+
+ KisUpdateInfoSP info = *it;
+
const KisMarkerUpdateInfo *batchInfo = dynamic_cast<const KisMarkerUpdateInfo*>(info.data());
if (batchInfo) {
if (!infoObjects.isEmpty()) {
uploadData(infoObjects);
infoObjects.clear();
}
if (batchInfo->type() == KisMarkerUpdateInfo::StartBatch) {
m_d->isBatchUpdateActive++;
} else if (batchInfo->type() == KisMarkerUpdateInfo::EndBatch) {
m_d->isBatchUpdateActive--;
KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->isBatchUpdateActive >= 0);
if (m_d->isBatchUpdateActive == 0) {
shouldExplicitlyIssueUpdates = true;
}
} else if (batchInfo->type() == KisMarkerUpdateInfo::BlockLodUpdates) {
m_d->canvasWidget->setLodResetInProgress(true);
} else if (batchInfo->type() == KisMarkerUpdateInfo::UnblockLodUpdates) {
m_d->canvasWidget->setLodResetInProgress(false);
shouldExplicitlyIssueUpdates = true;
}
} else {
infoObjects << info;
}
}
if (!infoObjects.isEmpty()) {
uploadData(infoObjects);
} else if (shouldExplicitlyIssueUpdates) {
tryIssueCanvasUpdates(m_d->coordinatesConverter->imageRectInImagePixels());
}
}
void KisCanvas2::slotBeginUpdatesBatch()
{
KisUpdateInfoSP info =
new KisMarkerUpdateInfo(KisMarkerUpdateInfo::StartBatch,
m_d->coordinatesConverter->imageRectInImagePixels());
m_d->projectionUpdatesCompressor.putUpdateInfo(info);
emit sigCanvasCacheUpdated();
}
void KisCanvas2::slotEndUpdatesBatch()
{
KisUpdateInfoSP info =
new KisMarkerUpdateInfo(KisMarkerUpdateInfo::EndBatch,
m_d->coordinatesConverter->imageRectInImagePixels());
m_d->projectionUpdatesCompressor.putUpdateInfo(info);
emit sigCanvasCacheUpdated();
}
void KisCanvas2::slotSetLodUpdatesBlocked(bool value)
{
KisUpdateInfoSP info =
new KisMarkerUpdateInfo(value ?
KisMarkerUpdateInfo::BlockLodUpdates :
KisMarkerUpdateInfo::UnblockLodUpdates,
m_d->coordinatesConverter->imageRectInImagePixels());
m_d->projectionUpdatesCompressor.putUpdateInfo(info);
emit sigCanvasCacheUpdated();
}
void KisCanvas2::slotDoCanvasUpdate()
{
/**
* WARNING: in isBusy() we access openGL functions without making the painting
* context current. We hope that currently active context will be Qt's one,
* which is shared with our own.
*/
if (m_d->canvasWidget->isBusy()) {
// just restarting the timer
updateCanvasWidgetImpl(m_d->savedUpdateRect);
return;
}
if (m_d->savedUpdateRect.isEmpty()) {
m_d->canvasWidget->widget()->update();
emit updateCanvasRequested(m_d->canvasWidget->widget()->rect());
} else {
emit updateCanvasRequested(m_d->savedUpdateRect);
m_d->canvasWidget->widget()->update(m_d->savedUpdateRect);
}
m_d->savedUpdateRect = QRect();
}
void KisCanvas2::updateCanvasWidgetImpl(const QRect &rc)
{
if (!m_d->canvasUpdateCompressor.isActive() ||
!m_d->savedUpdateRect.isEmpty()) {
m_d->savedUpdateRect |= rc;
}
m_d->canvasUpdateCompressor.start();
}
void KisCanvas2::updateCanvas()
{
updateCanvasWidgetImpl();
}
void KisCanvas2::updateCanvas(const QRectF& documentRect)
{
if (m_d->currentCanvasIsOpenGL && m_d->canvasWidget->decorations().size() > 0) {
updateCanvasWidgetImpl();
}
else {
// updateCanvas is called from tools, never from the projection
// updates, so no need to prescale!
QRect widgetRect = m_d->coordinatesConverter->documentToWidget(documentRect).toAlignedRect();
widgetRect.adjust(-2, -2, 2, 2);
if (!widgetRect.isEmpty()) {
updateCanvasWidgetImpl(widgetRect);
}
}
}
void KisCanvas2::disconnectCanvasObserver(QObject *object)
{
KoCanvasBase::disconnectCanvasObserver(object);
m_d->view->disconnect(object);
}
void KisCanvas2::notifyZoomChanged()
{
if (!m_d->currentCanvasIsOpenGL) {
Q_ASSERT(m_d->prescaledProjection);
m_d->prescaledProjection->notifyZoomChanged();
}
notifyLevelOfDetailChange();
updateCanvas(); // update the canvas, because that isn't done when zooming using KoZoomAction
m_d->regionOfInterestUpdateCompressor.start();
}
QRect KisCanvas2::regionOfInterest() const
{
return m_d->regionOfInterest;
}
void KisCanvas2::slotUpdateRegionOfInterest()
{
const QRect oldRegionOfInterest = m_d->regionOfInterest;
const qreal ratio = 0.25;
const QRect proposedRoi = KisAlgebra2D::blowRect(m_d->coordinatesConverter->widgetRectInImagePixels(), ratio).toAlignedRect();
const QRect imageRect = m_d->coordinatesConverter->imageRectInImagePixels();
m_d->regionOfInterest = imageRect.contains(proposedRoi) ? proposedRoi : imageRect;
if (m_d->regionOfInterest != oldRegionOfInterest) {
emit sigRegionOfInterestChanged(m_d->regionOfInterest);
}
}
void KisCanvas2::slotReferenceImagesChanged()
{
canvasController()->resetScrollBars();
}
void KisCanvas2::setRenderingLimit(const QRect &rc)
{
m_d->renderingLimit = rc;
}
QRect KisCanvas2::renderingLimit() const
{
return m_d->renderingLimit;
}
void KisCanvas2::slotTrySwitchShapeManager()
{
KisNodeSP node = m_d->view->currentNode();
QPointer<KoShapeManager> newManager;
newManager = fetchShapeManagerFromNode(node);
m_d->setActiveShapeManager(newManager);
}
void KisCanvas2::notifyLevelOfDetailChange()
{
if (!m_d->effectiveLodAllowedInImage()) return;
const qreal effectiveZoom = m_d->coordinatesConverter->effectiveZoom();
KisConfig cfg(true);
const int maxLod = cfg.numMipmapLevels();
const int lod = KisLodTransform::scaleToLod(effectiveZoom, maxLod);
if (m_d->effectiveLodAllowedInImage()) {
KisImageSP image = this->image();
image->setDesiredLevelOfDetail(lod);
}
}
const KoColorProfile * KisCanvas2::monitorProfile()
{
return m_d->displayColorConverter.monitorProfile();
}
KisViewManager* KisCanvas2::viewManager() const
{
if (m_d->view) {
return m_d->view->viewManager();
}
return 0;
}
QPointer<KisView>KisCanvas2::imageView() const
{
return m_d->view;
}
KisImageWSP KisCanvas2::image() const
{
return m_d->view->image();
}
KisImageWSP KisCanvas2::currentImage() const
{
return m_d->view->image();
}
void KisCanvas2::documentOffsetMoved(const QPoint &documentOffset)
{
QPointF offsetBefore = m_d->coordinatesConverter->imageRectInViewportPixels().topLeft();
m_d->coordinatesConverter->setDocumentOffset(documentOffset);
QPointF offsetAfter = m_d->coordinatesConverter->imageRectInViewportPixels().topLeft();
QPointF moveOffset = offsetAfter - offsetBefore;
if (!m_d->currentCanvasIsOpenGL)
m_d->prescaledProjection->viewportMoved(moveOffset);
emit documentOffsetUpdateFinished();
updateCanvas();
m_d->regionOfInterestUpdateCompressor.start();
}
void KisCanvas2::slotConfigChanged()
{
KisConfig cfg(true);
m_d->vastScrolling = cfg.vastScrolling();
resetCanvas(cfg.useOpenGL());
setDisplayProfile(cfg.displayProfile(QApplication::desktop()->screenNumber(this->canvasWidget())));
initializeFpsDecoration();
}
void KisCanvas2::refetchDataFromImage()
{
KisImageSP image = this->image();
KisImageBarrierLocker l(image);
startUpdateInPatches(image->bounds());
}
void KisCanvas2::setDisplayProfile(const KoColorProfile *monitorProfile)
{
if (m_d->displayColorConverter.monitorProfile() == monitorProfile) return;
m_d->displayColorConverter.setMonitorProfile(monitorProfile);
{
KisImageSP image = this->image();
KisImageBarrierLocker l(image);
m_d->canvasWidget->setDisplayColorConverter(&m_d->displayColorConverter);
}
refetchDataFromImage();
}
void KisCanvas2::addDecoration(KisCanvasDecorationSP deco)
{
m_d->canvasWidget->addDecoration(deco);
}
KisCanvasDecorationSP KisCanvas2::decoration(const QString& id) const
{
return m_d->canvasWidget->decoration(id);
}
QPoint KisCanvas2::documentOrigin() const
{
/**
* In Krita we don't use document origin anymore.
* All the centering when needed (vastScrolling < 0.5) is done
* automatically by the KisCoordinatesConverter.
*/
return QPoint();
}
QPoint KisCanvas2::documentOffset() const
{
return m_d->coordinatesConverter->documentOffset();
}
void KisCanvas2::setFavoriteResourceManager(KisFavoriteResourceManager* favoriteResourceManager)
{
m_d->popupPalette = new KisPopupPalette(viewManager(), m_d->coordinatesConverter, favoriteResourceManager, displayColorConverter()->displayRendererInterface(),
m_d->view->resourceProvider(), m_d->canvasWidget->widget());
connect(m_d->popupPalette, SIGNAL(zoomLevelChanged(int)), this, SLOT(slotPopupPaletteRequestedZoomChange(int)));
connect(m_d->popupPalette, SIGNAL(sigUpdateCanvas()), this, SLOT(updateCanvas()));
connect(m_d->view->mainWindow(), SIGNAL(themeChanged()), m_d->popupPalette, SLOT(slotUpdateIcons()));
m_d->popupPalette->showPopupPalette(false);
}
void KisCanvas2::slotPopupPaletteRequestedZoomChange(int zoom ) {
m_d->view->viewManager()->zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, (qreal)(zoom/100.0)); // 1.0 is 100% zoom
notifyZoomChanged();
}
void KisCanvas2::setCursor(const QCursor &cursor)
{
canvasWidget()->setCursor(cursor);
}
KisAnimationFrameCacheSP KisCanvas2::frameCache() const
{
return m_d->frameCache;
}
KisAnimationPlayer *KisCanvas2::animationPlayer() const
{
return m_d->animationPlayer;
}
void KisCanvas2::slotSelectionChanged()
{
KisShapeLayer* shapeLayer = dynamic_cast<KisShapeLayer*>(viewManager()->activeLayer().data());
if (!shapeLayer) {
return;
}
m_d->shapeManager.selection()->deselectAll();
Q_FOREACH (KoShape* shape, shapeLayer->shapeManager()->selection()->selectedShapes()) {
m_d->shapeManager.selection()->select(shape);
}
}
bool KisCanvas2::isPopupPaletteVisible() const
{
if (!m_d->popupPalette) {
return false;
}
return m_d->popupPalette->isVisible();
}
void KisCanvas2::setWrapAroundViewingMode(bool value)
{
KisCanvasDecorationSP infinityDecoration =
m_d->canvasWidget->decoration(INFINITY_DECORATION_ID);
if (infinityDecoration) {
infinityDecoration->setVisible(!value);
}
m_d->canvasWidget->setWrapAroundViewingMode(value);
}
bool KisCanvas2::wrapAroundViewingMode() const
{
KisCanvasDecorationSP infinityDecoration =
m_d->canvasWidget->decoration(INFINITY_DECORATION_ID);
if (infinityDecoration) {
return !(infinityDecoration->visible());
}
return false;
}
void KisCanvas2::bootstrapFinished()
{
if (!m_d->bootstrapLodBlocked) return;
m_d->bootstrapLodBlocked = false;
setLodAllowedInCanvas(m_d->lodAllowedInImage);
}
void KisCanvas2::setLodAllowedInCanvas(bool value)
{
if (!KisOpenGL::supportsLoD()) {
qWarning() << "WARNING: Level of Detail functionality is available only with openGL + GLSL 1.3 support";
}
m_d->lodAllowedInImage =
value &&
m_d->currentCanvasIsOpenGL &&
KisOpenGL::supportsLoD() &&
(m_d->openGLFilterMode == KisOpenGL::TrilinearFilterMode ||
m_d->openGLFilterMode == KisOpenGL::HighQualityFiltering);
KisImageSP image = this->image();
if (m_d->effectiveLodAllowedInImage() != !image->levelOfDetailBlocked()) {
image->setLevelOfDetailBlocked(!m_d->effectiveLodAllowedInImage());
}
notifyLevelOfDetailChange();
KisConfig cfg(false);
cfg.setLevelOfDetailEnabled(m_d->lodAllowedInImage);
}
bool KisCanvas2::lodAllowedInCanvas() const
{
return m_d->lodAllowedInImage;
}
void KisCanvas2::slotShowPopupPalette(const QPoint &p)
{
if (!m_d->popupPalette) {
return;
}
m_d->popupPalette->showPopupPalette(p);
}
KisPaintingAssistantsDecorationSP KisCanvas2::paintingAssistantsDecoration() const
{
KisCanvasDecorationSP deco = decoration("paintingAssistantsDecoration");
return qobject_cast<KisPaintingAssistantsDecoration*>(deco.data());
}
KisReferenceImagesDecorationSP KisCanvas2::referenceImagesDecoration() const
{
KisCanvasDecorationSP deco = decoration("referenceImagesDecoration");
return qobject_cast<KisReferenceImagesDecoration*>(deco.data());
}
diff --git a/libs/ui/canvas/kis_canvas_decoration.h b/libs/ui/canvas/kis_canvas_decoration.h
index da49f69285..59094ebb08 100644
--- a/libs/ui/canvas/kis_canvas_decoration.h
+++ b/libs/ui/canvas/kis_canvas_decoration.h
@@ -1,105 +1,108 @@
/*
* Copyright (c) 2008 Cyrille Berger <cberger@cberger.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _KIS_CANVAS_DECORATION_H_
#define _KIS_CANVAS_DECORATION_H_
#include <QObject>
#include <QPointer>
#include <kritaui_export.h>
#include <kis_image.h>
#include "KisView.h"
#include <kis_shared.h>
class KisCanvas2;
class QRectF;
class QPainter;
class KisCoordinatesConverter;
class KisCanvasDecoration;
typedef KisSharedPtr<KisCanvasDecoration> KisCanvasDecorationSP;
/**
* This class is the base class for object that draw a decoration on the canvas,
* for instance, selections, grids, tools, ...
*/
class KRITAUI_EXPORT KisCanvasDecoration : public QObject, public KisShared
{
Q_OBJECT
public:
KisCanvasDecoration(const QString& id, QPointer<KisView>parent);
~KisCanvasDecoration() override;
void setView(QPointer<KisView> imageView);
const QString& id() const;
/**
* @return whether the decoration is visible.
*/
bool visible() const;
/**
- * Will paint the decoration on the QPainter, if the visible is set to true.
+ * Will paint the decoration on the QPainter, if the visible is set to @c true.
*
+ * @param gc the painter
* @param updateRect dirty rect in document pixels
+ * @param converter coordinate converter
+ * @param canvas the canvas
*/
void paint(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter,KisCanvas2* canvas);
/**
* Return z-order priority of the decoration. The higher the priority, the higher
* the decoration is painted.
*/
int priority() const;
static bool comparePriority(KisCanvasDecorationSP decoration1, KisCanvasDecorationSP decoration2);
public Q_SLOTS:
/**
* Set if the decoration is visible or not.
*/
virtual void setVisible(bool v);
/**
* If decoration is visible, hide it, if not show it.
*/
void toggleVisibility();
protected:
virtual void drawDecoration(QPainter& gc, const QRectF& updateArea, const KisCoordinatesConverter *converter,KisCanvas2* canvas) = 0;
/// XXX: unify view and imageview!
QPointer<KisView>imageView();
/**
* @return the parent KisView
*/
QPointer<KisView> view() const;
/**
* Set the priority of the decoration. The higher the priority, the higher
* the decoration is painted.
*/
void setPriority(int value);
private:
struct Private;
Private* const d;
};
#endif
diff --git a/libs/ui/canvas/kis_canvas_updates_compressor.cpp b/libs/ui/canvas/kis_canvas_updates_compressor.cpp
index c016e66d0e..e5e2769a49 100644
--- a/libs/ui/canvas/kis_canvas_updates_compressor.cpp
+++ b/libs/ui/canvas/kis_canvas_updates_compressor.cpp
@@ -1,54 +1,56 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_canvas_updates_compressor.h"
bool KisCanvasUpdatesCompressor::putUpdateInfo(KisUpdateInfoSP info)
{
const int levelOfDetail = info->levelOfDetail();
const QRect newUpdateRect = info->dirtyImageRect();
if (newUpdateRect.isEmpty()) return false;
QMutexLocker l(&m_mutex);
- UpdateInfoList::iterator it = m_updatesList.begin();
+ KisUpdateInfoList::iterator it = m_updatesList.begin();
while (it != m_updatesList.end()) {
if (levelOfDetail == (*it)->levelOfDetail() &&
newUpdateRect.contains((*it)->dirtyImageRect())) {
/**
* We should always remove the overridden update and put 'info' to the end
* of the queue. Otherwise, the updates will become reordered and the canvas
* may have tiles artifacts with "outdated" data
*/
it = m_updatesList.erase(it);
} else {
++it;
}
}
m_updatesList.append(info);
return m_updatesList.size() <= 1;
}
-KisUpdateInfoSP KisCanvasUpdatesCompressor::takeUpdateInfo()
+void KisCanvasUpdatesCompressor::takeUpdateInfo(KisUpdateInfoList &list)
{
+ KIS_SAFE_ASSERT_RECOVER(list.isEmpty()) { list.clear(); }
+
QMutexLocker l(&m_mutex);
- return !m_updatesList.isEmpty() ? m_updatesList.takeFirst() : 0;
+ m_updatesList.swap(list);
}
diff --git a/libs/ui/canvas/kis_canvas_updates_compressor.h b/libs/ui/canvas/kis_canvas_updates_compressor.h
index b13018f3d9..21b38d4d0b 100644
--- a/libs/ui/canvas/kis_canvas_updates_compressor.h
+++ b/libs/ui/canvas/kis_canvas_updates_compressor.h
@@ -1,42 +1,41 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_CANVAS_UPDATES_COMPRESSOR_H
#define __KIS_CANVAS_UPDATES_COMPRESSOR_H
#include <QList>
#include <QMutex>
#include <QMutexLocker>
#include "kis_update_info.h"
+typedef QList<KisUpdateInfoSP> KisUpdateInfoList;
class KisCanvasUpdatesCompressor
{
- typedef QList<KisUpdateInfoSP> UpdateInfoList;
-
public:
bool putUpdateInfo(KisUpdateInfoSP info);
- KisUpdateInfoSP takeUpdateInfo();
+ void takeUpdateInfo(KisUpdateInfoList &list);
private:
QMutex m_mutex;
- UpdateInfoList m_updatesList;
+ KisUpdateInfoList m_updatesList;
};
#endif /* __KIS_CANVAS_UPDATES_COMPRESSOR_H */
diff --git a/libs/ui/canvas/kis_display_color_converter.cpp b/libs/ui/canvas/kis_display_color_converter.cpp
index debf26f440..96eaf76a14 100644
--- a/libs/ui/canvas/kis_display_color_converter.cpp
+++ b/libs/ui/canvas/kis_display_color_converter.cpp
@@ -1,697 +1,697 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_display_color_converter.h"
#include <QGlobalStatic>
#include <QPointer>
#include <KoColor.h>
#include <KoColorDisplayRendererInterface.h>
#include <KoColorSpaceMaths.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorModelStandardIds.h>
#include <KoColorConversions.h>
#include <KoCanvasResourceProvider.h>
#include "kis_config_notifier.h"
#include "kis_canvas_resource_provider.h"
#include "kis_canvas2.h"
#include "KisViewManager.h"
#include "kis_image.h"
#include "kis_node.h"
#include "kundo2command.h"
#include "kis_config.h"
#include "kis_paint_device.h"
#include "kis_iterator_ng.h"
#include "kis_fixed_paint_device.h"
#include "opengl/KisOpenGLModeProber.h"
Q_GLOBAL_STATIC(KisDisplayColorConverter, s_instance)
struct KisDisplayColorConverter::Private
{
Private(KisDisplayColorConverter *_q, KoCanvasResourceProvider *_resourceManager)
: q(_q),
resourceManager(_resourceManager),
nodeColorSpace(0),
paintingColorSpace(0),
monitorProfile(0),
renderingIntent(KoColorConversionTransformation::internalRenderingIntent()),
conversionFlags(KoColorConversionTransformation::internalConversionFlags()),
displayFilter(0),
displayRenderer(new DisplayRenderer(_q, _resourceManager))
{
useHDRMode = KisOpenGLModeProber::instance()->useHDRMode();
}
KisDisplayColorConverter *const q;
KoCanvasResourceProvider *resourceManager;
const KoColorSpace *nodeColorSpace;
const KoColorSpace *paintingColorSpace;
const KoColorProfile* inputImageProfile = 0;
const KoColorProfile* qtWidgetsProfile() const {
return useHDRMode ? KoColorSpaceRegistry::instance()->p709SRGBProfile() : monitorProfile;
}
const KoColorProfile* openGLSurfaceProfile() const {
return useHDRMode && openGLCanvasIsActive ? KisOpenGLModeProber::instance()->rootSurfaceColorProfile() : monitorProfile;
}
const KoColorProfile* ocioInputProfile() const {
return displayFilter && displayFilter->useInternalColorManagement() ?
openGLSurfaceProfile() : inputImageProfile;
}
const KoColorProfile* ocioOutputProfile() const {
return openGLSurfaceProfile();
}
const KoColorSpace* ocioInputColorSpace() const {
return KoColorSpaceRegistry::instance()->
colorSpace(
RGBAColorModelID.id(),
Float32BitsColorDepthID.id(),
ocioInputProfile());
}
const KoColorSpace* ocioOutputColorSpace() const {
return KoColorSpaceRegistry::instance()->
colorSpace(
RGBAColorModelID.id(),
Float32BitsColorDepthID.id(),
ocioOutputProfile());
}
const KoColorSpace* qtWidgetsColorSpace() const {
return KoColorSpaceRegistry::instance()->
colorSpace(
RGBAColorModelID.id(),
Integer8BitsColorDepthID.id(),
qtWidgetsProfile());
}
const KoColorSpace* openGLSurfaceColorSpace(const KoID &bitDepthId) const {
return KoColorSpaceRegistry::instance()->
colorSpace(
RGBAColorModelID.id(),
bitDepthId.id(),
openGLSurfaceProfile());
}
const KoColorSpace* intermediateColorSpace() const {
// the color space where we apply exposure and
// gamma should always be linear
return KoColorSpaceRegistry::instance()->
colorSpace(
RGBAColorModelID.id(),
Float32BitsColorDepthID.id(),
KoColorSpaceRegistry::instance()->p2020G10Profile());
}
const KoColorProfile *monitorProfile;
KoColorConversionTransformation::Intent renderingIntent;
KoColorConversionTransformation::ConversionFlags conversionFlags;
QSharedPointer<KisDisplayFilter> displayFilter;
KoColor intermediateFgColor;
KisNodeSP connectedNode;
KisImageSP image;
bool useHDRMode = false;
bool openGLCanvasIsActive = false;
inline KoColor approximateFromQColor(const QColor &qcolor);
inline QColor approximateToQColor(const KoColor &color);
void slotCanvasResourceChanged(int key, const QVariant &v);
void slotUpdateCurrentNodeColorSpace();
void selectPaintingColorSpace();
void updateIntermediateFgColor(const KoColor &color);
void setCurrentNode(KisNodeSP node);
bool useOcio() const;
class DisplayRenderer : public KoColorDisplayRendererInterface {
public:
DisplayRenderer(KisDisplayColorConverter *displayColorConverter, KoCanvasResourceProvider *resourceManager)
: m_displayColorConverter(displayColorConverter),
m_resourceManager(resourceManager)
{
displayColorConverter->connect(displayColorConverter, SIGNAL(displayConfigurationChanged()),
- this, SIGNAL(displayConfigurationChanged()));
+ this, SIGNAL(displayConfigurationChanged()), Qt::UniqueConnection);
}
QImage convertToQImage(const KoColorSpace *srcColorSpace, const quint8 *data, qint32 width, qint32 height) const override {
KisPaintDeviceSP dev = new KisPaintDevice(srcColorSpace);
dev->writeBytes(data, 0, 0, width, height);
return m_displayColorConverter->toQImage(dev);
}
QColor toQColor(const KoColor &c) const override {
return m_displayColorConverter->toQColor(c);
}
KoColor approximateFromRenderedQColor(const QColor &c) const override {
return m_displayColorConverter->approximateFromRenderedQColor(c);
}
KoColor fromHsv(int h, int s, int v, int a) const override {
return m_displayColorConverter->fromHsv(h, s, v, a);
}
void getHsv(const KoColor &srcColor, int *h, int *s, int *v, int *a) const override {
m_displayColorConverter->getHsv(srcColor, h, s, v, a);
}
qreal minVisibleFloatValue(const KoChannelInfo *chaninfo) const override {
return chaninfo->getUIMin();
}
qreal maxVisibleFloatValue(const KoChannelInfo *chaninfo) const override {
qreal maxValue = chaninfo->getUIMax();
if (m_resourceManager) {
qreal exposure = m_resourceManager->resource(KisCanvasResourceProvider::HdrExposure).value<qreal>();
// not sure if *= is what we want
maxValue *= std::pow(2.0, -exposure);
}
return maxValue;
}
const KoColorSpace* getPaintingColorSpace() const override {
return m_displayColorConverter->paintingColorSpace();
}
private:
KisDisplayColorConverter *m_displayColorConverter;
QPointer<KoCanvasResourceProvider> m_resourceManager;
};
QScopedPointer<KoColorDisplayRendererInterface> displayRenderer;
};
KisDisplayColorConverter::KisDisplayColorConverter(KoCanvasResourceProvider *resourceManager, QObject *parent)
: QObject(parent),
m_d(new Private(this, resourceManager))
{
connect(m_d->resourceManager, SIGNAL(canvasResourceChanged(int,QVariant)),
SLOT(slotCanvasResourceChanged(int,QVariant)));
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()),
SLOT(selectPaintingColorSpace()));
m_d->inputImageProfile = KoColorSpaceRegistry::instance()->p709SRGBProfile();
m_d->setCurrentNode(0);
setMonitorProfile(0);
setDisplayFilter(QSharedPointer<KisDisplayFilter>(0));
}
KisDisplayColorConverter::KisDisplayColorConverter()
: m_d(new Private(this, 0))
{
setDisplayFilter(QSharedPointer<KisDisplayFilter>(0));
m_d->inputImageProfile = KoColorSpaceRegistry::instance()->p709SRGBProfile();
m_d->paintingColorSpace = KoColorSpaceRegistry::instance()->rgb8();
m_d->setCurrentNode(0);
setMonitorProfile(0);
}
KisDisplayColorConverter::~KisDisplayColorConverter()
{
}
void KisDisplayColorConverter::setImageColorSpace(const KoColorSpace *cs)
{
m_d->inputImageProfile =
cs->colorModelId() == RGBAColorModelID ?
cs->profile() :
KoColorSpaceRegistry::instance()->p709SRGBProfile();
emit displayConfigurationChanged();
}
KisDisplayColorConverter* KisDisplayColorConverter::dumbConverterInstance()
{
return s_instance;
}
KoColorDisplayRendererInterface* KisDisplayColorConverter::displayRendererInterface() const
{
return m_d->displayRenderer.data();
}
bool KisDisplayColorConverter::Private::useOcio() const
{
return displayFilter && paintingColorSpace && paintingColorSpace->colorModelId() == RGBAColorModelID;
}
void KisDisplayColorConverter::Private::updateIntermediateFgColor(const KoColor &srcColor)
{
KIS_ASSERT_RECOVER_RETURN(displayFilter);
KoColor color = srcColor;
color.convertTo(intermediateColorSpace());
displayFilter->approximateForwardTransformation(color.data(), 1);
intermediateFgColor = color;
}
void KisDisplayColorConverter::Private::slotCanvasResourceChanged(int key, const QVariant &v)
{
if (key == KisCanvasResourceProvider::CurrentKritaNode) {
KisNodeSP currentNode = v.value<KisNodeWSP>();
setCurrentNode(currentNode);
} else if (useOcio() && key == KoCanvasResourceProvider::ForegroundColor) {
updateIntermediateFgColor(v.value<KoColor>());
}
}
void KisDisplayColorConverter::Private::slotUpdateCurrentNodeColorSpace()
{
setCurrentNode(connectedNode);
}
inline KisPaintDeviceSP findValidDevice(KisNodeSP node) {
return node->paintDevice() ? node->paintDevice() : node->original();
}
void KisDisplayColorConverter::Private::setCurrentNode(KisNodeSP node)
{
if (connectedNode) {
KisPaintDeviceSP device = findValidDevice(connectedNode);
if (device) {
q->disconnect(device, 0);
}
}
nodeColorSpace = 0;
if (node) {
KisPaintDeviceSP device = findValidDevice(node);
nodeColorSpace = device ?
device->compositionSourceColorSpace() :
node->colorSpace();
KIS_SAFE_ASSERT_RECOVER_NOOP(nodeColorSpace);
if (device) {
q->connect(device, SIGNAL(profileChanged(const KoColorProfile*)),
SLOT(slotUpdateCurrentNodeColorSpace()), Qt::UniqueConnection);
q->connect(device, SIGNAL(colorSpaceChanged(const KoColorSpace*)),
SLOT(slotUpdateCurrentNodeColorSpace()), Qt::UniqueConnection);
}
}
if (!nodeColorSpace) {
nodeColorSpace = KoColorSpaceRegistry::instance()->rgb8();
}
connectedNode = node;
selectPaintingColorSpace();
}
void KisDisplayColorConverter::Private::selectPaintingColorSpace()
{
KisConfig cfg(true);
paintingColorSpace = cfg.customColorSelectorColorSpace();
if (!paintingColorSpace || displayFilter) {
paintingColorSpace = nodeColorSpace;
}
emit q->displayConfigurationChanged();
}
const KoColorSpace* KisDisplayColorConverter::paintingColorSpace() const
{
KIS_ASSERT_RECOVER(m_d->paintingColorSpace) {
return KoColorSpaceRegistry::instance()->rgb8();
}
return m_d->paintingColorSpace;
}
void KisDisplayColorConverter::setMonitorProfile(const KoColorProfile *monitorProfile)
{
if (m_d->useHDRMode) {
// we don't use ICCcolor management in HDR mode
monitorProfile = KoColorSpaceRegistry::instance()->p709SRGBProfile();
}
m_d->monitorProfile = monitorProfile;
m_d->renderingIntent = renderingIntent();
m_d->conversionFlags = conversionFlags();
emit displayConfigurationChanged();
}
void KisDisplayColorConverter::setDisplayFilter(QSharedPointer<KisDisplayFilter> displayFilter)
{
if (m_d->displayFilter && displayFilter &&
displayFilter->lockCurrentColorVisualRepresentation()) {
KoColor color(m_d->intermediateFgColor);
displayFilter->approximateInverseTransformation(color.data(), 1);
color.convertTo(m_d->paintingColorSpace);
m_d->resourceManager->setForegroundColor(color);
}
m_d->displayFilter = displayFilter;
if (m_d->displayFilter) {
m_d->updateIntermediateFgColor(
m_d->resourceManager->foregroundColor());
}
{ // sanity check
// KisConfig cfg;
// KIS_ASSERT_RECOVER_NOOP(cfg.useOcio() == (bool) m_d->displayFilter);
}
m_d->selectPaintingColorSpace();
}
KoColorConversionTransformation::Intent
KisDisplayColorConverter::renderingIntent()
{
KisConfig cfg(true);
return (KoColorConversionTransformation::Intent)cfg.monitorRenderIntent();
}
KoColorConversionTransformation::ConversionFlags
KisDisplayColorConverter::conversionFlags()
{
KoColorConversionTransformation::ConversionFlags conversionFlags =
KoColorConversionTransformation::HighQuality;
KisConfig cfg(true);
if (cfg.useBlackPointCompensation()) conversionFlags |= KoColorConversionTransformation::BlackpointCompensation;
if (!cfg.allowLCMSOptimization()) conversionFlags |= KoColorConversionTransformation::NoOptimization;
return conversionFlags;
}
QSharedPointer<KisDisplayFilter> KisDisplayColorConverter::displayFilter() const
{
return m_d->displayFilter;
}
const KoColorProfile* KisDisplayColorConverter::monitorProfile() const
{
return m_d->monitorProfile;
}
const KoColorProfile* KisDisplayColorConverter::openGLCanvasSurfaceProfile() const
{
return m_d->openGLSurfaceProfile();
}
bool KisDisplayColorConverter::isHDRMode() const
{
return m_d->useHDRMode;
}
void KisDisplayColorConverter::notifyOpenGLCanvasIsActive(bool value)
{
m_d->openGLCanvasIsActive = value;
emit displayConfigurationChanged();
}
QColor KisDisplayColorConverter::toQColor(const KoColor &srcColor) const
{
KoColor c(srcColor);
if (m_d->useOcio()) {
KIS_ASSERT_RECOVER(m_d->ocioInputColorSpace()->pixelSize() == 16) {
return QColor(Qt::green);
}
c.convertTo(m_d->ocioInputColorSpace());
m_d->displayFilter->filter(c.data(), 1);
c.setProfile(m_d->ocioOutputProfile());
}
// we expect the display profile is rgb8, which is BGRA here
KIS_ASSERT_RECOVER(m_d->qtWidgetsColorSpace()->pixelSize() == 4) {
return QColor(Qt::red);
}
c.convertTo(m_d->qtWidgetsColorSpace(), m_d->renderingIntent, m_d->conversionFlags);
const quint8 *p = c.data();
return QColor(p[2], p[1], p[0], p[3]);
}
KoColor KisDisplayColorConverter::applyDisplayFiltering(const KoColor &srcColor,
const KoID &bitDepthId) const
{
KoColor c(srcColor);
if (m_d->useOcio()) {
KIS_ASSERT_RECOVER(m_d->ocioInputColorSpace()->pixelSize() == 16) {
return srcColor;
}
c.convertTo(m_d->ocioInputColorSpace());
m_d->displayFilter->filter(c.data(), 1);
c.setProfile(m_d->ocioOutputProfile());
}
c.convertTo(m_d->openGLSurfaceColorSpace(bitDepthId), m_d->renderingIntent, m_d->conversionFlags);
return c;
}
bool KisDisplayColorConverter::canSkipDisplayConversion(const KoColorSpace *cs) const
{
const KoColorProfile *displayProfile = m_d->openGLSurfaceProfile();
return !m_d->useOcio() &&
cs->colorModelId() == RGBAColorModelID &&
(!!cs->profile() == !!displayProfile) &&
(!cs->profile() ||
cs->profile()->uniqueId() == displayProfile->uniqueId());
}
KoColor KisDisplayColorConverter::approximateFromRenderedQColor(const QColor &c) const
{
return m_d->approximateFromQColor(c);
}
QImage KisDisplayColorConverter::toQImage(KisPaintDeviceSP srcDevice) const
{
KisPaintDeviceSP device = srcDevice;
QRect bounds = srcDevice->exactBounds();
if (bounds.isEmpty()) return QImage();
if (m_d->useOcio()) {
KIS_ASSERT_RECOVER(m_d->ocioInputColorSpace()->pixelSize() == 16) {
return QImage();
}
device = new KisPaintDevice(*srcDevice);
device->convertTo(m_d->ocioInputColorSpace());
KisSequentialIterator it(device, bounds);
int numConseqPixels = it.nConseqPixels();
while (it.nextPixels(numConseqPixels)) {
numConseqPixels = it.nConseqPixels();
m_d->displayFilter->filter(it.rawData(), numConseqPixels);
}
device->setProfile(m_d->ocioOutputProfile());
}
// we expect the display profile is rgb8, which is BGRA here
KIS_ASSERT_RECOVER(m_d->qtWidgetsColorSpace()->pixelSize() == 4) {
return QImage();
}
return device->convertToQImage(m_d->qtWidgetsProfile(),
bounds,
m_d->renderingIntent, m_d->conversionFlags);
}
void KisDisplayColorConverter::applyDisplayFilteringF32(KisFixedPaintDeviceSP device,
const KoID &bitDepthId) const
{
/**
* This method is optimized for the case when device is already in 32f
* version of the pating color space.
*/
KIS_SAFE_ASSERT_RECOVER_RETURN(device->colorSpace()->colorDepthId() == Float32BitsColorDepthID);
KIS_SAFE_ASSERT_RECOVER_RETURN(device->colorSpace()->colorModelId() == RGBAColorModelID);
if (m_d->useOcio()) {
KIS_ASSERT_RECOVER_RETURN(m_d->ocioInputColorSpace()->pixelSize() == 16);
device->convertTo(m_d->ocioInputColorSpace());
m_d->displayFilter->filter(device->data(), device->bounds().width() * device->bounds().height());
device->setProfile(m_d->ocioOutputProfile());
}
device->convertTo(m_d->openGLSurfaceColorSpace(bitDepthId));
}
KoColor KisDisplayColorConverter::Private::approximateFromQColor(const QColor &qcolor)
{
if (!useOcio()) {
return KoColor(qcolor, paintingColorSpace);
} else {
KoColor color(qcolor, intermediateColorSpace());
displayFilter->approximateInverseTransformation(color.data(), 1);
color.convertTo(paintingColorSpace);
return color;
}
qFatal("Must not be reachable");
return KoColor();
}
QColor KisDisplayColorConverter::Private::approximateToQColor(const KoColor &srcColor)
{
KoColor color(srcColor);
if (useOcio()) {
color.convertTo(intermediateColorSpace());
displayFilter->approximateForwardTransformation(color.data(), 1);
}
return color.toQColor();
}
KoColor KisDisplayColorConverter::fromHsv(int h, int s, int v, int a) const
{
// generate HSV from sRGB!
QColor qcolor(QColor::fromHsv(h, s, v, a));
return m_d->approximateFromQColor(qcolor);
}
void KisDisplayColorConverter::getHsv(const KoColor &srcColor, int *h, int *s, int *v, int *a) const
{
// we are going through sRGB here!
QColor color = m_d->approximateToQColor(srcColor);
color.getHsv(h, s, v, a);
}
KoColor KisDisplayColorConverter::fromHsvF(qreal h, qreal s, qreal v, qreal a)
{
// generate HSV from sRGB!
QColor qcolor(QColor::fromHsvF(h, s, v, a));
return m_d->approximateFromQColor(qcolor);
}
void KisDisplayColorConverter::getHsvF(const KoColor &srcColor, qreal *h, qreal *s, qreal *v, qreal *a)
{
// we are going through sRGB here!
QColor color = m_d->approximateToQColor(srcColor);
color.getHsvF(h, s, v, a);
}
KoColor KisDisplayColorConverter::fromHslF(qreal h, qreal s, qreal l, qreal a)
{
// generate HSL from sRGB!
QColor qcolor(QColor::fromHslF(h, s, l, a));
if (!qcolor.isValid()) {
warnKrita << "Could not construct valid color from h" << h << "s" << s << "l" << l << "a" << a;
qcolor = Qt::black;
}
return m_d->approximateFromQColor(qcolor);
}
void KisDisplayColorConverter::getHslF(const KoColor &srcColor, qreal *h, qreal *s, qreal *l, qreal *a)
{
// we are going through sRGB here!
QColor color = m_d->approximateToQColor(srcColor);
color.getHslF(h, s, l, a);
}
KoColor KisDisplayColorConverter::fromHsiF(qreal h, qreal s, qreal i)
{
// generate HSI from sRGB!
qreal r=0.0;
qreal g=0.0;
qreal b=0.0;
qreal a=1.0;
HSIToRGB(h, s, i, &r, &g, &b);
QColor qcolor;
qcolor.setRgbF(qBound(0.0,r,1.0), qBound(0.0,g,1.0), qBound(0.0,b,1.0), a);
return m_d->approximateFromQColor(qcolor);
}
void KisDisplayColorConverter::getHsiF(const KoColor &srcColor, qreal *h, qreal *s, qreal *i)
{
// we are going through sRGB here!
QColor color = m_d->approximateToQColor(srcColor);
qreal r=color.redF();
qreal g=color.greenF();
qreal b=color.blueF();
RGBToHSI(r, g, b, h, s, i);
}
KoColor KisDisplayColorConverter::fromHsyF(qreal h, qreal s, qreal y, qreal R, qreal G, qreal B, qreal gamma)
{
// generate HSL from sRGB!
QVector <qreal> channelValues(3);
y = pow(y, gamma);
HSYToRGB(h, s, y, &channelValues[0], &channelValues[1], &channelValues[2], R, G, B);
KoColorSpaceRegistry::instance()->rgb8()->profile()->delinearizeFloatValueFast(channelValues);
QColor qcolor;
qcolor.setRgbF(qBound(0.0,channelValues[0],1.0), qBound(0.0,channelValues[1],1.0), qBound(0.0,channelValues[2],1.0), 1.0);
return m_d->approximateFromQColor(qcolor);
}
void KisDisplayColorConverter::getHsyF(const KoColor &srcColor, qreal *h, qreal *s, qreal *y, qreal R, qreal G, qreal B, qreal gamma)
{
// we are going through sRGB here!
QColor color = m_d->approximateToQColor(srcColor);
QVector <qreal> channelValues(3);
channelValues[0]=color.redF();
channelValues[1]=color.greenF();
channelValues[2]=color.blueF();
//TODO: if we're going to have KoColor here, remember to check whether the TRC of the profile exists...
KoColorSpaceRegistry::instance()->rgb8()->profile()->linearizeFloatValueFast(channelValues);
RGBToHSY(channelValues[0], channelValues[1], channelValues[2], h, s, y, R, G, B);
*y = pow(*y, 1/gamma);
}
#include "moc_kis_display_color_converter.cpp"
diff --git a/libs/ui/canvas/kis_image_patch.h b/libs/ui/canvas/kis_image_patch.h
index fe826ab288..5ddcbc6f3a 100644
--- a/libs/ui/canvas/kis_image_patch.h
+++ b/libs/ui/canvas/kis_image_patch.h
@@ -1,111 +1,111 @@
/*
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_IMAGE_PATCH_H_
#define KIS_IMAGE_PATCH_H_
#include <QPainter>
#include <QImage>
#include <kis_types.h>
#define BORDER_SIZE(scale) (ceil(0.5/scale))
class KisImagePatch
{
public:
/**
* A default constructor initializing invalid patch
*/
KisImagePatch();
/**
* Initializes a new patch with given values.
* Be careful, because the constructor does not fill
* QImage of the patch, as the patch rect is not known yet
*
* \see setImage
*/
KisImagePatch(QRect imageRect, qint32 borderWidth,
qreal scaleX, qreal scaleY);
/**
* Sets the image of the patch
* Should be called right after the constructor
* to finish initializing the object
*/
void setImage(QImage image);
/**
* prescale the patch image. Call after setImage().
* This ensures that we use the QImage smoothscale method, not the QPainter scaling,
* which is far inferior.
*/
void preScale(const QRectF &dstRect);
/**
* Returns the rect of KisImage covered by the image
* of the patch (in KisImage pixels)
*
* \see m_patchRect
*/
QRect patchRect();
/**
- * Draws an m_interestRect of the patch onto @gc
- * By the way it fits this rect into @dstRect
- * @renderHints are directly transmitted to QPainter
+ * Draws an m_interestRect of the patch onto @p gc
+ * By the way it fits this rect into @p dstRect
+ * @p renderHints are directly transmitted to QPainter
*/
void drawMe(QPainter &gc,
const QRectF &dstRect,
QPainter::RenderHints renderHints);
/**
* Checks whether the patch can be used for drawing the image
*/
bool isValid();
private:
/**
* The scale of the image stored in the patch
*/
qreal m_scaleX;
qreal m_scaleY;
/**
* The rect of KisImage covered by the image
* of the patch (in KisImage pixels)
*/
QRect m_patchRect;
/**
* The rect that was requested during creation
* of the patch. It equals to patchRect withount
* borders
* These borders are introdused for more accurate
* smooth scaling to reduce border effects
* (IN m_image PIXELS, relative to m_image's topLeft);
*/
QRectF m_interestRect;
QImage m_image;
bool m_isScaled;
};
#endif /* KIS_IMAGE_PATCH_H_ */
diff --git a/libs/ui/canvas/kis_image_pyramid.cpp b/libs/ui/canvas/kis_image_pyramid.cpp
index 04b74c0ef3..75d2a4f786 100644
--- a/libs/ui/canvas/kis_image_pyramid.cpp
+++ b/libs/ui/canvas/kis_image_pyramid.cpp
@@ -1,537 +1,537 @@
/*
* Copyright (c) 2009 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_image_pyramid.h"
#include <QBitArray>
#include <KoChannelInfo.h>
#include <KoCompositeOp.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorModelStandardIds.h>
#include <KoColorSpaceMaths.h>
#include "kis_display_filter.h"
#include "kis_painter.h"
#include "kis_iterator_ng.h"
#include "kis_datamanager.h"
#include "kis_config_notifier.h"
#include "kis_debug.h"
#include "kis_config.h"
#include "kis_image_config.h"
//#define DEBUG_PYRAMID
#include <config-ocio.h>
#ifdef HAVE_OCIO
#include <OpenColorIO/OpenColorIO.h>
#include <OpenColorIO/OpenColorTransforms.h>
#endif
#define ORIGINAL_INDEX 0
#define FIRST_NOT_ORIGINAL_INDEX 1
#define SCALE_FROM_INDEX(idx) (1./qreal(1<<(idx)))
/************* AUXILIARY FUNCTIONS **********************************/
#include <KoConfig.h>
#ifdef HAVE_OPENEXR
#include <half.h>
#endif
#define ceiledSize(sz) QSize(ceil((sz).width()), ceil((sz).height()))
#define isOdd(x) ((x) & 0x01)
/**
- * Aligns @value to the lowest integer not smaller than @value and
+ * Aligns @p value to the lowest integer not smaller than @p value and
* that is a divident of alignment
*/
inline void alignByPow2Hi(qint32 &value, qint32 alignment)
{
qint32 mask = alignment - 1;
value |= mask;
value++;
}
/**
- * Aligns @value to the lowest integer not smaller than @value and
+ * Aligns @p value to the lowest integer not smaller than @p value and
* that is, increased by one, a divident of alignment
*/
inline void alignByPow2ButOneHi(qint32 &value, qint32 alignment)
{
qint32 mask = alignment - 1;
value |= mask;
}
/**
- * Aligns @value to the highest integer not exceeding @value and
- * that is a divident of @alignment
+ * Aligns @p value to the highest integer not exceeding @p value and
+ * that is a divident of @p alignment
*/
inline void alignByPow2Lo(qint32 &value, qint32 alignment)
{
qint32 mask = alignment - 1;
value &= ~mask;
}
inline void alignRectBy2(qint32 &x, qint32 &y, qint32 &w, qint32 &h)
{
x -= isOdd(x);
y -= isOdd(y);
w += isOdd(x);
w += isOdd(w);
h += isOdd(y);
h += isOdd(h);
}
/************* class KisImagePyramid ********************************/
KisImagePyramid::KisImagePyramid(qint32 pyramidHeight)
: m_monitorProfile(0)
, m_monitorColorSpace(0)
, m_pyramidHeight(pyramidHeight)
{
configChanged();
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), this, SLOT(configChanged()));
}
KisImagePyramid::~KisImagePyramid()
{
setImage(0);
}
void KisImagePyramid::setMonitorProfile(const KoColorProfile* monitorProfile,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags)
{
m_monitorProfile = monitorProfile;
/**
* If you change pixel size here, don't forget to change it
* in optimized function downsamplePixels()
*/
m_monitorColorSpace = KoColorSpaceRegistry::instance()->rgb8(monitorProfile);
m_renderingIntent = renderingIntent;
m_conversionFlags = conversionFlags;
rebuildPyramid();
}
void KisImagePyramid::setChannelFlags(const QBitArray &channelFlags)
{
m_channelFlags = channelFlags;
int selectedChannels = 0;
const KoColorSpace *projectionCs = m_originalImage->projection()->colorSpace();
QList<KoChannelInfo*> channelInfo = projectionCs->channels();
if (channelInfo.size() != m_channelFlags.size()) {
m_channelFlags = QBitArray();
}
for (int i = 0; i < m_channelFlags.size(); ++i) {
if (m_channelFlags.testBit(i) && channelInfo[i]->channelType() == KoChannelInfo::COLOR) {
selectedChannels++;
m_selectedChannelIndex = i;
}
}
m_allChannelsSelected = (selectedChannels == m_channelFlags.size());
m_onlyOneChannelSelected = (selectedChannels == 1);
}
void KisImagePyramid::setDisplayFilter(QSharedPointer<KisDisplayFilter> displayFilter)
{
m_displayFilter = displayFilter;
}
void KisImagePyramid::rebuildPyramid()
{
m_pyramid.clear();
for (qint32 i = 0; i < m_pyramidHeight; i++) {
m_pyramid.append(new KisPaintDevice(m_monitorColorSpace));
}
}
void KisImagePyramid::clearPyramid()
{
for (qint32 i = 0; i < m_pyramidHeight; i++) {
m_pyramid[i]->clear();
}
}
void KisImagePyramid::setImage(KisImageWSP newImage)
{
if (newImage) {
m_originalImage = newImage;
clearPyramid();
setImageSize(m_originalImage->width(), m_originalImage->height());
// Get the full image size
QRect rc = m_originalImage->projection()->exactBounds();
KisImageConfig config(true);
int patchWidth = config.updatePatchWidth();
int patchHeight = config.updatePatchHeight();
if (rc.width() * rc.height() <= patchWidth * patchHeight) {
retrieveImageData(rc);
}
else {
qint32 firstCol = rc.x() / patchWidth;
qint32 firstRow = rc.y() / patchHeight;
qint32 lastCol = (rc.x() + rc.width()) / patchWidth;
qint32 lastRow = (rc.y() + rc.height()) / patchHeight;
for(qint32 i = firstRow; i <= lastRow; i++) {
for(qint32 j = firstCol; j <= lastCol; j++) {
QRect maxPatchRect(j * patchWidth,
i * patchHeight,
patchWidth, patchHeight);
QRect patchRect = rc & maxPatchRect;
retrieveImageData(patchRect);
}
}
}
//TODO: check whether there is needed recalculateCache()
}
}
void KisImagePyramid::setImageSize(qint32 w, qint32 h)
{
Q_UNUSED(w);
Q_UNUSED(h);
/* nothing interesting */
}
void KisImagePyramid::updateCache(const QRect &dirtyImageRect)
{
retrieveImageData(dirtyImageRect);
}
void KisImagePyramid::retrieveImageData(const QRect &rect)
{
// XXX: use QThreadStorage to cache the two patches (512x512) of pixels. Note
// that when we do that, we need to reset that cache when the projection's
// colorspace changes.
const KoColorSpace *projectionCs = m_originalImage->projection()->colorSpace();
KisPaintDeviceSP originalProjection = m_originalImage->projection();
quint32 numPixels = rect.width() * rect.height();
QScopedArrayPointer<quint8> originalBytes(
new quint8[originalProjection->colorSpace()->pixelSize() * numPixels]);
originalProjection->readBytes(originalBytes.data(), rect);
if (m_displayFilter &&
m_useOcio &&
projectionCs->colorModelId() == RGBAColorModelID) {
#ifdef HAVE_OCIO
const KoColorProfile *destinationProfile =
m_displayFilter->useInternalColorManagement() ?
m_monitorProfile : projectionCs->profile();
const KoColorSpace *floatCs =
KoColorSpaceRegistry::instance()->colorSpace(
RGBAColorModelID.id(),
Float32BitsColorDepthID.id(),
destinationProfile);
const KoColorSpace *modifiedMonitorCs =
KoColorSpaceRegistry::instance()->colorSpace(
RGBAColorModelID.id(),
Integer8BitsColorDepthID.id(),
destinationProfile);
if (projectionCs->colorDepthId() == Float32BitsColorDepthID) {
m_displayFilter->filter(originalBytes.data(), numPixels);
} else {
QScopedArrayPointer<quint8> dst(new quint8[floatCs->pixelSize() * numPixels]);
projectionCs->convertPixelsTo(originalBytes.data(), dst.data(), floatCs, numPixels, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
m_displayFilter->filter(dst.data(), numPixels);
originalBytes.swap(dst);
}
{
QScopedArrayPointer<quint8> dst(new quint8[modifiedMonitorCs->pixelSize() * numPixels]);
floatCs->convertPixelsTo(originalBytes.data(), dst.data(), modifiedMonitorCs, numPixels, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
originalBytes.swap(dst);
}
#endif
}
else {
QList<KoChannelInfo*> channelInfo = projectionCs->channels();
if (m_channelFlags.size() != channelInfo.size()) {
setChannelFlags(QBitArray());
}
if (!m_channelFlags.isEmpty() && !m_allChannelsSelected) {
QScopedArrayPointer<quint8> dst(new quint8[projectionCs->pixelSize() * numPixels]);
int channelSize = channelInfo[m_selectedChannelIndex]->size();
int pixelSize = projectionCs->pixelSize();
KisConfig cfg(true);
if (m_onlyOneChannelSelected && !cfg.showSingleChannelAsColor()) {
int selectedChannelPos = channelInfo[m_selectedChannelIndex]->pos();
for (uint pixelIndex = 0; pixelIndex < numPixels; ++pixelIndex) {
for (uint channelIndex = 0; channelIndex < projectionCs->channelCount(); ++channelIndex) {
if (channelInfo[channelIndex]->channelType() == KoChannelInfo::COLOR) {
memcpy(dst.data() + (pixelIndex * pixelSize) + (channelIndex * channelSize),
originalBytes.data() + (pixelIndex * pixelSize) + selectedChannelPos,
channelSize);
}
else if (channelInfo[channelIndex]->channelType() == KoChannelInfo::ALPHA) {
memcpy(dst.data() + (pixelIndex * pixelSize) + (channelIndex * channelSize),
originalBytes.data() + (pixelIndex * pixelSize) + (channelIndex * channelSize),
channelSize);
}
}
}
}
else {
for (uint pixelIndex = 0; pixelIndex < numPixels; ++pixelIndex) {
for (uint channelIndex = 0; channelIndex < projectionCs->channelCount(); ++channelIndex) {
if (m_channelFlags.testBit(channelIndex)) {
memcpy(dst.data() + (pixelIndex * pixelSize) + (channelIndex * channelSize),
originalBytes.data() + (pixelIndex * pixelSize) + (channelIndex * channelSize),
channelSize);
}
else {
memset(dst.data() + (pixelIndex * pixelSize) + (channelIndex * channelSize), 0, channelSize);
}
}
}
}
originalBytes.swap(dst);
}
QScopedArrayPointer<quint8> dst(new quint8[m_monitorColorSpace->pixelSize() * numPixels]);
projectionCs->convertPixelsTo(originalBytes.data(), dst.data(), m_monitorColorSpace, numPixels, m_renderingIntent, m_conversionFlags);
originalBytes.swap(dst);
}
m_pyramid[ORIGINAL_INDEX]->writeBytes(originalBytes.data(), rect);
}
void KisImagePyramid::recalculateCache(KisPPUpdateInfoSP info)
{
KisPaintDevice *src;
KisPaintDevice *dst;
QRect currentSrcRect = info->dirtyImageRectVar;
for (int i = FIRST_NOT_ORIGINAL_INDEX; i < m_pyramidHeight; i++) {
src = m_pyramid[i-1].data();
dst = m_pyramid[i].data();
if (!currentSrcRect.isEmpty()) {
currentSrcRect = downsampleByFactor2(currentSrcRect, src, dst);
}
}
#ifdef DEBUG_PYRAMID
QImage image = m_pyramid[ORIGINAL_INDEX]->convertToQImage(m_monitorProfile, m_renderingIntent, m_conversionFlags);
image.save("./PYRAMID_BASE.png");
image = m_pyramid[1]->convertToQImage(m_monitorProfile, m_renderingIntent, m_conversionFlags);
image.save("./LEVEL1.png");
image = m_pyramid[2]->convertToQImage(m_monitorProfile, m_renderingIntent, m_conversionFlags);
image.save("./LEVEL2.png");
image = m_pyramid[3]->convertToQImage(m_monitorProfile, m_renderingIntent, m_conversionFlags);
image.save("./LEVEL3.png");
#endif
}
QRect KisImagePyramid::downsampleByFactor2(const QRect& srcRect,
KisPaintDevice* src,
KisPaintDevice* dst)
{
qint32 srcX, srcY, srcWidth, srcHeight;
srcRect.getRect(&srcX, &srcY, &srcWidth, &srcHeight);
alignRectBy2(srcX, srcY, srcWidth, srcHeight);
// Nothing to do
if (srcWidth < 1) return QRect();
if (srcHeight < 1) return QRect();
qint32 dstX = srcX / 2;
qint32 dstY = srcY / 2;
qint32 dstWidth = srcWidth / 2;
qint32 dstHeight = srcHeight / 2;
KisHLineConstIteratorSP srcIt0 = src->createHLineConstIteratorNG(srcX, srcY, srcWidth);
KisHLineConstIteratorSP srcIt1 = src->createHLineConstIteratorNG(srcX, srcY + 1, srcWidth);
KisHLineIteratorSP dstIt = dst->createHLineIteratorNG(dstX, dstY, dstWidth);
int conseqPixels = 0;
for (int row = 0; row < dstHeight; ++row) {
do {
int srcItConseq = srcIt0->nConseqPixels();
int dstItConseq = dstIt->nConseqPixels();
conseqPixels = qMin(srcItConseq, dstItConseq * 2);
Q_ASSERT(!isOdd(conseqPixels));
downsamplePixels(srcIt0->oldRawData(), srcIt1->oldRawData(),
dstIt->rawData(), conseqPixels);
srcIt1->nextPixels(conseqPixels);
dstIt->nextPixels(conseqPixels / 2);
} while (srcIt0->nextPixels(conseqPixels));
srcIt0->nextRow();
srcIt0->nextRow();
srcIt1->nextRow();
srcIt1->nextRow();
dstIt->nextRow();
}
return QRect(dstX, dstY, dstWidth, dstHeight);
}
void KisImagePyramid::downsamplePixels(const quint8 *srcRow0,
const quint8 *srcRow1,
quint8 *dstRow,
qint32 numSrcPixels)
{
/**
* FIXME (mandatory): Use SSE and friends here.
*/
qint16 b = 0;
qint16 g = 0;
qint16 r = 0;
qint16 a = 0;
static const qint32 pixelSize = 4; // This is preview argb8 mode
for (qint32 i = 0; i < numSrcPixels / 2; i++) {
b = srcRow0[0] + srcRow1[0] + srcRow0[4] + srcRow1[4];
g = srcRow0[1] + srcRow1[1] + srcRow0[5] + srcRow1[5];
r = srcRow0[2] + srcRow1[2] + srcRow0[6] + srcRow1[6];
a = srcRow0[3] + srcRow1[3] + srcRow0[7] + srcRow1[7];
dstRow[0] = b / 4;
dstRow[1] = g / 4;
dstRow[2] = r / 4;
dstRow[3] = a / 4;
dstRow += pixelSize;
srcRow0 += 2 * pixelSize;
srcRow1 += 2 * pixelSize;
}
}
int KisImagePyramid::findFirstGoodPlaneIndex(qreal scale,
QSize originalSize)
{
qint32 nearest = 0;
for (qint32 i = 0; i < m_pyramidHeight; i++) {
qreal planeScale = SCALE_FROM_INDEX(i);
if (planeScale < scale) {
if (originalSize*scale == originalSize*planeScale)
nearest = i;
break;
}
nearest = i;
}
// FOR DEBUGGING
//nearest = 0;
//nearest = qMin(1, nearest);
dbgRender << "First good plane:" << nearest << "(sc:" << scale << ")";
return nearest;
}
void KisImagePyramid::alignSourceRect(QRect& rect, qreal scale)
{
qint32 index = findFirstGoodPlaneIndex(scale, rect.size());
qint32 alignment = 1 << index;
dbgRender << "Before alignment:\t" << rect;
/**
* Assume that KisImage pixels are always positive
* It allows us to use binary op-s for aligning
*/
Q_ASSERT(rect.left() >= 0 && rect.top() >= 0);
qint32 x1, y1, x2, y2;
rect.getCoords(&x1, &y1, &x2, &y2);
alignByPow2Lo(x1, alignment);
alignByPow2Lo(y1, alignment);
/**
* Here is a workaround of Qt's QRect::right()/bottom()
* "historical reasons". It should be one pixel smaller
* than actual right/bottom position
*/
alignByPow2ButOneHi(x2, alignment);
alignByPow2ButOneHi(y2, alignment);
rect.setCoords(x1, y1, x2, y2);
dbgRender << "After alignment:\t" << rect;
}
KisImagePatch KisImagePyramid::getNearestPatch(KisPPUpdateInfoSP info)
{
qint32 index = findFirstGoodPlaneIndex(qMax(info->scaleX, info->scaleY),
info->imageRect.size());
qreal planeScale = SCALE_FROM_INDEX(index);
qint32 alignment = 1 << index;
alignByPow2Hi(info->borderWidth, alignment);
KisImagePatch patch(info->imageRect, info->borderWidth,
planeScale, planeScale);
patch.setImage(convertToQImageFast(m_pyramid[index],
patch.patchRect()));
return patch;
}
void KisImagePyramid::drawFromOriginalImage(QPainter& gc, KisPPUpdateInfoSP info)
{
KisImagePatch patch = getNearestPatch(info);
patch.drawMe(gc, info->viewportRect, info->renderHints);
}
QImage KisImagePyramid::convertToQImageFast(KisPaintDeviceSP paintDevice,
const QRect& unscaledRect)
{
qint32 x, y, w, h;
unscaledRect.getRect(&x, &y, &w, &h);
QImage image = QImage(w, h, QImage::Format_ARGB32);
paintDevice->dataManager()->readBytes(image.bits(), x, y, w, h);
return image;
}
void KisImagePyramid::configChanged()
{
KisConfig cfg(true);
m_useOcio = cfg.useOcio();
}
diff --git a/libs/ui/canvas/kis_projection_backend.h b/libs/ui/canvas/kis_projection_backend.h
index 012062e0fb..fb3b7d4bfd 100644
--- a/libs/ui/canvas/kis_projection_backend.h
+++ b/libs/ui/canvas/kis_projection_backend.h
@@ -1,100 +1,100 @@
/*
* Copyright (c) 2009 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_PROJECTION_BACKEND
#define KIS_PROJECTION_BACKEND
#include "kis_update_info.h"
class KoColorProfile;
class KisImagePatch;
class KisDisplayFilter;
#include <KoColorConversionTransformation.h>
/**
* KisProjectionBackend is an abstract class representing
* an object that can store a cache of KisImage projection.
* More than that this object can perform some scaling operations
* that are based on "patches" paradigm
*/
class KisProjectionBackend
{
public:
virtual ~KisProjectionBackend();
/**
* Those methods are related to KisPrescaledProjection's
* equivalents
*/
virtual void setImage(KisImageWSP image) = 0;
virtual void setImageSize(qint32 w, qint32 h) = 0;
virtual void setMonitorProfile(const KoColorProfile* monitorProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) = 0;
virtual void setChannelFlags(const QBitArray &channelFlags) = 0;
virtual void setDisplayFilter(QSharedPointer<KisDisplayFilter> displayFilter) = 0;
/**
* Updates the cache of the backend by reading from
* an associated image. All data transfers with
* KisImage should happen here
*/
virtual void updateCache(const QRect &dirtyImageRect) = 0;
/**
* Prescales the cache of the backend. It is intended to be
* called from a separate thread where you can easily
* do the calculations. No data transfers with KisImage
* should happen during this phase
*/
virtual void recalculateCache(KisPPUpdateInfoSP info) = 0;
/**
* Some backends cannot work with arbitrary areas due to
* scaling stuff. That's why KisPrescaledProjection asks
* a backend to align an image rect before any operations.
*/
virtual void alignSourceRect(QRect& rect, qreal scale);
/**
* Gets a patch from a backend that can draw a info.imageRect on some
* QPainter in future. info.scaleX and info.scaleY are the scales
* of planned drawing, btw, it doesn't mean that an QImage inside
* the patch will have these scales - it'll have the nearest suitable
* scale or even original scale (e.g. KisProjectionCache)
*
* If info.borderWidth is non-zero, info.requestedRect will
* be expended by info.borderWidth pixels to all directions and
* image of this rect will actually be written to the patch's QImage.
* That is done to eliminate border effects in smooth scaling.
*/
virtual KisImagePatch getNearestPatch(KisPPUpdateInfoSP info) = 0;
/**
- * Draws a piece of original image onto @gc's canvas
- * @param info.imageRect - area in KisImage pixels where to read from
- * @param info.viewportRect - area in canvas pixels where to write to
+ * Draws a piece of original image onto @p gc 's canvas
+ * @p info.imageRect - area in KisImage pixels where to read from
+ * @p info.viewportRect - area in canvas pixels where to write to
* If info.imageRect and info.viewportRect don't agree, the image
* will be scaled
- * @param info.borderWidth has the same meaning as in getNearestPatch
- * @param info.renderHints - hints, transmitted to QPainter during darwing
+ * @p info.borderWidth has the same meaning as in getNearestPatch
+ * @p info.renderHints - hints, transmitted to QPainter during darwing
*/
virtual void drawFromOriginalImage(QPainter& gc,
KisPPUpdateInfoSP info) = 0;
};
#endif /* KIS_PROJECTION_BACKEND */
diff --git a/libs/ui/canvas/kis_update_info.h b/libs/ui/canvas/kis_update_info.h
index 455955e2d3..62d29966b5 100644
--- a/libs/ui/canvas/kis_update_info.h
+++ b/libs/ui/canvas/kis_update_info.h
@@ -1,168 +1,168 @@
/*
* Copyright (c) 2010, Dmitry Kazakov <dimula73@gmail.com>
*
* 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_UPDATE_INFO_H_
#define KIS_UPDATE_INFO_H_
#include <QPainter>
#include "kis_image_patch.h"
#include "kis_shared.h"
#include "kritaui_export.h"
#include "opengl/kis_texture_tile_update_info.h"
#include "kis_ui_types.h"
class KRITAUI_EXPORT KisUpdateInfo : public KisShared
{
public:
KisUpdateInfo();
virtual ~KisUpdateInfo();
virtual QRect dirtyViewportRect();
virtual QRect dirtyImageRect() const = 0;
virtual int levelOfDetail() const = 0;
};
Q_DECLARE_METATYPE(KisUpdateInfoSP)
struct ConversionOptions {
ConversionOptions() : m_needsConversion(false) {}
ConversionOptions(const KoColorSpace *destinationColorSpace,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags)
: m_needsConversion(true),
m_destinationColorSpace(destinationColorSpace),
m_renderingIntent(renderingIntent),
m_conversionFlags(conversionFlags)
{
}
bool m_needsConversion;
const KoColorSpace *m_destinationColorSpace = 0;
KoColorConversionTransformation::Intent m_renderingIntent;
KoColorConversionTransformation::ConversionFlags m_conversionFlags;
};
class KisOpenGLUpdateInfo;
typedef KisSharedPtr<KisOpenGLUpdateInfo> KisOpenGLUpdateInfoSP;
class KisOpenGLUpdateInfo : public KisUpdateInfo
{
public:
KisOpenGLUpdateInfo();
KisTextureTileUpdateInfoSPList tileList;
QRect dirtyViewportRect() override;
QRect dirtyImageRect() const override;
void assignDirtyImageRect(const QRect &rect);
void assignLevelOfDetail(int lod);
int levelOfDetail() const override;
bool tryMergeWith(const KisOpenGLUpdateInfo& rhs);
private:
QRect m_dirtyImageRect;
int m_levelOfDetail;
};
class KisPPUpdateInfo : public KisUpdateInfo
{
public:
enum TransferType {
DIRECT,
PATCH
};
QRect dirtyViewportRect() override;
QRect dirtyImageRect() const override;
int levelOfDetail() const override;
/**
* The rect that was reported by KisImage as dirty
*/
QRect dirtyImageRectVar;
/**
- * Rect of KisImage corresponding to @viewportRect.
+ * Rect of KisImage corresponding to @ref viewportRect .
* It is cropped and aligned corresponding to the canvas.
*/
QRect imageRect;
/**
- * Rect of canvas widget corresponding to @imageRect
+ * Rect of canvas widget corresponding to @ref imageRect
*/
QRectF viewportRect;
qreal scaleX;
qreal scaleY;
/**
* Defines the way the source image is painted onto
* prescaled QImage
*/
TransferType transfer;
/**
* Render hints for painting the direct painting/patch painting
*/
QPainter::RenderHints renderHints;
/**
* The number of additional pixels those should be added
* to the patch
*/
qint32 borderWidth;
/**
* Used for temporary storage of KisImage's data
* by KisProjectionCache
*/
KisImagePatch patch;
};
class KisMarkerUpdateInfo : public KisUpdateInfo
{
public:
enum Type {
StartBatch = 0,
EndBatch,
BlockLodUpdates,
UnblockLodUpdates,
};
public:
KisMarkerUpdateInfo(Type type, const QRect &dirtyImageRect);
Type type() const;
QRect dirtyImageRect() const override;
int levelOfDetail() const override;
private:
Type m_type;
QRect m_dirtyImageRect;
};
#endif /* KIS_UPDATE_INFO_H_ */
diff --git a/libs/ui/dialogs/kis_about_application.cpp b/libs/ui/dialogs/kis_about_application.cpp
index 82e4366bdb..2403e5874a 100644
--- a/libs/ui/dialogs/kis_about_application.cpp
+++ b/libs/ui/dialogs/kis_about_application.cpp
@@ -1,202 +1,215 @@
/*
* Copyright (c) 2014 Boudewijn Rempt <boud@valdyas.org>
*
* 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_about_application.h"
#include <kis_debug.h>
#include <QStandardPaths>
#include <QTabWidget>
#include <QLabel>
#include <QTextEdit>
#include <QTextBrowser>
#include <QString>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
+#include <QDate>
#include <QApplication>
#include <QFile>
#include <QDesktopServices>
#include <klocalizedstring.h>
#include "../../krita/data/splash/splash_screen.xpm"
+#include "../../krita/data/splash/splash_holidays.xpm"
#include "../../krita/data/splash/splash_screen_x2.xpm"
+#include "../../krita/data/splash/splash_holidays_x2.xpm"
+
#include "kis_splash_screen.h"
KisAboutApplication::KisAboutApplication(QWidget *parent)
: QDialog(parent)
{
setWindowTitle(i18n("About Krita"));
QVBoxLayout *vlayout = new QVBoxLayout(this);
vlayout->setMargin(0);
QTabWidget *wdgTab = new QTabWidget;
vlayout->addWidget(wdgTab);
+ KisSplashScreen *splash = 0;
+
+ QDate currentDate = QDate::currentDate();
+ if (currentDate > QDate(currentDate.year(), 12, 4) ||
+ currentDate < QDate(currentDate.year(), 1, 9)) {
+ splash = new KisSplashScreen(qApp->applicationVersion(), QPixmap(splash_holidays_xpm), QPixmap(splash_holidays_x2_xpm));
+ }
+ else {
+ splash = new KisSplashScreen(qApp->applicationVersion(), QPixmap(splash_screen_xpm), QPixmap(splash_screen_x2_xpm));
+ }
- KisSplashScreen *splash = new KisSplashScreen(qApp->applicationVersion(), QPixmap(splash_screen_xpm), QPixmap(splash_screen_x2_xpm), true);
splash->setWindowFlags(Qt::Widget);
splash->displayLinks(true);
splash->setFixedSize(splash->sizeHint());
wdgTab->addTab(splash, i18n("About"));
setMinimumSize(wdgTab->sizeHint());
QTextEdit *lblAuthors = new QTextEdit();
lblAuthors->setReadOnly(true);
QString authors = i18n("<html>"
"<head/>"
"<body>"
"<h1 align=\"center\">Created By</h1></p>"
"<p>");
QFile fileDevelopers(":/developers.txt");
Q_ASSERT(fileDevelopers.exists());
fileDevelopers.open(QIODevice::ReadOnly);
Q_FOREACH (const QByteArray &author, fileDevelopers.readAll().split('\n')) {
authors.append(QString::fromUtf8(author));
authors.append(", ");
}
authors.chop(2);
authors.append(".</p></body></html>");
lblAuthors->setText(authors);
wdgTab->addTab(lblAuthors, i18n("Authors"));
QTextEdit *lblKickstarter = new QTextEdit();
lblKickstarter->setReadOnly(true);
QString backers = i18n("<html>"
"<head/>"
"<body>"
"<h1 align=\"center\">Backed By</h1>"
"<p>");
QFile fileBackers(":/backers.txt");
Q_ASSERT(fileBackers.exists());
fileBackers.open(QIODevice::ReadOnly);
Q_FOREACH (const QByteArray &backer, fileBackers.readAll().split('\n')) {
backers.append(QString::fromUtf8(backer));
backers.append(", ");
}
backers.chop(2);
backers.append(i18n(".</p><p><i>Thanks! You were all <b>awesome</b>!</i></p></body></html>"));
lblKickstarter->setText(backers);
wdgTab->addTab(lblKickstarter, i18n("Backers"));
QTextEdit *lblCredits = new QTextEdit();
lblCredits->setReadOnly(true);
QString credits = i18n("<html>"
"<head/>"
"<body>"
"<h1 align=\"center\">Thanks To</h1>"
"<p>");
QFile fileCredits(":/credits.txt");
Q_ASSERT(fileCredits.exists());
fileCredits.open(QIODevice::ReadOnly);
Q_FOREACH (const QString &credit, QString::fromUtf8(fileCredits.readAll()).split('\n', QString::SkipEmptyParts)) {
if (credit.contains(":")) {
QList<QString> creditSplit = credit.split(':');
credits.append(creditSplit.at(0));
credits.append(" (<i>" + creditSplit.at(1) + "</i>)");
credits.append(", ");
}
}
credits.chop(2);
credits.append(i18n(".</p><p><i>For supporting Krita development with advice, icons, brush sets and more.</i></p></body></html>"));
lblCredits->setText(credits);
wdgTab->addTab(lblCredits, i18n("Also Thanks To"));
QTextEdit *lblLicense = new QTextEdit();
lblLicense->setReadOnly(true);
QString license = i18n("<html>"
"<head/>"
"<body>"
"<h1 align=\"center\"><b>Your Rights</b></h1>"
"<p>Krita is released under the GNU General Public License (version 3 or any later version).</p>"
"<p>This license grants people a number of freedoms:</p>"
"<ul>"
"<li>You are free to use Krita, for any purpose</li>"
"<li>You are free to distribute Krita</li>"
"<li>You can study how Krita works and change it</li>"
"<li>You can distribute changed versions of Krita</li>"
"</ul>"
"<p>The Krita Foundation and its projects on krita.org are <b>committed</b> to preserving Krita as free software.</p>"
"<h1 align=\"center\">Your artwork</h1>"
"<p>What you create with Krita is your sole property. All your artwork is free for you to use as you like.</p>"
"<p>That means that Krita can be used commercially, for any purpose. There are no restrictions whatsoever.</p>"
"<p>Krita’s GNU GPL license guarantees you this freedom. Nobody is ever permitted to take it away, in contrast "
"to trial or educational versions of commercial software that will forbid your work in commercial situations.</p>"
"<br/><hr/><pre>");
QFile licenseFile(":/LICENSE");
Q_ASSERT(licenseFile.exists());
licenseFile.open(QIODevice::ReadOnly);
QByteArray ba = licenseFile.readAll();
license.append(QString::fromUtf8(ba));
license.append("</pre></body></html>");
lblLicense->setText(license);
wdgTab->addTab(lblLicense, i18n("License"));
QTextBrowser *lblThirdParty = new QTextBrowser();
lblThirdParty->setOpenExternalLinks(true);
QFile thirdPartyFile(":/libraries.txt");
if (thirdPartyFile.open(QIODevice::ReadOnly)) {
ba = thirdPartyFile.readAll();
QString thirdPartyHtml = i18n("<html>"
"<head/>"
"<body>"
"<h1 align=\"center\"><b>Third-party Libraries used by Krita</b></h1>"
"<p>Krita is built on the following free software libraries:</p><p><ul>");
Q_FOREACH(const QString &lib, QString::fromUtf8(ba).split('\n')) {
if (!lib.startsWith("#")) {
QStringList parts = lib.split(',');
if (parts.size() >= 3) {
thirdPartyHtml.append(QString("<li><a href=\"%2\">%1</a>: %3</li>").arg(parts[0], parts[1], parts[2]));
}
}
}
thirdPartyHtml.append("<ul></p></body></html>");
lblThirdParty->setText(thirdPartyHtml);
}
wdgTab->addTab(lblThirdParty, i18n("Third-party libraries"));
QPushButton *bnClose = new QPushButton(i18n("Close"));
connect(bnClose, SIGNAL(clicked()), SLOT(close()));
QHBoxLayout *hlayout = new QHBoxLayout;
hlayout->setMargin(10);
hlayout->addStretch(10);
hlayout->addWidget(bnClose);
vlayout->addLayout(hlayout);
}
diff --git a/libs/ui/dialogs/kis_dlg_adj_layer_props.h b/libs/ui/dialogs/kis_dlg_adj_layer_props.h
index 384305abe4..07c4a09525 100644
--- a/libs/ui/dialogs/kis_dlg_adj_layer_props.h
+++ b/libs/ui/dialogs/kis_dlg_adj_layer_props.h
@@ -1,78 +1,83 @@
/*
* Copyright (c) 2006 Boudewijn Rempt <boud@valdyas.org>
*
* 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_ADJ_LAYER_PROPS_H
#define KIS_DLG_ADJ_LAYER_PROPS_H
#include <KoDialog.h>
class QLineEdit;
class KisFilter;
class KisFilterConfiguration;
class KisConfigWidget;
class KisNodeFilterInterface;
class KisViewManager;
#include "kis_types.h"
/**
* Create a new adjustment layer.
*/
class KisDlgAdjLayerProps : public KoDialog
{
Q_OBJECT
public:
/**
* Create a new adjustmentlayer dialog
*
+ * @param node the node
+ * @param nfi the node filter interface
+ * @param paintDevice the painting device
+ * @param view the view manager
+ * @param configuration filter configuration
* @param layerName the name of the adjustment layer
* @param caption the caption for the dialog -- create or properties
* @param parent the widget parent of this dialog
* @param name the QObject name, if any
*/
KisDlgAdjLayerProps(KisNodeSP node,
KisNodeFilterInterface *nfi,
KisPaintDeviceSP paintDevice,
KisViewManager *view,
KisFilterConfigurationSP configuration,
const QString & layerName,
const QString & caption,
QWidget *parent = 0,
const char *name = 0);
KisFilterConfigurationSP filterConfiguration() const;
QString layerName() const;
private Q_SLOTS:
void slotNameChanged(const QString &);
void slotConfigChanged();
private:
KisNodeSP m_node;
KisPaintDeviceSP m_paintDevice;
KisConfigWidget *m_currentConfigWidget;
KisFilter *m_currentFilter;
KisFilterConfigurationSP m_currentConfiguration;
QLineEdit *m_layerName;
KisNodeFilterInterface *m_nodeFilterInterface;
};
#endif // KIS_DLG_ADJ_LAYER_PROPS_H
diff --git a/libs/ui/dialogs/kis_dlg_adjustment_layer.h b/libs/ui/dialogs/kis_dlg_adjustment_layer.h
index 8c583f533f..015b41f034 100644
--- a/libs/ui/dialogs/kis_dlg_adjustment_layer.h
+++ b/libs/ui/dialogs/kis_dlg_adjustment_layer.h
@@ -1,79 +1,81 @@
/*
* Copyright (c) 2006 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2008 Cyrille Berger <cberger@cberger.net>
*
* 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 KISDLGAdjustMENTLAYER_H
#define KISDLGAdjustMENTLAYER_H
#include <KoDialog.h>
#include <QLabel>
class KisFilterConfiguration;
class KisNodeFilterInterface;
class KisViewManager;
#include "kis_types.h"
#include "ui_wdgfilternodecreation.h"
/**
* Create a new adjustment layer.
*/
class KisDlgAdjustmentLayer : public KoDialog
{
Q_OBJECT
public:
/**
* Create a new adjustmentlayer dialog
*
- * @param layerName the name of the adjustment layer
+ * @param node the name of the adjustment node
+ * @param nfi filter interface
* @param paintDevice the paint device that is used as source for the preview
+ * @param layerName the name of the layer
* @param caption the caption for the dialog -- create or properties
+ * @param view the view manager
* @param parent the widget parent of this dialog
- * @param name the QObject name, if any
*/
KisDlgAdjustmentLayer(KisNodeSP node,
KisNodeFilterInterface* nfi,
KisPaintDeviceSP paintDevice,
const QString & layerName,
const QString & caption,
KisViewManager *view,
QWidget *parent = 0);
~KisDlgAdjustmentLayer() override;
KisFilterConfigurationSP filterConfiguration() const;
QString layerName() const;
public Q_SLOTS:
void adjustSize();
protected Q_SLOTS:
void slotNameChanged(const QString &);
void slotConfigChanged();
void slotFilterWidgetSizeChanged();
private:
KisNodeSP m_node;
KisNodeFilterInterface *m_nodeFilterInterface;
Ui::WdgFilterNodeCreation wdgFilterNodeCreation;
KisFilterConfigurationSP m_currentFilter;
bool m_customName;
QString m_layerName;
};
#endif
diff --git a/libs/ui/dialogs/kis_dlg_file_layer.cpp b/libs/ui/dialogs/kis_dlg_file_layer.cpp
index 040bd84484..87632b08ff 100644
--- a/libs/ui/dialogs/kis_dlg_file_layer.cpp
+++ b/libs/ui/dialogs/kis_dlg_file_layer.cpp
@@ -1,120 +1,118 @@
/* This file is part of the KDE project
* Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2013
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_dlg_file_layer.h"
#include <QLineEdit>
#include <QCheckBox>
#include <QStandardPaths>
#include <klocalizedstring.h>
#include <KoFileDialog.h>
#include <KisApplication.h>
#include <KisImportExportManager.h>
#include <kis_file_name_requester.h>
#include <kis_config_widget.h>
#include <kis_paint_device.h>
#include <kis_transaction.h>
#include <kis_node.h>
#include <kis_file_layer.h>
KisDlgFileLayer::KisDlgFileLayer(const QString &basePath, const QString & name, QWidget * parent)
: KoDialog(parent)
, m_basePath(basePath)
{
setButtons(Ok | Cancel);
setDefaultButton(Ok);
QWidget * page = new QWidget(this);
dlgWidget.setupUi(page);
QStringList mimes = KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import);
- // Nesting .kra files is not supported, so do not offer that option. See https://bugs.kde.org/show_bug.cgi?id=386515
- mimes.removeAll("application/x-krita");
dlgWidget.wdgUrlRequester->setMimeTypeFilters(mimes);
setMainWidget(page);
//dlgWidget.wdgUrlRequester->setBasePath(m_basePath);
dlgWidget.wdgUrlRequester->setStartDir(m_basePath);
dlgWidget.txtLayerName->setText(name);
connect(dlgWidget.wdgUrlRequester, SIGNAL(textChanged(QString)),
SLOT(slotNameChanged(QString)));
enableButtonOk(false);
}
void KisDlgFileLayer::slotNameChanged(const QString & text)
{
enableButtonOk(!text.isEmpty());
}
QString KisDlgFileLayer::layerName() const
{
return dlgWidget.txtLayerName->text();
}
KisFileLayer::ScalingMethod KisDlgFileLayer::scaleToImageResolution() const
{
if (dlgWidget.radioDontScale->isChecked()) {
return KisFileLayer::None;
}
else if (dlgWidget.radioScaleToImageSize->isChecked()) {
return KisFileLayer::ToImageSize;
}
else {
return KisFileLayer::ToImagePPI;
}
}
void KisDlgFileLayer::setFileName(QString fileName)
{
dlgWidget.wdgUrlRequester->setFileName(fileName);
}
void KisDlgFileLayer::setScalingMethod(KisFileLayer::ScalingMethod method)
{
dlgWidget.radioDontScale->setChecked(false);
dlgWidget.radioScaleToImageSize->setChecked(false);
dlgWidget.radioScalePPI->setChecked(false);
if (method == KisFileLayer::None) {
dlgWidget.radioDontScale->setChecked(true);
} else if (method == KisFileLayer::ToImageSize) {
dlgWidget.radioScaleToImageSize->setChecked(true);
} else {
dlgWidget.radioScalePPI->setChecked(true);
}
}
QString KisDlgFileLayer::fileName() const
{
QString path = dlgWidget.wdgUrlRequester->fileName();
QFileInfo fi(path);
if (fi.isSymLink()) {
path = fi.symLinkTarget();
fi = QFileInfo(path);
}
if (!m_basePath.isEmpty() && fi.isAbsolute()) {
QDir directory(m_basePath);
path = directory.relativeFilePath(path);
}
return path;
}
diff --git a/libs/ui/dialogs/kis_dlg_file_layer.h b/libs/ui/dialogs/kis_dlg_file_layer.h
index b7d83aa686..df34cd1650 100644
--- a/libs/ui/dialogs/kis_dlg_file_layer.h
+++ b/libs/ui/dialogs/kis_dlg_file_layer.h
@@ -1,63 +1,64 @@
/* This file is part of the KDE project
* Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2013
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KIS_DLG_FILE_LAYER_H
#define KIS_DLG_FILE_LAYER_H
#include <KoDialog.h>
#include <QString>
#include <kis_file_layer.h>
#include "ui_wdgdlgfilelayer.h"
/**
* Create a new file layer
*/
class KisDlgFileLayer : public KoDialog
{
public:
Q_OBJECT
public:
/**
* Create a new file layer
+ * @param basePath the base path of the layer
* @param name the proposed name for this layer
* @param parent the widget parent of this dialog
*/
KisDlgFileLayer(const QString &basePath, const QString &name, QWidget *parent = 0);
QString fileName() const;
QString layerName() const;
KisFileLayer::ScalingMethod scaleToImageResolution() const;
void setFileName(QString fileName);
void setScalingMethod(KisFileLayer::ScalingMethod method);
protected Q_SLOTS:
void slotNameChanged(const QString &);
private:
Ui_WdgDlgFileLayer dlgWidget;
QString m_basePath;
};
#endif
diff --git a/libs/ui/dialogs/kis_dlg_generator_layer.cpp b/libs/ui/dialogs/kis_dlg_generator_layer.cpp
index 4f0dff2f44..d4819e6bef 100644
--- a/libs/ui/dialogs/kis_dlg_generator_layer.cpp
+++ b/libs/ui/dialogs/kis_dlg_generator_layer.cpp
@@ -1,76 +1,128 @@
/* This file is part of the KDE project
* Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2008
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_dlg_generator_layer.h"
#include <QGroupBox>
#include <QLabel>
#include <QLayout>
#include <QGridLayout>
#include <klocalizedstring.h>
#include <kis_config_widget.h>
#include <filter/kis_filter_configuration.h>
#include <kis_paint_device.h>
#include <kis_transaction.h>
+#include <commands/kis_change_filter_command.h>
+#include <kis_generator_layer.h>
#include <KisViewManager.h>
+#include <KisDocument.h>
-KisDlgGeneratorLayer::KisDlgGeneratorLayer(const QString & name, KisViewManager *view, QWidget *parent)
+KisDlgGeneratorLayer::KisDlgGeneratorLayer(const QString & defaultName, KisViewManager *view, QWidget *parent, KisGeneratorLayerSP glayer = 0, const KisFilterConfigurationSP previousConfig = 0)
: KoDialog(parent)
, m_customName(false)
, m_freezeName(false)
{
setButtons(Ok | Cancel);
setDefaultButton(Ok);
- QWidget * page = new QWidget(this);
+ isEditing = glayer && previousConfig;
+
+ if(isEditing){
+ setModal(false);
+ layer = glayer;
+ configBefore = previousConfig;
+ }
+
+ QWidget *page = new QWidget(this);
+
+ m_view = view;
dlgWidget.setupUi(page);
- dlgWidget.wdgGenerator->initialize(view);
+ dlgWidget.wdgGenerator->initialize(m_view);
setMainWidget(page);
- dlgWidget.txtLayerName->setText(name);
+ dlgWidget.txtLayerName->setText( isEditing ? layer->name() : defaultName );
connect(dlgWidget.txtLayerName, SIGNAL(textChanged(QString)),
this, SLOT(slotNameChanged(QString)));
+ connect(dlgWidget.wdgGenerator, SIGNAL(previewConfiguration()), this, SLOT(previewGenerator()));
+}
+
+KisDlgGeneratorLayer::~KisDlgGeneratorLayer()
+{
+ /*Editing a layer should be using the show function with automatic deletion on close.
+ *Because of this, the action should be taken care of when the window is closed and
+ *the user has accepted the changes.*/
+ if(isEditing && result() == QDialog::Accepted) {
+
+ layer->setName(layerName());
+
+ KisFilterConfigurationSP configAfter(configuration());
+ Q_ASSERT(configAfter);
+ QString xmlBefore = configBefore->toXML();
+ QString xmlAfter = configAfter->toXML();
+
+ if (xmlBefore != xmlAfter) {
+ KisChangeFilterCmd *cmd
+ = new KisChangeFilterCmd(layer,
+ configBefore->name(),
+ xmlBefore,
+ configAfter->name(),
+ xmlAfter,
+ true);
+
+ m_view->undoAdapter()->addCommand(cmd);
+ m_view->document()->setModified(true);
+ }
+ }
+ else if(isEditing && result() == QDialog::Rejected){
+ layer->setFilter(configBefore);
+ }
}
void KisDlgGeneratorLayer::slotNameChanged(const QString & text)
{
if (m_freezeName)
return;
m_customName = !text.isEmpty();
enableButtonOk(m_customName);
}
+void KisDlgGeneratorLayer::previewGenerator()
+{
+ if (isEditing && layer)
+ layer->setFilter(configuration());
+}
+
void KisDlgGeneratorLayer::setConfiguration(const KisFilterConfigurationSP config)
{
dlgWidget.wdgGenerator->setConfiguration(config);
}
KisFilterConfigurationSP KisDlgGeneratorLayer::configuration() const
{
return dlgWidget.wdgGenerator->configuration();
}
QString KisDlgGeneratorLayer::layerName() const
{
return dlgWidget.txtLayerName->text();
}
diff --git a/libs/ui/dialogs/kis_dlg_generator_layer.h b/libs/ui/dialogs/kis_dlg_generator_layer.h
index d0c9df3454..6216f0d1ae 100644
--- a/libs/ui/dialogs/kis_dlg_generator_layer.h
+++ b/libs/ui/dialogs/kis_dlg_generator_layer.h
@@ -1,63 +1,71 @@
/* This file is part of the KDE project
* Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2008
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KIS_DLG_GENERATORLAYER_H
#define KIS_DLG_GENERATORLAYER_H
#include <KoDialog.h>
#include <QString>
class KisFilterConfiguration;
class KisViewManager;
#include "ui_wdgdlggeneratorlayer.h"
#include <generator/kis_generator.h>
/**
* Create a new generator layer
*/
class KisDlgGeneratorLayer : public KoDialog
{
public:
Q_OBJECT
public:
/**
* Create a new generator layer
* @param name the proposed name for this layer
+ * @param view the view manager
* @param parent the widget parent of this dialog
+ * @param glayer optional generator layer for editing
+ * @param previousConfig optional configuration of layer being edited.
*/
- KisDlgGeneratorLayer(const QString & name, KisViewManager *view, QWidget *parent);
+ KisDlgGeneratorLayer(const QString & defaultLayerName, KisViewManager *arg_view, QWidget *parent, KisGeneratorLayerSP glayer, const KisFilterConfigurationSP previousConfig);
+ ~KisDlgGeneratorLayer() override;
void setConfiguration(const KisFilterConfigurationSP config);
KisFilterConfigurationSP configuration() const;
QString layerName() const;
protected Q_SLOTS:
-
void slotNameChanged(const QString &);
+ void previewGenerator();
private:
-
Ui_WdgDlgGeneratorLayer dlgWidget;
+ KisGeneratorLayerSP layer;
+ KisFilterConfigurationSP configBefore;
+ KisViewManager *m_view;
+ bool isEditing;
+
bool m_customName;
bool m_freezeName;
};
#endif
diff --git a/libs/ui/dialogs/kis_dlg_layer_style.cpp b/libs/ui/dialogs/kis_dlg_layer_style.cpp
index ee36d22d3a..9ba1fca21e 100644
--- a/libs/ui/dialogs/kis_dlg_layer_style.cpp
+++ b/libs/ui/dialogs/kis_dlg_layer_style.cpp
@@ -1,1452 +1,1343 @@
/*
* Copyright (c) 2014 Boudewijn Rempt <boud@valdyas.org>
*
* 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_dlg_layer_style.h"
#include <QWidget>
#include <QStackedWidget>
#include <QTreeWidget>
#include <QListWidget>
#include <QListWidgetItem>
#include <QComboBox>
#include <QDial>
#include <QCheckBox>
#include <QSpinBox>
#include <QUuid>
#include <QInputDialog>
-
#include <KoColorPopupButton.h>
#include <KoColorSpaceRegistry.h>
#include <KoResourceServerProvider.h>
#include "kis_config.h"
#include "kis_cmb_contour.h"
#include "kis_cmb_gradient.h"
#include "KisResourceServerProvider.h"
#include "kis_psd_layer_style_resource.h"
#include "kis_psd_layer_style.h"
#include "kis_signals_blocker.h"
#include "kis_signal_compressor.h"
#include "kis_canvas_resource_provider.h"
#include <KoFileDialog.h>
-
KoAbstractGradient* fetchGradientLazy(KoAbstractGradient *gradient,
KisCanvasResourceProvider *resourceProvider)
{
if (!gradient) {
gradient = resourceProvider->currentGradient();
}
return gradient;
}
KisDlgLayerStyle::KisDlgLayerStyle(KisPSDLayerStyleSP layerStyle, KisCanvasResourceProvider *resourceProvider, QWidget *parent)
: KoDialog(parent)
, m_layerStyle(layerStyle)
, m_initialLayerStyle(layerStyle->clone())
, m_isSwitchingPredefinedStyle(false)
, m_sanityLayerStyleDirty(false)
{
setCaption(i18n("Layer Styles"));
setButtons(Ok | Cancel);
setDefaultButton(Ok);
m_configChangedCompressor =
new KisSignalCompressor(1000, KisSignalCompressor::POSTPONE, this);
connect(m_configChangedCompressor, SIGNAL(timeout()), SIGNAL(configChanged()));
QWidget *page = new QWidget(this);
wdgLayerStyles.setupUi(page);
setMainWidget(page);
wdgLayerStyles.chkPreview->setVisible(false);
connect(wdgLayerStyles.lstStyleSelector, SIGNAL(itemChanged(QListWidgetItem*)), SLOT(notifyGuiConfigChanged()));
m_stylesSelector = new StylesSelector(this);
connect(m_stylesSelector, SIGNAL(styleSelected(KisPSDLayerStyleSP)), SLOT(notifyPredefinedStyleSelected(KisPSDLayerStyleSP)));
wdgLayerStyles.stylesStack->addWidget(m_stylesSelector);
m_blendingOptions = new BlendingOptions(this);
wdgLayerStyles.stylesStack->addWidget(m_blendingOptions);
m_dropShadow = new DropShadow(DropShadow::DropShadowMode, this);
wdgLayerStyles.stylesStack->addWidget(m_dropShadow);
connect(m_dropShadow, SIGNAL(configChanged()), SLOT(notifyGuiConfigChanged()));
m_innerShadow = new DropShadow(DropShadow::InnerShadowMode, this);
wdgLayerStyles.stylesStack->addWidget(m_innerShadow);
connect(m_innerShadow, SIGNAL(configChanged()), SLOT(notifyGuiConfigChanged()));
m_outerGlow = new InnerGlow(InnerGlow::OuterGlowMode, resourceProvider, this);
wdgLayerStyles.stylesStack->addWidget(m_outerGlow);
connect(m_outerGlow, SIGNAL(configChanged()), SLOT(notifyGuiConfigChanged()));
m_innerGlow = new InnerGlow(InnerGlow::InnerGlowMode, resourceProvider, this);
wdgLayerStyles.stylesStack->addWidget(m_innerGlow);
connect(m_innerGlow, SIGNAL(configChanged()), SLOT(notifyGuiConfigChanged()));
m_contour = new Contour(this);
m_texture = new Texture(this);
m_bevelAndEmboss = new BevelAndEmboss(m_contour, m_texture, this);
wdgLayerStyles.stylesStack->addWidget(m_bevelAndEmboss);
wdgLayerStyles.stylesStack->addWidget(m_contour);
wdgLayerStyles.stylesStack->addWidget(m_texture);
connect(m_bevelAndEmboss, SIGNAL(configChanged()), SLOT(notifyGuiConfigChanged()));
m_satin = new Satin(this);
wdgLayerStyles.stylesStack->addWidget(m_satin);
connect(m_satin, SIGNAL(configChanged()), SLOT(notifyGuiConfigChanged()));
m_colorOverlay = new ColorOverlay(this);
wdgLayerStyles.stylesStack->addWidget(m_colorOverlay);
connect(m_colorOverlay, SIGNAL(configChanged()), SLOT(notifyGuiConfigChanged()));
m_gradientOverlay = new GradientOverlay(resourceProvider, this);
wdgLayerStyles.stylesStack->addWidget(m_gradientOverlay);
connect(m_gradientOverlay, SIGNAL(configChanged()), SLOT(notifyGuiConfigChanged()));
m_patternOverlay = new PatternOverlay(this);
wdgLayerStyles.stylesStack->addWidget(m_patternOverlay);
connect(m_patternOverlay, SIGNAL(configChanged()), SLOT(notifyGuiConfigChanged()));
m_stroke = new Stroke(resourceProvider, this);
wdgLayerStyles.stylesStack->addWidget(m_stroke);
connect(m_stroke, SIGNAL(configChanged()), SLOT(notifyGuiConfigChanged()));
KisConfig cfg(true);
wdgLayerStyles.stylesStack->setCurrentIndex(cfg.readEntry("KisDlgLayerStyle::current", 1));
wdgLayerStyles.lstStyleSelector->setCurrentRow(cfg.readEntry("KisDlgLayerStyle::current", 1));
connect(wdgLayerStyles.lstStyleSelector,
SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
this, SLOT(changePage(QListWidgetItem*,QListWidgetItem*)));
notifyPredefinedStyleSelected(layerStyle);
connect(m_dropShadow, SIGNAL(globalAngleChanged(int)), SLOT(syncGlobalAngle(int)));
connect(m_innerShadow, SIGNAL(globalAngleChanged(int)), SLOT(syncGlobalAngle(int)));
connect(m_bevelAndEmboss, SIGNAL(globalAngleChanged(int)), SLOT(syncGlobalAngle(int)));
connect(wdgLayerStyles.btnNewStyle, SIGNAL(clicked()), SLOT(slotNewStyle()));
connect(wdgLayerStyles.btnLoadStyle, SIGNAL(clicked()), SLOT(slotLoadStyle()));
connect(wdgLayerStyles.btnSaveStyle, SIGNAL(clicked()), SLOT(slotSaveStyle()));
connect(wdgLayerStyles.chkMasterFxSwitch, SIGNAL(toggled(bool)), SLOT(slotMasterFxSwitchChanged(bool)));
connect(this, SIGNAL(accepted()), SLOT(slotNotifyOnAccept()));
connect(this, SIGNAL(rejected()), SLOT(slotNotifyOnReject()));
}
KisDlgLayerStyle::~KisDlgLayerStyle()
{
}
void KisDlgLayerStyle::slotMasterFxSwitchChanged(bool value)
{
wdgLayerStyles.lstStyleSelector->setEnabled(value);
wdgLayerStyles.stylesStack->setEnabled(value);
wdgLayerStyles.btnNewStyle->setEnabled(value);
wdgLayerStyles.btnLoadStyle->setEnabled(value);
wdgLayerStyles.btnSaveStyle->setEnabled(value);
notifyGuiConfigChanged();
}
void KisDlgLayerStyle::notifyGuiConfigChanged()
{
if (m_isSwitchingPredefinedStyle) return;
m_configChangedCompressor->start();
m_layerStyle->setUuid(QUuid::createUuid());
m_sanityLayerStyleDirty = true;
m_stylesSelector->notifyExternalStyleChanged(m_layerStyle->name(), m_layerStyle->uuid());
}
void KisDlgLayerStyle::notifyPredefinedStyleSelected(KisPSDLayerStyleSP style)
{
m_isSwitchingPredefinedStyle = true;
setStyle(style);
m_isSwitchingPredefinedStyle = false;
m_configChangedCompressor->start();
}
void KisDlgLayerStyle::slotNotifyOnAccept()
{
if (m_configChangedCompressor->isActive()) {
m_configChangedCompressor->stop();
emit configChanged();
}
}
void KisDlgLayerStyle::slotNotifyOnReject()
{
notifyPredefinedStyleSelected(m_initialLayerStyle);
m_configChangedCompressor->stop();
emit configChanged();
}
bool checkCustomNameAvailable(const QString &name)
{
const QString customName = "CustomStyles.asl";
KoResourceServer<KisPSDLayerStyleCollectionResource> *server = KisResourceServerProvider::instance()->layerStyleCollectionServer();
KoResource *resource = server->resourceByName(customName);
if (!resource) return true;
KisPSDLayerStyleCollectionResource *collection = dynamic_cast<KisPSDLayerStyleCollectionResource*>(resource);
Q_FOREACH (KisPSDLayerStyleSP style, collection->layerStyles()) {
if (style->name() == name) {
return false;
}
}
return true;
}
QString selectAvailableStyleName(const QString &name)
{
QString finalName = name;
if (checkCustomNameAvailable(finalName)) {
return finalName;
}
int i = 0;
do {
finalName = QString("%1%2").arg(name).arg(i++);
} while (!checkCustomNameAvailable(finalName));
return finalName;
}
void KisDlgLayerStyle::slotNewStyle()
{
QString styleName =
QInputDialog::getText(this,
i18nc("@title:window", "Enter new style name"),
i18nc("@label:textbox", "Name:"),
QLineEdit::Normal, i18nc("Default name for a new style", "New Style"));
KisPSDLayerStyleSP style = this->style();
style->setName(selectAvailableStyleName(styleName));
m_stylesSelector->addNewStyle(style->clone());
}
void KisDlgLayerStyle::slotLoadStyle()
{
QString filename; // default value?
KoFileDialog dialog(this, KoFileDialog::OpenFile, "layerstyle");
dialog.setCaption(i18n("Select ASL file"));
dialog.setMimeTypeFilters(QStringList() << "application/x-photoshop-style-library", "application/x-photoshop-style-library");
filename = dialog.filename();
m_stylesSelector->loadCollection(filename);
wdgLayerStyles.lstStyleSelector->setCurrentRow(0);
}
void KisDlgLayerStyle::slotSaveStyle()
{
QString filename; // default value?
KoFileDialog dialog(this, KoFileDialog::SaveFile, "layerstyle");
dialog.setCaption(i18n("Select ASL file"));
dialog.setMimeTypeFilters(QStringList() << "application/x-photoshop-style-library", "application/x-photoshop-style-library");
filename = dialog.filename();
QScopedPointer<KisPSDLayerStyleCollectionResource> collection(
new KisPSDLayerStyleCollectionResource(filename));
KisPSDLayerStyleSP newStyle = style()->clone();
newStyle->setName(QFileInfo(filename).baseName());
KisPSDLayerStyleCollectionResource::StylesVector vector = collection->layerStyles();
vector << newStyle;
collection->setLayerStyles(vector);
collection->save();
}
void KisDlgLayerStyle::changePage(QListWidgetItem *current, QListWidgetItem *previous)
{
if (!current) {
current = previous;
}
wdgLayerStyles.stylesStack->setCurrentIndex(wdgLayerStyles.lstStyleSelector->row(current));
}
void KisDlgLayerStyle::setStyle(KisPSDLayerStyleSP style)
{
// we may self-assign style is some cases
if (style != m_layerStyle) {
*m_layerStyle = *style;
}
m_sanityLayerStyleDirty = false;
{
KisSignalsBlocker b(m_stylesSelector);
m_stylesSelector->notifyExternalStyleChanged(m_layerStyle->name(), m_layerStyle->uuid());
}
QListWidgetItem *item;
item = wdgLayerStyles.lstStyleSelector->item(2);
item->setCheckState(m_layerStyle->dropShadow()->effectEnabled() ? Qt::Checked : Qt::Unchecked);
item = wdgLayerStyles.lstStyleSelector->item(3);
item->setCheckState(m_layerStyle->innerShadow()->effectEnabled() ? Qt::Checked : Qt::Unchecked);
item = wdgLayerStyles.lstStyleSelector->item(4);
item->setCheckState(m_layerStyle->outerGlow()->effectEnabled() ? Qt::Checked : Qt::Unchecked);
item = wdgLayerStyles.lstStyleSelector->item(5);
item->setCheckState(m_layerStyle->innerGlow()->effectEnabled() ? Qt::Checked : Qt::Unchecked);
item = wdgLayerStyles.lstStyleSelector->item(6);
item->setCheckState(m_layerStyle->bevelAndEmboss()->effectEnabled() ? Qt::Checked : Qt::Unchecked);
item = wdgLayerStyles.lstStyleSelector->item(7);
item->setCheckState(m_layerStyle->bevelAndEmboss()->contourEnabled() ? Qt::Checked : Qt::Unchecked);
item = wdgLayerStyles.lstStyleSelector->item(8);
item->setCheckState(m_layerStyle->bevelAndEmboss()->textureEnabled() ? Qt::Checked : Qt::Unchecked);
item = wdgLayerStyles.lstStyleSelector->item(9);
item->setCheckState(m_layerStyle->satin()->effectEnabled() ? Qt::Checked : Qt::Unchecked);
item = wdgLayerStyles.lstStyleSelector->item(10);
item->setCheckState(m_layerStyle->colorOverlay()->effectEnabled() ? Qt::Checked : Qt::Unchecked);
item = wdgLayerStyles.lstStyleSelector->item(11);
item->setCheckState(m_layerStyle->gradientOverlay()->effectEnabled() ? Qt::Checked : Qt::Unchecked);
item = wdgLayerStyles.lstStyleSelector->item(12);
item->setCheckState(m_layerStyle->patternOverlay()->effectEnabled() ? Qt::Checked : Qt::Unchecked);
item = wdgLayerStyles.lstStyleSelector->item(13);
item->setCheckState(m_layerStyle->stroke()->effectEnabled() ? Qt::Checked : Qt::Unchecked);
m_dropShadow->setShadow(m_layerStyle->dropShadow());
m_innerShadow->setShadow(m_layerStyle->innerShadow());
m_outerGlow->setConfig(m_layerStyle->outerGlow());
m_innerGlow->setConfig(m_layerStyle->innerGlow());
m_bevelAndEmboss->setBevelAndEmboss(m_layerStyle->bevelAndEmboss());
m_satin->setSatin(m_layerStyle->satin());
m_colorOverlay->setColorOverlay(m_layerStyle->colorOverlay());
m_gradientOverlay->setGradientOverlay(m_layerStyle->gradientOverlay());
m_patternOverlay->setPatternOverlay(m_layerStyle->patternOverlay());
m_stroke->setStroke(m_layerStyle->stroke());
wdgLayerStyles.chkMasterFxSwitch->setChecked(m_layerStyle->isEnabled());
slotMasterFxSwitchChanged(m_layerStyle->isEnabled());
}
KisPSDLayerStyleSP KisDlgLayerStyle::style() const
{
m_layerStyle->setEnabled(wdgLayerStyles.chkMasterFxSwitch->isChecked());
m_layerStyle->dropShadow()->setEffectEnabled(wdgLayerStyles.lstStyleSelector->item(2)->checkState() == Qt::Checked);
m_layerStyle->innerShadow()->setEffectEnabled(wdgLayerStyles.lstStyleSelector->item(3)->checkState() == Qt::Checked);
m_layerStyle->outerGlow()->setEffectEnabled(wdgLayerStyles.lstStyleSelector->item(4)->checkState() == Qt::Checked);
m_layerStyle->innerGlow()->setEffectEnabled(wdgLayerStyles.lstStyleSelector->item(5)->checkState() == Qt::Checked);
m_layerStyle->bevelAndEmboss()->setEffectEnabled(wdgLayerStyles.lstStyleSelector->item(6)->checkState() == Qt::Checked);
m_layerStyle->bevelAndEmboss()->setContourEnabled(wdgLayerStyles.lstStyleSelector->item(7)->checkState() == Qt::Checked);
m_layerStyle->bevelAndEmboss()->setTextureEnabled(wdgLayerStyles.lstStyleSelector->item(8)->checkState() == Qt::Checked);
m_layerStyle->satin()->setEffectEnabled(wdgLayerStyles.lstStyleSelector->item(9)->checkState() == Qt::Checked);
m_layerStyle->colorOverlay()->setEffectEnabled(wdgLayerStyles.lstStyleSelector->item(10)->checkState() == Qt::Checked);
m_layerStyle->gradientOverlay()->setEffectEnabled(wdgLayerStyles.lstStyleSelector->item(11)->checkState() == Qt::Checked);
m_layerStyle->patternOverlay()->setEffectEnabled(wdgLayerStyles.lstStyleSelector->item(12)->checkState() == Qt::Checked);
m_layerStyle->stroke()->setEffectEnabled(wdgLayerStyles.lstStyleSelector->item(13)->checkState() == Qt::Checked);
m_dropShadow->fetchShadow(m_layerStyle->dropShadow());
m_innerShadow->fetchShadow(m_layerStyle->innerShadow());
m_outerGlow->fetchConfig(m_layerStyle->outerGlow());
m_innerGlow->fetchConfig(m_layerStyle->innerGlow());
m_bevelAndEmboss->fetchBevelAndEmboss(m_layerStyle->bevelAndEmboss());
m_satin->fetchSatin(m_layerStyle->satin());
m_colorOverlay->fetchColorOverlay(m_layerStyle->colorOverlay());
m_gradientOverlay->fetchGradientOverlay(m_layerStyle->gradientOverlay());
m_patternOverlay->fetchPatternOverlay(m_layerStyle->patternOverlay());
m_stroke->fetchStroke(m_layerStyle->stroke());
m_sanityLayerStyleDirty = false;
m_stylesSelector->notifyExternalStyleChanged(m_layerStyle->name(), m_layerStyle->uuid());
return m_layerStyle;
}
void KisDlgLayerStyle::syncGlobalAngle(int angle)
{
KisPSDLayerStyleSP style = this->style();
if (style->dropShadow()->useGlobalLight()) {
style->dropShadow()->setAngle(angle);
}
if (style->innerShadow()->useGlobalLight()) {
style->innerShadow()->setAngle(angle);
}
if (style->bevelAndEmboss()->useGlobalLight()) {
style->bevelAndEmboss()->setAngle(angle);
}
setStyle(style);
}
/********************************************************************/
/***** Styles Selector **********************************************/
/********************************************************************/
class StyleItem : public QListWidgetItem {
public:
StyleItem(KisPSDLayerStyleSP style)
: QListWidgetItem(style->name())
, m_style(style)
{
}
public:
KisPSDLayerStyleSP m_style;
};
StylesSelector::StylesSelector(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
connect(ui.cmbStyleCollections, SIGNAL(activated(QString)), this, SLOT(loadStyles(QString)));
connect(ui.listStyles, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, SLOT(selectStyle(QListWidgetItem*,QListWidgetItem*)));
refillCollections();
if (ui.cmbStyleCollections->count()) {
ui.cmbStyleCollections->setCurrentIndex(0);
loadStyles(ui.cmbStyleCollections->currentText());
}
}
void StylesSelector::refillCollections()
{
QString previousCollection = ui.cmbStyleCollections->currentText();
ui.cmbStyleCollections->clear();
Q_FOREACH (KoResource *res, KisResourceServerProvider::instance()->layerStyleCollectionServer()->resources()) {
ui.cmbStyleCollections->addItem(res->name());
}
if (!previousCollection.isEmpty()) {
KisSignalsBlocker blocker(this);
int index = ui.cmbStyleCollections->findText(previousCollection);
ui.cmbStyleCollections->setCurrentIndex(index);
}
}
void StylesSelector::notifyExternalStyleChanged(const QString &name, const QUuid &uuid)
{
int currentIndex = -1;
for (int i = 0; i < ui.listStyles->count(); i++ ) {
StyleItem *item = dynamic_cast<StyleItem*>(ui.listStyles->item(i));
QString itemName = item->m_style->name();
if (itemName == name) {
bool isDirty = item->m_style->uuid() != uuid;
if (isDirty) {
itemName += "*";
}
currentIndex = i;
}
item->setText(itemName);
}
ui.listStyles->setCurrentRow(currentIndex);
}
void StylesSelector::loadStyles(const QString &name)
{
ui.listStyles->clear();
KoResource *res = KisResourceServerProvider::instance()->layerStyleCollectionServer()->resourceByName(name);
KisPSDLayerStyleCollectionResource *collection = dynamic_cast<KisPSDLayerStyleCollectionResource*>(res);
if (collection) {
Q_FOREACH (KisPSDLayerStyleSP style, collection->layerStyles()) {
// XXX: also use the preview image, when we have one
ui.listStyles->addItem(new StyleItem(style));
}
}
}
void StylesSelector::selectStyle(QListWidgetItem *current, QListWidgetItem* /*previous*/)
{
StyleItem *item = dynamic_cast<StyleItem*>(current);
if (item) {
emit styleSelected(item->m_style);
}
}
void StylesSelector::loadCollection(const QString &fileName)
{
if (!QFileInfo(fileName).exists()) {
warnKrita << "Loaded style collection doesn't exist!";
return;
}
KisPSDLayerStyleCollectionResource *collection =
new KisPSDLayerStyleCollectionResource(fileName);
collection->load();
KoResourceServer<KisPSDLayerStyleCollectionResource> *server = KisResourceServerProvider::instance()->layerStyleCollectionServer();
collection->setFilename(server->saveLocation() + QDir::separator() + collection->name());
server->addResource(collection);
refillCollections();
int index = ui.cmbStyleCollections->findText(collection->name());
ui.cmbStyleCollections->setCurrentIndex(index);
loadStyles(collection->name());
}
void StylesSelector::addNewStyle(KisPSDLayerStyleSP style)
{
KoResourceServer<KisPSDLayerStyleCollectionResource> *server = KisResourceServerProvider::instance()->layerStyleCollectionServer();
// NOTE: not translatable, since it is a key!
const QString customName = "CustomStyles.asl";
const QString saveLocation = server->saveLocation();
const QString fullFilename = saveLocation + customName;
KoResource *resource = server->resourceByName(customName);
KisPSDLayerStyleCollectionResource *collection = 0;
if (!resource) {
collection = new KisPSDLayerStyleCollectionResource("");
collection->setName(customName);
collection->setFilename(fullFilename);
KisPSDLayerStyleCollectionResource::StylesVector vector;
vector << style;
collection->setLayerStyles(vector);
server->addResource(collection);
} else {
collection = dynamic_cast<KisPSDLayerStyleCollectionResource*>(resource);
KisPSDLayerStyleCollectionResource::StylesVector vector;
vector = collection->layerStyles();
vector << style;
collection->setLayerStyles(vector);
collection->save();
}
refillCollections();
// select in gui
int index = ui.cmbStyleCollections->findText(customName);
KIS_ASSERT_RECOVER_RETURN(index >= 0);
ui.cmbStyleCollections->setCurrentIndex(index);
loadStyles(customName);
notifyExternalStyleChanged(style->name(), style->uuid());
}
/********************************************************************/
/***** Bevel and Emboss *********************************************/
/********************************************************************/
BevelAndEmboss::BevelAndEmboss(Contour *contour, Texture *texture, QWidget *parent)
: QWidget(parent)
, m_contour(contour)
, m_texture(texture)
{
ui.setupUi(this);
// Structure
ui.intDepth->setRange(0, 100);
ui.intDepth->setSuffix(i18n(" %"));
ui.intSize->setRange(0, 250);
ui.intSize->setSuffix(i18n(" px"));
ui.intSoften->setRange(0, 18);
ui.intSoften->setSuffix(i18n(" px"));
connect(ui.cmbStyle, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.cmbTechnique, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.intDepth, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.cmbDirection, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.intSize, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.intSoften, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
// Shading
ui.intOpacity->setRange(0, 100);
ui.intOpacity->setSuffix(i18n(" %"));
ui.intOpacity2->setRange(0, 100);
ui.intOpacity2->setSuffix(i18n(" %"));
- connect(ui.dialAngle, SIGNAL(valueChanged(int)), SLOT(slotDialAngleChanged(int)));
- connect(ui.intAngle, SIGNAL(valueChanged(int)), SLOT(slotIntAngleChanged(int)));
- connect(ui.chkUseGlobalLight, SIGNAL(toggled(bool)), SLOT(slotGlobalLightToggled()));
+ ui.angleSelector->enableGlobalLight(true);
+ connect(ui.angleSelector, SIGNAL(globalAngleChanged(int)), SIGNAL(globalAngleChanged(int)));
+ connect(ui.angleSelector, SIGNAL(configChanged()), SIGNAL(configChanged()));
- connect(ui.dialAngle, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
- connect(ui.intAngle, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
- connect(ui.chkUseGlobalLight, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.intAltitude, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.cmbContour, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.chkAntiAliased, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.cmbHighlightMode, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.bnHighlightColor, SIGNAL(changed(KoColor)), SIGNAL(configChanged()));
connect(ui.intOpacity, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.cmbShadowMode, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.bnShadowColor, SIGNAL(changed(KoColor)), SIGNAL(configChanged()));
connect(ui.intOpacity2, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
// Contour
m_contour->ui.intRange->setRange(1, 100);
m_contour->ui.intRange->setSuffix(i18n(" %"));
connect(m_contour->ui.cmbContour, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(m_contour->ui.chkAntiAliased, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(m_contour->ui.intRange, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
// Texture
m_texture->ui.intScale->setRange(0, 100);
m_texture->ui.intScale->setSuffix(i18n(" %"));
m_texture->ui.intDepth->setRange(-1000, 1000);
m_texture->ui.intDepth->setSuffix(i18n(" %"));
connect(m_texture->ui.patternChooser, SIGNAL(resourceSelected(KoResource*)), SIGNAL(configChanged()));
connect(m_texture->ui.intScale, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(m_texture->ui.intDepth, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(m_texture->ui.chkInvert, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(m_texture->ui.chkLinkWithLayer, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
}
void BevelAndEmboss::setBevelAndEmboss(const psd_layer_effects_bevel_emboss *bevelAndEmboss)
{
ui.cmbStyle->setCurrentIndex((int)bevelAndEmboss->style());
ui.cmbTechnique->setCurrentIndex((int)bevelAndEmboss->technique());
ui.intDepth->setValue(bevelAndEmboss->depth());
ui.cmbDirection->setCurrentIndex((int)bevelAndEmboss->direction());
ui.intSize->setValue(bevelAndEmboss->size());
ui.intSoften->setValue(bevelAndEmboss->soften());
- ui.dialAngle->setValue(bevelAndEmboss->angle());
- ui.intAngle->setValue(bevelAndEmboss->angle());
- ui.chkUseGlobalLight->setChecked(bevelAndEmboss->useGlobalLight());
+ ui.angleSelector->setValue(bevelAndEmboss->angle());
+ ui.angleSelector->setUseGlobalLight(bevelAndEmboss->useGlobalLight());
+
ui.intAltitude->setValue(bevelAndEmboss->altitude());
// FIXME: curve editing
// ui.cmbContour;
ui.chkAntiAliased->setChecked(bevelAndEmboss->glossAntiAliased());
ui.cmbHighlightMode->selectCompositeOp(KoID(bevelAndEmboss->highlightBlendMode()));
KoColor highlightshadow(KoColorSpaceRegistry::instance()->rgb8());
highlightshadow.fromQColor(bevelAndEmboss->highlightColor());
ui.bnHighlightColor->setColor(highlightshadow);
ui.intOpacity->setValue(bevelAndEmboss->highlightOpacity());
ui.cmbShadowMode->selectCompositeOp(KoID(bevelAndEmboss->shadowBlendMode()));
highlightshadow.fromQColor(bevelAndEmboss->shadowColor());
ui.bnShadowColor->setColor(highlightshadow);
ui.intOpacity2->setValue(bevelAndEmboss->shadowOpacity());
// FIXME: curve editing
// m_contour->ui.cmbContour;
m_contour->ui.chkAntiAliased->setChecked(bevelAndEmboss->antiAliased());
m_contour->ui.intRange->setValue(bevelAndEmboss->contourRange());
m_texture->ui.patternChooser->setCurrentPattern(bevelAndEmboss->texturePattern());
m_texture->ui.intScale->setValue(bevelAndEmboss->textureScale());
m_texture->ui.intDepth->setValue(bevelAndEmboss->textureDepth());
m_texture->ui.chkInvert->setChecked(bevelAndEmboss->textureInvert());
m_texture->ui.chkLinkWithLayer->setChecked(bevelAndEmboss->textureAlignWithLayer());
}
void BevelAndEmboss::fetchBevelAndEmboss(psd_layer_effects_bevel_emboss *bevelAndEmboss) const
{
bevelAndEmboss->setStyle((psd_bevel_style)ui.cmbStyle->currentIndex());
bevelAndEmboss->setTechnique((psd_technique_type)ui.cmbTechnique->currentIndex());
bevelAndEmboss->setDepth(ui.intDepth->value());
bevelAndEmboss->setDirection((psd_direction)ui.cmbDirection->currentIndex());
bevelAndEmboss->setSize(ui.intSize->value());
bevelAndEmboss->setSoften(ui.intSoften->value());
- bevelAndEmboss->setAngle(ui.dialAngle->value());
- bevelAndEmboss->setUseGlobalLight(ui.chkUseGlobalLight->isChecked());
+ bevelAndEmboss->setAngle(ui.angleSelector->value());
+ bevelAndEmboss->setUseGlobalLight(ui.angleSelector->useGlobalLight());
bevelAndEmboss->setAltitude(ui.intAltitude->value());
bevelAndEmboss->setGlossAntiAliased(ui.chkAntiAliased->isChecked());
bevelAndEmboss->setHighlightBlendMode(ui.cmbHighlightMode->selectedCompositeOp().id());
bevelAndEmboss->setHighlightColor(ui.bnHighlightColor->color().toQColor());
bevelAndEmboss->setHighlightOpacity(ui.intOpacity->value());
bevelAndEmboss->setShadowBlendMode(ui.cmbShadowMode->selectedCompositeOp().id());
bevelAndEmboss->setShadowColor(ui.bnShadowColor->color().toQColor());
bevelAndEmboss->setShadowOpacity(ui.intOpacity2->value());
// FIXME: curve editing
bevelAndEmboss->setAntiAliased(m_contour->ui.chkAntiAliased->isChecked());
bevelAndEmboss->setContourRange(m_contour->ui.intRange->value());
bevelAndEmboss->setTexturePattern(static_cast<KoPattern*>(m_texture->ui.patternChooser->currentResource()));
bevelAndEmboss->setTextureScale(m_texture->ui.intScale->value());
bevelAndEmboss->setTextureDepth(m_texture->ui.intDepth->value());
bevelAndEmboss->setTextureInvert(m_texture->ui.chkInvert->isChecked());
bevelAndEmboss->setTextureAlignWithLayer(m_texture->ui.chkLinkWithLayer->isChecked());
}
-void BevelAndEmboss::slotDialAngleChanged(int value)
-{
- KisSignalsBlocker b(ui.intAngle);
- ui.intAngle->setValue(value);
-
- if (ui.chkUseGlobalLight->isChecked()) {
- emit globalAngleChanged(value);
- }
-}
-
-void BevelAndEmboss::slotIntAngleChanged(int value)
-{
- KisSignalsBlocker b(ui.dialAngle);
- ui.dialAngle->setValue(value);
-
- if (ui.chkUseGlobalLight->isChecked()) {
- emit globalAngleChanged(value);
- }
-}
-
-void BevelAndEmboss::slotGlobalLightToggled()
-{
- if (ui.chkUseGlobalLight->isChecked()) {
- emit globalAngleChanged(ui.intAngle->value());
- }
-}
/********************************************************************/
/***** Texture *********************************************/
/********************************************************************/
Texture::Texture(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
}
/********************************************************************/
/***** Contour *********************************************/
/********************************************************************/
Contour::Contour(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
}
/********************************************************************/
/***** Blending Options *********************************************/
/********************************************************************/
BlendingOptions::BlendingOptions(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
// FIXME: Blend options are not implemented yet
ui.grpBlendingOptions->setTitle(QString("%1 (%2)").arg(ui.grpBlendingOptions->title()).arg(i18n("Not Implemented Yet")));
ui.grpBlendingOptions->setEnabled(false);
}
/********************************************************************/
/***** Color Overlay *********************************************/
/********************************************************************/
ColorOverlay::ColorOverlay(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
ui.intOpacity->setRange(0, 100);
ui.intOpacity->setSuffix(i18n(" %"));
connect(ui.cmbCompositeOp, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.intOpacity, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.bnColor, SIGNAL(changed(KoColor)), SIGNAL(configChanged()));
}
void ColorOverlay::setColorOverlay(const psd_layer_effects_color_overlay *colorOverlay)
{
ui.cmbCompositeOp->selectCompositeOp(KoID(colorOverlay->blendMode()));
ui.intOpacity->setValue(colorOverlay->opacity());
KoColor color(KoColorSpaceRegistry::instance()->rgb8());
color.fromQColor(colorOverlay->color());
ui.bnColor->setColor(color);
}
void ColorOverlay::fetchColorOverlay(psd_layer_effects_color_overlay *colorOverlay) const
{
colorOverlay->setBlendMode(ui.cmbCompositeOp->selectedCompositeOp().id());
colorOverlay->setOpacity(ui.intOpacity->value());
colorOverlay->setColor(ui.bnColor->color().toQColor());
}
/********************************************************************/
/***** Drop Shadow **************************************************/
/********************************************************************/
DropShadow::DropShadow(Mode mode, QWidget *parent)
: QWidget(parent),
m_mode(mode)
{
ui.setupUi(this);
ui.intOpacity->setRange(0, 100);
ui.intOpacity->setSuffix(i18n(" %"));
ui.intDistance->setRange(0, 500);
ui.intDistance->setSuffix(i18n(" px"));
ui.intDistance->setExponentRatio(3.0);
ui.intSpread->setRange(0, 100);
ui.intSpread->setSuffix(i18n(" %"));
ui.intSize->setRange(0, 250);
ui.intSize->setSuffix(i18n(" px"));
ui.intNoise->setRange(0, 100);
ui.intNoise->setSuffix(i18n(" %"));
- connect(ui.dialAngle, SIGNAL(valueChanged(int)), SLOT(slotDialAngleChanged(int)));
- connect(ui.intAngle, SIGNAL(valueChanged(int)), SLOT(slotIntAngleChanged(int)));
- connect(ui.chkUseGlobalLight, SIGNAL(toggled(bool)), SLOT(slotGlobalLightToggled()));
+ ui.angleSelector->enableGlobalLight(true);
+ connect(ui.angleSelector, SIGNAL(globalAngleChanged(int)), SIGNAL(globalAngleChanged(int)));
+ connect(ui.angleSelector, SIGNAL(configChanged()), SIGNAL(configChanged()));
// connect everything to configChanged() signal
connect(ui.cmbCompositeOp, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.intOpacity, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.bnColor, SIGNAL(changed(KoColor)), SIGNAL(configChanged()));
- connect(ui.dialAngle, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
- connect(ui.intAngle, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
- connect(ui.chkUseGlobalLight, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
-
connect(ui.intDistance, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.intSpread, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.intSize, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.cmbContour, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.chkAntiAliased, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.intNoise, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.chkLayerKnocksOutDropShadow, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
if (m_mode == InnerShadowMode) {
ui.chkLayerKnocksOutDropShadow->setVisible(false);
ui.grpMain->setTitle(i18n("Inner Shadow"));
ui.lblSpread->setText(i18n("Choke:"));
}
}
-void DropShadow::slotDialAngleChanged(int value)
-{
- KisSignalsBlocker b(ui.intAngle);
- ui.intAngle->setValue(value);
-
- if (ui.chkUseGlobalLight->isChecked()) {
- emit globalAngleChanged(value);
- }
-}
-
-void DropShadow::slotIntAngleChanged(int value)
-{
- KisSignalsBlocker b(ui.dialAngle);
- ui.dialAngle->setValue(value);
-
- if (ui.chkUseGlobalLight->isChecked()) {
- emit globalAngleChanged(value);
- }
-}
-
-void DropShadow::slotGlobalLightToggled()
-{
- if (ui.chkUseGlobalLight->isChecked()) {
- emit globalAngleChanged(ui.intAngle->value());
- }
-}
-
void DropShadow::setShadow(const psd_layer_effects_shadow_common *shadow)
{
ui.cmbCompositeOp->selectCompositeOp(KoID(shadow->blendMode()));
ui.intOpacity->setValue(shadow->opacity());
KoColor color(KoColorSpaceRegistry::instance()->rgb8());
color.fromQColor(shadow->color());
ui.bnColor->setColor(color);
- ui.dialAngle->setValue(shadow->angle());
- ui.intAngle->setValue(shadow->angle());
- ui.chkUseGlobalLight->setChecked(shadow->useGlobalLight());
+ ui.angleSelector->setValue(shadow->angle());
+ ui.angleSelector->setUseGlobalLight(shadow->useGlobalLight());
ui.intDistance->setValue(shadow->distance());
ui.intSpread->setValue(shadow->spread());
ui.intSize->setValue(shadow->size());
// FIXME: curve editing
// ui.cmbContour;
ui.chkAntiAliased->setChecked(shadow->antiAliased());
ui.intNoise->setValue(shadow->noise());
if (m_mode == DropShadowMode) {
const psd_layer_effects_drop_shadow *realDropShadow = dynamic_cast<const psd_layer_effects_drop_shadow*>(shadow);
KIS_ASSERT_RECOVER_NOOP(realDropShadow);
ui.chkLayerKnocksOutDropShadow->setChecked(shadow->knocksOut());
}
}
void DropShadow::fetchShadow(psd_layer_effects_shadow_common *shadow) const
{
shadow->setBlendMode(ui.cmbCompositeOp->selectedCompositeOp().id());
shadow->setOpacity(ui.intOpacity->value());
shadow->setColor(ui.bnColor->color().toQColor());
- shadow->setAngle(ui.dialAngle->value());
- shadow->setUseGlobalLight(ui.chkUseGlobalLight->isChecked());
+ shadow->setAngle(ui.angleSelector->value());
+ shadow->setUseGlobalLight(ui.angleSelector->useGlobalLight());
shadow->setDistance(ui.intDistance->value());
shadow->setSpread(ui.intSpread->value());
shadow->setSize(ui.intSize->value());
// FIXME: curve editing
// ui.cmbContour;
shadow->setAntiAliased(ui.chkAntiAliased->isChecked());
shadow->setNoise(ui.intNoise->value());
if (m_mode == DropShadowMode) {
psd_layer_effects_drop_shadow *realDropShadow = dynamic_cast<psd_layer_effects_drop_shadow*>(shadow);
KIS_ASSERT_RECOVER_NOOP(realDropShadow);
realDropShadow->setKnocksOut(ui.chkLayerKnocksOutDropShadow->isChecked());
}
}
class GradientPointerConverter
{
public:
static KoAbstractGradientSP resourceToStyle(KoAbstractGradient *gradient) {
return gradient ? KoAbstractGradientSP(gradient->clone()) : KoAbstractGradientSP();
}
static KoAbstractGradient* styleToResource(KoAbstractGradientSP gradient) {
if (!gradient) return 0;
KoResourceServer<KoAbstractGradient> *server = KoResourceServerProvider::instance()->gradientServer();
KoAbstractGradient *resource = server->resourceByMD5(gradient->md5());
if (!resource) {
KoAbstractGradient *clone = gradient->clone();
clone->setName(findAvailableName(gradient->name()));
server->addResource(clone, false);
resource = clone;
}
return resource;
}
private:
static QString findAvailableName(const QString &name) {
KoResourceServer<KoAbstractGradient> *server = KoResourceServerProvider::instance()->gradientServer();
QString newName = name;
int i = 0;
while (server->resourceByName(newName)) {
newName = QString("%1%2").arg(name).arg(i++);
}
return newName;
}
};
/********************************************************************/
/***** Gradient Overlay *********************************************/
/********************************************************************/
GradientOverlay::GradientOverlay(KisCanvasResourceProvider *resourceProvider, QWidget *parent)
: QWidget(parent),
m_resourceProvider(resourceProvider)
{
ui.setupUi(this);
ui.intOpacity->setRange(0, 100);
ui.intOpacity->setSuffix(i18n(" %"));
ui.intScale->setRange(0, 100);
ui.intScale->setSuffix(i18n(" %"));
- connect(ui.dialAngle, SIGNAL(valueChanged(int)), SLOT(slotDialAngleChanged(int)));
- connect(ui.intAngle, SIGNAL(valueChanged(int)), SLOT(slotIntAngleChanged(int)));
+ connect(ui.angleSelector, SIGNAL(configChanged()), SIGNAL(configChanged()));
connect(ui.cmbCompositeOp, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.intOpacity, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.cmbGradient, SIGNAL(gradientChanged(KoAbstractGradient*)), SIGNAL(configChanged()));
connect(ui.chkReverse, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.cmbStyle, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.chkAlignWithLayer, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
- connect(ui.dialAngle, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
- connect(ui.intAngle, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.intScale, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
}
void GradientOverlay::setGradientOverlay(const psd_layer_effects_gradient_overlay *config)
{
ui.cmbCompositeOp->selectCompositeOp(KoID(config->blendMode()));
ui.intOpacity->setValue(config->opacity());
KoAbstractGradient *gradient = fetchGradientLazy(
GradientPointerConverter::styleToResource(config->gradient()), m_resourceProvider);
if (gradient) {
ui.cmbGradient->setGradient(gradient);
}
ui.chkReverse->setChecked(config->reverse());
ui.cmbStyle->setCurrentIndex((int)config->style());
ui.chkAlignWithLayer->setCheckable(config->alignWithLayer());
- ui.dialAngle->setValue(config->angle());
- ui.intAngle->setValue(config->angle());
+ ui.angleSelector->setValue(config->angle());
ui.intScale->setValue(config->scale());
}
void GradientOverlay::fetchGradientOverlay(psd_layer_effects_gradient_overlay *config) const
{
config->setBlendMode(ui.cmbCompositeOp->selectedCompositeOp().id());
config->setOpacity(ui.intOpacity->value());
config->setGradient(GradientPointerConverter::resourceToStyle(ui.cmbGradient->gradient()));
config->setReverse(ui.chkReverse->isChecked());
config->setStyle((psd_gradient_style)ui.cmbStyle->currentIndex());
config->setAlignWithLayer(ui.chkAlignWithLayer->isChecked());
- config->setAngle(ui.dialAngle->value());
+ config->setAngle(ui.angleSelector->value());
config->setScale(ui.intScale->value());
}
-void GradientOverlay::slotDialAngleChanged(int value)
-{
- KisSignalsBlocker b(ui.intAngle);
- ui.intAngle->setValue(value);
-}
-
-void GradientOverlay::slotIntAngleChanged(int value)
-{
- KisSignalsBlocker b(ui.dialAngle);
- ui.dialAngle->setValue(value);
-}
/********************************************************************/
/***** Innner Glow *********************************************/
/********************************************************************/
InnerGlow::InnerGlow(Mode mode, KisCanvasResourceProvider *resourceProvider, QWidget *parent)
: QWidget(parent),
m_mode(mode),
m_resourceProvider(resourceProvider)
{
ui.setupUi(this);
ui.intOpacity->setRange(0, 100);
ui.intOpacity->setSuffix(i18n(" %"));
ui.intNoise->setRange(0, 100);
ui.intNoise->setSuffix(i18n(" %"));
ui.intChoke->setRange(0, 100);
ui.intChoke->setSuffix(i18n(" %"));
ui.intSize->setRange(0, 250);
ui.intSize->setSuffix(i18n(" px"));
ui.intRange->setRange(1, 100);
ui.intRange->setSuffix(i18n(" %"));
ui.intJitter->setRange(0, 100);
ui.intJitter->setSuffix(i18n(" %"));
connect(ui.cmbCompositeOp, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.intOpacity, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.intNoise, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.radioColor, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.bnColor, SIGNAL(changed(KoColor)), SIGNAL(configChanged()));
connect(ui.radioGradient, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.cmbGradient, SIGNAL(gradientChanged(KoAbstractGradient*)), SIGNAL(configChanged()));
connect(ui.cmbTechnique, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.cmbSource, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.intChoke, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.intSize, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.cmbContour, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.chkAntiAliased, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.intRange, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.intJitter, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
if (m_mode == OuterGlowMode) {
ui.cmbSource->hide();
ui.lblSource->hide();
ui.lblChoke->setText(i18nc("layer styles parameter", "Spread:"));
}
}
void InnerGlow::setConfig(const psd_layer_effects_glow_common *config)
{
ui.cmbCompositeOp->selectCompositeOp(KoID(config->blendMode()));
ui.intOpacity->setValue(config->opacity());
ui.intNoise->setValue(config->noise());
ui.radioColor->setChecked(config->fillType() == psd_fill_solid_color);
KoColor color(KoColorSpaceRegistry::instance()->rgb8());
color.fromQColor(config->color());
ui.bnColor->setColor(color);
ui.radioGradient->setChecked(config->fillType() == psd_fill_gradient);
KoAbstractGradient *gradient = fetchGradientLazy(
GradientPointerConverter::styleToResource(config->gradient()), m_resourceProvider);
if (gradient) {
ui.cmbGradient->setGradient(gradient);
}
ui.cmbTechnique->setCurrentIndex((int)config->technique());
ui.intChoke->setValue(config->spread());
ui.intSize->setValue(config->size());
if (m_mode == InnerGlowMode) {
const psd_layer_effects_inner_glow *iglow =
dynamic_cast<const psd_layer_effects_inner_glow *>(config);
KIS_ASSERT_RECOVER_RETURN(iglow);
ui.cmbSource->setCurrentIndex(iglow->source() == psd_glow_edge);
}
// FIXME: Curve editing
//ui.cmbContour;
ui.chkAntiAliased->setChecked(config->antiAliased());
ui.intRange->setValue(config->range());
ui.intJitter->setValue(config->jitter());
}
void InnerGlow::fetchConfig(psd_layer_effects_glow_common *config) const
{
config->setBlendMode(ui.cmbCompositeOp->selectedCompositeOp().id());
config->setOpacity(ui.intOpacity->value());
config->setNoise(ui.intNoise->value());
if (ui.radioColor->isChecked()) {
config->setFillType(psd_fill_solid_color);
}
else {
config->setFillType(psd_fill_gradient);
}
config->setColor(ui.bnColor->color().toQColor());
config->setGradient(GradientPointerConverter::resourceToStyle(ui.cmbGradient->gradient()));
config->setTechnique((psd_technique_type)ui.cmbTechnique->currentIndex());
config->setSpread(ui.intChoke->value());
config->setSize(ui.intSize->value());
if (m_mode == InnerGlowMode) {
psd_layer_effects_inner_glow *iglow =
dynamic_cast<psd_layer_effects_inner_glow *>(config);
KIS_ASSERT_RECOVER_RETURN(iglow);
iglow->setSource((psd_glow_source)ui.cmbSource->currentIndex());
}
// FIXME: Curve editing
//ui.cmbContour;
config->setAntiAliased(ui.chkAntiAliased->isChecked());
config->setRange(ui.intRange->value());
config->setJitter(ui.intJitter->value());
}
/********************************************************************/
/***** Pattern Overlay *********************************************/
/********************************************************************/
PatternOverlay::PatternOverlay(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
ui.intOpacity->setRange(0, 100);
ui.intOpacity->setSuffix(i18n(" %"));
ui.intScale->setRange(0, 100);
ui.intScale->setSuffix(i18n(" %"));
connect(ui.cmbCompositeOp, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.intOpacity, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.patternChooser, SIGNAL(resourceSelected(KoResource*)), SIGNAL(configChanged()));
connect(ui.chkLinkWithLayer, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.intScale, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
}
void PatternOverlay::setPatternOverlay(const psd_layer_effects_pattern_overlay *pattern)
{
ui.cmbCompositeOp->selectCompositeOp(KoID(pattern->blendMode()));
ui.intOpacity->setValue(pattern->opacity());
ui.patternChooser->setCurrentPattern(pattern->pattern());
ui.chkLinkWithLayer->setChecked(pattern->alignWithLayer());
ui.intScale->setValue(pattern->scale());
}
void PatternOverlay::fetchPatternOverlay(psd_layer_effects_pattern_overlay *pattern) const
{
pattern->setBlendMode(ui.cmbCompositeOp->selectedCompositeOp().id());
pattern->setOpacity(ui.intOpacity->value());
pattern->setPattern(static_cast<KoPattern*>(ui.patternChooser->currentResource()));
pattern->setAlignWithLayer(ui.chkLinkWithLayer->isChecked());
pattern->setScale(ui.intScale->value());
}
/********************************************************************/
/***** Satin *********************************************/
/********************************************************************/
Satin::Satin(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
ui.intOpacity->setRange(0, 100);
ui.intOpacity->setSuffix(i18n(" %"));
ui.intDistance->setRange(0, 250);
ui.intDistance->setSuffix(i18n(" px"));
ui.intSize->setRange(0, 250);
ui.intSize->setSuffix(i18n(" px"));
- connect(ui.dialAngle, SIGNAL(valueChanged(int)), SLOT(slotDialAngleChanged(int)));
- connect(ui.intAngle, SIGNAL(valueChanged(int)), SLOT(slotIntAngleChanged(int)));
-
connect(ui.cmbCompositeOp, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.bnColor, SIGNAL(changed(KoColor)), SIGNAL(configChanged()));
connect(ui.intOpacity, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
- connect(ui.dialAngle, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
- connect(ui.intAngle, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
+ connect(ui.angleSelector, SIGNAL(configChanged()), SIGNAL(configChanged()));
connect(ui.intDistance, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.intSize, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.cmbContour, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.chkAntiAliased, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.chkInvert, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
}
-void Satin::slotDialAngleChanged(int value)
-{
- KisSignalsBlocker b(ui.intAngle);
- ui.intAngle->setValue(value);
-}
-
-void Satin::slotIntAngleChanged(int value)
-{
- KisSignalsBlocker b(ui.dialAngle);
- ui.dialAngle->setValue(value);
-}
-
void Satin::setSatin(const psd_layer_effects_satin *satin)
{
ui.cmbCompositeOp->selectCompositeOp(KoID(satin->blendMode()));
KoColor color(KoColorSpaceRegistry::instance()->rgb8());
color.fromQColor(satin->color());
ui.bnColor->setColor(color);
ui.intOpacity->setValue(satin->opacity());
- ui.dialAngle->setValue(satin->angle());
- ui.intAngle->setValue(satin->angle());
+ ui.angleSelector->setValue(satin->angle());
ui.intDistance->setValue(satin->distance());
ui.intSize->setValue(satin->size());
// FIXME: Curve editing
//ui.cmbContour;
ui.chkAntiAliased->setChecked(satin->antiAliased());
ui.chkInvert->setChecked(satin->invert());
}
void Satin::fetchSatin(psd_layer_effects_satin *satin) const
{
satin->setBlendMode(ui.cmbCompositeOp->selectedCompositeOp().id());
satin->setOpacity(ui.intOpacity->value());
satin->setColor(ui.bnColor->color().toQColor());
- satin->setAngle(ui.dialAngle->value());
+ satin->setAngle(ui.angleSelector->value());
satin->setDistance(ui.intDistance->value());
satin->setSize(ui.intSize->value());
// FIXME: curve editing
// ui.cmbContour;
satin->setAntiAliased(ui.chkAntiAliased->isChecked());
satin->setInvert(ui.chkInvert->isChecked());
}
/********************************************************************/
/***** Stroke *********************************************/
/********************************************************************/
Stroke::Stroke(KisCanvasResourceProvider *resourceProvider, QWidget *parent)
: QWidget(parent),
m_resourceProvider(resourceProvider)
{
ui.setupUi(this);
ui.intSize->setRange(0, 250);
ui.intSize->setSuffix(i18n(" px"));
ui.intOpacity->setRange(0, 100);
ui.intOpacity->setSuffix(i18n(" %"));
ui.intScale->setRange(0, 100);
ui.intScale->setSuffix(i18n(" %"));
ui.intScale_2->setRange(0, 100);
ui.intScale_2->setSuffix(i18n(" %"));
connect(ui.cmbFillType, SIGNAL(currentIndexChanged(int)), ui.fillStack, SLOT(setCurrentIndex(int)));
connect(ui.intSize, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.cmbPosition, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.cmbCompositeOp, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.intOpacity, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
connect(ui.cmbFillType, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.bnColor, SIGNAL(changed(KoColor)), SIGNAL(configChanged()));
connect(ui.cmbGradient, SIGNAL(gradientChanged(KoAbstractGradient*)), SIGNAL(configChanged()));
connect(ui.chkReverse, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.cmbStyle, SIGNAL(currentIndexChanged(int)), SIGNAL(configChanged()));
connect(ui.chkAlignWithLayer, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
- connect(ui.dialAngle, SIGNAL(valueChanged(int)), SLOT(slotDialAngleChanged(int)));
- connect(ui.intAngle, SIGNAL(valueChanged(int)), SLOT(slotIntAngleChanged(int)));
connect(ui.intScale, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
+ connect(ui.angleSelector, SIGNAL(configChanged()), SIGNAL(configChanged()));
+
connect(ui.patternChooser, SIGNAL(resourceSelected(KoResource*)), SIGNAL(configChanged()));
connect(ui.chkLinkWithLayer, SIGNAL(toggled(bool)), SIGNAL(configChanged()));
connect(ui.intScale_2, SIGNAL(valueChanged(int)), SIGNAL(configChanged()));
// cold initialization
ui.fillStack->setCurrentIndex(ui.cmbFillType->currentIndex());
}
-void Stroke::slotDialAngleChanged(int value)
-{
- KisSignalsBlocker b(ui.intAngle);
- ui.intAngle->setValue(value);
-}
-
-void Stroke::slotIntAngleChanged(int value)
-{
- KisSignalsBlocker b(ui.dialAngle);
- ui.dialAngle->setValue(value);
-}
-
-
void Stroke::setStroke(const psd_layer_effects_stroke *stroke)
{
ui.intSize->setValue(stroke->size());
ui.cmbPosition->setCurrentIndex((int)stroke->position());
ui.cmbCompositeOp->selectCompositeOp(KoID(stroke->blendMode()));
ui.intOpacity->setValue(stroke->opacity());
ui.cmbFillType->setCurrentIndex((int)stroke->fillType());
KoColor color(KoColorSpaceRegistry::instance()->rgb8());
color.fromQColor(stroke->color());
ui.bnColor->setColor(color);
KoAbstractGradient *gradient =
fetchGradientLazy(GradientPointerConverter::styleToResource(stroke->gradient()), m_resourceProvider);
if (gradient) {
ui.cmbGradient->setGradient(gradient);
}
ui.chkReverse->setChecked(stroke->antiAliased());
ui.cmbStyle->setCurrentIndex((int)stroke->style());
ui.chkAlignWithLayer->setCheckable(stroke->alignWithLayer());
- ui.dialAngle->setValue(stroke->angle());
- ui.intAngle->setValue(stroke->angle());
+ ui.angleSelector->setValue(stroke->angle());
ui.intScale->setValue(stroke->scale());
ui.patternChooser->setCurrentPattern(stroke->pattern());
ui.chkLinkWithLayer->setChecked(stroke->alignWithLayer());
ui.intScale_2->setValue(stroke->scale());
}
void Stroke::fetchStroke(psd_layer_effects_stroke *stroke) const
{
stroke->setSize(ui.intSize->value());
stroke->setPosition((psd_stroke_position)ui.cmbPosition->currentIndex());
stroke->setBlendMode(ui.cmbCompositeOp->selectedCompositeOp().id());
stroke->setOpacity(ui.intOpacity->value());
stroke->setFillType((psd_fill_type)ui.cmbFillType->currentIndex());
stroke->setColor(ui.bnColor->color().toQColor());
stroke->setGradient(GradientPointerConverter::resourceToStyle(ui.cmbGradient->gradient()));
stroke->setReverse(ui.chkReverse->isChecked());
stroke->setStyle((psd_gradient_style)ui.cmbStyle->currentIndex());
stroke->setAlignWithLayer(ui.chkAlignWithLayer->isChecked());
- stroke->setAngle(ui.dialAngle->value());
+ stroke->setAngle(ui.angleSelector->value());
stroke->setScale(ui.intScale->value());
stroke->setPattern(static_cast<KoPattern*>(ui.patternChooser->currentResource()));
stroke->setAlignWithLayer(ui.chkLinkWithLayer->isChecked());
stroke->setScale(ui.intScale->value());
}
diff --git a/libs/ui/dialogs/kis_dlg_layer_style.h b/libs/ui/dialogs/kis_dlg_layer_style.h
index 98a561e53b..0101b62933 100644
--- a/libs/ui/dialogs/kis_dlg_layer_style.h
+++ b/libs/ui/dialogs/kis_dlg_layer_style.h
@@ -1,323 +1,299 @@
/*
* Copyright (c) 2014 Boudewijn Rempt <boud@valdyas.org>
*
* 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_LAYER_STYLE_H
#define KIS_DLG_LAYER_STYLE_H
#include <QUuid>
#include <KoDialog.h>
#include "kis_types.h"
#include <psd.h>
#include "ui_wdglayerstyles.h"
#include "ui_wdgBevelAndEmboss.h"
#include "ui_wdgblendingoptions.h"
#include "ui_WdgColorOverlay.h"
#include "ui_wdgContour.h"
#include "ui_wdgdropshadow.h"
#include "ui_WdgGradientOverlay.h"
#include "ui_wdgInnerGlow.h"
#include "ui_WdgPatternOverlay.h"
#include "ui_WdgSatin.h"
#include "ui_WdgStroke.h"
#include "ui_wdgstylesselector.h"
#include "ui_wdgTexture.h"
#include <kis_psd_layer_style.h>
class QListWidgetItem;
class KisSignalCompressor;
class KisCanvasResourceProvider;
class Contour : public QWidget {
Q_OBJECT
public:
Contour(QWidget *parent);
Ui::WdgContour ui;
};
class Texture : public QWidget {
Q_OBJECT
public:
Texture(QWidget *parent);
Ui::WdgTexture ui;
};
class BevelAndEmboss : public QWidget {
Q_OBJECT
public:
BevelAndEmboss(Contour *contour, Texture *texture, QWidget *parent);
void setBevelAndEmboss(const psd_layer_effects_bevel_emboss *bevelAndEmboss);
void fetchBevelAndEmboss(psd_layer_effects_bevel_emboss *bevelAndEmboss) const;
-private Q_SLOTS:
- void slotDialAngleChanged(int value);
- void slotIntAngleChanged(int value);
-
- void slotGlobalLightToggled();
-
Q_SIGNALS:
void configChanged();
void globalAngleChanged(int value);
private:
Contour *m_contour;
Texture *m_texture;
Ui::WdgBevelAndEmboss ui;
};
class BlendingOptions : public QWidget {
Q_OBJECT
public:
BlendingOptions(QWidget *parent);
Q_SIGNALS:
void configChanged();
private:
Ui::WdgBlendingOptions ui;
};
class ColorOverlay : public QWidget {
Q_OBJECT
public:
ColorOverlay(QWidget *parent);
void setColorOverlay(const psd_layer_effects_color_overlay *colorOverlay);
void fetchColorOverlay(psd_layer_effects_color_overlay *colorOverlay) const;
Q_SIGNALS:
void configChanged();
private:
Ui::WdgColorOverlay ui;
};
class DropShadow : public QWidget {
Q_OBJECT
public:
enum Mode {
DropShadowMode,
InnerShadowMode
};
public:
DropShadow(Mode mode, QWidget *parent);
void setShadow(const psd_layer_effects_shadow_common *shadow);
void fetchShadow(psd_layer_effects_shadow_common *shadow) const;
-private Q_SLOTS:
- void slotDialAngleChanged(int value);
- void slotIntAngleChanged(int value);
-
- void slotGlobalLightToggled();
-
Q_SIGNALS:
void configChanged();
void globalAngleChanged(int value);
private:
Ui::WdgDropShadow ui;
Mode m_mode;
};
class GradientOverlay : public QWidget {
Q_OBJECT
public:
GradientOverlay(KisCanvasResourceProvider *resourceProvider, QWidget *parent);
void setGradientOverlay(const psd_layer_effects_gradient_overlay *gradient);
void fetchGradientOverlay(psd_layer_effects_gradient_overlay *gradient) const;
-private Q_SLOTS:
- void slotDialAngleChanged(int value);
- void slotIntAngleChanged(int value);
-
Q_SIGNALS:
void configChanged();
private:
Ui::WdgGradientOverlay ui;
KisCanvasResourceProvider *m_resourceProvider;
};
class InnerGlow : public QWidget {
Q_OBJECT
public:
enum Mode {
InnerGlowMode = 0,
OuterGlowMode
};
public:
InnerGlow(Mode mode, KisCanvasResourceProvider *resourceProvider, QWidget *parent);
void setConfig(const psd_layer_effects_glow_common *innerGlow);
void fetchConfig(psd_layer_effects_glow_common *innerGlow) const;
Q_SIGNALS:
void configChanged();
private:
Ui::WdgInnerGlow ui;
Mode m_mode;
KisCanvasResourceProvider *m_resourceProvider;
};
class PatternOverlay : public QWidget {
Q_OBJECT
public:
PatternOverlay(QWidget *parent);
void setPatternOverlay(const psd_layer_effects_pattern_overlay *pattern);
void fetchPatternOverlay(psd_layer_effects_pattern_overlay *pattern) const;
Q_SIGNALS:
void configChanged();
private:
Ui::WdgPatternOverlay ui;
};
class Satin : public QWidget {
Q_OBJECT
public:
Satin(QWidget *parent);
void setSatin(const psd_layer_effects_satin *satin);
void fetchSatin(psd_layer_effects_satin *satin) const;
-private Q_SLOTS:
- void slotDialAngleChanged(int value);
- void slotIntAngleChanged(int value);
-
Q_SIGNALS:
void configChanged();
private:
Ui::WdgSatin ui;
};
class Stroke : public QWidget {
Q_OBJECT
public:
Stroke(KisCanvasResourceProvider *resourceProvider, QWidget *parent);
void setStroke(const psd_layer_effects_stroke *stroke);
void fetchStroke(psd_layer_effects_stroke *stroke) const;
-private Q_SLOTS:
- void slotDialAngleChanged(int value);
- void slotIntAngleChanged(int value);
-
Q_SIGNALS:
void configChanged();
private:
Ui::WdgStroke ui;
KisCanvasResourceProvider *m_resourceProvider;
};
class StylesSelector : public QWidget {
Q_OBJECT
public:
StylesSelector(QWidget *parent);
void notifyExternalStyleChanged(const QString &name, const QUuid &uuid);
void addNewStyle(KisPSDLayerStyleSP style);
void loadCollection(const QString &fileName);
private Q_SLOTS:
void loadStyles(const QString &name);
void selectStyle(QListWidgetItem *previous, QListWidgetItem* current);
Q_SIGNALS:
void styleSelected(KisPSDLayerStyleSP style);
private:
void refillCollections();
private:
Ui::WdgStylesSelector ui;
};
class KisDlgLayerStyle : public KoDialog
{
Q_OBJECT
public:
explicit KisDlgLayerStyle(KisPSDLayerStyleSP layerStyle, KisCanvasResourceProvider *resourceProvider, QWidget *parent = 0);
~KisDlgLayerStyle() override;
KisPSDLayerStyleSP style() const;
Q_SIGNALS:
void configChanged();
public Q_SLOTS:
void slotMasterFxSwitchChanged(bool value);
void syncGlobalAngle(int angle);
void notifyGuiConfigChanged();
void notifyPredefinedStyleSelected(KisPSDLayerStyleSP style);
void changePage(QListWidgetItem *, QListWidgetItem*);
void slotNotifyOnAccept();
void slotNotifyOnReject();
// Sets all the widgets to the contents of the given style
void setStyle(KisPSDLayerStyleSP style);
void slotLoadStyle();
void slotSaveStyle();
void slotNewStyle();
private:
KisPSDLayerStyleSP m_layerStyle;
KisPSDLayerStyleSP m_initialLayerStyle;
Ui::WdgStylesDialog wdgLayerStyles;
BevelAndEmboss *m_bevelAndEmboss;
BlendingOptions *m_blendingOptions;
ColorOverlay *m_colorOverlay;
Contour *m_contour;
DropShadow *m_dropShadow;
GradientOverlay *m_gradientOverlay;
InnerGlow *m_innerGlow;
DropShadow *m_innerShadow;
InnerGlow *m_outerGlow;
PatternOverlay * m_patternOverlay;
Satin *m_satin;
Stroke *m_stroke;
StylesSelector *m_stylesSelector;
Texture *m_texture;
KisSignalCompressor *m_configChangedCompressor;
bool m_isSwitchingPredefinedStyle;
/**
* Used for debugging purposes only to track if m_layerStyle is in
* sync with what is stored in the GUI
*/
mutable bool m_sanityLayerStyleDirty;
};
#endif // KIS_DLG_LAYER_STYLE_H
diff --git a/libs/ui/dialogs/kis_dlg_preferences.cc b/libs/ui/dialogs/kis_dlg_preferences.cc
index 74f568cc24..e0309f6ffd 100644
--- a/libs/ui/dialogs/kis_dlg_preferences.cc
+++ b/libs/ui/dialogs/kis_dlg_preferences.cc
@@ -1,1554 +1,1612 @@
/*
* preferencesdlg.cc - part of KImageShop
*
* Copyright (c) 1999 Michael Koch <koch@kde.org>
* Copyright (c) 2003-2011 Boudewijn Rempt <boud@valdyas.org>
*
* 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_dlg_preferences.h"
#include <config-hdr.h>
#include <opengl/kis_opengl.h>
#include <QBitmap>
#include <QCheckBox>
#include <QCursor>
#include <QLabel>
#include <QLayout>
#include <QLineEdit>
#include <QPushButton>
#include <QSlider>
#include <QToolButton>
#include <QThread>
#include <QStandardPaths>
#include <QGroupBox>
#include <QGridLayout>
#include <QRadioButton>
#include <QGroupBox>
#include <QMdiArea>
#include <QMessageBox>
#include <QDesktopWidget>
#include <QFileDialog>
#include <QFormLayout>
#include <QSettings>
#include <KisDocument.h>
#include <KoColorProfile.h>
#include <KisApplication.h>
#include <KoFileDialog.h>
#include <KisPart.h>
#include <KoColorSpaceEngine.h>
#include <kis_icon.h>
#include <KoConfig.h>
#include "KoID.h"
#include <KoConfigAuthorPage.h>
#include <KoVBox.h>
#include <klocalizedstring.h>
#include <kformat.h>
#include <kundo2stack.h>
#include <KoResourcePaths.h>
#include "kis_action_registry.h"
#include <kis_image.h>
#include <squeezedcombobox.h>
#include "kis_clipboard.h"
#include "widgets/kis_cmb_idlist.h"
#include "KoColorSpace.h"
#include "KoColorSpaceRegistry.h"
#include "KoColorConversionTransformation.h"
#include "kis_cursor.h"
#include "kis_config.h"
#include "kis_canvas_resource_provider.h"
#include "kis_preference_set_registry.h"
#include "kis_color_manager.h"
#include "KisProofingConfiguration.h"
#include "kis_image_config.h"
#include "slider_and_spin_box_sync.h"
// for the performance update
#include <kis_cubic_curve.h>
#include <kis_signals_blocker.h>
#include "input/config/kis_input_configuration_page.h"
#include "input/wintab/drawpile_tablettester/tablettester.h"
#ifdef Q_OS_WIN
# include <kis_tablet_support_win8.h>
#endif
+struct BackupSuffixValidator : public QValidator {
+ BackupSuffixValidator(QObject *parent)
+ : QValidator(parent)
+ , invalidCharacters(QStringList()
+ << "0" << "1" << "2" << "3" << "4" << "5" << "6" << "7" << "8" << "9"
+ << "/" << "\\" << ":" << ";" << " ")
+ {}
+
+ ~BackupSuffixValidator() override {}
+
+ const QStringList invalidCharacters;
+
+ State validate(QString &line, int &/*pos*/) const override
+ {
+ Q_FOREACH(const QString invalidChar, invalidCharacters) {
+ if (line.contains(invalidChar)) {
+ return Invalid;
+ }
+ }
+ return Acceptable;
+ }
+};
+
GeneralTab::GeneralTab(QWidget *_parent, const char *_name)
: WdgGeneralSettings(_parent, _name)
{
KisConfig cfg(true);
//
// Cursor Tab
//
m_cmbCursorShape->addItem(i18n("No Cursor"));
m_cmbCursorShape->addItem(i18n("Tool Icon"));
m_cmbCursorShape->addItem(i18n("Arrow"));
m_cmbCursorShape->addItem(i18n("Small Circle"));
m_cmbCursorShape->addItem(i18n("Crosshair"));
m_cmbCursorShape->addItem(i18n("Triangle Righthanded"));
m_cmbCursorShape->addItem(i18n("Triangle Lefthanded"));
m_cmbCursorShape->addItem(i18n("Black Pixel"));
m_cmbCursorShape->addItem(i18n("White Pixel"));
m_cmbCursorShape->setCurrentIndex(cfg.newCursorStyle());
m_cmbOutlineShape->addItem(i18n("No Outline"));
m_cmbOutlineShape->addItem(i18n("Circle Outline"));
m_cmbOutlineShape->addItem(i18n("Preview Outline"));
m_cmbOutlineShape->addItem(i18n("Tilt Outline"));
m_cmbOutlineShape->setCurrentIndex(cfg.newOutlineStyle());
m_showOutlinePainting->setChecked(cfg.showOutlineWhilePainting());
m_changeBrushOutline->setChecked(!cfg.forceAlwaysFullSizedOutline());
KoColor cursorColor(KoColorSpaceRegistry::instance()->rgb8());
cursorColor.fromQColor(cfg.getCursorMainColor());
cursorColorBtutton->setColor(cursorColor);
//
// Window Tab
//
m_cmbMDIType->setCurrentIndex(cfg.readEntry<int>("mdi_viewmode", (int)QMdiArea::TabbedView));
m_backgroundimage->setText(cfg.getMDIBackgroundImage());
connect(m_bnFileName, SIGNAL(clicked()), SLOT(getBackgroundImage()));
connect(clearBgImageButton, SIGNAL(clicked()), SLOT(clearBackgroundImage()));
KoColor mdiColor;
mdiColor.fromQColor(cfg.getMDIBackgroundColor());
m_mdiColor->setColor(mdiColor);
m_chkRubberBand->setChecked(cfg.readEntry<int>("mdi_rubberband", cfg.useOpenGL()));
m_chkCanvasMessages->setChecked(cfg.showCanvasMessages());
const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat);
m_chkHiDPI->setChecked(kritarc.value("EnableHiDPI", true).toBool());
-
+ chkUsageLogging->setChecked(kritarc.value("LogUsage", true).toBool());
m_chkSingleApplication->setChecked(kritarc.value("EnableSingleApplication", true).toBool());
//
// Tools tab
//
m_radioToolOptionsInDocker->setChecked(cfg.toolOptionsInDocker());
+ cmbFlowMode->setCurrentIndex((int)!cfg.readEntry<bool>("useCreamyAlphaDarken", true));
m_chkSwitchSelectionCtrlAlt->setChecked(cfg.switchSelectionCtrlAlt());
chkEnableTouch->setChecked(!cfg.disableTouchOnCanvas());
chkEnableTranformToolAfterPaste->setChecked(cfg.activateTransformToolAfterPaste());
m_groupBoxKineticScrollingSettings->setChecked(cfg.kineticScrollingEnabled());
m_cmbKineticScrollingGesture->addItem(i18n("On Touch Drag"));
m_cmbKineticScrollingGesture->addItem(i18n("On Click Drag"));
m_cmbKineticScrollingGesture->addItem(i18n("On Middle-Click Drag"));
//m_cmbKineticScrollingGesture->addItem(i18n("On Right Click Drag"));
m_cmbKineticScrollingGesture->setCurrentIndex(cfg.kineticScrollingGesture());
m_kineticScrollingSensitivitySlider->setRange(0, 100);
m_kineticScrollingSensitivitySlider->setValue(cfg.kineticScrollingSensitivity());
m_chkKineticScrollingHideScrollbars->setChecked(cfg.kineticScrollingHiddenScrollbars());
-
//
- // Miscellaneous
+ // File handling
//
- cmbStartupSession->addItem(i18n("Open default window"));
- cmbStartupSession->addItem(i18n("Load previous session"));
- cmbStartupSession->addItem(i18n("Show session manager"));
- cmbStartupSession->setCurrentIndex(cfg.sessionOnStartup());
-
- chkSaveSessionOnQuit->setChecked(cfg.saveSessionOnQuit(false));
-
int autosaveInterval = cfg.autoSaveInterval();
//convert to minutes
m_autosaveSpinBox->setValue(autosaveInterval / 60);
m_autosaveCheckBox->setChecked(autosaveInterval > 0);
+ chkHideAutosaveFiles->setChecked(cfg.readEntry<bool>("autosavefileshidden", true));
m_chkCompressKra->setChecked(cfg.compressKra());
+ chkZip64->setChecked(cfg.useZip64());
m_backupFileCheckBox->setChecked(cfg.backupFile());
+ cmbBackupFileLocation->setCurrentIndex(cfg.readEntry<int>("backupfilelocation", 0));
+ txtBackupFileSuffix->setText(cfg.readEntry<QString>("backupfilesuffix", "~"));
+ QValidator *validator = new BackupSuffixValidator(txtBackupFileSuffix);
+ txtBackupFileSuffix->setValidator(validator);
+ intNumBackupFiles->setValue(cfg.readEntry<int>("numberofbackupfiles", 1));
+
+ //
+ // Miscellaneous
+ //
+ cmbStartupSession->addItem(i18n("Open default window"));
+ cmbStartupSession->addItem(i18n("Load previous session"));
+ cmbStartupSession->addItem(i18n("Show session manager"));
+ cmbStartupSession->setCurrentIndex(cfg.sessionOnStartup());
+
+ chkSaveSessionOnQuit->setChecked(cfg.saveSessionOnQuit(false));
m_chkConvertOnImport->setChecked(cfg.convertToImageColorspaceOnImport());
m_undoStackSize->setValue(cfg.undoStackLimit());
m_favoritePresetsSpinBox->setValue(cfg.favoritePresets());
chkShowRootLayer->setChecked(cfg.showRootLayer());
KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs");
bool dontUseNative = true;
#ifdef Q_OS_UNIX
if (qgetenv("XDG_CURRENT_DESKTOP") == "KDE") {
dontUseNative = false;
}
#endif
#ifdef Q_OS_WIN
dontUseNative = false;
#endif
m_chkNativeFileDialog->setChecked(!group.readEntry("DontUseNativeFileDialog", dontUseNative));
intMaxBrushSize->setValue(cfg.readEntry("maximumBrushSize", 1000));
}
void GeneralTab::setDefault()
{
KisConfig cfg(true);
m_cmbCursorShape->setCurrentIndex(cfg.newCursorStyle(true));
m_cmbOutlineShape->setCurrentIndex(cfg.newOutlineStyle(true));
chkShowRootLayer->setChecked(cfg.showRootLayer(true));
m_autosaveCheckBox->setChecked(cfg.autoSaveInterval(true) > 0);
//convert to minutes
m_autosaveSpinBox->setValue(cfg.autoSaveInterval(true) / 60);
+ chkHideAutosaveFiles->setChecked(true);
+
m_undoStackSize->setValue(cfg.undoStackLimit(true));
+
m_backupFileCheckBox->setChecked(cfg.backupFile(true));
+ cmbBackupFileLocation->setCurrentIndex(0);
+ txtBackupFileSuffix->setText("~");
+ intNumBackupFiles->setValue(1);
+
m_showOutlinePainting->setChecked(cfg.showOutlineWhilePainting(true));
m_changeBrushOutline->setChecked(!cfg.forceAlwaysFullSizedOutline(true));
m_chkNativeFileDialog->setChecked(false);
intMaxBrushSize->setValue(1000);
m_cmbMDIType->setCurrentIndex((int)QMdiArea::TabbedView);
m_chkRubberBand->setChecked(cfg.useOpenGL(true));
m_favoritePresetsSpinBox->setValue(cfg.favoritePresets(true));
KoColor mdiColor;
mdiColor.fromQColor(cfg.getMDIBackgroundColor(true));
m_mdiColor->setColor(mdiColor);
m_backgroundimage->setText(cfg.getMDIBackgroundImage(true));
m_chkCanvasMessages->setChecked(cfg.showCanvasMessages(true));
m_chkCompressKra->setChecked(cfg.compressKra(true));
+ chkZip64->setChecked(cfg.useZip64(true));
m_chkHiDPI->setChecked(false);
m_chkSingleApplication->setChecked(true);
m_chkHiDPI->setChecked(true);
+ chkUsageLogging->setChecked(true);
m_radioToolOptionsInDocker->setChecked(cfg.toolOptionsInDocker(true));
+ cmbFlowMode->setCurrentIndex(0);
m_groupBoxKineticScrollingSettings->setChecked(cfg.kineticScrollingEnabled(true));
m_cmbKineticScrollingGesture->setCurrentIndex(cfg.kineticScrollingGesture(true));
m_kineticScrollingSensitivitySlider->setValue(cfg.kineticScrollingSensitivity(true));
m_chkKineticScrollingHideScrollbars->setChecked(cfg.kineticScrollingHiddenScrollbars(true));
m_chkSwitchSelectionCtrlAlt->setChecked(cfg.switchSelectionCtrlAlt(true));
chkEnableTouch->setChecked(!cfg.disableTouchOnCanvas(true));
chkEnableTranformToolAfterPaste->setChecked(cfg.activateTransformToolAfterPaste(true));
m_chkConvertOnImport->setChecked(cfg.convertToImageColorspaceOnImport(true));
KoColor cursorColor(KoColorSpaceRegistry::instance()->rgb8());
cursorColor.fromQColor(cfg.getCursorMainColor(true));
cursorColorBtutton->setColor(cursorColor);
}
CursorStyle GeneralTab::cursorStyle()
{
return (CursorStyle)m_cmbCursorShape->currentIndex();
}
OutlineStyle GeneralTab::outlineStyle()
{
return (OutlineStyle)m_cmbOutlineShape->currentIndex();
}
KisConfig::SessionOnStartup GeneralTab::sessionOnStartup() const
{
return (KisConfig::SessionOnStartup)cmbStartupSession->currentIndex();
}
bool GeneralTab::saveSessionOnQuit() const
{
return chkSaveSessionOnQuit->isChecked();
}
bool GeneralTab::showRootLayer()
{
return chkShowRootLayer->isChecked();
}
int GeneralTab::autoSaveInterval()
{
//convert to seconds
return m_autosaveCheckBox->isChecked() ? m_autosaveSpinBox->value() * 60 : 0;
}
int GeneralTab::undoStackSize()
{
return m_undoStackSize->value();
}
bool GeneralTab::showOutlineWhilePainting()
{
return m_showOutlinePainting->isChecked();
}
int GeneralTab::mdiMode()
{
return m_cmbMDIType->currentIndex();
}
int GeneralTab::favoritePresets()
{
return m_favoritePresetsSpinBox->value();
}
bool GeneralTab::showCanvasMessages()
{
return m_chkCanvasMessages->isChecked();
}
bool GeneralTab::compressKra()
{
return m_chkCompressKra->isChecked();
}
+bool GeneralTab::useZip64()
+{
+ return chkZip64->isChecked();
+}
+
bool GeneralTab::toolOptionsInDocker()
{
return m_radioToolOptionsInDocker->isChecked();
}
bool GeneralTab::kineticScrollingEnabled()
{
return m_groupBoxKineticScrollingSettings->isChecked();
}
int GeneralTab::kineticScrollingGesture()
{
return m_cmbKineticScrollingGesture->currentIndex();
}
int GeneralTab::kineticScrollingSensitivity()
{
return m_kineticScrollingSensitivitySlider->value();
}
bool GeneralTab::kineticScrollingHiddenScrollbars()
{
return m_chkKineticScrollingHideScrollbars->isChecked();
}
bool GeneralTab::switchSelectionCtrlAlt()
{
return m_chkSwitchSelectionCtrlAlt->isChecked();
}
bool GeneralTab::convertToImageColorspaceOnImport()
{
return m_chkConvertOnImport->isChecked();
}
void GeneralTab::getBackgroundImage()
{
KoFileDialog dialog(this, KoFileDialog::OpenFile, "BackgroundImages");
dialog.setCaption(i18n("Select a Background Image"));
dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
dialog.setImageFilters();
QString fn = dialog.filename();
// dialog box was canceled or somehow no file was selected
if (fn.isEmpty()) {
return;
}
QImage image(fn);
if (image.isNull()) {
QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("%1 is not a valid image file!", fn));
}
else {
m_backgroundimage->setText(fn);
}
}
void GeneralTab::clearBackgroundImage()
{
// clearing the background image text will implicitly make the background color be used
m_backgroundimage->setText("");
}
#include "kactioncollection.h"
#include "KisActionsSnapshot.h"
ShortcutSettingsTab::ShortcutSettingsTab(QWidget *parent, const char *name)
: QWidget(parent)
{
setObjectName(name);
QGridLayout * l = new QGridLayout(this);
l->setMargin(0);
m_page = new WdgShortcutSettings(this);
l->addWidget(m_page, 0, 0);
m_snapshot.reset(new KisActionsSnapshot);
KActionCollection *collection =
KisPart::instance()->currentMainwindow()->actionCollection();
Q_FOREACH (QAction *action, collection->actions()) {
m_snapshot->addAction(action->objectName(), action);
}
QMap<QString, KActionCollection*> sortedCollections =
m_snapshot->actionCollections();
for (auto it = sortedCollections.constBegin(); it != sortedCollections.constEnd(); ++it) {
m_page->addCollection(it.value(), it.key());
}
}
ShortcutSettingsTab::~ShortcutSettingsTab()
{
}
void ShortcutSettingsTab::setDefault()
{
m_page->allDefault();
}
void ShortcutSettingsTab::saveChanges()
{
m_page->save();
KisActionRegistry::instance()->settingsPageSaved();
}
void ShortcutSettingsTab::cancelChanges()
{
m_page->undo();
}
ColorSettingsTab::ColorSettingsTab(QWidget *parent, const char *name)
: QWidget(parent)
{
setObjectName(name);
// XXX: Make sure only profiles that fit the specified color model
// are shown in the profile combos
QGridLayout * l = new QGridLayout(this);
l->setMargin(0);
m_page = new WdgColorSettings(this);
l->addWidget(m_page, 0, 0);
KisConfig cfg(true);
m_page->chkUseSystemMonitorProfile->setChecked(cfg.useSystemMonitorProfile());
connect(m_page->chkUseSystemMonitorProfile, SIGNAL(toggled(bool)), this, SLOT(toggleAllowMonitorProfileSelection(bool)));
m_page->cmbWorkingColorSpace->setIDList(KoColorSpaceRegistry::instance()->listKeys());
m_page->cmbWorkingColorSpace->setCurrent(cfg.workingColorSpace());
m_page->bnAddColorProfile->setIcon(KisIconUtils::loadIcon("document-open"));
m_page->bnAddColorProfile->setToolTip( i18n("Open Color Profile") );
connect(m_page->bnAddColorProfile, SIGNAL(clicked()), SLOT(installProfile()));
QFormLayout *monitorProfileGrid = new QFormLayout(m_page->monitorprofileholder);
for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) {
QLabel *lbl = new QLabel(i18nc("The number of the screen", "Screen %1:", i + 1));
m_monitorProfileLabels << lbl;
SqueezedComboBox *cmb = new SqueezedComboBox();
cmb->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
monitorProfileGrid->addRow(lbl, cmb);
m_monitorProfileWidgets << cmb;
}
refillMonitorProfiles(KoID("RGBA"));
for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) {
if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) {
m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i));
}
}
m_page->chkBlackpoint->setChecked(cfg.useBlackPointCompensation());
m_page->chkAllowLCMSOptimization->setChecked(cfg.allowLCMSOptimization());
m_page->chkForcePaletteColor->setChecked(cfg.forcePaletteColors());
KisImageConfig cfgImage(true);
KisProofingConfigurationSP proofingConfig = cfgImage.defaultProofingconfiguration();
m_page->sldAdaptationState->setMaximum(20);
m_page->sldAdaptationState->setMinimum(0);
m_page->sldAdaptationState->setValue((int)proofingConfig->adaptationState*20);
//probably this should become the screenprofile?
KoColor ga(KoColorSpaceRegistry::instance()->rgb8());
ga.fromKoColor(proofingConfig->warningColor);
m_page->gamutAlarm->setColor(ga);
const KoColorSpace *proofingSpace = KoColorSpaceRegistry::instance()->colorSpace(proofingConfig->proofingModel,
proofingConfig->proofingDepth,
proofingConfig->proofingProfile);
if (proofingSpace) {
m_page->proofingSpaceSelector->setCurrentColorSpace(proofingSpace);
}
m_page->cmbProofingIntent->setCurrentIndex((int)proofingConfig->intent);
m_page->ckbProofBlackPoint->setChecked(proofingConfig->conversionFlags.testFlag(KoColorConversionTransformation::BlackpointCompensation));
m_pasteBehaviourGroup.addButton(m_page->radioPasteWeb, PASTE_ASSUME_WEB);
m_pasteBehaviourGroup.addButton(m_page->radioPasteMonitor, PASTE_ASSUME_MONITOR);
m_pasteBehaviourGroup.addButton(m_page->radioPasteAsk, PASTE_ASK);
QAbstractButton *button = m_pasteBehaviourGroup.button(cfg.pasteBehaviour());
Q_ASSERT(button);
if (button) {
button->setChecked(true);
}
m_page->cmbMonitorIntent->setCurrentIndex(cfg.monitorRenderIntent());
toggleAllowMonitorProfileSelection(cfg.useSystemMonitorProfile());
}
void ColorSettingsTab::installProfile()
{
KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocumentICC");
dialog.setCaption(i18n("Install Color Profiles"));
dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation));
dialog.setMimeTypeFilters(QStringList() << "application/vnd.iccprofile", "application/vnd.iccprofile");
QStringList profileNames = dialog.filenames();
KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc");
Q_ASSERT(iccEngine);
QString saveLocation = KoResourcePaths::saveLocation("icc_profiles");
Q_FOREACH (const QString &profileName, profileNames) {
if (!QFile::copy(profileName, saveLocation + QFileInfo(profileName).fileName())) {
qWarning() << "Could not install profile!" << saveLocation + QFileInfo(profileName).fileName();
continue;
}
iccEngine->addProfile(saveLocation + QFileInfo(profileName).fileName());
}
KisConfig cfg(true);
refillMonitorProfiles(KoID("RGBA"));
for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) {
if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) {
m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i));
}
}
}
void ColorSettingsTab::toggleAllowMonitorProfileSelection(bool useSystemProfile)
{
KisConfig cfg(true);
if (useSystemProfile) {
QStringList devices = KisColorManager::instance()->devices();
if (devices.size() == QApplication::desktop()->screenCount()) {
for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) {
m_monitorProfileWidgets[i]->clear();
QString monitorForScreen = cfg.monitorForScreen(i, devices[i]);
Q_FOREACH (const QString &device, devices) {
m_monitorProfileLabels[i]->setText(i18nc("The display/screen we got from Qt", "Screen %1:", i + 1));
m_monitorProfileWidgets[i]->addSqueezedItem(KisColorManager::instance()->deviceName(device), device);
if (devices[i] == monitorForScreen) {
m_monitorProfileWidgets[i]->setCurrentIndex(i);
}
}
}
}
}
else {
refillMonitorProfiles(KoID("RGBA"));
for(int i = 0; i < QApplication::desktop()->screenCount(); ++i) {
if (m_monitorProfileWidgets[i]->contains(cfg.monitorProfile(i))) {
m_monitorProfileWidgets[i]->setCurrent(cfg.monitorProfile(i));
}
}
}
}
void ColorSettingsTab::setDefault()
{
m_page->cmbWorkingColorSpace->setCurrent("RGBA");
refillMonitorProfiles(KoID("RGBA"));
KisConfig cfg(true);
KisImageConfig cfgImage(true);
KisProofingConfigurationSP proofingConfig = cfgImage.defaultProofingconfiguration();
const KoColorSpace *proofingSpace = KoColorSpaceRegistry::instance()->colorSpace(proofingConfig->proofingModel,proofingConfig->proofingDepth,proofingConfig->proofingProfile);
if (proofingSpace) {
m_page->proofingSpaceSelector->setCurrentColorSpace(proofingSpace);
}
m_page->cmbProofingIntent->setCurrentIndex((int)proofingConfig->intent);
m_page->ckbProofBlackPoint->setChecked(proofingConfig->conversionFlags.testFlag(KoColorConversionTransformation::BlackpointCompensation));
m_page->sldAdaptationState->setValue(0);
//probably this should become the screenprofile?
KoColor ga(KoColorSpaceRegistry::instance()->rgb8());
ga.fromKoColor(proofingConfig->warningColor);
m_page->gamutAlarm->setColor(ga);
m_page->chkBlackpoint->setChecked(cfg.useBlackPointCompensation(true));
m_page->chkAllowLCMSOptimization->setChecked(cfg.allowLCMSOptimization(true));
m_page->chkForcePaletteColor->setChecked(cfg.forcePaletteColors(true));
m_page->cmbMonitorIntent->setCurrentIndex(cfg.monitorRenderIntent(true));
m_page->chkUseSystemMonitorProfile->setChecked(cfg.useSystemMonitorProfile(true));
QAbstractButton *button = m_pasteBehaviourGroup.button(cfg.pasteBehaviour(true));
Q_ASSERT(button);
if (button) {
button->setChecked(true);
}
}
void ColorSettingsTab::refillMonitorProfiles(const KoID & colorSpaceId)
{
for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) {
m_monitorProfileWidgets[i]->clear();
}
QMap<QString, const KoColorProfile *> profileList;
Q_FOREACH(const KoColorProfile *profile, KoColorSpaceRegistry::instance()->profilesFor(colorSpaceId.id())) {
profileList[profile->name()] = profile;
}
Q_FOREACH (const KoColorProfile *profile, profileList.values()) {
//qDebug() << "Profile" << profile->name() << profile->isSuitableForDisplay() << csf->defaultProfile();
if (profile->isSuitableForDisplay()) {
for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) {
m_monitorProfileWidgets[i]->addSqueezedItem(profile->name());
}
}
}
for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) {
m_monitorProfileLabels[i]->setText(i18nc("The number of the screen", "Screen %1:", i + 1));
m_monitorProfileWidgets[i]->setCurrent(KoColorSpaceRegistry::instance()->defaultProfileForColorSpace(colorSpaceId.id()));
}
}
//---------------------------------------------------------------------------------------------------
void TabletSettingsTab::setDefault()
{
KisCubicCurve curve;
curve.fromString(DEFAULT_CURVE_STRING);
m_page->pressureCurve->setCurve(curve);
#ifdef Q_OS_WIN
if (KisTabletSupportWin8::isAvailable()) {
KisConfig cfg(true);
m_page->radioWintab->setChecked(!cfg.useWin8PointerInput(true));
m_page->radioWin8PointerInput->setChecked(cfg.useWin8PointerInput(true));
} else {
m_page->radioWintab->setChecked(true);
m_page->radioWin8PointerInput->setChecked(false);
}
#endif
}
TabletSettingsTab::TabletSettingsTab(QWidget* parent, const char* name): QWidget(parent)
{
setObjectName(name);
QGridLayout * l = new QGridLayout(this);
l->setMargin(0);
m_page = new WdgTabletSettings(this);
l->addWidget(m_page, 0, 0);
KisConfig cfg(true);
KisCubicCurve curve;
curve.fromString( cfg.pressureTabletCurve() );
m_page->pressureCurve->setMaximumSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
m_page->pressureCurve->setCurve(curve);
#ifdef Q_OS_WIN
if (KisTabletSupportWin8::isAvailable()) {
m_page->radioWintab->setChecked(!cfg.useWin8PointerInput());
m_page->radioWin8PointerInput->setChecked(cfg.useWin8PointerInput());
} else {
m_page->radioWintab->setChecked(true);
m_page->radioWin8PointerInput->setChecked(false);
m_page->grpTabletApi->setVisible(false);
}
#else
m_page->grpTabletApi->setVisible(false);
#endif
connect(m_page->btnTabletTest, SIGNAL(clicked()), SLOT(slotTabletTest()));
}
void TabletSettingsTab::slotTabletTest()
{
TabletTestDialog tabletTestDialog(this);
tabletTestDialog.exec();
}
//---------------------------------------------------------------------------------------------------
#include "kis_acyclic_signal_connector.h"
int getTotalRAM()
{
return KisImageConfig(true).totalRAM();
}
int PerformanceTab::realTilesRAM()
{
return intMemoryLimit->value() - intPoolLimit->value();
}
PerformanceTab::PerformanceTab(QWidget *parent, const char *name)
: WdgPerformanceSettings(parent, name)
{
KisImageConfig cfg(true);
const double totalRAM = cfg.totalRAM();
lblTotalMemory->setText(KFormat().formatByteSize(totalRAM * 1024 * 1024, 0, KFormat::IECBinaryDialect, KFormat::UnitMegaByte));
sliderMemoryLimit->setSuffix(i18n(" %"));
sliderMemoryLimit->setRange(1, 100, 2);
sliderMemoryLimit->setSingleStep(0.01);
sliderPoolLimit->setSuffix(i18n(" %"));
sliderPoolLimit->setRange(0, 20, 2);
sliderMemoryLimit->setSingleStep(0.01);
sliderUndoLimit->setSuffix(i18n(" %"));
sliderUndoLimit->setRange(0, 50, 2);
sliderMemoryLimit->setSingleStep(0.01);
intMemoryLimit->setMinimumWidth(80);
intPoolLimit->setMinimumWidth(80);
intUndoLimit->setMinimumWidth(80);
SliderAndSpinBoxSync *sync1 =
new SliderAndSpinBoxSync(sliderMemoryLimit,
intMemoryLimit,
getTotalRAM);
sync1->slotParentValueChanged();
m_syncs << sync1;
SliderAndSpinBoxSync *sync2 =
new SliderAndSpinBoxSync(sliderPoolLimit,
intPoolLimit,
std::bind(&KisIntParseSpinBox::value,
intMemoryLimit));
connect(intMemoryLimit, SIGNAL(valueChanged(int)), sync2, SLOT(slotParentValueChanged()));
sync2->slotParentValueChanged();
m_syncs << sync2;
SliderAndSpinBoxSync *sync3 =
new SliderAndSpinBoxSync(sliderUndoLimit,
intUndoLimit,
std::bind(&PerformanceTab::realTilesRAM,
this));
connect(intPoolLimit, SIGNAL(valueChanged(int)), sync3, SLOT(slotParentValueChanged()));
sync3->slotParentValueChanged();
m_syncs << sync3;
sliderSwapSize->setSuffix(i18n(" GiB"));
sliderSwapSize->setRange(1, 64);
intSwapSize->setRange(1, 64);
KisAcyclicSignalConnector *swapSizeConnector = new KisAcyclicSignalConnector(this);
swapSizeConnector->connectForwardInt(sliderSwapSize, SIGNAL(valueChanged(int)),
intSwapSize, SLOT(setValue(int)));
swapSizeConnector->connectBackwardInt(intSwapSize, SIGNAL(valueChanged(int)),
sliderSwapSize, SLOT(setValue(int)));
lblSwapFileLocation->setText(cfg.swapDir());
connect(bnSwapFile, SIGNAL(clicked()), SLOT(selectSwapDir()));
sliderThreadsLimit->setRange(1, QThread::idealThreadCount());
sliderFrameClonesLimit->setRange(1, QThread::idealThreadCount());
sliderFpsLimit->setRange(20, 100);
sliderFpsLimit->setSuffix(i18n(" fps"));
connect(sliderThreadsLimit, SIGNAL(valueChanged(int)), SLOT(slotThreadsLimitChanged(int)));
connect(sliderFrameClonesLimit, SIGNAL(valueChanged(int)), SLOT(slotFrameClonesLimitChanged(int)));
intCachedFramesSizeLimit->setRange(1, 10000);
intCachedFramesSizeLimit->setSuffix(i18n(" px"));
intCachedFramesSizeLimit->setSingleStep(1);
intCachedFramesSizeLimit->setPageStep(1000);
intRegionOfInterestMargin->setRange(1, 100);
intRegionOfInterestMargin->setSuffix(i18n(" %"));
intRegionOfInterestMargin->setSingleStep(1);
intRegionOfInterestMargin->setPageStep(10);
connect(chkCachedFramesSizeLimit, SIGNAL(toggled(bool)), intCachedFramesSizeLimit, SLOT(setEnabled(bool)));
connect(chkUseRegionOfInterest, SIGNAL(toggled(bool)), intRegionOfInterestMargin, SLOT(setEnabled(bool)));
load(false);
}
PerformanceTab::~PerformanceTab()
{
qDeleteAll(m_syncs);
}
void PerformanceTab::load(bool requestDefault)
{
KisImageConfig cfg(true);
sliderMemoryLimit->setValue(cfg.memoryHardLimitPercent(requestDefault));
sliderPoolLimit->setValue(cfg.memoryPoolLimitPercent(requestDefault));
sliderUndoLimit->setValue(cfg.memorySoftLimitPercent(requestDefault));
chkPerformanceLogging->setChecked(cfg.enablePerfLog(requestDefault));
chkProgressReporting->setChecked(cfg.enableProgressReporting(requestDefault));
sliderSwapSize->setValue(cfg.maxSwapSize(requestDefault) / 1024);
lblSwapFileLocation->setText(cfg.swapDir(requestDefault));
m_lastUsedThreadsLimit = cfg.maxNumberOfThreads(requestDefault);
m_lastUsedClonesLimit = cfg.frameRenderingClones(requestDefault);
sliderThreadsLimit->setValue(m_lastUsedThreadsLimit);
sliderFrameClonesLimit->setValue(m_lastUsedClonesLimit);
sliderFpsLimit->setValue(cfg.fpsLimit(requestDefault));
{
KisConfig cfg2(true);
chkOpenGLFramerateLogging->setChecked(cfg2.enableOpenGLFramerateLogging(requestDefault));
chkBrushSpeedLogging->setChecked(cfg2.enableBrushSpeedLogging(requestDefault));
chkDisableVectorOptimizations->setChecked(cfg2.enableAmdVectorizationWorkaround(requestDefault));
chkBackgroundCacheGeneration->setChecked(cfg2.calculateAnimationCacheInBackground(requestDefault));
}
if (cfg.useOnDiskAnimationCacheSwapping(requestDefault)) {
optOnDisk->setChecked(true);
} else {
optInMemory->setChecked(true);
}
chkCachedFramesSizeLimit->setChecked(cfg.useAnimationCacheFrameSizeLimit(requestDefault));
intCachedFramesSizeLimit->setValue(cfg.animationCacheFrameSizeLimit(requestDefault));
intCachedFramesSizeLimit->setEnabled(chkCachedFramesSizeLimit->isChecked());
chkUseRegionOfInterest->setChecked(cfg.useAnimationCacheRegionOfInterest(requestDefault));
intRegionOfInterestMargin->setValue(cfg.animationCacheRegionOfInterestMargin(requestDefault) * 100.0);
intRegionOfInterestMargin->setEnabled(chkUseRegionOfInterest->isChecked());
}
void PerformanceTab::save()
{
KisImageConfig cfg(false);
cfg.setMemoryHardLimitPercent(sliderMemoryLimit->value());
cfg.setMemorySoftLimitPercent(sliderUndoLimit->value());
cfg.setMemoryPoolLimitPercent(sliderPoolLimit->value());
cfg.setEnablePerfLog(chkPerformanceLogging->isChecked());
cfg.setEnableProgressReporting(chkProgressReporting->isChecked());
cfg.setMaxSwapSize(sliderSwapSize->value() * 1024);
cfg.setSwapDir(lblSwapFileLocation->text());
cfg.setMaxNumberOfThreads(sliderThreadsLimit->value());
cfg.setFrameRenderingClones(sliderFrameClonesLimit->value());
cfg.setFpsLimit(sliderFpsLimit->value());
{
KisConfig cfg2(true);
cfg2.setEnableOpenGLFramerateLogging(chkOpenGLFramerateLogging->isChecked());
cfg2.setEnableBrushSpeedLogging(chkBrushSpeedLogging->isChecked());
cfg2.setEnableAmdVectorizationWorkaround(chkDisableVectorOptimizations->isChecked());
cfg2.setCalculateAnimationCacheInBackground(chkBackgroundCacheGeneration->isChecked());
}
cfg.setUseOnDiskAnimationCacheSwapping(optOnDisk->isChecked());
cfg.setUseAnimationCacheFrameSizeLimit(chkCachedFramesSizeLimit->isChecked());
cfg.setAnimationCacheFrameSizeLimit(intCachedFramesSizeLimit->value());
cfg.setUseAnimationCacheRegionOfInterest(chkUseRegionOfInterest->isChecked());
cfg.setAnimationCacheRegionOfInterestMargin(intRegionOfInterestMargin->value() / 100.0);
}
void PerformanceTab::selectSwapDir()
{
KisImageConfig cfg(true);
QString swapDir = cfg.swapDir();
swapDir = QFileDialog::getExistingDirectory(0, i18nc("@title:window", "Select a swap directory"), swapDir);
if (swapDir.isEmpty()) {
return;
}
lblSwapFileLocation->setText(swapDir);
}
void PerformanceTab::slotThreadsLimitChanged(int value)
{
KisSignalsBlocker b(sliderFrameClonesLimit);
sliderFrameClonesLimit->setValue(qMin(m_lastUsedClonesLimit, value));
m_lastUsedThreadsLimit = value;
}
void PerformanceTab::slotFrameClonesLimitChanged(int value)
{
KisSignalsBlocker b(sliderThreadsLimit);
sliderThreadsLimit->setValue(qMax(m_lastUsedThreadsLimit, value));
m_lastUsedClonesLimit = value;
}
//---------------------------------------------------------------------------------------------------
#include "KoColor.h"
#include "opengl/KisOpenGLModeProber.h"
#include "opengl/KisScreenInformationAdapter.h"
#include <QOpenGLContext>
#include <QScreen>
QString colorSpaceString(QSurfaceFormat::ColorSpace cs, int depth)
{
const QString csString =
#ifdef HAVE_HDR
cs == QSurfaceFormat::bt2020PQColorSpace ? "Rec. 2020 PQ" :
cs == QSurfaceFormat::scRGBColorSpace ? "Rec. 709 Linear" :
#endif
cs == QSurfaceFormat::sRGBColorSpace ? "sRGB" :
cs == QSurfaceFormat::DefaultColorSpace ? "sRGB" :
"Unknown Color Space";
return QString("%1 (%2 bit)").arg(csString).arg(depth);
}
int formatToIndex(KisConfig::RootSurfaceFormat fmt)
{
return fmt == KisConfig::BT2020_PQ ? 1 :
fmt == KisConfig::BT709_G10 ? 2 :
0;
}
KisConfig::RootSurfaceFormat indexToFormat(int value)
{
return value == 1 ? KisConfig::BT2020_PQ :
value == 2 ? KisConfig::BT709_G10 :
KisConfig::BT709_G22;
}
DisplaySettingsTab::DisplaySettingsTab(QWidget *parent, const char *name)
: WdgDisplaySettings(parent, name)
{
KisConfig cfg(true);
const QString rendererOpenGLText = i18nc("canvas renderer", "OpenGL");
#ifdef Q_OS_WIN
const QString rendererOpenGLESText = i18nc("canvas renderer", "Direct3D 11 via ANGLE");
#else
const QString rendererOpenGLESText = i18nc("canvas renderer", "OpenGL ES");
#endif
lblCurrentRenderer->setText(KisOpenGL::hasOpenGLES() ? rendererOpenGLESText : rendererOpenGLText);
cmbPreferredRenderer->clear();
QString qtPreferredRendererText;
if (KisOpenGL::getQtPreferredOpenGLRenderer() == KisOpenGL::RendererOpenGLES) {
qtPreferredRendererText = rendererOpenGLESText;
} else {
qtPreferredRendererText = rendererOpenGLText;
}
cmbPreferredRenderer->addItem(i18nc("canvas renderer", "Auto (%1)", qtPreferredRendererText), KisOpenGL::RendererAuto);
cmbPreferredRenderer->setCurrentIndex(0);
if (KisOpenGL::getSupportedOpenGLRenderers() & KisOpenGL::RendererDesktopGL) {
cmbPreferredRenderer->addItem(rendererOpenGLText, KisOpenGL::RendererDesktopGL);
if (KisOpenGL::getUserPreferredOpenGLRendererConfig() == KisOpenGL::RendererDesktopGL) {
cmbPreferredRenderer->setCurrentIndex(cmbPreferredRenderer->count() - 1);
}
}
#ifdef Q_OS_WIN
if (KisOpenGL::getSupportedOpenGLRenderers() & KisOpenGL::RendererOpenGLES) {
cmbPreferredRenderer->addItem(rendererOpenGLESText, KisOpenGL::RendererOpenGLES);
if (KisOpenGL::getUserPreferredOpenGLRendererConfig() == KisOpenGL::RendererOpenGLES) {
cmbPreferredRenderer->setCurrentIndex(cmbPreferredRenderer->count() - 1);
}
}
#endif
if (!(KisOpenGL::getSupportedOpenGLRenderers() &
(KisOpenGL::RendererDesktopGL | KisOpenGL::RendererOpenGLES))) {
grpOpenGL->setEnabled(false);
grpOpenGL->setChecked(false);
chkUseTextureBuffer->setEnabled(false);
chkDisableVsync->setEnabled(false);
cmbFilterMode->setEnabled(false);
} else {
grpOpenGL->setEnabled(true);
grpOpenGL->setChecked(cfg.useOpenGL());
chkUseTextureBuffer->setEnabled(cfg.useOpenGL());
chkUseTextureBuffer->setChecked(cfg.useOpenGLTextureBuffer());
chkDisableVsync->setVisible(cfg.showAdvancedOpenGLSettings());
chkDisableVsync->setEnabled(cfg.useOpenGL());
chkDisableVsync->setChecked(cfg.disableVSync());
cmbFilterMode->setEnabled(cfg.useOpenGL());
cmbFilterMode->setCurrentIndex(cfg.openGLFilteringMode());
// Don't show the high quality filtering mode if it's not available
if (!KisOpenGL::supportsLoD()) {
cmbFilterMode->removeItem(3);
}
}
lblCurrentDisplayFormat->setText("");
lblCurrentRootSurfaceFormat->setText("");
lblHDRWarning->setText("");
cmbPreferedRootSurfaceFormat->addItem(colorSpaceString(QSurfaceFormat::sRGBColorSpace, 8));
#ifdef HAVE_HDR
cmbPreferedRootSurfaceFormat->addItem(colorSpaceString(QSurfaceFormat::bt2020PQColorSpace, 10));
cmbPreferedRootSurfaceFormat->addItem(colorSpaceString(QSurfaceFormat::scRGBColorSpace, 16));
#endif
cmbPreferedRootSurfaceFormat->setCurrentIndex(formatToIndex(KisConfig::BT709_G22));
slotPreferredSurfaceFormatChanged(cmbPreferedRootSurfaceFormat->currentIndex());
QOpenGLContext *context = QOpenGLContext::currentContext();
if (!context) {
context = QOpenGLContext::globalShareContext();
}
if (context) {
QScreen *screen = QGuiApplication::screenAt(rect().center());
KisScreenInformationAdapter adapter(context);
if (screen && adapter.isValid()) {
KisScreenInformationAdapter::ScreenInfo info = adapter.infoForScreen(screen);
if (info.isValid()) {
QStringList toolTip;
toolTip << i18n("Display Id: %1", info.screen->name());
toolTip << i18n("Display Name: %1 %2", info.screen->manufacturer(), info.screen->model());
toolTip << i18n("Min Luminance: %1", info.minLuminance);
toolTip << i18n("Max Luminance: %1", info.maxLuminance);
toolTip << i18n("Max Full Frame Luminance: %1", info.maxFullFrameLuminance);
toolTip << i18n("Red Primary: %1, %2", info.redPrimary[0], info.redPrimary[1]);
toolTip << i18n("Green Primary: %1, %2", info.greenPrimary[0], info.greenPrimary[1]);
toolTip << i18n("Blue Primary: %1, %2", info.bluePrimary[0], info.bluePrimary[1]);
toolTip << i18n("White Point: %1, %2", info.whitePoint[0], info.whitePoint[1]);
lblCurrentDisplayFormat->setToolTip(toolTip.join('\n'));
lblCurrentDisplayFormat->setText(colorSpaceString(info.colorSpace, info.bitsPerColor));
} else {
lblCurrentDisplayFormat->setToolTip("");
lblCurrentDisplayFormat->setText(i18n("Unknown"));
}
} else {
lblCurrentDisplayFormat->setToolTip("");
lblCurrentDisplayFormat->setText(i18n("Unknown"));
qWarning() << "Failed to fetch display info:" << adapter.errorString();
}
const QSurfaceFormat currentFormat = KisOpenGLModeProber::instance()->surfaceformatInUse();
lblCurrentRootSurfaceFormat->setText(colorSpaceString(currentFormat.colorSpace(), currentFormat.redBufferSize()));
cmbPreferedRootSurfaceFormat->setCurrentIndex(formatToIndex(cfg.rootSurfaceFormat()));
connect(cmbPreferedRootSurfaceFormat, SIGNAL(currentIndexChanged(int)), SLOT(slotPreferredSurfaceFormatChanged(int)));
slotPreferredSurfaceFormatChanged(cmbPreferedRootSurfaceFormat->currentIndex());
}
#ifndef HAVE_HDR
grpHDRSettings->setVisible(false);
#endif
const QStringList openglWarnings = KisOpenGL::getOpenGLWarnings();
if (openglWarnings.isEmpty()) {
lblOpenGLWarnings->setVisible(false);
} else {
QString text("<span style=\"color: yellow;\">&#x26A0;</span> ");
text.append(i18n("Warning(s):"));
text.append("<ul>");
Q_FOREACH (const QString &warning, openglWarnings) {
text.append("<li>");
text.append(warning.toHtmlEscaped());
text.append("</li>");
}
text.append("</ul>");
lblOpenGLWarnings->setText(text);
lblOpenGLWarnings->setVisible(true);
}
if (qApp->applicationName() == "kritasketch" || qApp->applicationName() == "kritagemini") {
grpOpenGL->setVisible(false);
grpOpenGL->setMaximumHeight(0);
}
KisImageConfig imageCfg(false);
KoColor c;
c.fromQColor(imageCfg.selectionOverlayMaskColor());
c.setOpacity(1.0);
btnSelectionOverlayColor->setColor(c);
sldSelectionOverlayOpacity->setRange(0.0, 1.0, 2);
sldSelectionOverlayOpacity->setSingleStep(0.05);
sldSelectionOverlayOpacity->setValue(imageCfg.selectionOverlayMaskColor().alphaF());
intCheckSize->setValue(cfg.checkSize());
chkMoving->setChecked(cfg.scrollCheckers());
KoColor ck1(KoColorSpaceRegistry::instance()->rgb8());
ck1.fromQColor(cfg.checkersColor1());
colorChecks1->setColor(ck1);
KoColor ck2(KoColorSpaceRegistry::instance()->rgb8());
ck2.fromQColor(cfg.checkersColor2());
colorChecks2->setColor(ck2);
KoColor cb(KoColorSpaceRegistry::instance()->rgb8());
cb.fromQColor(cfg.canvasBorderColor());
canvasBorder->setColor(cb);
hideScrollbars->setChecked(cfg.hideScrollbars());
chkCurveAntialiasing->setChecked(cfg.antialiasCurves());
chkSelectionOutlineAntialiasing->setChecked(cfg.antialiasSelectionOutline());
chkChannelsAsColor->setChecked(cfg.showSingleChannelAsColor());
chkHidePopups->setChecked(cfg.hidePopups());
connect(grpOpenGL, SIGNAL(toggled(bool)), SLOT(slotUseOpenGLToggled(bool)));
KoColor gridColor(KoColorSpaceRegistry::instance()->rgb8());
gridColor.fromQColor(cfg.getPixelGridColor());
pixelGridColorButton->setColor(gridColor);
pixelGridDrawingThresholdBox->setValue(cfg.getPixelGridDrawingThreshold() * 100);
}
void DisplaySettingsTab::setDefault()
{
KisConfig cfg(true);
cmbPreferredRenderer->setCurrentIndex(0);
if (!(KisOpenGL::getSupportedOpenGLRenderers() &
(KisOpenGL::RendererDesktopGL | KisOpenGL::RendererOpenGLES))) {
grpOpenGL->setEnabled(false);
grpOpenGL->setChecked(false);
chkUseTextureBuffer->setEnabled(false);
chkDisableVsync->setEnabled(false);
cmbFilterMode->setEnabled(false);
}
else {
grpOpenGL->setEnabled(true);
grpOpenGL->setChecked(cfg.useOpenGL(true));
chkUseTextureBuffer->setChecked(cfg.useOpenGLTextureBuffer(true));
chkUseTextureBuffer->setEnabled(true);
chkDisableVsync->setEnabled(true);
chkDisableVsync->setChecked(cfg.disableVSync(true));
cmbFilterMode->setEnabled(true);
cmbFilterMode->setCurrentIndex(cfg.openGLFilteringMode(true));
}
chkMoving->setChecked(cfg.scrollCheckers(true));
intCheckSize->setValue(cfg.checkSize(true));
KoColor ck1(KoColorSpaceRegistry::instance()->rgb8());
ck1.fromQColor(cfg.checkersColor1(true));
colorChecks1->setColor(ck1);
KoColor ck2(KoColorSpaceRegistry::instance()->rgb8());
ck2.fromQColor(cfg.checkersColor2(true));
colorChecks2->setColor(ck2);
KoColor cvb(KoColorSpaceRegistry::instance()->rgb8());
cvb.fromQColor(cfg.canvasBorderColor(true));
canvasBorder->setColor(cvb);
hideScrollbars->setChecked(cfg.hideScrollbars(true));
chkCurveAntialiasing->setChecked(cfg.antialiasCurves(true));
chkSelectionOutlineAntialiasing->setChecked(cfg.antialiasSelectionOutline(true));
chkChannelsAsColor->setChecked(cfg.showSingleChannelAsColor(true));
chkHidePopups->setChecked(cfg.hidePopups(true));
KoColor gridColor(KoColorSpaceRegistry::instance()->rgb8());
gridColor.fromQColor(cfg.getPixelGridColor(true));
pixelGridColorButton->setColor(gridColor);
pixelGridDrawingThresholdBox->setValue(cfg.getPixelGridDrawingThreshold(true) * 100);
cmbPreferedRootSurfaceFormat->setCurrentIndex(formatToIndex(KisConfig::BT709_G22));
slotPreferredSurfaceFormatChanged(cmbPreferedRootSurfaceFormat->currentIndex());
}
void DisplaySettingsTab::slotUseOpenGLToggled(bool isChecked)
{
chkUseTextureBuffer->setEnabled(isChecked);
chkDisableVsync->setEnabled(isChecked);
cmbFilterMode->setEnabled(isChecked);
}
void DisplaySettingsTab::slotPreferredSurfaceFormatChanged(int index)
{
Q_UNUSED(index);
QOpenGLContext *context = QOpenGLContext::currentContext();
if (context) {
QScreen *screen = QGuiApplication::screenAt(rect().center());
KisScreenInformationAdapter adapter(context);
if (adapter.isValid()) {
KisScreenInformationAdapter::ScreenInfo info = adapter.infoForScreen(screen);
if (info.isValid()) {
if (cmbPreferedRootSurfaceFormat->currentIndex() != formatToIndex(KisConfig::BT709_G22) &&
info.colorSpace == QSurfaceFormat::sRGBColorSpace) {
lblHDRWarning->setText(i18n("WARNING: current display doesn't support HDR rendering"));
} else {
lblHDRWarning->setText("");
}
}
}
}
}
//---------------------------------------------------------------------------------------------------
FullscreenSettingsTab::FullscreenSettingsTab(QWidget* parent) : WdgFullscreenSettingsBase(parent)
{
KisConfig cfg(true);
chkDockers->setChecked(cfg.hideDockersFullscreen());
chkMenu->setChecked(cfg.hideMenuFullscreen());
chkScrollbars->setChecked(cfg.hideScrollbarsFullscreen());
chkStatusbar->setChecked(cfg.hideStatusbarFullscreen());
chkTitlebar->setChecked(cfg.hideTitlebarFullscreen());
chkToolbar->setChecked(cfg.hideToolbarFullscreen());
}
void FullscreenSettingsTab::setDefault()
{
KisConfig cfg(true);
chkDockers->setChecked(cfg.hideDockersFullscreen(true));
chkMenu->setChecked(cfg.hideMenuFullscreen(true));
chkScrollbars->setChecked(cfg.hideScrollbarsFullscreen(true));
chkStatusbar->setChecked(cfg.hideStatusbarFullscreen(true));
chkTitlebar->setChecked(cfg.hideTitlebarFullscreen(true));
chkToolbar->setChecked(cfg.hideToolbarFullscreen(true));
}
//---------------------------------------------------------------------------------------------------
KisDlgPreferences::KisDlgPreferences(QWidget* parent, const char* name)
: KPageDialog(parent)
{
Q_UNUSED(name);
setWindowTitle(i18n("Configure Krita"));
setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::RestoreDefaults);
setFaceType(KPageDialog::Tree);
// General
KoVBox *vbox = new KoVBox();
KPageWidgetItem *page = new KPageWidgetItem(vbox, i18n("General"));
page->setObjectName("general");
page->setHeader(i18n("General"));
page->setIcon(KisIconUtils::loadIcon("go-home"));
m_pages << page;
addPage(page);
m_general = new GeneralTab(vbox);
// Shortcuts
vbox = new KoVBox();
page = new KPageWidgetItem(vbox, i18n("Keyboard Shortcuts"));
page->setObjectName("shortcuts");
page->setHeader(i18n("Shortcuts"));
page->setIcon(KisIconUtils::loadIcon("document-export"));
m_pages << page;
addPage(page);
m_shortcutSettings = new ShortcutSettingsTab(vbox);
connect(this, SIGNAL(accepted()), m_shortcutSettings, SLOT(saveChanges()));
connect(this, SIGNAL(rejected()), m_shortcutSettings, SLOT(cancelChanges()));
// Canvas input settings
m_inputConfiguration = new KisInputConfigurationPage();
page = addPage(m_inputConfiguration, i18n("Canvas Input Settings"));
page->setHeader(i18n("Canvas Input"));
page->setObjectName("canvasinput");
page->setIcon(KisIconUtils::loadIcon("configure"));
m_pages << page;
// Display
vbox = new KoVBox();
page = new KPageWidgetItem(vbox, i18n("Display"));
page->setObjectName("display");
page->setHeader(i18n("Display"));
page->setIcon(KisIconUtils::loadIcon("preferences-desktop-display"));
m_pages << page;
addPage(page);
m_displaySettings = new DisplaySettingsTab(vbox);
// Color
vbox = new KoVBox();
page = new KPageWidgetItem(vbox, i18n("Color Management"));
page->setObjectName("colormanagement");
page->setHeader(i18n("Color"));
page->setIcon(KisIconUtils::loadIcon("preferences-desktop-color"));
m_pages << page;
addPage(page);
m_colorSettings = new ColorSettingsTab(vbox);
// Performance
vbox = new KoVBox();
page = new KPageWidgetItem(vbox, i18n("Performance"));
page->setObjectName("performance");
page->setHeader(i18n("Performance"));
page->setIcon(KisIconUtils::loadIcon("applications-system"));
m_pages << page;
addPage(page);
m_performanceSettings = new PerformanceTab(vbox);
// Tablet
vbox = new KoVBox();
page = new KPageWidgetItem(vbox, i18n("Tablet settings"));
page->setObjectName("tablet");
page->setHeader(i18n("Tablet"));
page->setIcon(KisIconUtils::loadIcon("document-edit"));
m_pages << page;
addPage(page);
m_tabletSettings = new TabletSettingsTab(vbox);
// full-screen mode
vbox = new KoVBox();
page = new KPageWidgetItem(vbox, i18n("Canvas-only settings"));
page->setObjectName("canvasonly");
page->setHeader(i18n("Canvas-only"));
page->setIcon(KisIconUtils::loadIcon("folder-pictures"));
m_pages << page;
addPage(page);
m_fullscreenSettings = new FullscreenSettingsTab(vbox);
// Author profiles
m_authorPage = new KoConfigAuthorPage();
page = addPage(m_authorPage, i18nc("@title:tab Author page", "Author" ));
page->setObjectName("author");
page->setHeader(i18n("Author"));
page->setIcon(KisIconUtils::loadIcon("im-user"));
m_pages << page;
QPushButton *restoreDefaultsButton = button(QDialogButtonBox::RestoreDefaults);
restoreDefaultsButton->setText(i18nc("@action:button", "Restore Defaults"));
connect(this, SIGNAL(accepted()), m_inputConfiguration, SLOT(saveChanges()));
connect(this, SIGNAL(rejected()), m_inputConfiguration, SLOT(revertChanges()));
KisPreferenceSetRegistry *preferenceSetRegistry = KisPreferenceSetRegistry::instance();
Q_FOREACH (KisAbstractPreferenceSetFactory *preferenceSetFactory, preferenceSetRegistry->values()) {
KisPreferenceSet* preferenceSet = preferenceSetFactory->createPreferenceSet();
vbox = new KoVBox();
page = new KPageWidgetItem(vbox, preferenceSet->name());
page->setHeader(preferenceSet->header());
page->setIcon(preferenceSet->icon());
addPage(page);
preferenceSet->setParent(vbox);
preferenceSet->loadPreferences();
connect(restoreDefaultsButton, SIGNAL(clicked(bool)), preferenceSet, SLOT(loadDefaultPreferences()), Qt::UniqueConnection);
connect(this, SIGNAL(accepted()), preferenceSet, SLOT(savePreferences()), Qt::UniqueConnection);
}
connect(restoreDefaultsButton, SIGNAL(clicked(bool)), this, SLOT(slotDefault()));
KisConfig cfg(true);
QString currentPageName = cfg.readEntry<QString>("KisDlgPreferences/CurrentPage");
Q_FOREACH(KPageWidgetItem *page, m_pages) {
if (page->objectName() == currentPageName) {
setCurrentPage(page);
break;
}
}
}
KisDlgPreferences::~KisDlgPreferences()
{
KisConfig cfg(true);
cfg.writeEntry<QString>("KisDlgPreferences/CurrentPage", currentPage()->objectName());
}
void KisDlgPreferences::showEvent(QShowEvent *event){
KPageDialog::showEvent(event);
button(QDialogButtonBox::Cancel)->setAutoDefault(false);
button(QDialogButtonBox::Ok)->setAutoDefault(false);
button(QDialogButtonBox::RestoreDefaults)->setAutoDefault(false);
button(QDialogButtonBox::Cancel)->setDefault(false);
button(QDialogButtonBox::Ok)->setDefault(false);
button(QDialogButtonBox::RestoreDefaults)->setDefault(false);
}
void KisDlgPreferences::slotDefault()
{
if (currentPage()->objectName() == "general") {
m_general->setDefault();
}
else if (currentPage()->objectName() == "shortcuts") {
m_shortcutSettings->setDefault();
}
else if (currentPage()->objectName() == "display") {
m_displaySettings->setDefault();
}
else if (currentPage()->objectName() == "colormanagement") {
m_colorSettings->setDefault();
}
else if (currentPage()->objectName() == "performance") {
m_performanceSettings->load(true);
}
else if (currentPage()->objectName() == "tablet") {
m_tabletSettings->setDefault();
}
else if (currentPage()->objectName() == "canvasonly") {
m_fullscreenSettings->setDefault();
}
else if (currentPage()->objectName() == "canvasinput") {
m_inputConfiguration->setDefaults();
}
}
bool KisDlgPreferences::editPreferences()
{
KisDlgPreferences* dialog;
dialog = new KisDlgPreferences();
bool baccept = (dialog->exec() == Accepted);
if (baccept) {
// General settings
KisConfig cfg(false);
cfg.setNewCursorStyle(dialog->m_general->cursorStyle());
cfg.setNewOutlineStyle(dialog->m_general->outlineStyle());
cfg.setShowRootLayer(dialog->m_general->showRootLayer());
cfg.setShowOutlineWhilePainting(dialog->m_general->showOutlineWhilePainting());
cfg.setForceAlwaysFullSizedOutline(!dialog->m_general->m_changeBrushOutline->isChecked());
cfg.setSessionOnStartup(dialog->m_general->sessionOnStartup());
cfg.setSaveSessionOnQuit(dialog->m_general->saveSessionOnQuit());
KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs");
group.writeEntry("DontUseNativeFileDialog", !dialog->m_general->m_chkNativeFileDialog->isChecked());
cfg.writeEntry<int>("maximumBrushSize", dialog->m_general->intMaxBrushSize->value());
cfg.writeEntry<int>("mdi_viewmode", dialog->m_general->mdiMode());
cfg.setMDIBackgroundColor(dialog->m_general->m_mdiColor->color().toQColor());
cfg.setMDIBackgroundImage(dialog->m_general->m_backgroundimage->text());
cfg.setAutoSaveInterval(dialog->m_general->autoSaveInterval());
+ cfg.writeEntry("autosavefileshidden", dialog->m_general->chkHideAutosaveFiles->isChecked());
+
cfg.setBackupFile(dialog->m_general->m_backupFileCheckBox->isChecked());
+ cfg.writeEntry("backupfilelocation", dialog->m_general->cmbBackupFileLocation->currentIndex());
+ cfg.writeEntry("backupfilesuffix", dialog->m_general->txtBackupFileSuffix->text());
+ cfg.writeEntry("numberofbackupfiles", dialog->m_general->intNumBackupFiles->value());
+
cfg.setShowCanvasMessages(dialog->m_general->showCanvasMessages());
cfg.setCompressKra(dialog->m_general->compressKra());
+ cfg.setUseZip64(dialog->m_general->useZip64());
const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat);
kritarc.setValue("EnableHiDPI", dialog->m_general->m_chkHiDPI->isChecked());
kritarc.setValue("EnableSingleApplication", dialog->m_general->m_chkSingleApplication->isChecked());
+ kritarc.setValue("LogUsage", dialog->m_general->chkUsageLogging->isChecked());
cfg.setToolOptionsInDocker(dialog->m_general->toolOptionsInDocker());
+ cfg.writeEntry<bool>("useCreamyAlphaDarken", (bool)!dialog->m_general->cmbFlowMode->currentIndex());
+
cfg.setKineticScrollingEnabled(dialog->m_general->kineticScrollingEnabled());
cfg.setKineticScrollingGesture(dialog->m_general->kineticScrollingGesture());
cfg.setKineticScrollingSensitivity(dialog->m_general->kineticScrollingSensitivity());
cfg.setKineticScrollingHideScrollbars(dialog->m_general->kineticScrollingHiddenScrollbars());
cfg.setSwitchSelectionCtrlAlt(dialog->m_general->switchSelectionCtrlAlt());
cfg.setDisableTouchOnCanvas(!dialog->m_general->chkEnableTouch->isChecked());
cfg.setActivateTransformToolAfterPaste(dialog->m_general->chkEnableTranformToolAfterPaste->isChecked());
cfg.setConvertToImageColorspaceOnImport(dialog->m_general->convertToImageColorspaceOnImport());
cfg.setUndoStackLimit(dialog->m_general->undoStackSize());
cfg.setFavoritePresets(dialog->m_general->favoritePresets());
// Color settings
cfg.setUseSystemMonitorProfile(dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked());
for (int i = 0; i < QApplication::desktop()->screenCount(); ++i) {
if (dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked()) {
int currentIndex = dialog->m_colorSettings->m_monitorProfileWidgets[i]->currentIndex();
QString monitorid = dialog->m_colorSettings->m_monitorProfileWidgets[i]->itemData(currentIndex).toString();
cfg.setMonitorForScreen(i, monitorid);
}
else {
cfg.setMonitorProfile(i,
dialog->m_colorSettings->m_monitorProfileWidgets[i]->itemHighlighted(),
dialog->m_colorSettings->m_page->chkUseSystemMonitorProfile->isChecked());
}
}
cfg.setWorkingColorSpace(dialog->m_colorSettings->m_page->cmbWorkingColorSpace->currentItem().id());
KisImageConfig cfgImage(false);
cfgImage.setDefaultProofingConfig(dialog->m_colorSettings->m_page->proofingSpaceSelector->currentColorSpace(),
dialog->m_colorSettings->m_page->cmbProofingIntent->currentIndex(),
dialog->m_colorSettings->m_page->ckbProofBlackPoint->isChecked(),
dialog->m_colorSettings->m_page->gamutAlarm->color(),
(double)dialog->m_colorSettings->m_page->sldAdaptationState->value()/20);
cfg.setUseBlackPointCompensation(dialog->m_colorSettings->m_page->chkBlackpoint->isChecked());
cfg.setAllowLCMSOptimization(dialog->m_colorSettings->m_page->chkAllowLCMSOptimization->isChecked());
cfg.setForcePaletteColors(dialog->m_colorSettings->m_page->chkForcePaletteColor->isChecked());
cfg.setPasteBehaviour(dialog->m_colorSettings->m_pasteBehaviourGroup.checkedId());
cfg.setRenderIntent(dialog->m_colorSettings->m_page->cmbMonitorIntent->currentIndex());
// Tablet settings
cfg.setPressureTabletCurve( dialog->m_tabletSettings->m_page->pressureCurve->curve().toString() );
#ifdef Q_OS_WIN
if (KisTabletSupportWin8::isAvailable()) {
cfg.setUseWin8PointerInput(dialog->m_tabletSettings->m_page->radioWin8PointerInput->isChecked());
}
#endif
dialog->m_performanceSettings->save();
{
KisOpenGL::OpenGLRenderer renderer = static_cast<KisOpenGL::OpenGLRenderer>(
dialog->m_displaySettings->cmbPreferredRenderer->itemData(
dialog->m_displaySettings->cmbPreferredRenderer->currentIndex()).toInt());
KisOpenGL::setUserPreferredOpenGLRendererConfig(renderer);
}
if (!cfg.useOpenGL() && dialog->m_displaySettings->grpOpenGL->isChecked())
cfg.setCanvasState("TRY_OPENGL");
cfg.setUseOpenGL(dialog->m_displaySettings->grpOpenGL->isChecked());
cfg.setUseOpenGLTextureBuffer(dialog->m_displaySettings->chkUseTextureBuffer->isChecked());
cfg.setOpenGLFilteringMode(dialog->m_displaySettings->cmbFilterMode->currentIndex());
cfg.setDisableVSync(dialog->m_displaySettings->chkDisableVsync->isChecked());
cfg.setRootSurfaceFormat(&kritarc, indexToFormat(dialog->m_displaySettings->cmbPreferedRootSurfaceFormat->currentIndex()));
cfg.setCheckSize(dialog->m_displaySettings->intCheckSize->value());
cfg.setScrollingCheckers(dialog->m_displaySettings->chkMoving->isChecked());
cfg.setCheckersColor1(dialog->m_displaySettings->colorChecks1->color().toQColor());
cfg.setCheckersColor2(dialog->m_displaySettings->colorChecks2->color().toQColor());
cfg.setCanvasBorderColor(dialog->m_displaySettings->canvasBorder->color().toQColor());
cfg.setHideScrollbars(dialog->m_displaySettings->hideScrollbars->isChecked());
KoColor c = dialog->m_displaySettings->btnSelectionOverlayColor->color();
c.setOpacity(dialog->m_displaySettings->sldSelectionOverlayOpacity->value());
cfgImage.setSelectionOverlayMaskColor(c.toQColor());
cfg.setAntialiasCurves(dialog->m_displaySettings->chkCurveAntialiasing->isChecked());
cfg.setAntialiasSelectionOutline(dialog->m_displaySettings->chkSelectionOutlineAntialiasing->isChecked());
cfg.setShowSingleChannelAsColor(dialog->m_displaySettings->chkChannelsAsColor->isChecked());
cfg.setHidePopups(dialog->m_displaySettings->chkHidePopups->isChecked());
cfg.setHideDockersFullscreen(dialog->m_fullscreenSettings->chkDockers->checkState());
cfg.setHideMenuFullscreen(dialog->m_fullscreenSettings->chkMenu->checkState());
cfg.setHideScrollbarsFullscreen(dialog->m_fullscreenSettings->chkScrollbars->checkState());
cfg.setHideStatusbarFullscreen(dialog->m_fullscreenSettings->chkStatusbar->checkState());
cfg.setHideTitlebarFullscreen(dialog->m_fullscreenSettings->chkTitlebar->checkState());
cfg.setHideToolbarFullscreen(dialog->m_fullscreenSettings->chkToolbar->checkState());
cfg.setCursorMainColor(dialog->m_general->cursorColorBtutton->color().toQColor());
cfg.setPixelGridColor(dialog->m_displaySettings->pixelGridColorButton->color().toQColor());
cfg.setPixelGridDrawingThreshold(dialog->m_displaySettings->pixelGridDrawingThresholdBox->value() / 100);
dialog->m_authorPage->apply();
}
delete dialog;
return baccept;
}
diff --git a/libs/ui/dialogs/kis_dlg_preferences.h b/libs/ui/dialogs/kis_dlg_preferences.h
index 5fe0308a83..5b81c295f9 100644
--- a/libs/ui/dialogs/kis_dlg_preferences.h
+++ b/libs/ui/dialogs/kis_dlg_preferences.h
@@ -1,354 +1,355 @@
/*
* preferencesdlg.h - part of KImageShop^WKrita
*
* Copyright (c) 1999 Michael Koch <koch@kde.org>
* Copyright (c) 2003-2011 Boudewijn Rempt <boud@valdyas.org>
*
* 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_PREFERENCES_H_
#define _KIS_DLG_PREFERENCES_H_
#include <QWidget>
#include <QButtonGroup>
#include <QMap>
#include <QString>
#include <kpagedialog.h>
#include <kis_config.h>
#include "kis_global.h"
#include <squeezedcombobox.h>
#include "ui_wdggeneralsettings.h"
#include "ui_wdgdisplaysettings.h"
#include "ui_wdgcolorsettings.h"
#include "ui_wdgtabletsettings.h"
#include "ui_wdgperformancesettings.h"
#include "ui_wdgfullscreensettings.h"
#include "KisShortcutsDialog.h"
class KoID;
class KisInputConfigurationPage;
class KoConfigAuthorPage;
/**
* "General"-tab for preferences dialog
*/
class WdgGeneralSettings : public QWidget, public Ui::WdgGeneralSettings
{
Q_OBJECT
public:
WdgGeneralSettings(QWidget *parent, const char *name) : QWidget(parent) {
setObjectName(name);
setupUi(this);
chkShowRootLayer->setVisible(false);
}
};
class GeneralTab : public WdgGeneralSettings
{
Q_OBJECT
public:
GeneralTab(QWidget *parent = 0, const char *name = 0);
CursorStyle cursorStyle();
OutlineStyle outlineStyle();
KisConfig::SessionOnStartup sessionOnStartup() const;
bool saveSessionOnQuit() const;
bool showRootLayer();
int autoSaveInterval();
void setDefault();
int undoStackSize();
bool showOutlineWhilePainting();
int mdiMode();
int favoritePresets();
bool showCanvasMessages();
bool compressKra();
+ bool useZip64();
bool toolOptionsInDocker();
bool kineticScrollingEnabled();
int kineticScrollingGesture();
int kineticScrollingSensitivity();
bool kineticScrollingHiddenScrollbars();
bool switchSelectionCtrlAlt();
bool convertToImageColorspaceOnImport();
private Q_SLOTS:
void getBackgroundImage();
void clearBackgroundImage();
};
/**
* "Shortcuts" tab for preferences dialog
*/
class WdgShortcutSettings : public KisShortcutsDialog
{
Q_OBJECT
public:
WdgShortcutSettings(QWidget *parent)
: KisShortcutsDialog(KisShortcutsEditor::AllActions,
KisShortcutsEditor::LetterShortcutsAllowed,
parent)
{ }
};
class KisActionsSnapshot;
class ShortcutSettingsTab : public QWidget
{
Q_OBJECT
public:
ShortcutSettingsTab(QWidget *parent = 0, const char *name = 0);
~ShortcutSettingsTab() override;
public:
void setDefault();
WdgShortcutSettings *m_page;
QScopedPointer<KisActionsSnapshot> m_snapshot;
public Q_SLOTS:
void saveChanges();
void cancelChanges();
};
/**
* "Color" tab for preferences dialog
*/
class WdgColorSettings : public QWidget, public Ui::WdgColorSettings
{
Q_OBJECT
public:
WdgColorSettings(QWidget *parent) : QWidget(parent) {
setupUi(this);
}
};
class ColorSettingsTab : public QWidget
{
Q_OBJECT
public:
ColorSettingsTab(QWidget *parent = 0, const char *name = 0);
private Q_SLOTS:
void refillMonitorProfiles(const KoID & s);
void installProfile();
void toggleAllowMonitorProfileSelection(bool useSystemProfile);
public:
void setDefault();
WdgColorSettings *m_page;
QButtonGroup m_pasteBehaviourGroup;
QList<QLabel*> m_monitorProfileLabels;
QList<SqueezedComboBox*> m_monitorProfileWidgets;
};
//=======================
class WdgTabletSettings : public QWidget, public Ui::WdgTabletSettings {
Q_OBJECT
public:
WdgTabletSettings(QWidget *parent) : QWidget(parent) {
setupUi(this);
}
};
class TabletSettingsTab : public QWidget {
Q_OBJECT
public:
TabletSettingsTab(QWidget *parent = 0, const char *name = 0);
private Q_SLOTS:
void slotTabletTest();
public:
void setDefault();
WdgTabletSettings *m_page;
};
//=======================
/**
* "Performance"-tab for preferences dialog
*/
class SliderAndSpinBoxSync;
class WdgPerformanceSettings : public QWidget, public Ui::WdgPerformanceSettings
{
Q_OBJECT
public:
WdgPerformanceSettings(QWidget *parent, const char *name) : QWidget(parent) {
setObjectName(name); setupUi(this);
}
};
class PerformanceTab : public WdgPerformanceSettings
{
Q_OBJECT
public:
PerformanceTab(QWidget *parent = 0, const char *name = 0);
~PerformanceTab() override;
void load(bool requestDefault);
void save();
private Q_SLOTS:
void selectSwapDir();
void slotThreadsLimitChanged(int value);
void slotFrameClonesLimitChanged(int value);
private:
int realTilesRAM();
private:
QVector<SliderAndSpinBoxSync*> m_syncs;
int m_lastUsedThreadsLimit;
int m_lastUsedClonesLimit;
};
//=======================
class WdgDisplaySettings : public QWidget, public Ui::WdgDisplaySettings
{
Q_OBJECT
public:
WdgDisplaySettings(QWidget *parent, const char *name) : QWidget(parent) {
setObjectName(name); setupUi(this);
}
};
/**
* Display settings tab for preferences dialog
*/
class DisplaySettingsTab : public WdgDisplaySettings
{
Q_OBJECT
public:
DisplaySettingsTab(QWidget *parent = 0, const char *name = 0);
public:
void setDefault();
protected Q_SLOTS:
void slotUseOpenGLToggled(bool isChecked);
void slotPreferredSurfaceFormatChanged(int index);
public:
};
//=======================
/**
* Full screen settings tab for preferences dialog
*/
class WdgFullscreenSettingsBase : public QWidget, public Ui::WdgFullscreenSettings
{
Q_OBJECT
public:
WdgFullscreenSettingsBase(QWidget *parent) : QWidget(parent) {
setupUi(this);
}
};
class FullscreenSettingsTab : public WdgFullscreenSettingsBase
{
Q_OBJECT
public:
FullscreenSettingsTab(QWidget *parent);
public:
void setDefault();
};
//=======================
/**
* Preferences dialog of KImageShop^WKrayon^WKrita
*/
class KisDlgPreferences : public KPageDialog
{
Q_OBJECT
public:
static bool editPreferences();
protected:
KisDlgPreferences(QWidget *parent = 0, const char *name = 0);
~KisDlgPreferences() override;
void showEvent(QShowEvent *event) override;
protected:
GeneralTab *m_general;
ShortcutSettingsTab *m_shortcutSettings;
ColorSettingsTab *m_colorSettings;
PerformanceTab *m_performanceSettings;
DisplaySettingsTab *m_displaySettings;
TabletSettingsTab *m_tabletSettings;
FullscreenSettingsTab *m_fullscreenSettings;
KisInputConfigurationPage *m_inputConfiguration;
KoConfigAuthorPage *m_authorPage;
QList<KPageWidgetItem*> m_pages;
protected Q_SLOTS:
void slotDefault();
};
#endif
diff --git a/libs/ui/flake/KisReferenceImagesLayer.h b/libs/ui/flake/KisReferenceImagesLayer.h
index e871a38cdb..4b49d03573 100644
--- a/libs/ui/flake/KisReferenceImagesLayer.h
+++ b/libs/ui/flake/KisReferenceImagesLayer.h
@@ -1,68 +1,70 @@
/*
* Copyright (C) 2017 Jouni Pentikäinen <joupent@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KRITA_KISREFERENCEIMAGESLAYER_H
#define KRITA_KISREFERENCEIMAGESLAYER_H
#include "kis_shape_layer.h"
#include <kis_types.h>
+class KisDocument;
+
class KRITAUI_EXPORT KisReferenceImagesLayer : public KisShapeLayer
{
Q_OBJECT
public:
KisReferenceImagesLayer(KoShapeControllerBase* shapeController, KisImageWSP image);
KisReferenceImagesLayer(const KisReferenceImagesLayer &rhs);
static KUndo2Command * addReferenceImages(KisDocument *document, QList<KoShape*> referenceImages);
KUndo2Command * removeReferenceImages(KisDocument *document, QList<KoShape*> referenceImages);
QVector<KisReferenceImage*> referenceImages() const;
QRectF boundingImageRect() const;
QColor getPixel(QPointF position) const;
void paintReferences(QPainter &painter);
bool allowAsChild(KisNodeSP) const override;
bool accept(KisNodeVisitor&) override;
void accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) override;
KisNodeSP clone() const override {
return new KisReferenceImagesLayer(*this);
}
Q_SIGNALS:
/**
* The content of the layer has changed, and the canvas decoration
* needs to update.
*/
void sigUpdateCanvas(const QRectF &rect);
private:
void signalUpdate(const QRectF &rect);
friend struct AddReferenceImagesCommand;
friend struct RemoveReferenceImagesCommand;
friend class ReferenceImagesCanvas;
};
#endif //KRITA_KISREFERENCEIMAGESLAYER_H
diff --git a/libs/ui/forms/wdggeneralsettings.ui b/libs/ui/forms/wdggeneralsettings.ui
index 5a49e1e2fe..c7505addaa 100644
--- a/libs/ui/forms/wdggeneralsettings.ui
+++ b/libs/ui/forms/wdggeneralsettings.ui
@@ -1,816 +1,915 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WdgGeneralSettings</class>
<widget class="QWidget" name="WdgGeneralSettings">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
- <width>759</width>
+ <width>552</width>
<height>468</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>552</width>
<height>295</height>
</size>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Cursor</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
<layout class="QFormLayout" name="formLayout_2">
<property name="horizontalSpacing">
<number>10</number>
</property>
<property name="verticalSpacing">
<number>10</number>
</property>
<property name="leftMargin">
<number>10</number>
</property>
<property name="topMargin">
<number>10</number>
</property>
<property name="rightMargin">
<number>10</number>
</property>
<property name="bottomMargin">
<number>10</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="textLabel1">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Cursor Shape:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="m_cmbCursorShape"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="textLabel1_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Outline Shape:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="m_cmbOutlineShape"/>
</item>
<item row="2" column="1">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>While painting...</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="QCheckBox" name="m_showOutlinePainting">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Show outline</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="m_changeBrushOutline">
<property name="text">
<string>Use effective outline size</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Cursor Color:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="KisColorButton" name="cursorColorBtutton">
<property name="maximumSize">
<size>
<width>48</width>
<height>25</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_3">
<attribute name="title">
<string>Window</string>
</attribute>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Multiple Document Mode:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="m_cmbMDIType">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>1</number>
</property>
<item>
<property name="text">
<string>Subwindows</string>
</property>
</item>
<item>
<property name="text">
<string>Tabs</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Background Image (overrides color):</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0">
<item>
<widget class="QLabel" name="m_backgroundimage">
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="m_bnFileName">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="clearBgImageButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Clear</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Window Background:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="KisColorButton" name="m_mdiColor">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>18</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="0">
<widget class="QLabel" name="lbl_window_general">
<property name="text">
<string>General:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="m_chkRubberBand">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Don't show contents when moving sub-windows</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="m_chkCanvasMessages">
<property name="text">
<string>Show on-canvas popup messages</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QCheckBox" name="m_chkHiDPI">
<property name="text">
<string>Enable Hi-DPI support</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QCheckBox" name="m_chkSingleApplication">
<property name="text">
<string>Allow only one instance of Krita</string>
</property>
</widget>
</item>
<item row="8" column="0" colspan="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="Tools">
<attribute name="title">
<string>Tools</string>
</attribute>
- <layout class="QGridLayout" name="gridLayout_4">
- <item row="1" column="1">
- <layout class="QVBoxLayout" name="verticalLayout">
- <property name="spacing">
- <number>10</number>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGroupBox" name="groupBox_4">
+ <property name="title">
+ <string>Tool Options Location (needs restart)</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QRadioButton" name="m_radioToolOptionsInDocker">
+ <property name="text">
+ <string>In Doc&amp;ker</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="m_radioToolOptionsInToolbar">
+ <property name="text">
+ <string>I&amp;n Toolbar</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QFormLayout" name="formLayout_4">
+ <item row="0" column="0">
+ <widget class="QLabel" name="lblFlowMode">
+ <property name="text">
+ <string>Brush Flow Mode (needs restart):</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="cmbFlowMode">
+ <item>
+ <property name="text">
+ <string>Creamy (Krita 4.2+)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Hard (Krita 4.1 and earlier versions)</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="m_chkSwitchSelectionCtrlAlt">
+ <property name="text">
+ <string>Switch Control/Alt Selection Modifiers</string>
</property>
- <property name="leftMargin">
- <number>10</number>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="chkEnableTouch">
+ <property name="text">
+ <string>Enable Touch Painting</string>
</property>
- <property name="topMargin">
- <number>10</number>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="chkEnableTranformToolAfterPaste">
+ <property name="text">
+ <string>Activate transform tool after pasting</string>
</property>
- <property name="rightMargin">
- <number>10</number>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="m_groupBoxKineticScrollingSettings">
+ <property name="title">
+ <string>Kinetic Scrolling (needs restart)</string>
</property>
- <property name="bottomMargin">
- <number>10</number>
+ <property name="checkable">
+ <bool>true</bool>
</property>
- <item>
- <widget class="QGroupBox" name="groupBox_4">
- <property name="title">
- <string>Tool Options Location (needs restart)</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayoutKineticScrollingSettings">
+ <item>
+ <widget class="QComboBox" name="m_cmbKineticScrollingGesture"/>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayoutKineticScrollingSensitivity">
<item>
- <widget class="QRadioButton" name="m_radioToolOptionsInDocker">
+ <widget class="QLabel" name="labelKineticScrollingSensitivity">
<property name="text">
- <string>In Doc&amp;ker</string>
+ <string>Sensitivity:</string>
</property>
</widget>
</item>
<item>
- <widget class="QRadioButton" name="m_radioToolOptionsInToolbar">
- <property name="text">
- <string>I&amp;n Toolbar</string>
- </property>
- <property name="checked">
- <bool>true</bool>
- </property>
- </widget>
+ <widget class="KisSliderSpinBox" name="m_kineticScrollingSensitivitySlider" native="true"/>
</item>
</layout>
- </widget>
- </item>
- <item>
- <widget class="QCheckBox" name="m_chkSwitchSelectionCtrlAlt">
- <property name="text">
- <string>Switch Control/Alt Selection Modifiers</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QCheckBox" name="chkEnableTouch">
- <property name="text">
- <string>Enable Touch Painting</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QCheckBox" name="chkEnableTranformToolAfterPaste">
- <property name="text">
- <string>Activate transform tool after pasting</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="m_groupBoxKineticScrollingSettings">
- <property name="title">
- <string>Kinetic Scrolling (needs restart)</string>
- </property>
- <property name="checkable">
- <bool>true</bool>
- </property>
- <property name="checked">
- <bool>true</bool>
- </property>
- <layout class="QVBoxLayout" name="verticalLayoutKineticScrollingSettings">
+ </item>
+ <item>
+ <widget class="QCheckBox" name="m_chkKineticScrollingHideScrollbars">
+ <property name="text">
+ <string>Hide Scrollbars</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>250</width>
+ <height>71</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_2">
+ <attribute name="title">
+ <string>File Handling</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <widget class="QGroupBox" name="m_autosaveCheckBox">
+ <property name="title">
+ <string>Enable Autosaving</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QFormLayout" name="formLayout_6">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>Autosave Interval:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="KisIntParseSpinBox" name="m_autosaveSpinBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>75</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="suffix">
+ <string comment="Suffix: “Every x min”"> min</string>
+ </property>
+ <property name="prefix">
+ <string comment="Prefix: “Every x min”">Every </string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>1440</number>
+ </property>
+ <property name="singleStep">
+ <number>5</number>
+ </property>
+ <property name="value">
+ <number>15</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="chkHideAutosaveFiles">
+ <property name="text">
+ <string>Unnamed autosave files are hidden by default</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="m_backupFileCheckBox">
+ <property name="title">
+ <string>Create a Backup File on Saving</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QFormLayout" name="formLayout_5">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_13">
+ <property name="text">
+ <string>Backup File Location</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="cmbBackupFileLocation">
<item>
- <widget class="QComboBox" name="m_cmbKineticScrollingGesture"/>
+ <property name="text">
+ <string>Same Folder as Original File</string>
+ </property>
</item>
<item>
- <layout class="QHBoxLayout" name="horizontalLayoutKineticScrollingSensitivity">
- <item>
- <widget class="QLabel" name="labelKineticScrollingSensitivity">
- <property name="text">
- <string>Sensitivity:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="KisSliderSpinBox" name="m_kineticScrollingSensitivitySlider" native="true"/>
- </item>
- </layout>
+ <property name="text">
+ <string>User Folder</string>
+ </property>
</item>
<item>
- <widget class="QCheckBox" name="m_chkKineticScrollingHideScrollbars">
- <property name="text">
- <string>Hide Scrollbars</string>
- </property>
- <property name="checked">
- <bool>false</bool>
- </property>
- </widget>
+ <property name="text">
+ <string>Temporary File Location</string>
+ </property>
</item>
- </layout>
- </widget>
- </item>
- <item>
- <spacer name="verticalSpacer_2">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>Backup File Suffix:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="txtBackupFileSuffix">
+ <property name="text">
+ <string>~</string>
+ </property>
+ <property name="maxLength">
+ <number>10</number>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>Number of Backup Files Kept:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QSpinBox" name="intNumBackupFiles">
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>1</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
</item>
- <item row="1" column="2">
- <spacer name="horizontalSpacer_6">
+ <item>
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="title">
+ <string>Kra File Compression</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="m_chkCompressKra">
+ <property name="text">
+ <string>Compress .kra files more (slows loading/saving)</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="chkZip64">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Only use this option for &lt;span style=&quot; font-weight:600;&quot;&gt;very&lt;/span&gt; large files: larger than 4 GiB on disk.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Use Zip64 (for very large files: cannot be opened in versions of Krita older than 4.2.0)</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_6">
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
- <width>40</width>
- <height>20</height>
+ <width>20</width>
+ <height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="Miscellaneous">
<attribute name="title">
<string>Miscellaneous</string>
</attribute>
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_12">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
- <string>When Krita starts</string>
+ <string>When Krita starts:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
- <widget class="QComboBox" name="cmbStartupSession"/>
- </item>
- <item row="1" column="1">
- <widget class="QCheckBox" name="chkSaveSessionOnQuit">
- <property name="text">
- <string>Save session when Krita closes</string>
- </property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QLabel" name="lbl_autosave">
- <property name="text">
- <string>Autosave:</string>
+ <widget class="QComboBox" name="cmbStartupSession">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
</property>
</widget>
</item>
- <item row="2" column="1">
- <layout class="QHBoxLayout" name="horizontalLayout_3">
- <item>
- <widget class="QCheckBox" name="m_autosaveCheckBox">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string comment="Checkbox after “Autosave:”">Enabled</string>
- </property>
- <property name="checked">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="KisIntParseSpinBox" name="m_autosaveSpinBox">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>75</width>
- <height>0</height>
- </size>
- </property>
- <property name="suffix">
- <string comment="Suffix: “Every x min”"> min</string>
- </property>
- <property name="prefix">
- <string comment="Prefix: “Every x min”">Every </string>
- </property>
- <property name="minimum">
- <number>1</number>
- </property>
- <property name="maximum">
- <number>1440</number>
- </property>
- <property name="singleStep">
- <number>5</number>
- </property>
- <property name="value">
- <number>15</number>
- </property>
- </widget>
- </item>
- </layout>
- </item>
<item row="3" column="1">
- <widget class="QCheckBox" name="m_chkCompressKra">
- <property name="text">
- <string>Compress .kra files more (slows loading/saving)</string>
- </property>
- </widget>
- </item>
- <item row="5" column="1">
- <widget class="QCheckBox" name="m_backupFileCheckBox">
- <property name="text">
- <string>Create backup file </string>
- </property>
- </widget>
- </item>
- <item row="7" column="1">
<widget class="QCheckBox" name="m_chkConvertOnImport">
<property name="text">
<string>On importing images as layers, convert to the image colorspace</string>
</property>
</widget>
</item>
- <item row="9" column="0">
+ <item row="5" column="0">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Undo stack size:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
- <item row="9" column="1">
+ <item row="5" column="1">
<widget class="KisIntParseSpinBox" name="m_undoStackSize">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>75</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="value">
<number>30</number>
</property>
</widget>
</item>
- <item row="10" column="0">
+ <item row="6" column="0">
<widget class="QLabel" name="label_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
- <string>Number of Palette Presets</string>
+ <string>Number of Palette Presets:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
- <item row="12" column="1">
+ <item row="6" column="1">
+ <widget class="KisIntParseSpinBox" name="m_favoritePresetsSpinBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>75</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="minimum">
+ <number>10</number>
+ </property>
+ <property name="maximum">
+ <number>30</number>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="1">
<widget class="QCheckBox" name="chkShowRootLayer">
<property name="text">
<string>Show root layer</string>
</property>
</widget>
</item>
- <item row="13" column="1">
+ <item row="9" column="1">
+ <widget class="QCheckBox" name="chkUsageLogging">
+ <property name="text">
+ <string>Enable Logging for bug reports</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="10" column="1">
<widget class="QCheckBox" name="m_chkNativeFileDialog">
<property name="toolTip">
<string>Warning: if you enable this setting and the file dialogs do weird stuff, do not report a bug.</string>
</property>
<property name="text">
<string>Enable native file dialogs (warning: may not work correctly on some systems)</string>
</property>
</widget>
</item>
- <item row="15" column="0">
+ <item row="12" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Maximum brush size:</string>
</property>
</widget>
</item>
- <item row="15" column="1">
+ <item row="12" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QSpinBox" name="intMaxBrushSize">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>The maximum diameter of a brush in pixels.</string>
</property>
<property name="suffix">
<string comment="pixel">px</string>
</property>
<property name="minimum">
<number>100</number>
</property>
<property name="maximum">
<number>10000</number>
</property>
<property name="value">
<number>1000</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_10">
<property name="text">
<string>(Needs restart)</string>
</property>
</widget>
</item>
</layout>
</item>
- <item row="16" column="1">
+ <item row="13" column="1">
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>504</width>
<height>13</height>
</size>
</property>
</spacer>
</item>
- <item row="10" column="1">
- <widget class="KisIntParseSpinBox" name="m_favoritePresetsSpinBox">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>75</width>
- <height>0</height>
- </size>
- </property>
- <property name="minimum">
- <number>10</number>
- </property>
- <property name="maximum">
- <number>30</number>
+ <item row="2" column="1">
+ <widget class="QCheckBox" name="chkSaveSessionOnQuit">
+ <property name="text">
+ <string>Save session when Krita closes</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KisIntParseSpinBox</class>
<extends>QSpinBox</extends>
<header>kis_int_parse_spin_box.h</header>
</customwidget>
<customwidget>
<class>KisColorButton</class>
<extends>QPushButton</extends>
<header>kis_color_button.h</header>
</customwidget>
<customwidget>
<class>KisSliderSpinBox</class>
<extends>QWidget</extends>
<header>kis_slider_spin_box.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
- <connections>
- <connection>
- <sender>m_autosaveCheckBox</sender>
- <signal>toggled(bool)</signal>
- <receiver>m_autosaveSpinBox</receiver>
- <slot>setEnabled(bool)</slot>
- <hints>
- <hint type="sourcelabel">
- <x>20</x>
- <y>20</y>
- </hint>
- <hint type="destinationlabel">
- <x>20</x>
- <y>20</y>
- </hint>
- </hints>
- </connection>
- </connections>
+ <connections/>
</ui>
diff --git a/libs/ui/forms/wdgnewimage.ui b/libs/ui/forms/wdgnewimage.ui
index 4aca85cf9b..8fb4311b32 100644
--- a/libs/ui/forms/wdgnewimage.ui
+++ b/libs/ui/forms/wdgnewimage.ui
@@ -1,663 +1,685 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WdgNewImage</class>
<widget class="QWidget" name="WdgNewImage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>600</width>
- <height>462</height>
+ <height>512</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>600</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="windowTitle">
<string>New Image</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<attribute name="title">
<string>Dimensions</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_3">
<item row="1" column="0">
<widget class="QGroupBox" name="grpMode">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Color</string>
</property>
<layout class="QVBoxLayout">
<item>
<widget class="KisColorSpaceSelector" name="colorSpaceSelector" native="true"/>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QGroupBox" name="grpImage">
<property name="minimumSize">
<size>
<width>0</width>
<height>140</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="title">
<string>Image Size</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="flat">
<bool>false</bool>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>P&amp;redefined:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>cmbPredefined</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cmbPredefined">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Save As:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="txtPredefinedName">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="bnSaveAsPredefined">
<property name="toolTip">
<string>Save the current dimensions</string>
</property>
<property name="text">
<string>&amp;Save</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<layout class="QHBoxLayout">
<item>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="3">
<widget class="QToolButton" name="bnLandscape">
<property name="toolTip">
<string>Landscape</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="KisDoubleParseSpinBox" name="doubleWidth">
<property name="decimals">
<number>2</number>
</property>
<property name="minimum">
<double>1.000000000000000</double>
</property>
<property name="maximum">
<double>100000000.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lblHeight">
<property name="text">
<string>&amp;Height:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>doubleHeight</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="KisDoubleParseSpinBox" name="doubleResolution">
<property name="decimals">
<number>0</number>
</property>
<property name="minimum">
<double>1.000000000000000</double>
</property>
<property name="maximum">
<double>9999.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lblResolution">
<property name="text">
<string>Resolution:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="KisDoubleParseSpinBox" name="doubleHeight">
<property name="minimum">
<double>1.000000000000000</double>
</property>
<property name="maximum">
<double>100000000.000000000000000</double>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QComboBox" name="cmbWidthUnit"/>
</item>
<item row="1" column="2">
<widget class="QComboBox" name="cmbHeightUnit"/>
</item>
<item row="2" column="2">
<widget class="QLabel" name="label">
<property name="toolTip">
<string>pixels-per-inch</string>
</property>
<property name="text">
<string>ppi</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="lblWidth">
<property name="text">
<string>W&amp;idth:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>doubleWidth</cstring>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QToolButton" name="bnPortrait">
<property name="toolTip">
<string>Portrait</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="orientationLayout"/>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>191</width>
<height>61</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="grpClipboard">
<property name="title">
<string>Clipboard</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="lblPreview">
<property name="maximumSize">
<size>
<width>75</width>
<height>75</height>
</size>
</property>
<property name="baseSize">
<size>
<width>250</width>
<height>250</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<spacer name="imageGroupSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<attribute name="title">
<string>Content</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="1">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Layers:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="lblOpacity">
<property name="text">
- <string>Ima&amp;ge Background Opacity:</string>
+ <string>Background Opacity:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>sliderOpacity</cstring>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="KisIntParseSpinBox" name="intNumLayers">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
+ <property name="toolTip">
+ <string>Number of layers that the image will start with, including optional background layer.</string>
+ </property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>200</number>
</property>
<property name="value">
<number>2</number>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="lblColor">
<property name="text">
- <string>Image Bac&amp;kground Color:</string>
+ <string>Bac&amp;kground Color:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>cmbColor</cstring>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="lblBackgroundStyle">
<property name="text">
<string>Background:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing</set>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="lblDescription">
<property name="text">
<string>&amp;Description:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing</set>
</property>
<property name="buddy">
<cstring>txtDescription</cstring>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QTextEdit" name="txtDescription">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>100</height>
</size>
</property>
</widget>
</item>
<item row="4" column="2">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
- <widget class="QRadioButton" name="radioBackgroundAsLayer">
+ <widget class="QRadioButton" name="radioBackgroundAsRaster">
+ <property name="toolTip">
+ <string>Use background color and opacity to create a background raster layer.</string>
+ </property>
<property name="text">
- <string>As fi&amp;rst layer</string>
+ <string>As &amp;raster layer</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioBackgroundAsProjection">
+ <property name="toolTip">
+ <string>Use background color and opacity as the base canvas color. This can be reconfigured in `Image &gt; Properties.`</string>
+ </property>
<property name="text">
<string>As can&amp;vas color</string>
</property>
</widget>
</item>
+ <item>
+ <widget class="QRadioButton" name="radioBackgroundAsFill">
+ <property name="toolTip">
+ <string>Use background color and opacity to create a background fill layer. The color for this layer can be reconfigured in the layer's properties.</string>
+ </property>
+ <property name="text">
+ <string>As fill la&amp;yer</string>
+ </property>
+ </widget>
+ </item>
</layout>
</item>
<item row="2" column="2">
<widget class="KisColorButton" name="cmbColor">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
- <string>Reset the image background color in the Image Properties dialog</string>
+ <string/>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QFrame" name="opacityPanel">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<layout class="QHBoxLayout" name="_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="KisDoubleSliderSpinBox" name="sliderOpacity" native="true"/>
</item>
</layout>
</widget>
</item>
<item row="6" column="2">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<widget class="QLabel" name="lblName">
<property name="text">
<string>&amp;Name:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>txtName</cstring>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="txtName">
+ <property name="toolTip">
+ <string/>
+ </property>
<property name="text">
<string>untitled-1</string>
</property>
</widget>
</item>
</layout>
<zorder>label_4</zorder>
<zorder>lblBackgroundStyle</zorder>
<zorder>txtDescription</zorder>
<zorder>lblDescription</zorder>
<zorder>intNumLayers</zorder>
<zorder>opacityPanel</zorder>
<zorder>lblColor</zorder>
<zorder>cmbColor</zorder>
<zorder>lblOpacity</zorder>
<zorder>lblName</zorder>
<zorder>txtName</zorder>
</widget>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="lblDocumentInfo">
<property name="text">
<string>This document...</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="newDialogConfirmationButtonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KisIntParseSpinBox</class>
<extends>QSpinBox</extends>
<header>kis_int_parse_spin_box.h</header>
</customwidget>
- <customwidget>
- <class>KisColorButton</class>
- <extends>QPushButton</extends>
- <header>kis_color_button.h</header>
- </customwidget>
<customwidget>
<class>KisDoubleSliderSpinBox</class>
<extends>QWidget</extends>
<header>kis_slider_spin_box.h</header>
<container>1</container>
</customwidget>
+ <customwidget>
+ <class>KisColorButton</class>
+ <extends>QPushButton</extends>
+ <header>kis_color_button.h</header>
+ </customwidget>
<customwidget>
<class>KisColorSpaceSelector</class>
<extends>QWidget</extends>
<header>widgets/kis_color_space_selector.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>KisDoubleParseSpinBox</class>
<extends>QDoubleSpinBox</extends>
<header>kis_double_parse_spin_box.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>tabWidget</tabstop>
<tabstop>cmbPredefined</tabstop>
<tabstop>txtPredefinedName</tabstop>
<tabstop>bnSaveAsPredefined</tabstop>
<tabstop>doubleWidth</tabstop>
<tabstop>doubleHeight</tabstop>
<tabstop>doubleResolution</tabstop>
<tabstop>cmbWidthUnit</tabstop>
<tabstop>cmbHeightUnit</tabstop>
<tabstop>bnLandscape</tabstop>
<tabstop>bnPortrait</tabstop>
<tabstop>intNumLayers</tabstop>
<tabstop>cmbColor</tabstop>
- <tabstop>radioBackgroundAsLayer</tabstop>
+ <tabstop>radioBackgroundAsRaster</tabstop>
<tabstop>radioBackgroundAsProjection</tabstop>
<tabstop>txtDescription</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>
diff --git a/libs/ui/input/config/kis_input_button.h b/libs/ui/input/config/kis_input_button.h
index b9ae279778..31d91b8583 100644
--- a/libs/ui/input/config/kis_input_button.h
+++ b/libs/ui/input/config/kis_input_button.h
@@ -1,143 +1,143 @@
/*
* This file is part of the KDE project
* Copyright (C) 2013 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* 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 KISINPUTBUTTON_H
#define KISINPUTBUTTON_H
#include <QPushButton>
#include "input/kis_shortcut_configuration.h"
/**
* \brief A button that can detect input and will store its value.
*
* This button, when pressed, will detect input based on what type has been set.
* It is mainly intended for shortcut configuration, that is, picking some input that is
* later reused for shortcuts or similar.
*
*/
class KisInputButton : public QPushButton
{
Q_OBJECT
public:
/**
* The type of button.
*/
enum ButtonType {
MouseType, ///< Detect and store any combination of pressed mouse buttons.
KeyType, ///< Detect and store any combination of key presses.
WheelType, ///< Detect and store mouse wheel movement.
};
/**
* Constructor.
*/
explicit KisInputButton(QWidget *parent = 0);
/**
* Destructor.
*/
~KisInputButton() override;
/**
* \return The type of input this button detects.
*/
ButtonType type() const;
/**
* Set the type of input this button should detect.
*
* \param newType The type of input to detect.
*/
void setType(ButtonType newType);
/**
* \return The list of keys that was detected. Only applicable when type is `KeyType`.
*/
QList<Qt::Key> keys() const;
/**
* Set the list of keys to display.
*
* This is mostly intended to make sure the button displays the right keys when viewed
* in a dialog or similar UI.
*
* Only applicable when type is `KeyType`.
*
* \param newKeys The list of keys to display.
*/
void setKeys(const QList<Qt::Key> &newKeys);
/**
* \return The mouse buttons that were detected. Only applicable when type is `MouseType`.
*/
Qt::MouseButtons buttons() const;
/**
* Set the mouse buttons to display.
*
* This is mostly intended to make sure the button displays the right buttons when viewed
* in a dialog or similar UI.
*
* Only applicable when type is `MouseType`.
*
* \param newButtons The mouse buttons to display.
*/
void setButtons(Qt::MouseButtons newButtons);
/**
* \return The mouse wheel movement that was detected. Only applicable when type is `WheelType`.
*/
KisShortcutConfiguration::MouseWheelMovement wheel() const;
/**
* Set the mouse wheel movement to display.
*
* This is mostly intended to make sure the button displays the right wheel movement when
* viewed in a dialog or similar UI.
*
* Only applicable when type is `WheelType`.
*
- * \param newButtons The wheel movement to display.
+ * \param wheel The wheel movement to display.
*/
void setWheel(KisShortcutConfiguration::MouseWheelMovement wheel);
public Q_SLOTS:
/**
* Clear all detected input and reset the button to an empty state.
*/
void clear();
Q_SIGNALS:
/**
* Emitted whenever one of the values (keys, buttons, wheel) changes.
*/
void dataChanged();
protected:
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *) override;
void wheelEvent(QWheelEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
void keyReleaseEvent(QKeyEvent *event) override;
private Q_SLOTS:
void reset();
private:
class Private;
Private *const d;
};
#endif // KISINPUTBUTTON_H
diff --git a/libs/ui/input/kis_input_manager.cpp b/libs/ui/input/kis_input_manager.cpp
index 1aa0e0bc29..9b255abecd 100644
--- a/libs/ui/input/kis_input_manager.cpp
+++ b/libs/ui/input/kis_input_manager.cpp
@@ -1,688 +1,693 @@
/* This file is part of the KDE project
*
* Copyright (C) 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
* Copyright (C) 2015 Michael Abrahams <miabraha@gmail.com>
*
* 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_input_manager.h"
#include <kis_debug.h>
#include <QQueue>
#include <klocalizedstring.h>
#include <QApplication>
#include <QTouchEvent>
#include <QElapsedTimer>
#include <KoToolManager.h>
#include "kis_tool_proxy.h"
#include <kis_config.h>
#include <kis_canvas2.h>
#include <KisViewManager.h>
#include <kis_image.h>
#include <kis_canvas_resource_provider.h>
#include <kis_favorite_resource_manager.h>
#include "kis_abstract_input_action.h"
#include "kis_tool_invocation_action.h"
#include "kis_pan_action.h"
#include "kis_alternate_invocation_action.h"
#include "kis_rotate_canvas_action.h"
#include "kis_zoom_action.h"
#include "kis_show_palette_action.h"
#include "kis_change_primary_setting_action.h"
#include "kis_shortcut_matcher.h"
#include "kis_stroke_shortcut.h"
#include "kis_single_action_shortcut.h"
#include "kis_touch_shortcut.h"
#include "kis_input_profile.h"
#include "kis_input_profile_manager.h"
#include "kis_shortcut_configuration.h"
#include <input/kis_tablet_debugger.h>
#include <kis_signal_compressor.h>
#include "kis_extended_modifiers_mapper.h"
#include "kis_input_manager_p.h"
template <typename T>
uint qHash(QPointer<T> value) {
return reinterpret_cast<quintptr>(value.data());
}
KisInputManager::KisInputManager(QObject *parent)
: QObject(parent), d(new Private(this))
{
d->setupActions();
connect(KoToolManager::instance(), SIGNAL(aboutToChangeTool(KoCanvasController*)), SLOT(slotAboutToChangeTool()));
connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*,int)), SLOT(slotToolChanged()));
connect(&d->moveEventCompressor, SIGNAL(timeout()), SLOT(slotCompressedMoveEvent()));
QApplication::instance()->
installEventFilter(new Private::ProximityNotifier(d, this));
}
KisInputManager::~KisInputManager()
{
delete d;
}
void KisInputManager::addTrackedCanvas(KisCanvas2 *canvas)
{
d->canvasSwitcher.addCanvas(canvas);
}
void KisInputManager::removeTrackedCanvas(KisCanvas2 *canvas)
{
d->canvasSwitcher.removeCanvas(canvas);
}
void KisInputManager::toggleTabletLogger()
{
KisTabletDebugger::instance()->toggleDebugging();
}
void KisInputManager::attachPriorityEventFilter(QObject *filter, int priority)
{
Private::PriorityList::iterator begin = d->priorityEventFilter.begin();
Private::PriorityList::iterator it = begin;
Private::PriorityList::iterator end = d->priorityEventFilter.end();
it = std::find_if(begin, end,
[filter] (const Private::PriorityPair &a) { return a.second == filter; });
if (it != end) return;
it = std::find_if(begin, end,
[priority] (const Private::PriorityPair &a) { return a.first > priority; });
d->priorityEventFilter.insert(it, qMakePair(priority, filter));
d->priorityEventFilterSeqNo++;
}
void KisInputManager::detachPriorityEventFilter(QObject *filter)
{
Private::PriorityList::iterator it = d->priorityEventFilter.begin();
Private::PriorityList::iterator end = d->priorityEventFilter.end();
it = std::find_if(it, end,
[filter] (const Private::PriorityPair &a) { return a.second == filter; });
if (it != end) {
d->priorityEventFilter.erase(it);
}
}
void KisInputManager::setupAsEventFilter(QObject *receiver)
{
if (d->eventsReceiver) {
d->eventsReceiver->removeEventFilter(this);
}
d->eventsReceiver = receiver;
if (d->eventsReceiver) {
d->eventsReceiver->installEventFilter(this);
}
}
void KisInputManager::stopIgnoringEvents()
{
d->allowMouseEvents();
}
#if defined (__clang__)
#pragma GCC diagnostic ignored "-Wswitch"
#endif
bool KisInputManager::eventFilter(QObject* object, QEvent* event)
{
if (object != d->eventsReceiver) return false;
if (d->eventEater.eventFilter(object, event)) return false;
if (!d->matcher.hasRunningShortcut()) {
int savedPriorityEventFilterSeqNo = d->priorityEventFilterSeqNo;
for (auto it = d->priorityEventFilter.begin(); it != d->priorityEventFilter.end(); /*noop*/) {
const QPointer<QObject> &filter = it->second;
if (filter.isNull()) {
it = d->priorityEventFilter.erase(it);
d->priorityEventFilterSeqNo++;
savedPriorityEventFilterSeqNo++;
continue;
}
if (filter->eventFilter(object, event)) return true;
/**
* If the filter removed itself from the filters list or
* added something there, just exit the loop
*/
if (d->priorityEventFilterSeqNo != savedPriorityEventFilterSeqNo) {
return true;
}
++it;
}
// KoToolProxy needs to pre-process some events to ensure the
// global shortcuts (not the input manager's ones) are not
// executed, in particular, this line will accept events when the
// tool is in text editing, preventing shortcut triggering
if (d->toolProxy) {
d->toolProxy->processEvent(event);
}
}
// Continue with the actual switch statement...
return eventFilterImpl(event);
}
template <class Event>
bool KisInputManager::compressMoveEventCommon(Event *event)
{
/**
* We construct a copy of this event object, so we must ensure it
* has a correct type.
*/
static_assert(std::is_same<Event, QMouseEvent>::value ||
std::is_same<Event, QTabletEvent>::value,
"event should be a mouse or a tablet event");
bool retval = false;
/**
* Compress the events if the tool doesn't need high resolution input
*/
+// See https://bugreports.qt.io/browse/QTBUG-72488
+#if QT_VERSION < QT_VERSION_CHECK(5, 11, 3)
if ((event->type() == QEvent::MouseMove ||
event->type() == QEvent::TabletMove) &&
(!d->matcher.supportsHiResInputEvents() ||
d->testingCompressBrushEvents)) {
d->compressedMoveEvent.reset(new Event(*event));
d->moveEventCompressor.start();
/**
* On Linux Qt eats the rest of unneeded events if we
* ignore the first of the chunk of tablet events. So
* generally we should never activate this feature. Only
* for testing purposes!
*/
if (d->testingAcceptCompressedTabletEvents) {
event->setAccepted(true);
}
retval = true;
} else {
+#endif
slotCompressedMoveEvent();
retval = d->handleCompressedTabletEvent(event);
+#if QT_VERSION < QT_VERSION_CHECK(5, 11, 3)
}
+#endif
return retval;
}
bool KisInputManager::eventFilterImpl(QEvent * event)
{
bool retval = false;
if (event->type() != QEvent::Wheel) {
d->accumulatedScrollDelta = 0;
}
switch (event->type()) {
case QEvent::MouseButtonPress:
case QEvent::MouseButtonDblClick: {
d->debugEvent<QMouseEvent, true>(event);
//Block mouse press events on Genius tablets
if (d->tabletActive) break;
if (d->ignoringQtCursorEvents()) break;
if (d->touchHasBlockedPressEvents) break;
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
if (d->tryHidePopupPalette()) {
retval = true;
} else {
//Make sure the input actions know we are active.
KisAbstractInputAction::setInputManager(this);
retval = d->matcher.buttonPressed(mouseEvent->button(), mouseEvent);
}
//Reset signal compressor to prevent processing events before press late
d->resetCompressor();
event->setAccepted(retval);
break;
}
case QEvent::MouseButtonRelease: {
d->debugEvent<QMouseEvent, true>(event);
if (d->ignoringQtCursorEvents()) break;
if (d->touchHasBlockedPressEvents) break;
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
retval = d->matcher.buttonReleased(mouseEvent->button(), mouseEvent);
event->setAccepted(retval);
break;
}
case QEvent::ShortcutOverride: {
d->debugEvent<QKeyEvent, false>(event);
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
Qt::Key key = KisExtendedModifiersMapper::workaroundShiftAltMetaHell(keyEvent);
if (!keyEvent->isAutoRepeat()) {
retval = d->matcher.keyPressed(key);
} else {
retval = d->matcher.autoRepeatedKeyPressed(key);
}
/**
* Workaround for temporary switching of tools by
* KoCanvasControllerWidget. We don't need this switch because
* we handle it ourselves.
*/
retval |= !d->forwardAllEventsToTool &&
(keyEvent->key() == Qt::Key_Space ||
keyEvent->key() == Qt::Key_Escape);
break;
}
case QEvent::KeyRelease: {
d->debugEvent<QKeyEvent, false>(event);
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
if (!keyEvent->isAutoRepeat()) {
Qt::Key key = KisExtendedModifiersMapper::workaroundShiftAltMetaHell(keyEvent);
retval = d->matcher.keyReleased(key);
}
break;
}
case QEvent::MouseMove: {
d->debugEvent<QMouseEvent, true>(event);
if (d->ignoringQtCursorEvents()) break;
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
retval = compressMoveEventCommon(mouseEvent);
break;
}
case QEvent::Wheel: {
d->debugEvent<QWheelEvent, false>(event);
QWheelEvent *wheelEvent = static_cast<QWheelEvent*>(event);
#ifdef Q_OS_OSX
// Some QT wheel events are actually touch pad pan events. From the QT docs:
// "Wheel events are generated for both mouse wheels and trackpad scroll gestures."
// We differentiate between touchpad events and real mouse wheels by inspecting the
// event source.
if (wheelEvent->source() == Qt::MouseEventSource::MouseEventSynthesizedBySystem) {
KisAbstractInputAction::setInputManager(this);
retval = d->matcher.wheelEvent(KisSingleActionShortcut::WheelTrackpad, wheelEvent);
break;
}
#endif
d->accumulatedScrollDelta += wheelEvent->delta();
KisSingleActionShortcut::WheelAction action;
/**
* Ignore delta 0 events on OSX, since they are triggered by tablet
* proximity when using Wacom devices.
*/
#ifdef Q_OS_OSX
if(wheelEvent->delta() == 0) {
retval = true;
break;
}
#endif
if (wheelEvent->orientation() == Qt::Horizontal) {
if(wheelEvent->delta() < 0) {
action = KisSingleActionShortcut::WheelRight;
}
else {
action = KisSingleActionShortcut::WheelLeft;
}
}
else {
if(wheelEvent->delta() > 0) {
action = KisSingleActionShortcut::WheelUp;
}
else {
action = KisSingleActionShortcut::WheelDown;
}
}
if (qAbs(d->accumulatedScrollDelta) >= QWheelEvent::DefaultDeltasPerStep) {
//Make sure the input actions know we are active.
KisAbstractInputAction::setInputManager(this);
retval = d->matcher.wheelEvent(action, wheelEvent);
d->accumulatedScrollDelta = 0;
}
else {
retval = true;
}
break;
}
case QEvent::Enter:
d->debugEvent<QEvent, false>(event);
//Make sure the input actions know we are active.
KisAbstractInputAction::setInputManager(this);
if (!d->containsPointer) {
d->containsPointer = true;
d->allowMouseEvents();
d->touchHasBlockedPressEvents = false;
}
d->matcher.enterEvent();
break;
case QEvent::Leave:
d->debugEvent<QEvent, false>(event);
d->containsPointer = false;
/**
* We won't get a TabletProximityLeave event when the tablet
* is hovering above some other widget, so restore cursor
* events processing right now.
*/
d->allowMouseEvents();
d->touchHasBlockedPressEvents = false;
d->matcher.leaveEvent();
break;
case QEvent::FocusIn:
d->debugEvent<QEvent, false>(event);
KisAbstractInputAction::setInputManager(this);
//Clear all state so we don't have half-matched shortcuts dangling around.
d->matcher.reinitialize();
{ // Emulate pressing of the key that are already pressed
KisExtendedModifiersMapper mapper;
Qt::KeyboardModifiers modifiers = mapper.queryStandardModifiers();
Q_FOREACH (Qt::Key key, mapper.queryExtendedModifiers()) {
QKeyEvent kevent(QEvent::ShortcutOverride, key, modifiers);
eventFilterImpl(&kevent);
}
}
d->allowMouseEvents();
break;
case QEvent::FocusOut: {
d->debugEvent<QEvent, false>(event);
KisAbstractInputAction::setInputManager(this);
QPointF currentLocalPos =
canvas()->canvasWidget()->mapFromGlobal(QCursor::pos());
d->matcher.lostFocusEvent(currentLocalPos);
break;
}
case QEvent::TabletPress: {
d->debugEvent<QTabletEvent, false>(event);
QTabletEvent *tabletEvent = static_cast<QTabletEvent*>(event);
if (d->tryHidePopupPalette()) {
retval = true;
} else {
//Make sure the input actions know we are active.
KisAbstractInputAction::setInputManager(this);
retval = d->matcher.buttonPressed(tabletEvent->button(), tabletEvent);
if (!d->containsPointer) {
d->containsPointer = true;
d->touchHasBlockedPressEvents = false;
}
}
event->setAccepted(true);
retval = true;
d->blockMouseEvents();
//Reset signal compressor to prevent processing events before press late
d->resetCompressor();
d->eatOneMousePress();
break;
}
case QEvent::TabletMove: {
d->debugEvent<QTabletEvent, false>(event);
QTabletEvent *tabletEvent = static_cast<QTabletEvent*>(event);
retval = compressMoveEventCommon(tabletEvent);
if (d->tabletLatencyTracker) {
d->tabletLatencyTracker->push(tabletEvent->timestamp());
}
/**
* The flow of tablet events means the tablet is in the
* proximity area, so activate it even when the
* TabletEnterProximity event was missed (may happen when
* changing focus of the window with tablet in the proximity
* area)
*/
d->blockMouseEvents();
break;
}
case QEvent::TabletRelease: {
#ifdef Q_OS_MAC
d->allowMouseEvents();
#endif
d->debugEvent<QTabletEvent, false>(event);
QTabletEvent *tabletEvent = static_cast<QTabletEvent*>(event);
retval = d->matcher.buttonReleased(tabletEvent->button(), tabletEvent);
retval = true;
event->setAccepted(true);
break;
}
case QEvent::TouchBegin:
{
if (startTouch(retval)) {
QTouchEvent *tevent = static_cast<QTouchEvent*>(event);
KisAbstractInputAction::setInputManager(this);
retval = d->matcher.touchBeginEvent(tevent);
event->accept();
}
break;
}
case QEvent::TouchUpdate:
{
QTouchEvent *tevent = static_cast<QTouchEvent*>(event);
#ifdef Q_OS_MAC
int count = 0;
Q_FOREACH (const QTouchEvent::TouchPoint &point, tevent->touchPoints()) {
if (point.state() != Qt::TouchPointReleased) {
count++;
}
}
if (count < 2 && tevent->touchPoints().length() > count) {
d->touchHasBlockedPressEvents = false;
retval = d->matcher.touchEndEvent(tevent);
} else {
#endif
d->touchHasBlockedPressEvents = KisConfig(true).disableTouchOnCanvas();
KisAbstractInputAction::setInputManager(this);
retval = d->matcher.touchUpdateEvent(tevent);
#ifdef Q_OS_OSX
}
#endif
event->accept();
break;
}
case QEvent::TouchEnd:
{
endTouch();
QTouchEvent *tevent = static_cast<QTouchEvent*>(event);
retval = d->matcher.touchEndEvent(tevent);
event->accept();
break;
}
case QEvent::NativeGesture:
{
QNativeGestureEvent *gevent = static_cast<QNativeGestureEvent*>(event);
switch (gevent->gestureType()) {
case Qt::BeginNativeGesture:
{
if (startTouch(retval)) {
KisAbstractInputAction::setInputManager(this);
retval = d->matcher.nativeGestureBeginEvent(gevent);
event->accept();
}
break;
}
case Qt::EndNativeGesture:
{
endTouch();
retval = d->matcher.nativeGestureEndEvent(gevent);
event->accept();
break;
}
default:
{
KisAbstractInputAction::setInputManager(this);
retval = d->matcher.nativeGestureEvent(gevent);
event->accept();
break;
}
}
break;
}
default:
break;
}
return !retval ? d->processUnhandledEvent(event) : true;
}
bool KisInputManager::startTouch(bool &retval)
{
d->touchHasBlockedPressEvents = KisConfig(true).disableTouchOnCanvas();
// Touch rejection: if touch is disabled on canvas, no need to block mouse press events
if (KisConfig(true).disableTouchOnCanvas()) {
d->eatOneMousePress();
}
if (d->tryHidePopupPalette()) {
retval = true;
return false;
} else {
return true;
}
}
void KisInputManager::endTouch()
{
d->touchHasBlockedPressEvents = false;
}
void KisInputManager::slotCompressedMoveEvent()
{
if (d->compressedMoveEvent) {
// d->touchHasBlockedPressEvents = false;
(void) d->handleCompressedTabletEvent(d->compressedMoveEvent.data());
d->compressedMoveEvent.reset();
//dbgInput << "Compressed move event received.";
} else {
//dbgInput << "Unexpected empty move event";
}
}
KisCanvas2* KisInputManager::canvas() const
{
return d->canvas;
}
QPointer<KisToolProxy> KisInputManager::toolProxy() const
{
return d->toolProxy;
}
void KisInputManager::slotAboutToChangeTool()
{
QPointF currentLocalPos;
if (canvas() && canvas()->canvasWidget()) {
currentLocalPos = canvas()->canvasWidget()->mapFromGlobal(QCursor::pos());
}
d->matcher.lostFocusEvent(currentLocalPos);
}
void KisInputManager::slotToolChanged()
{
if (!d->canvas) return;
KoToolManager *toolManager = KoToolManager::instance();
KoToolBase *tool = toolManager->toolById(canvas(), toolManager->activeToolId());
if (tool) {
d->setMaskSyntheticEvents(tool->maskSyntheticEvents());
if (tool->isInTextMode()) {
d->forwardAllEventsToTool = true;
d->matcher.suppressAllActions(true);
} else {
d->forwardAllEventsToTool = false;
d->matcher.suppressAllActions(false);
}
}
}
void KisInputManager::profileChanged()
{
d->matcher.clearShortcuts();
KisInputProfile *profile = KisInputProfileManager::instance()->currentProfile();
if (profile) {
const QList<KisShortcutConfiguration*> shortcuts = profile->allShortcuts();
for (KisShortcutConfiguration * const shortcut : shortcuts) {
dbgUI << "Adding shortcut" << shortcut->keys() << "for action" << shortcut->action()->name();
switch(shortcut->type()) {
case KisShortcutConfiguration::KeyCombinationType:
d->addKeyShortcut(shortcut->action(), shortcut->mode(), shortcut->keys());
break;
case KisShortcutConfiguration::MouseButtonType:
d->addStrokeShortcut(shortcut->action(), shortcut->mode(), shortcut->keys(), shortcut->buttons());
break;
case KisShortcutConfiguration::MouseWheelType:
d->addWheelShortcut(shortcut->action(), shortcut->mode(), shortcut->keys(), shortcut->wheel());
break;
case KisShortcutConfiguration::GestureType:
if (!d->addNativeGestureShortcut(shortcut->action(), shortcut->mode(), shortcut->gesture())) {
d->addTouchShortcut(shortcut->action(), shortcut->mode(), shortcut->gesture());
}
break;
default:
break;
}
}
}
else {
dbgInput << "No Input Profile Found: canvas interaction will be impossible";
}
}
diff --git a/libs/ui/input/wintab/kis_tablet_support_win.cpp b/libs/ui/input/wintab/kis_tablet_support_win.cpp
index 93d3504624..f977bdfa5e 100644
--- a/libs/ui/input/wintab/kis_tablet_support_win.cpp
+++ b/libs/ui/input/wintab/kis_tablet_support_win.cpp
@@ -1,990 +1,990 @@
/*
* Copyright (c) 2013 Digia Plc and/or its subsidiary(-ies).
* Copyright (c) 2013 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2013 Dmitry Kazakov <dimula73@gmail.com>
* Copyright (c) 2015 Michael Abrahams <miabraha@gmail.com>
* Copyright (c) 2015 The Qt Company Ltd.
*
* 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_tablet_support_win_p.h"
#include "kis_tablet_support_win.h"
#include <kis_debug.h>
#include <QApplication>
#include <QGuiApplication>
#include <QDesktopWidget>
// #include <qpa/qwindowsysteminterface.h>
// #include <qpa/qplatformscreen.h>
// #include <private/qguiapplication_p.h>
#include <QPointer>
#include <QScreen>
#include <QWidget>
#include <QLibrary>
#include <cmath>
#define Q_PI M_PI
#include <input/kis_extended_modifiers_mapper.h>
#include <input/kis_tablet_debugger.h>
// For "inline tool switches"
#include <KoToolManager.h>
#include <KoInputDevice.h>
#include "kis_screen_size_choice_dialog.h"
// NOTE: we stub out qwindowcontext.cpp::347 to disable Qt's own tablet support.
// Note: The definition of the PACKET structure in pktdef.h depends on this define.
#define PACKETDATA (PK_X | PK_Y | PK_BUTTONS | PK_TIME | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION | PK_CURSOR | PK_Z)
#include "pktdef.h"
QT_BEGIN_NAMESPACE
enum {
PacketMode = 0,
TabletPacketQSize = 128,
DeviceIdMask = 0xFF6, // device type mask && device color mask
CursorTypeBitMask = 0x0F06 // bitmask to find the specific cursor type (see Wacom FAQ)
};
/*
*
* Krita extensions begin here
*
*
*/
QWindowsTabletSupport *QTAB = 0;
static QPointer<QWidget> targetWindow = 0; //< Window receiving last tablet event
static QPointer<QWidget> qt_tablet_target = 0; //< Widget receiving last tablet event
static bool dialogOpen = false; //< KisTabletSupportWin is not a Q_OBJECT and can't accept dialog signals
HWND createDummyWindow(const QString &className, const wchar_t *windowName, WNDPROC wndProc)
{
if (!wndProc)
wndProc = DefWindowProc;
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = wndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = (HINSTANCE)GetModuleHandle(0);
wc.hCursor = 0;
wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
wc.hIcon = 0;
wc.hIconSm = 0;
wc.lpszMenuName = 0;
wc.lpszClassName = (wchar_t*)className.utf16();
ATOM atom = RegisterClassEx(&wc);
if (!atom)
qErrnoWarning("Registering tablet fake window class failed.");
return CreateWindowEx(0, (wchar_t*)className.utf16(),
windowName, WS_OVERLAPPED,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
HWND_MESSAGE, NULL, (HINSTANCE)GetModuleHandle(0), NULL);
}
void printContext(const LOGCONTEXT &lc)
{
dbgTablet << "# Getting current context data:";
dbgTablet << ppVar(lc.lcName);
dbgTablet << ppVar(lc.lcDevice);
dbgTablet << ppVar(lc.lcInOrgX);
dbgTablet << ppVar(lc.lcInOrgY);
dbgTablet << ppVar(lc.lcInExtX);
dbgTablet << ppVar(lc.lcInExtY);
dbgTablet << ppVar(lc.lcOutOrgX);
dbgTablet << ppVar(lc.lcOutOrgY);
dbgTablet << ppVar(lc.lcOutExtX);
dbgTablet << ppVar(lc.lcOutExtY);
dbgTablet << ppVar(lc.lcSysOrgX);
dbgTablet << ppVar(lc.lcSysOrgY);
dbgTablet << ppVar(lc.lcSysExtX);
dbgTablet << ppVar(lc.lcSysExtY);
dbgTablet << "Qt Desktop Geometry" << QApplication::desktop()->geometry();
}
static QRect mapToNative(const QRect &qRect, int m_factor)
{
return QRect(qRect.x() * m_factor, qRect.y() * m_factor, qRect.width() * m_factor, qRect.height() * m_factor);
}
static inline QEvent::Type mouseEventType(QEvent::Type t)
{
return (t == QEvent::TabletMove ? QEvent::MouseMove :
t == QEvent::TabletPress ? QEvent::MouseButtonPress :
t == QEvent::TabletRelease ? QEvent::MouseButtonRelease :
QEvent::None);
}
static inline bool isMouseEventType(QEvent::Type t)
{
return (t == QEvent::MouseMove ||
t == QEvent::MouseButtonPress ||
t == QEvent::MouseButtonRelease);
}
static QPoint mousePosition()
{
POINT p;
GetCursorPos(&p);
return QPoint(p.x, p.y);
}
QWindowsWinTab32DLL QWindowsTabletSupport::m_winTab32DLL;
bool KisTabletSupportWin::init()
{
if (!QWindowsTabletSupport::m_winTab32DLL.init()) {
qWarning() << "Failed to initialize Wintab";
return false;
}
QTAB = QWindowsTabletSupport::create();
// Refresh tablet context after tablet rotated, screen added, etc.
QObject::connect(qApp->primaryScreen(), &QScreen::geometryChanged,
[=](const QRect & geometry){
delete QTAB;
QTAB = QWindowsTabletSupport::create();
});
return true;
}
// Derived from qwidgetwindow.
//
// The work done by processTabletEvent from qguiapplicationprivate is divided
// between here and translateTabletPacketEvent.
static void handleTabletEvent(QWidget *windowWidget, const QPointF &local, const QPointF &global,
int device, int pointerType, Qt::MouseButton button, Qt::MouseButtons buttons,
qreal pressure,int xTilt, int yTilt, qreal tangentialPressure, qreal rotation,
int z, qint64 uniqueId, Qt::KeyboardModifiers modifiers, QEvent::Type type, LONG time)
{
// Lock in target window
if (type == QEvent::TabletPress) {
targetWindow = windowWidget;
dbgInput << "Locking target window" << targetWindow;
} else if ((type == QEvent::TabletRelease || buttons == Qt::NoButton) && (targetWindow != 0)) {
dbgInput << "Releasing target window" << targetWindow;
targetWindow = 0;
}
if (!windowWidget) // Should never happen
return;
// We do this instead of constructing the event e beforehand
const QPoint localPos = local.toPoint();
const QPoint globalPos = global.toPoint();
if (type == QEvent::TabletPress) {
QWidget *widget = windowWidget->childAt(localPos);
if (!widget)
widget = windowWidget;
qt_tablet_target = widget;
}
QWidget *finalDestination = qt_tablet_target;
if (!finalDestination) {
finalDestination = windowWidget->childAt(localPos);
}
if ((type == QEvent::TabletRelease || buttons == Qt::NoButton) && (qt_tablet_target != 0)) {
dbgInput << "releasing tablet target" << qt_tablet_target;
qt_tablet_target = 0;
}
if (finalDestination) {
// The event was specified relative to windowWidget, so we remap it
QPointF delta = global - globalPos;
QPointF mapped = finalDestination->mapFromGlobal(global.toPoint()) + delta;
QTabletEvent ev(type, mapped, global, device, pointerType, pressure, xTilt, yTilt,
tangentialPressure, rotation, z, modifiers, uniqueId, button, buttons);
ev.setTimestamp(time);
QGuiApplication::sendEvent(finalDestination, &ev);
if (ev.isAccepted()) {
// dbgTablet << "Tablet event" << type << "accepted" << "by target widget" << finalDestination;
}
else {
// Turn off eventEater send a synthetic mouse event.
// dbgTablet << "Tablet event" << type << "rejected; sending mouse event to" << finalDestination;
qt_tablet_target = 0;
// We shouldn't ever get a widget accepting a tablet event from this
// call, so we won't worry about any interactions with our own
// widget-locking code.
// QWindow *target = platformScreen->topLevelAt(globalPos);
// if (!target) return;
// QPointF windowLocal = global - QPointF(target->mapFromGlobal(QPoint())) + delta;
// QWindowSystemInterface::handleTabletEvent(target, ev.timestamp(), windowLocal,
// global, device, pointerType,
// buttons, pressure, xTilt, yTilt,
// tangentialPressure, rotation, z,
// uniqueId, modifiers);
}
} else {
qt_tablet_target = 0;
targetWindow = 0;
}
}
/**
* This is a default implementation of a class for converting the
* WinTab value of the buttons pressed to the Qt buttons. This class
* may be substituted from the UI.
*/
struct DefaultButtonsConverter
{
void convert(DWORD btnOld, DWORD btnNew,
Qt::MouseButton *button,
Qt::MouseButtons *buttons,
const QWindowsTabletDeviceData &tdd) {
int pressedButtonValue = btnNew ^ btnOld;
*button = buttonValueToEnum(pressedButtonValue, tdd);
*buttons = Qt::NoButton;
for (int i = 0; i < 3; i++) {
int btn = 0x1 << i;
if (btn & btnNew) {
Qt::MouseButton convertedButton =
buttonValueToEnum(btn, tdd);
*buttons |= convertedButton;
/**
* If a button that is present in hardware input is
* mapped to a Qt::NoButton, it means that it is going
* to be eaten by the driver, for example by its
* "Pan/Scroll" feature. Therefore we shouldn't handle
* any of the events associated to it. So just return
* Qt::NoButton here.
*/
if (convertedButton == Qt::NoButton) {
/**
* Sometimes the driver-handled shortcuts are just
* keyboard modifiers, so ideally we should handle
* them as well. The problem is that we cannot
* know if the shortcut was a pan/zoom action or a
* shortcut. So here we use a "hackish" approach.
* We just check if any modifier has been pressed
* and, if so, pass the button to Krita. Of
* course, if the driver uses some really complex
* shortcuts like "Shift + stylus btn" to generate
* some recorded shortcut, it will not work. But I
* guess it will be ok for the most of the
* usecases.
*
* WARNING: this hack will *not* work if you bind
* any non-modifier key to the stylus
* button, e.g. Space.
*/
const Qt::KeyboardModifiers keyboardModifiers = QApplication::queryKeyboardModifiers();
if (KisTabletDebugger::instance()->shouldEatDriverShortcuts() ||
keyboardModifiers == Qt::NoModifier) {
*button = Qt::NoButton;
*buttons = Qt::NoButton;
break;
}
}
}
}
}
private:
Qt::MouseButton buttonValueToEnum(DWORD button,
const QWindowsTabletDeviceData &tdd) {
const int leftButtonValue = 0x1;
const int middleButtonValue = 0x2;
const int rightButtonValue = 0x4;
const int doubleClickButtonValue = 0x7;
button = tdd.buttonsMap.value(button);
return button == leftButtonValue ? Qt::LeftButton :
button == rightButtonValue ? Qt::RightButton :
button == doubleClickButtonValue ? Qt::MiddleButton :
button == middleButtonValue ? Qt::MiddleButton :
button ? Qt::LeftButton /* fallback item */ :
Qt::NoButton;
}
};
static DefaultButtonsConverter *globalButtonsConverter =
new DefaultButtonsConverter();
/*
*
* Krita extensions end here
*
*
*/
// This is the WndProc for a single additional hidden window used to collect tablet events.
extern "C" LRESULT QT_WIN_CALLBACK qWindowsTabletSupportWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WT_PROXIMITY:
if (QTAB->translateTabletProximityEvent(wParam, lParam))
return 0;
break;
case WT_PACKET:
if (QTAB->translateTabletPacketEvent())
return 0;
break;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
// Scale tablet coordinates to screen coordinates.
static inline int sign(int x)
{
return x >= 0 ? 1 : -1;
}
inline QPointF QWindowsTabletDeviceData::scaleCoordinates(int coordX, int coordY, const QRect &targetArea) const
{
const int targetX = targetArea.x();
const int targetY = targetArea.y();
const int targetWidth = targetArea.width();
const int targetHeight = targetArea.height();
const qreal x = sign(targetWidth) == sign(maxX) ?
((coordX - minX) * qAbs(targetWidth) / qAbs(qreal(maxX - minX))) + targetX :
((qAbs(maxX) - (coordX - minX)) * qAbs(targetWidth) / qAbs(qreal(maxX - minX))) + targetX;
const qreal y = sign(targetHeight) == sign(maxY) ?
((coordY - minY) * qAbs(targetHeight) / qAbs(qreal(maxY - minY))) + targetY :
((qAbs(maxY) - (coordY - minY)) * qAbs(targetHeight) / qAbs(qreal(maxY - minY))) + targetY;
return QPointF(x, y);
}
/*!
- \class QWindowsWinTab32DLL QWindowsTabletSupport
+ \class QWindowsWinTab32DLL
\brief Functions from wintabl32.dll shipped with WACOM tablets used by QWindowsTabletSupport.
\internal
\ingroup qt-lighthouse-win
*/
bool QWindowsWinTab32DLL::init()
{
if (wTInfo)
return true;
QLibrary library(QStringLiteral("wintab32"));
if (!library.load()) {
qWarning() << QString("Could not load wintab32 dll: %1").arg(library.errorString());
return false;
}
wTOpen = (PtrWTOpen) library.resolve("WTOpenW");
wTClose = (PtrWTClose) library.resolve("WTClose");
wTInfo = (PtrWTInfo) library.resolve("WTInfoW");
wTEnable = (PtrWTEnable) library.resolve("WTEnable");
wTOverlap = (PtrWTOverlap) library.resolve("WTOverlap");
wTPacketsGet = (PtrWTPacketsGet) library.resolve("WTPacketsGet");
wTGet = (PtrWTGet) library.resolve("WTGetW");
wTQueueSizeGet = (PtrWTQueueSizeGet) library.resolve("WTQueueSizeGet");
wTQueueSizeSet = (PtrWTQueueSizeSet) library.resolve("WTQueueSizeSet");
if (wTOpen && wTClose && wTInfo && wTEnable && wTOverlap && wTPacketsGet && wTQueueSizeGet && wTQueueSizeSet) {
return true;
}
qWarning() << "Could not resolve the following symbols:\n"
<< "\t wTOpen" << wTOpen << "\n"
<< "\t wtClose" << wTClose << "\n"
<< "\t wtInfo" << wTInfo << "\n"
<< "\t wTEnable" << wTEnable << "\n"
<< "\t wTOverlap" << wTOverlap << "\n"
<< "\t wTPacketsGet" << wTPacketsGet << "\n"
<< "\t wTQueueSizeGet" << wTQueueSizeGet << "\n"
<< "\t wTQueueSizeSet" << wTQueueSizeSet << "\n";
return false;
}
/*!
\class QWindowsTabletSupport
\brief Tablet support for Windows.
Support for WACOM tablets.
\sa http://www.wacomeng.com/windows/docs/Wintab_v140.htm
\internal
\since 5.2
\ingroup qt-lighthouse-win
*/
QWindowsTabletSupport::QWindowsTabletSupport(HWND window, HCTX context)
: m_window(window)
, m_context(context)
, m_absoluteRange(20)
, m_tiltSupport(false)
, m_currentDevice(-1)
{
AXIS orientation[3];
// Some tablets don't support tilt, check if it is possible,
if (QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEVICES, DVC_ORIENTATION, &orientation))
m_tiltSupport = orientation[0].axResolution && orientation[1].axResolution;
}
QWindowsTabletSupport::~QWindowsTabletSupport()
{
QWindowsTabletSupport::m_winTab32DLL.wTClose(m_context);
DestroyWindow(m_window);
}
QWindowsTabletSupport *QWindowsTabletSupport::create()
{
const HWND window = createDummyWindow(QStringLiteral("TabletDummyWindow"),
L"TabletDummyWindow",
qWindowsTabletSupportWndProc);
LOGCONTEXT lcMine;
// build our context from the default context
QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEFSYSCTX, 0, &lcMine);
// Go for the raw coordinates, the tablet event will return good stuff. The
// defaults for lcOut rectangle are the desktop dimensions in pixels, which
// means Wintab will do lossy rounding. Instead we specify this trivial
// scaling for the output context, then do the scaling ourselves later to
// obtain higher resolution coordinates.
lcMine.lcOptions |= CXO_MESSAGES | CXO_CSRMESSAGES;
lcMine.lcPktData = lcMine.lcMoveMask = PACKETDATA;
lcMine.lcPktMode = PacketMode;
lcMine.lcOutOrgX = 0;
lcMine.lcOutExtX = lcMine.lcInExtX;
lcMine.lcOutOrgY = 0;
lcMine.lcOutExtY = -lcMine.lcInExtY;
const HCTX context = QWindowsTabletSupport::m_winTab32DLL.wTOpen(window, &lcMine, true);
if (!context) {
dbgTablet << __FUNCTION__ << "Unable to open tablet.";
DestroyWindow(window);
return 0;
}
// Set the size of the Packet Queue to the correct size
const int currentQueueSize = QWindowsTabletSupport::m_winTab32DLL.wTQueueSizeGet(context);
if (currentQueueSize != TabletPacketQSize) {
if (!QWindowsTabletSupport::m_winTab32DLL.wTQueueSizeSet(context, TabletPacketQSize)) {
if (!QWindowsTabletSupport::m_winTab32DLL.wTQueueSizeSet(context, currentQueueSize)) {
qWarning() << "Unable to set queue size on tablet. The tablet will not work.";
QWindowsTabletSupport::m_winTab32DLL.wTClose(context);
DestroyWindow(window);
return 0;
} // cannot restore old size
} // cannot set
} // mismatch
dbgTablet << "Opened tablet context " << context << " on window "
<< window << "changed packet queue size " << currentQueueSize
<< "->" << TabletPacketQSize;
return new QWindowsTabletSupport(window, context);
}
unsigned QWindowsTabletSupport::options() const
{
UINT result = 0;
m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_CTXOPTIONS, &result);
return result;
}
QString QWindowsTabletSupport::description() const
{
const unsigned size = m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_WINTABID, 0);
if (!size)
return QString();
QVarLengthArray<TCHAR> winTabId(size + 1);
m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_WINTABID, winTabId.data());
WORD implementationVersion = 0;
m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_IMPLVERSION, &implementationVersion);
WORD specificationVersion = 0;
m_winTab32DLL.wTInfo(WTI_INTERFACE, IFC_SPECVERSION, &specificationVersion);
const unsigned opts = options();
QString result = QString::fromLatin1("%1 specification: v%2.%3 implementation: v%4.%5 options: 0x%6")
.arg(QString::fromWCharArray(winTabId.data()))
.arg(specificationVersion >> 8).arg(specificationVersion & 0xFF)
.arg(implementationVersion >> 8).arg(implementationVersion & 0xFF)
.arg(opts, 0, 16);
if (opts & CXO_MESSAGES)
result += QStringLiteral(" CXO_MESSAGES");
if (opts & CXO_CSRMESSAGES)
result += QStringLiteral(" CXO_CSRMESSAGES");
if (m_tiltSupport)
result += QStringLiteral(" tilt");
return result;
}
void QWindowsTabletSupport::notifyActivate()
{
// Cooperate with other tablet applications, but when we get focus, I want to use the tablet.
const bool result = QWindowsTabletSupport::m_winTab32DLL.wTEnable(m_context, true)
&& QWindowsTabletSupport::m_winTab32DLL.wTOverlap(m_context, true);
dbgTablet << __FUNCTION__ << result;
}
static inline int indexOfDevice(const QVector<QWindowsTabletDeviceData> &devices, qint64 uniqueId)
{
for (int i = 0; i < devices.size(); ++i)
if (devices.at(i).uniqueId == uniqueId)
return i;
return -1;
}
static inline QTabletEvent::TabletDevice deviceType(const UINT cursorType)
{
if (((cursorType & 0x0006) == 0x0002) && ((cursorType & CursorTypeBitMask) != 0x0902))
return QTabletEvent::Stylus;
if (cursorType == 0x4020) // Surface Pro 2 tablet device
return QTabletEvent::Stylus;
switch (cursorType & CursorTypeBitMask) {
case 0x0802:
return QTabletEvent::Stylus;
case 0x0902:
return QTabletEvent::Airbrush;
case 0x0004:
return QTabletEvent::FourDMouse;
case 0x0006:
return QTabletEvent::Puck;
case 0x0804:
return QTabletEvent::RotationStylus;
default:
break;
}
return QTabletEvent::NoDevice;
};
static inline QTabletEvent::PointerType pointerType(unsigned pkCursor)
{
switch (pkCursor % 3) { // %3 for dual track
case 0:
return QTabletEvent::Cursor;
case 1:
return QTabletEvent::Pen;
case 2:
return QTabletEvent::Eraser;
default:
break;
}
return QTabletEvent::UnknownPointer;
}
QDebug operator<<(QDebug d, const QWindowsTabletDeviceData &t)
{
d << "TabletDevice id:" << t.uniqueId << " pressure: " << t.minPressure
<< ".." << t.maxPressure << " tan pressure: " << t.minTanPressure << ".."
<< t.maxTanPressure << " area:" << t.minX << t.minY << t.minZ
<< ".." << t.maxX << t.maxY << t.maxZ << " device " << t.currentDevice
<< " pointer " << t.currentPointerType;
return d;
}
QWindowsTabletDeviceData QWindowsTabletSupport::tabletInit(const quint64 uniqueId, const UINT cursorType) const
{
QWindowsTabletDeviceData result;
result.uniqueId = uniqueId;
/* browse WinTab's many info items to discover pressure handling. */
AXIS axis;
LOGCONTEXT lc;
/* get the current context for its device variable. */
QWindowsTabletSupport::m_winTab32DLL.wTGet(m_context, &lc);
if (KisTabletDebugger::instance()->initializationDebugEnabled()) {
printContext(lc);
}
/* get the size of the pressure axis. */
QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEVICES + lc.lcDevice, DVC_NPRESSURE, &axis);
result.minPressure = int(axis.axMin);
result.maxPressure = int(axis.axMax);
QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEVICES + lc.lcDevice, DVC_TPRESSURE, &axis);
result.minTanPressure = int(axis.axMin);
result.maxTanPressure = int(axis.axMax);
result.minX = int(lc.lcOutOrgX);
result.maxX = int(lc.lcOutExtX) + int(lc.lcOutOrgX);
result.minY = int(lc.lcOutOrgY);
result.maxY = -int(lc.lcOutExtY) + int(lc.lcOutOrgY);
// These are set to 0 when we opened the tablet context in QWindowsTabletSupport::create
KIS_SAFE_ASSERT_RECOVER_NOOP(lc.lcOutOrgX == 0);
KIS_SAFE_ASSERT_RECOVER_NOOP(lc.lcOutOrgY == 0);
result.maxZ = int(lc.lcOutExtZ) - int(lc.lcOutOrgZ);
result.currentDevice = deviceType(cursorType);
// Define a rectangle representing the whole screen as seen by Wintab.
QRect qtDesktopRect = QApplication::desktop()->geometry();
QRect wintabDesktopRect(lc.lcSysOrgX, lc.lcSysOrgY,
lc.lcSysExtX, lc.lcSysExtY);
qDebug() << ppVar(qtDesktopRect);
qDebug() << ppVar(wintabDesktopRect);
// Show screen choice dialog
if (!dialogOpen) {
KisScreenSizeChoiceDialog dlg(0,
wintabDesktopRect,
qtDesktopRect);
KisExtendedModifiersMapper mapper;
KisExtendedModifiersMapper::ExtendedModifiers modifiers =
mapper.queryExtendedModifiers();
if (modifiers.contains(Qt::Key_Shift) ||
(!dlg.canUseDefaultSettings() &&
qtDesktopRect != wintabDesktopRect)) {
dialogOpen = true;
dlg.exec();
}
result.virtualDesktopArea = dlg.screenRect();
dialogOpen = false;
} else {
// This branch should've been explicitly prevented.
KIS_SAFE_ASSERT_RECOVER_NOOP(!dialogOpen);
warnTablet << "Trying to init a WinTab device while screen resolution dialog is active, this should not happen!";
warnTablet << "Tablet coordinates could be wrong as a result.";
result.virtualDesktopArea = qtDesktopRect;
}
return result;
};
bool QWindowsTabletSupport::translateTabletProximityEvent(WPARAM /* wParam */, LPARAM lParam)
{
if (dialogOpen) {
// tabletInit(...) may show the screen resolution dialog and is blocking.
// During this period, don't process any tablet events at all.
dbgTablet << "WinTab screen resolution dialog is active, ignoring WinTab proximity event";
return false;
}
auto sendProximityEvent = [&](QEvent::Type type) {
QPointF emptyPos;
qreal zero = 0.0;
QTabletEvent e(type, emptyPos, emptyPos, 0, m_devices.at(m_currentDevice).currentPointerType,
zero, 0, 0, zero, zero, 0, Qt::NoModifier,
m_devices.at(m_currentDevice).uniqueId, Qt::NoButton, (Qt::MouseButtons)0);
qApp->sendEvent(qApp, &e);
};
if (!LOWORD(lParam)) {
// dbgTablet << "leave proximity for device #" << m_currentDevice;
sendProximityEvent(QEvent::TabletLeaveProximity);
return true;
}
PACKET proximityBuffer[1]; // we are only interested in the first packet in this case
const int totalPacks = QWindowsTabletSupport::m_winTab32DLL.wTPacketsGet(m_context, 1, proximityBuffer);
if (!totalPacks)
return false;
UINT pkCursor = proximityBuffer[0].pkCursor;
// initializing and updating the cursor should be done in response to
// WT_CSRCHANGE. We do it in WT_PROXIMITY because some wintab never send
// the event WT_CSRCHANGE even if asked with CXO_CSRMESSAGES
tabletUpdateCursor(pkCursor);
// dbgTablet << "enter proximity for device #" << m_currentDevice << m_devices.at(m_currentDevice);
sendProximityEvent(QEvent::TabletEnterProximity);
return true;
}
bool QWindowsTabletSupport::translateTabletPacketEvent()
{
static PACKET localPacketBuf[TabletPacketQSize]; // our own tablet packet queue.
const int packetCount = QWindowsTabletSupport::m_winTab32DLL.wTPacketsGet(m_context, TabletPacketQSize, &localPacketBuf);
if (!packetCount || m_currentDevice < 0 || dialogOpen)
return false;
// In contrast to Qt, these will not be "const" during our loop.
// This is because the Surface Pro 3 may cause us to switch devices.
QWindowsTabletDeviceData tabletData = m_devices.at(m_currentDevice);
int currentDevice = tabletData.currentDevice;
int currentPointerType = tabletData.currentPointerType;
// static Qt::MouseButtons buttons = Qt::NoButton, btnOld, btnChange;
static DWORD btnNew, btnOld, btnChange;
// The tablet can be used in 2 different modes, depending on its settings:
// 1) Absolute (pen) mode:
// The coordinates are scaled to the virtual desktop (by default). The user
// can also choose to scale to the monitor or a region of the screen.
// When entering proximity, the tablet driver snaps the mouse pointer to the
// tablet position scaled to that area and keeps it in sync.
// 2) Relative (mouse) mode:
// The pen follows the mouse. The constant 'absoluteRange' specifies the
// manhattanLength difference for detecting if a tablet input device is in this mode,
// in which case we snap the position to the mouse position.
// It seems there is no way to find out the mode programmatically, the LOGCONTEXT orgX/Y/Ext
// area is always the virtual desktop.
static qreal dpr = 1.0;
auto activeWindow = qApp->activeWindow();
if (activeWindow) {
dpr = activeWindow->devicePixelRatio();
}
const Qt::KeyboardModifiers keyboardModifiers = QApplication::queryKeyboardModifiers();
for (int i = 0; i < packetCount; ++i) {
const PACKET &packet = localPacketBuf[i];
btnOld = btnNew;
btnNew = localPacketBuf[i].pkButtons;
btnChange = btnOld ^ btnNew;
bool buttonPressed = btnChange && btnNew > btnOld;
bool buttonReleased = btnChange && btnNew < btnOld;
bool anyButtonsStillPressed = btnNew;
Qt::MouseButton button = Qt::NoButton;
Qt::MouseButtons buttons;
globalButtonsConverter->convert(btnOld, btnNew, &button, &buttons, tabletData);
QEvent::Type type = QEvent::TabletMove;
if (buttonPressed && button != Qt::NoButton) {
type = QEvent::TabletPress;
} else if (buttonReleased && button != Qt::NoButton) {
type = QEvent::TabletRelease;
}
const int z = currentDevice == QTabletEvent::FourDMouse ? int(packet.pkZ) : 0;
// NOTE: we shouldn't postpone the tablet events like Qt does, because we
// don't support mouse mode (which was the reason for introducing this
// postponing). See bug 363284.
QPointF globalPosF =
tabletData.scaleCoordinates(packet.pkX, packet.pkY,
tabletData.virtualDesktopArea);
globalPosF /= dpr; // Convert from "native" to "device independent pixels."
QPoint globalPos = globalPosF.toPoint();
// Find top-level window
QWidget *w = targetWindow; // If we had a target already, use it.
if (!w) {
w = qApp->activePopupWidget();
if (!w) w = qApp->activeModalWidget();
if (!w) w = qApp->topLevelAt(globalPos);
if (!w) continue;
w = w->window();
}
const QPoint localPos = w->mapFromGlobal(globalPos);
const QPointF delta = globalPosF - globalPos;
const QPointF localPosF = globalPosF + QPointF(localPos - globalPos) + delta;
const qreal pressureNew = packet.pkButtons && (currentPointerType == QTabletEvent::Pen || currentPointerType == QTabletEvent::Eraser) ?
m_devices.at(m_currentDevice).scalePressure(packet.pkNormalPressure) :
qreal(0);
const qreal tangentialPressure = currentDevice == QTabletEvent::Airbrush ?
m_devices.at(m_currentDevice).scaleTangentialPressure(packet.pkTangentPressure) :
qreal(0);
int tiltX = 0;
int tiltY = 0;
qreal rotation = 0;
if (m_tiltSupport) {
// Convert from azimuth and altitude to x tilt and y tilt. What
// follows is the optimized version. Here are the equations used:
// X = sin(azimuth) * cos(altitude)
// Y = cos(azimuth) * cos(altitude)
// Z = sin(altitude)
// X Tilt = arctan(X / Z)
// Y Tilt = arctan(Y / Z)
const double radAzim = (packet.pkOrientation.orAzimuth / 10.0) * (M_PI / 180);
const double tanAlt = std::tan((std::abs(packet.pkOrientation.orAltitude / 10.0)) * (M_PI / 180));
const double degX = std::atan(std::sin(radAzim) / tanAlt);
const double degY = std::atan(std::cos(radAzim) / tanAlt);
tiltX = int(degX * (180 / M_PI));
tiltY = int(-degY * (180 / M_PI));
rotation = 360.0 - (packet.pkOrientation.orTwist / 10.0);
if (rotation > 180.0)
rotation -= 360.0;
}
// This is adds *a lot* of noise to the output log
if (false) {
dbgTablet
<< "Packet #" << (i+1) << '/' << packetCount << "button:" << packet.pkButtons
<< globalPosF << z << "to:" << w << localPos << "(packet" << packet.pkX
<< packet.pkY << ") dev:" << currentDevice << "pointer:"
<< currentPointerType << "P:" << pressureNew << "tilt:" << tiltX << ','
<< tiltY << "tanP:" << tangentialPressure << "rotation:" << rotation;
}
// Reusable helper function. Better than compiler macros!
auto sendTabletEvent = [&](QTabletEvent::Type t){
handleTabletEvent(w, localPosF, globalPosF, currentDevice, currentPointerType,
button, buttons, pressureNew, tiltX, tiltY, tangentialPressure, rotation, z,
m_devices.at(m_currentDevice).uniqueId, keyboardModifiers, t, packet.pkTime);
};
/**
* Workaround to deal with "inline" tool switches.
* These are caused by the eraser trigger button on the Surface Pro 3.
* We shoot out a tabletUpdateCursor request and a switchInputDevice request.
*/
if (isSurfacePro3 && (packet.pkCursor != currentPkCursor)) {
dbgTablet << "Got an inline tool switch.";
// Send tablet release event.
sendTabletEvent(QTabletEvent::TabletRelease);
// Read the new cursor info.
UINT pkCursor = packet.pkCursor;
tabletUpdateCursor(pkCursor);
// Update the local loop variables.
tabletData = m_devices.at(m_currentDevice);
currentDevice = deviceType(tabletData.currentDevice);
currentPointerType = pointerType(pkCursor);
sendTabletEvent(QTabletEvent::TabletPress);
}
sendTabletEvent(type);
} // Loop over packets
return true;
}
void QWindowsTabletSupport::tabletUpdateCursor(const int pkCursor)
{
UINT physicalCursorId;
QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_CURSORS + pkCursor, CSR_PHYSID, &physicalCursorId);
UINT cursorType;
QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_CURSORS + pkCursor, CSR_TYPE, &cursorType);
const qint64 uniqueId = (qint64(cursorType & DeviceIdMask) << 32L) | qint64(physicalCursorId);
m_currentDevice = indexOfDevice(m_devices, uniqueId);
if (m_currentDevice < 0) {
m_currentDevice = m_devices.size();
m_devices.push_back(tabletInit(uniqueId, cursorType));
// Note: ideally we might check this button map for changes every
// update. However there seems to be an issue with Wintab altering the
// button map when the user right-clicks in Krita while another
// application has focus. This forces Krita to load button settings only
// once, when the tablet is first detected.
//
// See https://bugs.kde.org/show_bug.cgi?id=359561
BYTE logicalButtons[32];
memset(logicalButtons, 0, 32);
m_winTab32DLL.wTInfo(WTI_CURSORS + pkCursor, CSR_SYSBTNMAP, &logicalButtons);
m_devices[m_currentDevice].buttonsMap[0x1] = logicalButtons[0];
m_devices[m_currentDevice].buttonsMap[0x2] = logicalButtons[1];
m_devices[m_currentDevice].buttonsMap[0x4] = logicalButtons[2];
}
m_devices[m_currentDevice].currentPointerType = pointerType(pkCursor);
currentPkCursor = pkCursor;
// Check tablet name to enable Surface Pro 3 workaround.
#ifdef UNICODE
if (!isSurfacePro3) {
/**
* Some really "nice" tablet drivers don't know that they are
* supposed to return their name length when the buffer is
* null and they try to write into it effectively causing a
* suicide. So we cannot rely on it :(
*
* We workaround it by just allocating a big array and hoping
* for the best.
*
* Failing tablets:
* - Adesso Cybertablet M14
* - Peritab-302
* - Trust Tablet TB7300
* - VisTablet Realm Pro
* - Aiptek 14000u (latest driver: v5.03, 2013-10-21)
* - Genius G-Pen F350
* - Genius G-Pen 560 (supported on win7 only)
*/
// we cannot use the correct api :(
// UINT nameLength = QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEVICES, DVC_NAME, 0);
// 1024 chars should be enough for everyone! (c)
UINT nameLength = 1024;
TCHAR* dvcName = new TCHAR[nameLength + 1];
memset(dvcName, 0, sizeof(TCHAR) * nameLength);
UINT writtenBytes = QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_DEVICES, DVC_NAME, dvcName);
if (writtenBytes > sizeof(TCHAR) * nameLength) {
qWarning() << "WINTAB WARNING: tablet name is too long!" << writtenBytes;
// avoid crash when trying to read it
dvcName[nameLength - 1] = (TCHAR)0;
}
QString qDvcName = QString::fromWCharArray((const wchar_t*)dvcName);
dbgInput << "DVC_NAME =" << qDvcName;
// Name changed between older and newer Surface Pro 3 drivers
if (qDvcName == QString::fromLatin1("N-trig DuoSense device") ||
qDvcName == QString::fromLatin1("Microsoft device")) {
dbgInput << "This looks like a Surface Pro 3. Enabling eraser workaround.";
isSurfacePro3 = true;
}
delete[] dvcName;
}
#endif
}
QT_END_NAMESPACE
diff --git a/libs/ui/kis_categorized_item_delegate.h b/libs/ui/kis_categorized_item_delegate.h
index 13acfcd4d4..0268b27cf2 100644
--- a/libs/ui/kis_categorized_item_delegate.h
+++ b/libs/ui/kis_categorized_item_delegate.h
@@ -1,43 +1,43 @@
/*
* Copyright (c) 2009 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2011 Silvio Heinrich <plassy@web.de>
* Copyright (c) 2014 Mohit Goyal <mohit.bits2011@gmail.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _KIS_CATEGORIZED_ITEM_DELEGATE_H_
#define _KIS_CATEGORIZED_ITEM_DELEGATE_H_
#include <kritaui_export.h>
#include <QIcon>
#include <QStyledItemDelegate>
/**
- * This delegate draw categories using information from a \ref KCategorizedSortFilterProxyModel .
+ * This delegate draw categories using information from a QSortFilterProxyModel.
*/
class KRITAUI_EXPORT KisCategorizedItemDelegate: public QStyledItemDelegate
{
public:
KisCategorizedItemDelegate(QObject *parent);
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
private:
void paintTriangle(QPainter* painter, qint32 x, qint32 y, qint32 size, bool rotate) const;
mutable qint32 m_minimumItemHeight;
};
#endif // _KIS_CATEGORIZED_ITEM_DELEGATE_H_
diff --git a/libs/ui/kis_config.cc b/libs/ui/kis_config.cc
index 1c6223d4b0..5198ceecb9 100644
--- a/libs/ui/kis_config.cc
+++ b/libs/ui/kis_config.cc
@@ -1,2085 +1,2095 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
*
* 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_config.h"
#include <limits.h>
#include <QtGlobal>
#include <QApplication>
#include <QDesktopWidget>
#include <QMutex>
#include <QFont>
#include <QThread>
#include <QStringList>
#include <QSettings>
#include <QStandardPaths>
#include <kconfig.h>
#include <KisDocument.h>
#include <KoColor.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorModelStandardIds.h>
#include <KoColorProfile.h>
#include <kis_debug.h>
#include <kis_types.h>
#include "kis_canvas_resource_provider.h"
#include "kis_config_notifier.h"
#include "kis_snap_config.h"
#include <config-ocio.h>
#include <kis_color_manager.h>
#include <KisOcioConfiguration.h>
KisConfig::KisConfig(bool readOnly)
: m_cfg( KSharedConfig::openConfig()->group(""))
, m_readOnly(readOnly)
{
if (!readOnly) {
KIS_SAFE_ASSERT_RECOVER_RETURN(qApp->thread() == QThread::currentThread());
}
}
KisConfig::~KisConfig()
{
if (m_readOnly) return;
if (qApp->thread() != QThread::currentThread()) {
dbgKrita << "WARNING: KisConfig: requested config synchronization from nonGUI thread! Called from:" << kisBacktrace();
return;
}
m_cfg.sync();
}
bool KisConfig::disableTouchOnCanvas(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("disableTouchOnCanvas", false));
}
void KisConfig::setDisableTouchOnCanvas(bool value) const
{
m_cfg.writeEntry("disableTouchOnCanvas", value);
}
bool KisConfig::useProjections(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("useProjections", true));
}
void KisConfig::setUseProjections(bool useProj) const
{
m_cfg.writeEntry("useProjections", useProj);
}
bool KisConfig::undoEnabled(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("undoEnabled", true));
}
void KisConfig::setUndoEnabled(bool undo) const
{
m_cfg.writeEntry("undoEnabled", undo);
}
int KisConfig::undoStackLimit(bool defaultValue) const
{
return (defaultValue ? 30 : m_cfg.readEntry("undoStackLimit", 30));
}
void KisConfig::setUndoStackLimit(int limit) const
{
m_cfg.writeEntry("undoStackLimit", limit);
}
bool KisConfig::useCumulativeUndoRedo(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("useCumulativeUndoRedo",false));
}
void KisConfig::setCumulativeUndoRedo(bool value)
{
m_cfg.writeEntry("useCumulativeUndoRedo", value);
}
qreal KisConfig::stackT1(bool defaultValue) const
{
return (defaultValue ? 5 : m_cfg.readEntry("stackT1",5));
}
void KisConfig::setStackT1(int T1)
{
m_cfg.writeEntry("stackT1", T1);
}
qreal KisConfig::stackT2(bool defaultValue) const
{
return (defaultValue ? 1 : m_cfg.readEntry("stackT2",1));
}
void KisConfig::setStackT2(int T2)
{
m_cfg.writeEntry("stackT2", T2);
}
int KisConfig::stackN(bool defaultValue) const
{
return (defaultValue ? 5 : m_cfg.readEntry("stackN",5));
}
void KisConfig::setStackN(int N)
{
m_cfg.writeEntry("stackN", N);
}
qint32 KisConfig::defImageWidth(bool defaultValue) const
{
return (defaultValue ? 1600 : m_cfg.readEntry("imageWidthDef", 1600));
}
qint32 KisConfig::defImageHeight(bool defaultValue) const
{
return (defaultValue ? 1200 : m_cfg.readEntry("imageHeightDef", 1200));
}
qreal KisConfig::defImageResolution(bool defaultValue) const
{
return (defaultValue ? 100.0 : m_cfg.readEntry("imageResolutionDef", 100.0)) / 72.0;
}
QString KisConfig::defColorModel(bool defaultValue) const
{
return (defaultValue ? KoColorSpaceRegistry::instance()->rgb8()->colorModelId().id()
: m_cfg.readEntry("colorModelDef", KoColorSpaceRegistry::instance()->rgb8()->colorModelId().id()));
}
void KisConfig::defColorModel(const QString & model) const
{
m_cfg.writeEntry("colorModelDef", model);
}
QString KisConfig::defaultColorDepth(bool defaultValue) const
{
return (defaultValue ? KoColorSpaceRegistry::instance()->rgb8()->colorDepthId().id()
: m_cfg.readEntry("colorDepthDef", KoColorSpaceRegistry::instance()->rgb8()->colorDepthId().id()));
}
void KisConfig::setDefaultColorDepth(const QString & depth) const
{
m_cfg.writeEntry("colorDepthDef", depth);
}
QString KisConfig::defColorProfile(bool defaultValue) const
{
return (defaultValue ? KoColorSpaceRegistry::instance()->rgb8()->profile()->name() :
m_cfg.readEntry("colorProfileDef",
KoColorSpaceRegistry::instance()->rgb8()->profile()->name()));
}
void KisConfig::defColorProfile(const QString & profile) const
{
m_cfg.writeEntry("colorProfileDef", profile);
}
void KisConfig::defImageWidth(qint32 width) const
{
m_cfg.writeEntry("imageWidthDef", width);
}
void KisConfig::defImageHeight(qint32 height) const
{
m_cfg.writeEntry("imageHeightDef", height);
}
void KisConfig::defImageResolution(qreal res) const
{
m_cfg.writeEntry("imageResolutionDef", res*72.0);
}
int KisConfig::preferredVectorImportResolutionPPI(bool defaultValue) const
{
return defaultValue ? 100.0 : m_cfg.readEntry("preferredVectorImportResolution", 100.0);
}
void KisConfig::setPreferredVectorImportResolutionPPI(int value) const
{
m_cfg.writeEntry("preferredVectorImportResolution", value);
}
void cleanOldCursorStyleKeys(KConfigGroup &cfg)
{
if (cfg.hasKey("newCursorStyle") &&
cfg.hasKey("newOutlineStyle")) {
cfg.deleteEntry("cursorStyleDef");
}
}
CursorStyle KisConfig::newCursorStyle(bool defaultValue) const
{
if (defaultValue) {
return CURSOR_STYLE_NO_CURSOR;
}
int style = m_cfg.readEntry("newCursorStyle", int(-1));
if (style < 0) {
// old style format
style = m_cfg.readEntry("cursorStyleDef", int(OLD_CURSOR_STYLE_OUTLINE));
switch (style) {
case OLD_CURSOR_STYLE_TOOLICON:
style = CURSOR_STYLE_TOOLICON;
break;
case OLD_CURSOR_STYLE_CROSSHAIR:
case OLD_CURSOR_STYLE_OUTLINE_CENTER_CROSS:
style = CURSOR_STYLE_CROSSHAIR;
break;
case OLD_CURSOR_STYLE_POINTER:
style = CURSOR_STYLE_POINTER;
break;
case OLD_CURSOR_STYLE_OUTLINE:
case OLD_CURSOR_STYLE_NO_CURSOR:
style = CURSOR_STYLE_NO_CURSOR;
break;
case OLD_CURSOR_STYLE_SMALL_ROUND:
case OLD_CURSOR_STYLE_OUTLINE_CENTER_DOT:
style = CURSOR_STYLE_SMALL_ROUND;
break;
case OLD_CURSOR_STYLE_TRIANGLE_RIGHTHANDED:
case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_RIGHTHANDED:
style = CURSOR_STYLE_TRIANGLE_RIGHTHANDED;
break;
case OLD_CURSOR_STYLE_TRIANGLE_LEFTHANDED:
case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_LEFTHANDED:
style = CURSOR_STYLE_TRIANGLE_LEFTHANDED;
break;
default:
style = -1;
}
}
cleanOldCursorStyleKeys(m_cfg);
// compatibility with future versions
if (style < 0 || style >= N_CURSOR_STYLE_SIZE) {
style = CURSOR_STYLE_NO_CURSOR;
}
return (CursorStyle) style;
}
void KisConfig::setNewCursorStyle(CursorStyle style)
{
m_cfg.writeEntry("newCursorStyle", (int)style);
}
QColor KisConfig::getCursorMainColor(bool defaultValue) const
{
QColor col;
col.setRgbF(0.501961, 1.0, 0.501961);
return (defaultValue ? col : m_cfg.readEntry("cursorMaincColor", col));
}
void KisConfig::setCursorMainColor(const QColor &v) const
{
m_cfg.writeEntry("cursorMaincColor", v);
}
OutlineStyle KisConfig::newOutlineStyle(bool defaultValue) const
{
if (defaultValue) {
return OUTLINE_FULL;
}
int style = m_cfg.readEntry("newOutlineStyle", int(-1));
if (style < 0) {
// old style format
style = m_cfg.readEntry("cursorStyleDef", int(OLD_CURSOR_STYLE_OUTLINE));
switch (style) {
case OLD_CURSOR_STYLE_TOOLICON:
case OLD_CURSOR_STYLE_CROSSHAIR:
case OLD_CURSOR_STYLE_POINTER:
case OLD_CURSOR_STYLE_NO_CURSOR:
case OLD_CURSOR_STYLE_SMALL_ROUND:
case OLD_CURSOR_STYLE_TRIANGLE_RIGHTHANDED:
case OLD_CURSOR_STYLE_TRIANGLE_LEFTHANDED:
style = OUTLINE_NONE;
break;
case OLD_CURSOR_STYLE_OUTLINE:
case OLD_CURSOR_STYLE_OUTLINE_CENTER_DOT:
case OLD_CURSOR_STYLE_OUTLINE_CENTER_CROSS:
case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_RIGHTHANDED:
case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_LEFTHANDED:
style = OUTLINE_FULL;
break;
default:
style = -1;
}
}
cleanOldCursorStyleKeys(m_cfg);
// compatibility with future versions
if (style < 0 || style >= N_OUTLINE_STYLE_SIZE) {
style = OUTLINE_FULL;
}
return (OutlineStyle) style;
}
void KisConfig::setNewOutlineStyle(OutlineStyle style)
{
m_cfg.writeEntry("newOutlineStyle", (int)style);
}
QRect KisConfig::colorPreviewRect() const
{
return m_cfg.readEntry("colorPreviewRect", QVariant(QRect(32, 32, 48, 48))).toRect();
}
void KisConfig::setColorPreviewRect(const QRect &rect)
{
m_cfg.writeEntry("colorPreviewRect", QVariant(rect));
}
bool KisConfig::useDirtyPresets(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("useDirtyPresets",false));
}
void KisConfig::setUseDirtyPresets(bool value)
{
m_cfg.writeEntry("useDirtyPresets",value);
KisConfigNotifier::instance()->notifyConfigChanged();
}
bool KisConfig::useEraserBrushSize(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("useEraserBrushSize",false));
}
void KisConfig::setUseEraserBrushSize(bool value)
{
m_cfg.writeEntry("useEraserBrushSize",value);
KisConfigNotifier::instance()->notifyConfigChanged();
}
bool KisConfig::useEraserBrushOpacity(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("useEraserBrushOpacity",false));
}
void KisConfig::setUseEraserBrushOpacity(bool value)
{
m_cfg.writeEntry("useEraserBrushOpacity",value);
KisConfigNotifier::instance()->notifyConfigChanged();
}
QColor KisConfig::getMDIBackgroundColor(bool defaultValue) const
{
QColor col(77, 77, 77);
return (defaultValue ? col : m_cfg.readEntry("mdiBackgroundColor", col));
}
void KisConfig::setMDIBackgroundColor(const QColor &v) const
{
m_cfg.writeEntry("mdiBackgroundColor", v);
}
QString KisConfig::getMDIBackgroundImage(bool defaultValue) const
{
return (defaultValue ? "" : m_cfg.readEntry("mdiBackgroundImage", ""));
}
void KisConfig::setMDIBackgroundImage(const QString &filename) const
{
m_cfg.writeEntry("mdiBackgroundImage", filename);
}
QString KisConfig::monitorProfile(int screen) const
{
// Note: keep this in sync with the default profile for the RGB colorspaces!
QString profile = m_cfg.readEntry("monitorProfile" + QString(screen == 0 ? "": QString("_%1").arg(screen)), "sRGB-elle-V2-srgbtrc.icc");
//dbgKrita << "KisConfig::monitorProfile()" << profile;
return profile;
}
QString KisConfig::monitorForScreen(int screen, const QString &defaultMonitor, bool defaultValue) const
{
return (defaultValue ? defaultMonitor
: m_cfg.readEntry(QString("monitor_for_screen_%1").arg(screen), defaultMonitor));
}
void KisConfig::setMonitorForScreen(int screen, const QString& monitor)
{
m_cfg.writeEntry(QString("monitor_for_screen_%1").arg(screen), monitor);
}
void KisConfig::setMonitorProfile(int screen, const QString & monitorProfile, bool override) const
{
m_cfg.writeEntry("monitorProfile/OverrideX11", override);
m_cfg.writeEntry("monitorProfile" + QString(screen == 0 ? "": QString("_%1").arg(screen)), monitorProfile);
}
const KoColorProfile *KisConfig::getScreenProfile(int screen)
{
if (screen < 0) return 0;
KisConfig cfg(true);
QString monitorId;
if (KisColorManager::instance()->devices().size() > screen) {
monitorId = cfg.monitorForScreen(screen, KisColorManager::instance()->devices()[screen]);
}
//dbgKrita << "getScreenProfile(). Screen" << screen << "monitor id" << monitorId;
if (monitorId.isEmpty()) {
return 0;
}
QByteArray bytes = KisColorManager::instance()->displayProfile(monitorId);
//dbgKrita << "\tgetScreenProfile()" << bytes.size();
if (bytes.length() > 0) {
const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(RGBAColorModelID.id(), Integer8BitsColorDepthID.id(), bytes);
//dbgKrita << "\tKisConfig::getScreenProfile for screen" << screen << profile->name();
return profile;
}
else {
//dbgKrita << "\tCould not get a system monitor profile";
return 0;
}
}
const KoColorProfile *KisConfig::displayProfile(int screen) const
{
if (screen < 0) return 0;
// if the user plays with the settings, they can override the display profile, in which case
// we don't want the system setting.
bool override = useSystemMonitorProfile();
//dbgKrita << "KisConfig::displayProfile(). Override X11:" << override;
const KoColorProfile *profile = 0;
if (override) {
//dbgKrita << "\tGoing to get the screen profile";
profile = KisConfig::getScreenProfile(screen);
}
// if it fails. check the configuration
if (!profile || !profile->isSuitableForDisplay()) {
//dbgKrita << "\tGoing to get the monitor profile";
QString monitorProfileName = monitorProfile(screen);
//dbgKrita << "\t\tmonitorProfileName:" << monitorProfileName;
if (!monitorProfileName.isEmpty()) {
profile = KoColorSpaceRegistry::instance()->profileByName(monitorProfileName);
}
if (profile) {
//dbgKrita << "\t\tsuitable for display" << profile->isSuitableForDisplay();
}
else {
//dbgKrita << "\t\tstill no profile";
}
}
// if we still don't have a profile, or the profile isn't suitable for display,
// we need to get a last-resort profile. the built-in sRGB is a good choice then.
if (!profile || !profile->isSuitableForDisplay()) {
//dbgKrita << "\tnothing worked, going to get sRGB built-in";
profile = KoColorSpaceRegistry::instance()->profileByName("sRGB Built-in");
}
if (profile) {
//dbgKrita << "\tKisConfig::displayProfile for screen" << screen << "is" << profile->name();
}
else {
//dbgKrita << "\tCouldn't get a display profile at all";
}
return profile;
}
QString KisConfig::workingColorSpace(bool defaultValue) const
{
return (defaultValue ? "RGBA" : m_cfg.readEntry("workingColorSpace", "RGBA"));
}
void KisConfig::setWorkingColorSpace(const QString & workingColorSpace) const
{
m_cfg.writeEntry("workingColorSpace", workingColorSpace);
}
QString KisConfig::printerColorSpace(bool /*defaultValue*/) const
{
//TODO currently only rgb8 is supported
//return (defaultValue ? "RGBA" : m_cfg.readEntry("printerColorSpace", "RGBA"));
return QString("RGBA");
}
void KisConfig::setPrinterColorSpace(const QString & printerColorSpace) const
{
m_cfg.writeEntry("printerColorSpace", printerColorSpace);
}
QString KisConfig::printerProfile(bool defaultValue) const
{
return (defaultValue ? "" : m_cfg.readEntry("printerProfile", ""));
}
void KisConfig::setPrinterProfile(const QString & printerProfile) const
{
m_cfg.writeEntry("printerProfile", printerProfile);
}
bool KisConfig::useBlackPointCompensation(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("useBlackPointCompensation", true));
}
void KisConfig::setUseBlackPointCompensation(bool useBlackPointCompensation) const
{
m_cfg.writeEntry("useBlackPointCompensation", useBlackPointCompensation);
}
bool KisConfig::allowLCMSOptimization(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("allowLCMSOptimization", true));
}
void KisConfig::setAllowLCMSOptimization(bool allowLCMSOptimization)
{
m_cfg.writeEntry("allowLCMSOptimization", allowLCMSOptimization);
}
bool KisConfig::forcePaletteColors(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("colorsettings/forcepalettecolors", false));
}
void KisConfig::setForcePaletteColors(bool forcePaletteColors)
{
m_cfg.writeEntry("colorsettings/forcepalettecolors", forcePaletteColors);
}
bool KisConfig::showRulers(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("showrulers", false));
}
void KisConfig::setShowRulers(bool rulers) const
{
m_cfg.writeEntry("showrulers", rulers);
}
bool KisConfig::forceShowSaveMessages(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("forceShowSaveMessages", false));
}
void KisConfig::setForceShowSaveMessages(bool value) const
{
m_cfg.writeEntry("forceShowSaveMessages", value);
}
bool KisConfig::forceShowAutosaveMessages(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("forceShowAutosaveMessages", false));
}
void KisConfig::setForceShowAutosaveMessages(bool value) const
{
m_cfg.writeEntry("forceShowAutosaveMessages", value);
}
bool KisConfig::rulersTrackMouse(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("rulersTrackMouse", true));
}
void KisConfig::setRulersTrackMouse(bool value) const
{
m_cfg.writeEntry("rulersTrackMouse", value);
}
qint32 KisConfig::pasteBehaviour(bool defaultValue) const
{
return (defaultValue ? 2 : m_cfg.readEntry("pasteBehaviour", 2));
}
void KisConfig::setPasteBehaviour(qint32 renderIntent) const
{
m_cfg.writeEntry("pasteBehaviour", renderIntent);
}
qint32 KisConfig::monitorRenderIntent(bool defaultValue) const
{
qint32 intent = m_cfg.readEntry("renderIntent", INTENT_PERCEPTUAL);
if (intent > 3) intent = 3;
if (intent < 0) intent = 0;
return (defaultValue ? INTENT_PERCEPTUAL : intent);
}
void KisConfig::setRenderIntent(qint32 renderIntent) const
{
if (renderIntent > 3) renderIntent = 3;
if (renderIntent < 0) renderIntent = 0;
m_cfg.writeEntry("renderIntent", renderIntent);
}
bool KisConfig::useOpenGL(bool defaultValue) const
{
if (defaultValue) {
return true;
}
//dbgKrita << "use opengl" << m_cfg.readEntry("useOpenGL", true) << "success" << m_cfg.readEntry("canvasState", "OPENGL_SUCCESS");
QString cs = canvasState();
#ifdef Q_OS_WIN
return (m_cfg.readEntry("useOpenGLWindows", true) && (cs == "OPENGL_SUCCESS" || cs == "TRY_OPENGL"));
#else
return (m_cfg.readEntry("useOpenGL", true) && (cs == "OPENGL_SUCCESS" || cs == "TRY_OPENGL"));
#endif
}
void KisConfig::setUseOpenGL(bool useOpenGL) const
{
#ifdef Q_OS_WIN
m_cfg.writeEntry("useOpenGLWindows", useOpenGL);
#else
m_cfg.writeEntry("useOpenGL", useOpenGL);
#endif
}
int KisConfig::openGLFilteringMode(bool defaultValue) const
{
return (defaultValue ? 3 : m_cfg.readEntry("OpenGLFilterMode", 3));
}
void KisConfig::setOpenGLFilteringMode(int filteringMode)
{
m_cfg.writeEntry("OpenGLFilterMode", filteringMode);
}
bool KisConfig::useOpenGLTextureBuffer(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("useOpenGLTextureBuffer", true));
}
void KisConfig::setUseOpenGLTextureBuffer(bool useBuffer)
{
m_cfg.writeEntry("useOpenGLTextureBuffer", useBuffer);
}
int KisConfig::openGLTextureSize(bool defaultValue) const
{
return (defaultValue ? 256 : m_cfg.readEntry("textureSize", 256));
}
bool KisConfig::disableVSync(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("disableVSync", true));
}
void KisConfig::setDisableVSync(bool disableVSync)
{
m_cfg.writeEntry("disableVSync", disableVSync);
}
bool KisConfig::showAdvancedOpenGLSettings(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("showAdvancedOpenGLSettings", false));
}
bool KisConfig::forceOpenGLFenceWorkaround(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("forceOpenGLFenceWorkaround", false));
}
int KisConfig::numMipmapLevels(bool defaultValue) const
{
return (defaultValue ? 4 : m_cfg.readEntry("numMipmapLevels", 4));
}
int KisConfig::textureOverlapBorder() const
{
return 1 << qMax(0, numMipmapLevels());
}
quint32 KisConfig::getGridMainStyle(bool defaultValue) const
{
int v = m_cfg.readEntry("gridmainstyle", 0);
v = qBound(0, v, 2);
return (defaultValue ? 0 : v);
}
void KisConfig::setGridMainStyle(quint32 v) const
{
m_cfg.writeEntry("gridmainstyle", v);
}
quint32 KisConfig::getGridSubdivisionStyle(bool defaultValue) const
{
quint32 v = m_cfg.readEntry("gridsubdivisionstyle", 1);
if (v > 2) v = 2;
return (defaultValue ? 1 : v);
}
void KisConfig::setGridSubdivisionStyle(quint32 v) const
{
m_cfg.writeEntry("gridsubdivisionstyle", v);
}
QColor KisConfig::getGridMainColor(bool defaultValue) const
{
QColor col(99, 99, 99);
return (defaultValue ? col : m_cfg.readEntry("gridmaincolor", col));
}
void KisConfig::setGridMainColor(const QColor & v) const
{
m_cfg.writeEntry("gridmaincolor", v);
}
QColor KisConfig::getGridSubdivisionColor(bool defaultValue) const
{
QColor col(150, 150, 150);
return (defaultValue ? col : m_cfg.readEntry("gridsubdivisioncolor", col));
}
void KisConfig::setGridSubdivisionColor(const QColor & v) const
{
m_cfg.writeEntry("gridsubdivisioncolor", v);
}
QColor KisConfig::getPixelGridColor(bool defaultValue) const
{
QColor col(255, 255, 255);
return (defaultValue ? col : m_cfg.readEntry("pixelGridColor", col));
}
void KisConfig::setPixelGridColor(const QColor & v) const
{
m_cfg.writeEntry("pixelGridColor", v);
}
qreal KisConfig::getPixelGridDrawingThreshold(bool defaultValue) const
{
qreal border = 24.0f;
return (defaultValue ? border : m_cfg.readEntry("pixelGridDrawingThreshold", border));
}
void KisConfig::setPixelGridDrawingThreshold(qreal v) const
{
m_cfg.writeEntry("pixelGridDrawingThreshold", v);
}
bool KisConfig::pixelGridEnabled(bool defaultValue) const
{
bool enabled = true;
return (defaultValue ? enabled : m_cfg.readEntry("pixelGridEnabled", enabled));
}
void KisConfig::enablePixelGrid(bool v) const
{
m_cfg.writeEntry("pixelGridEnabled", v);
}
quint32 KisConfig::guidesLineStyle(bool defaultValue) const
{
int v = m_cfg.readEntry("guidesLineStyle", 0);
v = qBound(0, v, 2);
return (defaultValue ? 0 : v);
}
void KisConfig::setGuidesLineStyle(quint32 v) const
{
m_cfg.writeEntry("guidesLineStyle", v);
}
QColor KisConfig::guidesColor(bool defaultValue) const
{
QColor col(99, 99, 99);
return (defaultValue ? col : m_cfg.readEntry("guidesColor", col));
}
void KisConfig::setGuidesColor(const QColor & v) const
{
m_cfg.writeEntry("guidesColor", v);
}
void KisConfig::loadSnapConfig(KisSnapConfig *config, bool defaultValue) const
{
KisSnapConfig defaultConfig(false);
if (defaultValue) {
*config = defaultConfig;
return;
}
config->setOrthogonal(m_cfg.readEntry("globalSnapOrthogonal", defaultConfig.orthogonal()));
config->setNode(m_cfg.readEntry("globalSnapNode", defaultConfig.node()));
config->setExtension(m_cfg.readEntry("globalSnapExtension", defaultConfig.extension()));
config->setIntersection(m_cfg.readEntry("globalSnapIntersection", defaultConfig.intersection()));
config->setBoundingBox(m_cfg.readEntry("globalSnapBoundingBox", defaultConfig.boundingBox()));
config->setImageBounds(m_cfg.readEntry("globalSnapImageBounds", defaultConfig.imageBounds()));
config->setImageCenter(m_cfg.readEntry("globalSnapImageCenter", defaultConfig.imageCenter()));
}
void KisConfig::saveSnapConfig(const KisSnapConfig &config)
{
m_cfg.writeEntry("globalSnapOrthogonal", config.orthogonal());
m_cfg.writeEntry("globalSnapNode", config.node());
m_cfg.writeEntry("globalSnapExtension", config.extension());
m_cfg.writeEntry("globalSnapIntersection", config.intersection());
m_cfg.writeEntry("globalSnapBoundingBox", config.boundingBox());
m_cfg.writeEntry("globalSnapImageBounds", config.imageBounds());
m_cfg.writeEntry("globalSnapImageCenter", config.imageCenter());
}
qint32 KisConfig::checkSize(bool defaultValue) const
{
return (defaultValue ? 32 : m_cfg.readEntry("checksize", 32));
}
void KisConfig::setCheckSize(qint32 checksize) const
{
m_cfg.writeEntry("checksize", checksize);
}
bool KisConfig::scrollCheckers(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("scrollingcheckers", false));
}
void KisConfig::setScrollingCheckers(bool sc) const
{
m_cfg.writeEntry("scrollingcheckers", sc);
}
QColor KisConfig::canvasBorderColor(bool defaultValue) const
{
QColor color(QColor(128,128,128));
return (defaultValue ? color : m_cfg.readEntry("canvasBorderColor", color));
}
void KisConfig::setCanvasBorderColor(const QColor& color) const
{
m_cfg.writeEntry("canvasBorderColor", color);
}
bool KisConfig::hideScrollbars(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("hideScrollbars", false));
}
void KisConfig::setHideScrollbars(bool value) const
{
m_cfg.writeEntry("hideScrollbars", value);
}
QColor KisConfig::checkersColor1(bool defaultValue) const
{
QColor col(220, 220, 220);
return (defaultValue ? col : m_cfg.readEntry("checkerscolor", col));
}
void KisConfig::setCheckersColor1(const QColor & v) const
{
m_cfg.writeEntry("checkerscolor", v);
}
QColor KisConfig::checkersColor2(bool defaultValue) const
{
return (defaultValue ? QColor(Qt::white) : m_cfg.readEntry("checkerscolor2", QColor(Qt::white)));
}
void KisConfig::setCheckersColor2(const QColor & v) const
{
m_cfg.writeEntry("checkerscolor2", v);
}
bool KisConfig::antialiasCurves(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("antialiascurves", true));
}
void KisConfig::setAntialiasCurves(bool v) const
{
m_cfg.writeEntry("antialiascurves", v);
}
bool KisConfig::antialiasSelectionOutline(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("AntialiasSelectionOutline", false));
}
void KisConfig::setAntialiasSelectionOutline(bool v) const
{
m_cfg.writeEntry("AntialiasSelectionOutline", v);
}
bool KisConfig::showRootLayer(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("ShowRootLayer", false));
}
void KisConfig::setShowRootLayer(bool showRootLayer) const
{
m_cfg.writeEntry("ShowRootLayer", showRootLayer);
}
bool KisConfig::showGlobalSelection(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("ShowGlobalSelection", false));
}
void KisConfig::setShowGlobalSelection(bool showGlobalSelection) const
{
m_cfg.writeEntry("ShowGlobalSelection", showGlobalSelection);
}
bool KisConfig::showOutlineWhilePainting(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("ShowOutlineWhilePainting", true));
}
void KisConfig::setShowOutlineWhilePainting(bool showOutlineWhilePainting) const
{
m_cfg.writeEntry("ShowOutlineWhilePainting", showOutlineWhilePainting);
}
bool KisConfig::forceAlwaysFullSizedOutline(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("forceAlwaysFullSizedOutline", false));
}
void KisConfig::setForceAlwaysFullSizedOutline(bool value) const
{
m_cfg.writeEntry("forceAlwaysFullSizedOutline", value);
}
KisConfig::SessionOnStartup KisConfig::sessionOnStartup(bool defaultValue) const
{
int value = defaultValue ? SOS_BlankSession : m_cfg.readEntry("sessionOnStartup", (int)SOS_BlankSession);
return (KisConfig::SessionOnStartup)value;
}
void KisConfig::setSessionOnStartup(SessionOnStartup value)
{
m_cfg.writeEntry("sessionOnStartup", (int)value);
}
bool KisConfig::saveSessionOnQuit(bool defaultValue) const
{
return defaultValue ? false : m_cfg.readEntry("saveSessionOnQuit", false);
}
void KisConfig::setSaveSessionOnQuit(bool value)
{
m_cfg.writeEntry("saveSessionOnQuit", value);
}
qreal KisConfig::outlineSizeMinimum(bool defaultValue) const
{
return (defaultValue ? 1.0 : m_cfg.readEntry("OutlineSizeMinimum", 1.0));
}
void KisConfig::setOutlineSizeMinimum(qreal outlineSizeMinimum) const
{
m_cfg.writeEntry("OutlineSizeMinimum", outlineSizeMinimum);
}
qreal KisConfig::selectionViewSizeMinimum(bool defaultValue) const
{
return (defaultValue ? 5.0 : m_cfg.readEntry("SelectionViewSizeMinimum", 5.0));
}
void KisConfig::setSelectionViewSizeMinimum(qreal outlineSizeMinimum) const
{
m_cfg.writeEntry("SelectionViewSizeMinimum", outlineSizeMinimum);
}
int KisConfig::autoSaveInterval(bool defaultValue) const
{
return (defaultValue ? 15 * 60 : m_cfg.readEntry("AutoSaveInterval", 15 * 60));
}
void KisConfig::setAutoSaveInterval(int seconds) const
{
return m_cfg.writeEntry("AutoSaveInterval", seconds);
}
bool KisConfig::backupFile(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("CreateBackupFile", true));
}
void KisConfig::setBackupFile(bool backupFile) const
{
m_cfg.writeEntry("CreateBackupFile", backupFile);
}
bool KisConfig::showFilterGallery(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("showFilterGallery", false));
}
void KisConfig::setShowFilterGallery(bool showFilterGallery) const
{
m_cfg.writeEntry("showFilterGallery", showFilterGallery);
}
bool KisConfig::showFilterGalleryLayerMaskDialog(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("showFilterGalleryLayerMaskDialog", true));
}
void KisConfig::setShowFilterGalleryLayerMaskDialog(bool showFilterGallery) const
{
m_cfg.writeEntry("setShowFilterGalleryLayerMaskDialog", showFilterGallery);
}
QString KisConfig::canvasState(bool defaultValue) const
{
const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat);
return (defaultValue ? "OPENGL_NOT_TRIED" : kritarc.value("canvasState", "OPENGL_NOT_TRIED").toString());
}
void KisConfig::setCanvasState(const QString& state) const
{
static QStringList acceptableStates;
if (acceptableStates.isEmpty()) {
acceptableStates << "OPENGL_SUCCESS" << "TRY_OPENGL" << "OPENGL_NOT_TRIED" << "OPENGL_FAILED";
}
if (acceptableStates.contains(state)) {
const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat);
kritarc.setValue("canvasState", state);
}
}
bool KisConfig::toolOptionsPopupDetached(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("ToolOptionsPopupDetached", false));
}
void KisConfig::setToolOptionsPopupDetached(bool detached) const
{
m_cfg.writeEntry("ToolOptionsPopupDetached", detached);
}
bool KisConfig::paintopPopupDetached(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("PaintopPopupDetached", false));
}
void KisConfig::setPaintopPopupDetached(bool detached) const
{
m_cfg.writeEntry("PaintopPopupDetached", detached);
}
QString KisConfig::pressureTabletCurve(bool defaultValue) const
{
return (defaultValue ? "0,0;1,1" : m_cfg.readEntry("tabletPressureCurve","0,0;1,1;"));
}
void KisConfig::setPressureTabletCurve(const QString& curveString) const
{
m_cfg.writeEntry("tabletPressureCurve", curveString);
}
bool KisConfig::useWin8PointerInput(bool defaultValue) const
{
#ifdef Q_OS_WIN
return (defaultValue ? false : m_cfg.readEntry("useWin8PointerInput", false));
#else
Q_UNUSED(defaultValue);
return false;
#endif
}
void KisConfig::setUseWin8PointerInput(bool value) const
{
#ifdef Q_OS_WIN
// Special handling: Only set value if changed
// I don't want it to be set if the user hasn't touched it
if (useWin8PointerInput() != value) {
m_cfg.writeEntry("useWin8PointerInput", value);
}
#else
Q_UNUSED(value)
#endif
}
qreal KisConfig::vastScrolling(bool defaultValue) const
{
return (defaultValue ? 0.9 : m_cfg.readEntry("vastScrolling", 0.9));
}
void KisConfig::setVastScrolling(const qreal factor) const
{
m_cfg.writeEntry("vastScrolling", factor);
}
int KisConfig::presetChooserViewMode(bool defaultValue) const
{
return (defaultValue ? 0 : m_cfg.readEntry("presetChooserViewMode", 0));
}
void KisConfig::setPresetChooserViewMode(const int mode) const
{
m_cfg.writeEntry("presetChooserViewMode", mode);
}
int KisConfig::presetIconSize(bool defaultValue) const
{
return (defaultValue ? 60 : m_cfg.readEntry("presetIconSize", 60));
}
void KisConfig::setPresetIconSize(const int value) const
{
m_cfg.writeEntry("presetIconSize", value);
}
bool KisConfig::firstRun(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("firstRun", true));
}
void KisConfig::setFirstRun(const bool first) const
{
m_cfg.writeEntry("firstRun", first);
}
int KisConfig::horizontalSplitLines(bool defaultValue) const
{
return (defaultValue ? 1 : m_cfg.readEntry("horizontalSplitLines", 1));
}
void KisConfig::setHorizontalSplitLines(const int numberLines) const
{
m_cfg.writeEntry("horizontalSplitLines", numberLines);
}
int KisConfig::verticalSplitLines(bool defaultValue) const
{
return (defaultValue ? 1 : m_cfg.readEntry("verticalSplitLines", 1));
}
void KisConfig::setVerticalSplitLines(const int numberLines) const
{
m_cfg.writeEntry("verticalSplitLines", numberLines);
}
bool KisConfig::clicklessSpacePan(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("clicklessSpacePan", true));
}
void KisConfig::setClicklessSpacePan(const bool toggle) const
{
m_cfg.writeEntry("clicklessSpacePan", toggle);
}
bool KisConfig::hideDockersFullscreen(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("hideDockersFullScreen", true));
}
void KisConfig::setHideDockersFullscreen(const bool value) const
{
m_cfg.writeEntry("hideDockersFullScreen", value);
}
bool KisConfig::showDockers(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("showDockers", true));
}
void KisConfig::setShowDockers(const bool value) const
{
m_cfg.writeEntry("showDockers", value);
}
bool KisConfig::showStatusBar(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("showStatusBar", true));
}
void KisConfig::setShowStatusBar(const bool value) const
{
m_cfg.writeEntry("showStatusBar", value);
}
bool KisConfig::hideMenuFullscreen(bool defaultValue) const
{
return (defaultValue ? true: m_cfg.readEntry("hideMenuFullScreen", true));
}
void KisConfig::setHideMenuFullscreen(const bool value) const
{
m_cfg.writeEntry("hideMenuFullScreen", value);
}
bool KisConfig::hideScrollbarsFullscreen(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("hideScrollbarsFullScreen", true));
}
void KisConfig::setHideScrollbarsFullscreen(const bool value) const
{
m_cfg.writeEntry("hideScrollbarsFullScreen", value);
}
bool KisConfig::hideStatusbarFullscreen(bool defaultValue) const
{
return (defaultValue ? true: m_cfg.readEntry("hideStatusbarFullScreen", true));
}
void KisConfig::setHideStatusbarFullscreen(const bool value) const
{
m_cfg.writeEntry("hideStatusbarFullScreen", value);
}
bool KisConfig::hideTitlebarFullscreen(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("hideTitleBarFullscreen", true));
}
void KisConfig::setHideTitlebarFullscreen(const bool value) const
{
m_cfg.writeEntry("hideTitleBarFullscreen", value);
}
bool KisConfig::hideToolbarFullscreen(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("hideToolbarFullscreen", true));
}
void KisConfig::setHideToolbarFullscreen(const bool value) const
{
m_cfg.writeEntry("hideToolbarFullscreen", value);
}
bool KisConfig::fullscreenMode(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("fullscreenMode", true));
}
void KisConfig::setFullscreenMode(const bool value) const
{
m_cfg.writeEntry("fullscreenMode", value);
}
QStringList KisConfig::favoriteCompositeOps(bool defaultValue) const
{
return (defaultValue ? QStringList() : m_cfg.readEntry("favoriteCompositeOps", QStringList()));
}
void KisConfig::setFavoriteCompositeOps(const QStringList& compositeOps) const
{
m_cfg.writeEntry("favoriteCompositeOps", compositeOps);
}
QString KisConfig::exportConfiguration(const QString &filterId, bool defaultValue) const
{
return (defaultValue ? QString() : m_cfg.readEntry("ExportConfiguration-" + filterId, QString()));
}
void KisConfig::setExportConfiguration(const QString &filterId, KisPropertiesConfigurationSP properties) const
{
QString exportConfig = properties->toXML();
m_cfg.writeEntry("ExportConfiguration-" + filterId, exportConfig);
}
QString KisConfig::importConfiguration(const QString &filterId, bool defaultValue) const
{
return (defaultValue ? QString() : m_cfg.readEntry("ImportConfiguration-" + filterId, QString()));
}
void KisConfig::setImportConfiguration(const QString &filterId, KisPropertiesConfigurationSP properties) const
{
QString importConfig = properties->toXML();
m_cfg.writeEntry("ImportConfiguration-" + filterId, importConfig);
}
bool KisConfig::useOcio(bool defaultValue) const
{
#ifdef HAVE_OCIO
return (defaultValue ? false : m_cfg.readEntry("Krita/Ocio/UseOcio", false));
#else
Q_UNUSED(defaultValue);
return false;
#endif
}
void KisConfig::setUseOcio(bool useOCIO) const
{
m_cfg.writeEntry("Krita/Ocio/UseOcio", useOCIO);
}
int KisConfig::favoritePresets(bool defaultValue) const
{
return (defaultValue ? 10 : m_cfg.readEntry("numFavoritePresets", 10));
}
void KisConfig::setFavoritePresets(const int value)
{
m_cfg.writeEntry("numFavoritePresets", value);
}
bool KisConfig::levelOfDetailEnabled(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("levelOfDetailEnabled", false));
}
void KisConfig::setLevelOfDetailEnabled(bool value)
{
m_cfg.writeEntry("levelOfDetailEnabled", value);
}
KisOcioConfiguration KisConfig::ocioConfiguration(bool defaultValue) const
{
KisOcioConfiguration cfg;
if (!defaultValue) {
cfg.mode = (KisOcioConfiguration::Mode)m_cfg.readEntry("Krita/Ocio/OcioColorManagementMode", 0);
cfg.configurationPath = m_cfg.readEntry("Krita/Ocio/OcioConfigPath", QString());
cfg.lutPath = m_cfg.readEntry("Krita/Ocio/OcioLutPath", QString());
cfg.inputColorSpace = m_cfg.readEntry("Krita/Ocio/InputColorSpace", QString());
cfg.displayDevice = m_cfg.readEntry("Krita/Ocio/DisplayDevice", QString());
cfg.displayView = m_cfg.readEntry("Krita/Ocio/DisplayView", QString());
cfg.look = m_cfg.readEntry("Krita/Ocio/DisplayLook", QString());
}
return cfg;
}
void KisConfig::setOcioConfiguration(const KisOcioConfiguration &cfg)
{
m_cfg.writeEntry("Krita/Ocio/OcioColorManagementMode", (int) cfg.mode);
m_cfg.writeEntry("Krita/Ocio/OcioConfigPath", cfg.configurationPath);
m_cfg.writeEntry("Krita/Ocio/OcioLutPath", cfg.lutPath);
m_cfg.writeEntry("Krita/Ocio/InputColorSpace", cfg.inputColorSpace);
m_cfg.writeEntry("Krita/Ocio/DisplayDevice", cfg.displayDevice);
m_cfg.writeEntry("Krita/Ocio/DisplayView", cfg.displayView);
m_cfg.writeEntry("Krita/Ocio/DisplayLook", cfg.look);
}
KisConfig::OcioColorManagementMode
KisConfig::ocioColorManagementMode(bool defaultValue) const
{
// FIXME: this option duplicates ocioConfiguration(), please deprecate it
return (OcioColorManagementMode)(defaultValue ? INTERNAL
: m_cfg.readEntry("Krita/Ocio/OcioColorManagementMode", (int) INTERNAL));
}
void KisConfig::setOcioColorManagementMode(OcioColorManagementMode mode) const
{
// FIXME: this option duplicates ocioConfiguration(), please deprecate it
m_cfg.writeEntry("Krita/Ocio/OcioColorManagementMode", (int) mode);
}
int KisConfig::ocioLutEdgeSize(bool defaultValue) const
{
return (defaultValue ? 64 : m_cfg.readEntry("Krita/Ocio/LutEdgeSize", 64));
}
void KisConfig::setOcioLutEdgeSize(int value)
{
m_cfg.writeEntry("Krita/Ocio/LutEdgeSize", value);
}
bool KisConfig::ocioLockColorVisualRepresentation(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("Krita/Ocio/OcioLockColorVisualRepresentation", false));
}
void KisConfig::setOcioLockColorVisualRepresentation(bool value)
{
m_cfg.writeEntry("Krita/Ocio/OcioLockColorVisualRepresentation", value);
}
QString KisConfig::defaultPalette(bool defaultValue) const
{
return (defaultValue ? QString() : m_cfg.readEntry("defaultPalette", "Default"));
}
void KisConfig::setDefaultPalette(const QString& name) const
{
m_cfg.writeEntry("defaultPalette", name);
}
QString KisConfig::toolbarSlider(int sliderNumber, bool defaultValue) const
{
QString def = "flow";
if (sliderNumber == 1) {
def = "opacity";
}
if (sliderNumber == 2) {
def = "size";
}
return (defaultValue ? def : m_cfg.readEntry(QString("toolbarslider_%1").arg(sliderNumber), def));
}
void KisConfig::setToolbarSlider(int sliderNumber, const QString &slider)
{
m_cfg.writeEntry(QString("toolbarslider_%1").arg(sliderNumber), slider);
}
int KisConfig::layerThumbnailSize(bool defaultValue) const
{
return (defaultValue ? 20 : m_cfg.readEntry("layerThumbnailSize", 20));
}
void KisConfig::setLayerThumbnailSize(int size)
{
m_cfg.writeEntry("layerThumbnailSize", size);
}
bool KisConfig::sliderLabels(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("sliderLabels", true));
}
void KisConfig::setSliderLabels(bool enabled)
{
m_cfg.writeEntry("sliderLabels", enabled);
}
QString KisConfig::currentInputProfile(bool defaultValue) const
{
return (defaultValue ? QString() : m_cfg.readEntry("currentInputProfile", QString()));
}
void KisConfig::setCurrentInputProfile(const QString& name)
{
m_cfg.writeEntry("currentInputProfile", name);
}
bool KisConfig::useSystemMonitorProfile(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("ColorManagement/UseSystemMonitorProfile", false));
}
void KisConfig::setUseSystemMonitorProfile(bool _useSystemMonitorProfile) const
{
m_cfg.writeEntry("ColorManagement/UseSystemMonitorProfile", _useSystemMonitorProfile);
}
bool KisConfig::presetStripVisible(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("presetStripVisible", true));
}
void KisConfig::setPresetStripVisible(bool visible)
{
m_cfg.writeEntry("presetStripVisible", visible);
}
bool KisConfig::scratchpadVisible(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("scratchpadVisible", true));
}
void KisConfig::setScratchpadVisible(bool visible)
{
m_cfg.writeEntry("scratchpadVisible", visible);
}
bool KisConfig::showSingleChannelAsColor(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("showSingleChannelAsColor", false));
}
void KisConfig::setShowSingleChannelAsColor(bool asColor)
{
m_cfg.writeEntry("showSingleChannelAsColor", asColor);
}
bool KisConfig::hidePopups(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("hidePopups", false));
}
void KisConfig::setHidePopups(bool hidepopups)
{
m_cfg.writeEntry("hidePopups", hidepopups);
}
int KisConfig::numDefaultLayers(bool defaultValue) const
{
return (defaultValue ? 2 : m_cfg.readEntry("NumberOfLayersForNewImage", 2));
}
void KisConfig::setNumDefaultLayers(int num)
{
m_cfg.writeEntry("NumberOfLayersForNewImage", num);
}
quint8 KisConfig::defaultBackgroundOpacity(bool defaultValue) const
{
return (defaultValue ? (int)OPACITY_OPAQUE_U8 : m_cfg.readEntry("BackgroundOpacityForNewImage", (int)OPACITY_OPAQUE_U8));
}
void KisConfig::setDefaultBackgroundOpacity(quint8 value)
{
m_cfg.writeEntry("BackgroundOpacityForNewImage", (int)value);
}
QColor KisConfig::defaultBackgroundColor(bool defaultValue) const
{
return (defaultValue ? QColor(Qt::white) : m_cfg.readEntry("BackgroundColorForNewImage", QColor(Qt::white)));
}
void KisConfig::setDefaultBackgroundColor(QColor value)
{
m_cfg.writeEntry("BackgroundColorForNewImage", value);
}
KisConfig::BackgroundStyle KisConfig::defaultBackgroundStyle(bool defaultValue) const
{
- return (KisConfig::BackgroundStyle)(defaultValue ? LAYER : m_cfg.readEntry("BackgroundStyleForNewImage", (int)LAYER));
+ return (KisConfig::BackgroundStyle)(defaultValue ? RASTER_LAYER : m_cfg.readEntry("BackgroundStyleForNewImage", (int)RASTER_LAYER));
}
void KisConfig::setDefaultBackgroundStyle(KisConfig::BackgroundStyle value)
{
m_cfg.writeEntry("BackgroundStyleForNewImage", (int)value);
}
int KisConfig::lineSmoothingType(bool defaultValue) const
{
return (defaultValue ? 1 : m_cfg.readEntry("LineSmoothingType", 1));
}
void KisConfig::setLineSmoothingType(int value)
{
m_cfg.writeEntry("LineSmoothingType", value);
}
qreal KisConfig::lineSmoothingDistance(bool defaultValue) const
{
return (defaultValue ? 50.0 : m_cfg.readEntry("LineSmoothingDistance", 50.0));
}
void KisConfig::setLineSmoothingDistance(qreal value)
{
m_cfg.writeEntry("LineSmoothingDistance", value);
}
qreal KisConfig::lineSmoothingTailAggressiveness(bool defaultValue) const
{
return (defaultValue ? 0.15 : m_cfg.readEntry("LineSmoothingTailAggressiveness", 0.15));
}
void KisConfig::setLineSmoothingTailAggressiveness(qreal value)
{
m_cfg.writeEntry("LineSmoothingTailAggressiveness", value);
}
bool KisConfig::lineSmoothingSmoothPressure(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("LineSmoothingSmoothPressure", false));
}
void KisConfig::setLineSmoothingSmoothPressure(bool value)
{
m_cfg.writeEntry("LineSmoothingSmoothPressure", value);
}
bool KisConfig::lineSmoothingScalableDistance(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("LineSmoothingScalableDistance", true));
}
void KisConfig::setLineSmoothingScalableDistance(bool value)
{
m_cfg.writeEntry("LineSmoothingScalableDistance", value);
}
qreal KisConfig::lineSmoothingDelayDistance(bool defaultValue) const
{
return (defaultValue ? 50.0 : m_cfg.readEntry("LineSmoothingDelayDistance", 50.0));
}
void KisConfig::setLineSmoothingDelayDistance(qreal value)
{
m_cfg.writeEntry("LineSmoothingDelayDistance", value);
}
bool KisConfig::lineSmoothingUseDelayDistance(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("LineSmoothingUseDelayDistance", true));
}
void KisConfig::setLineSmoothingUseDelayDistance(bool value)
{
m_cfg.writeEntry("LineSmoothingUseDelayDistance", value);
}
bool KisConfig::lineSmoothingFinishStabilizedCurve(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("LineSmoothingFinishStabilizedCurve", true));
}
void KisConfig::setLineSmoothingFinishStabilizedCurve(bool value)
{
m_cfg.writeEntry("LineSmoothingFinishStabilizedCurve", value);
}
bool KisConfig::lineSmoothingStabilizeSensors(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("LineSmoothingStabilizeSensors", true));
}
void KisConfig::setLineSmoothingStabilizeSensors(bool value)
{
m_cfg.writeEntry("LineSmoothingStabilizeSensors", value);
}
int KisConfig::tabletEventsDelay(bool defaultValue) const
{
return (defaultValue ? 10 : m_cfg.readEntry("tabletEventsDelay", 10));
}
void KisConfig::setTabletEventsDelay(int value)
{
m_cfg.writeEntry("tabletEventsDelay", value);
}
bool KisConfig::trackTabletEventLatency(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("trackTabletEventLatency", false));
}
void KisConfig::setTrackTabletEventLatency(bool value)
{
m_cfg.writeEntry("trackTabletEventLatency", value);
}
bool KisConfig::testingAcceptCompressedTabletEvents(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("testingAcceptCompressedTabletEvents", false));
}
void KisConfig::setTestingAcceptCompressedTabletEvents(bool value)
{
m_cfg.writeEntry("testingAcceptCompressedTabletEvents", value);
}
bool KisConfig::shouldEatDriverShortcuts(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("shouldEatDriverShortcuts", false));
}
bool KisConfig::testingCompressBrushEvents(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("testingCompressBrushEvents", false));
}
void KisConfig::setTestingCompressBrushEvents(bool value)
{
m_cfg.writeEntry("testingCompressBrushEvents", value);
}
int KisConfig::workaroundX11SmoothPressureSteps(bool defaultValue) const
{
return (defaultValue ? 0 : m_cfg.readEntry("workaroundX11SmoothPressureSteps", 0));
}
bool KisConfig::showCanvasMessages(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("showOnCanvasMessages", true));
}
void KisConfig::setShowCanvasMessages(bool show)
{
m_cfg.writeEntry("showOnCanvasMessages", show);
}
bool KisConfig::compressKra(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("compressLayersInKra", false));
}
void KisConfig::setCompressKra(bool compress)
{
m_cfg.writeEntry("compressLayersInKra", compress);
}
bool KisConfig::toolOptionsInDocker(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("ToolOptionsInDocker", true));
}
void KisConfig::setToolOptionsInDocker(bool inDocker)
{
m_cfg.writeEntry("ToolOptionsInDocker", inDocker);
}
bool KisConfig::kineticScrollingEnabled(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("KineticScrollingEnabled", true));
}
void KisConfig::setKineticScrollingEnabled(bool value)
{
m_cfg.writeEntry("KineticScrollingEnabled", value);
}
int KisConfig::kineticScrollingGesture(bool defaultValue) const
{
return (defaultValue ? 2 : m_cfg.readEntry("KineticScrollingGesture", 2));
}
void KisConfig::setKineticScrollingGesture(int gesture)
{
m_cfg.writeEntry("KineticScrollingGesture", gesture);
}
int KisConfig::kineticScrollingSensitivity(bool defaultValue) const
{
return (defaultValue ? 75 : m_cfg.readEntry("KineticScrollingSensitivity", 75));
}
void KisConfig::setKineticScrollingSensitivity(int sensitivity)
{
m_cfg.writeEntry("KineticScrollingSensitivity", sensitivity);
}
bool KisConfig::kineticScrollingHiddenScrollbars(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("KineticScrollingHideScrollbar", false));
}
void KisConfig::setKineticScrollingHideScrollbars(bool scrollbar)
{
m_cfg.writeEntry("KineticScrollingHideScrollbar", scrollbar);
}
const KoColorSpace* KisConfig::customColorSelectorColorSpace(bool defaultValue) const
{
const KoColorSpace *cs = 0;
KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector");
if (defaultValue || cfg.readEntry("useCustomColorSpace", true)) {
KoColorSpaceRegistry* csr = KoColorSpaceRegistry::instance();
QString modelID = cfg.readEntry("customColorSpaceModel", "RGBA");
QString depthID = cfg.readEntry("customColorSpaceDepthID", "U8");
QString profile = cfg.readEntry("customColorSpaceProfile", "sRGB built-in - (lcms internal)");
if (profile == "default") {
// qDebug() << "Falling back to default color profile.";
profile = "sRGB built-in - (lcms internal)";
}
cs = csr->colorSpace(modelID, depthID, profile);
}
return cs;
}
void KisConfig::setCustomColorSelectorColorSpace(const KoColorSpace *cs)
{
KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector");
cfg.writeEntry("useCustomColorSpace", bool(cs));
if(cs) {
cfg.writeEntry("customColorSpaceModel", cs->colorModelId().id());
cfg.writeEntry("customColorSpaceDepthID", cs->colorDepthId().id());
cfg.writeEntry("customColorSpaceProfile", cs->profile()->name());
}
KisConfigNotifier::instance()->notifyConfigChanged();
}
bool KisConfig::enableOpenGLFramerateLogging(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("enableOpenGLFramerateLogging", false));
}
void KisConfig::setEnableOpenGLFramerateLogging(bool value) const
{
m_cfg.writeEntry("enableOpenGLFramerateLogging", value);
}
bool KisConfig::enableBrushSpeedLogging(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("enableBrushSpeedLogging", false));
}
void KisConfig::setEnableBrushSpeedLogging(bool value) const
{
m_cfg.writeEntry("enableBrushSpeedLogging", value);
}
void KisConfig::setEnableAmdVectorizationWorkaround(bool value)
{
m_cfg.writeEntry("amdDisableVectorWorkaround", value);
}
bool KisConfig::enableAmdVectorizationWorkaround(bool defaultValue) const
{
return (defaultValue ? false : m_cfg.readEntry("amdDisableVectorWorkaround", false));
}
void KisConfig::setAnimationDropFrames(bool value)
{
bool oldValue = animationDropFrames();
if (value == oldValue) return;
m_cfg.writeEntry("animationDropFrames", value);
KisConfigNotifier::instance()->notifyDropFramesModeChanged();
}
bool KisConfig::animationDropFrames(bool defaultValue) const
{
return (defaultValue ? true : m_cfg.readEntry("animationDropFrames", true));
}
int KisConfig::scrubbingUpdatesDelay(bool defaultValue) const
{
return (defaultValue ? 30 : m_cfg.readEntry("scrubbingUpdatesDelay", 30));
}
void KisConfig::setScrubbingUpdatesDelay(int value)
{
m_cfg.writeEntry("scrubbingUpdatesDelay", value);
}
int KisConfig::scrubbingAudioUpdatesDelay(bool defaultValue) const
{
return (defaultValue ? -1 : m_cfg.readEntry("scrubbingAudioUpdatesDelay", -1));
}
void KisConfig::setScrubbingAudioUpdatesDelay(int value)
{
m_cfg.writeEntry("scrubbingAudioUpdatesDelay", value);
}
int KisConfig::audioOffsetTolerance(bool defaultValue) const
{
return (defaultValue ? -1 : m_cfg.readEntry("audioOffsetTolerance", -1));
}
void KisConfig::setAudioOffsetTolerance(int value)
{
m_cfg.writeEntry("audioOffsetTolerance", value);
}
bool KisConfig::switchSelectionCtrlAlt(bool defaultValue) const
{
return defaultValue ? false : m_cfg.readEntry("switchSelectionCtrlAlt", false);
}
void KisConfig::setSwitchSelectionCtrlAlt(bool value)
{
m_cfg.writeEntry("switchSelectionCtrlAlt", value);
KisConfigNotifier::instance()->notifyConfigChanged();
}
bool KisConfig::convertToImageColorspaceOnImport(bool defaultValue) const
{
return defaultValue ? false : m_cfg.readEntry("ConvertToImageColorSpaceOnImport", false);
}
void KisConfig::setConvertToImageColorspaceOnImport(bool value)
{
m_cfg.writeEntry("ConvertToImageColorSpaceOnImport", value);
}
int KisConfig::stabilizerSampleSize(bool defaultValue) const
{
#ifdef Q_OS_WIN
const int defaultSampleSize = 50;
#else
const int defaultSampleSize = 15;
#endif
return defaultValue ?
defaultSampleSize : m_cfg.readEntry("stabilizerSampleSize", defaultSampleSize);
}
void KisConfig::setStabilizerSampleSize(int value)
{
m_cfg.writeEntry("stabilizerSampleSize", value);
}
bool KisConfig::stabilizerDelayedPaint(bool defaultValue) const
{
const bool defaultEnabled = true;
return defaultValue ?
defaultEnabled : m_cfg.readEntry("stabilizerDelayedPaint", defaultEnabled);
}
void KisConfig::setStabilizerDelayedPaint(bool value)
{
m_cfg.writeEntry("stabilizerDelayedPaint", value);
}
QString KisConfig::customFFMpegPath(bool defaultValue) const
{
return defaultValue ? QString() : m_cfg.readEntry("ffmpegExecutablePath", QString());
}
void KisConfig::setCustomFFMpegPath(const QString &value) const
{
m_cfg.writeEntry("ffmpegExecutablePath", value);
}
bool KisConfig::showBrushHud(bool defaultValue) const
{
return defaultValue ? false : m_cfg.readEntry("showBrushHud", false);
}
void KisConfig::setShowBrushHud(bool value)
{
m_cfg.writeEntry("showBrushHud", value);
}
QString KisConfig::brushHudSetting(bool defaultValue) const
{
QString defaultDoc = "<!DOCTYPE hud_properties>\n<hud_properties>\n <version value=\"1\" type=\"value\"/>\n <paintbrush>\n <properties_list type=\"array\">\n <item_0 value=\"size\" type=\"value\"/>\n <item_1 value=\"opacity\" type=\"value\"/>\n <item_2 value=\"angle\" type=\"value\"/>\n </properties_list>\n </paintbrush>\n <colorsmudge>\n <properties_list type=\"array\">\n <item_0 value=\"size\" type=\"value\"/>\n <item_1 value=\"opacity\" type=\"value\"/>\n <item_2 value=\"smudge_mode\" type=\"value\"/>\n <item_3 value=\"smudge_length\" type=\"value\"/>\n <item_4 value=\"smudge_color_rate\" type=\"value\"/>\n </properties_list>\n </colorsmudge>\n <sketchbrush>\n <properties_list type=\"array\">\n <item_0 value=\"opacity\" type=\"value\"/>\n <item_1 value=\"size\" type=\"value\"/>\n </properties_list>\n </sketchbrush>\n <hairybrush>\n <properties_list type=\"array\">\n <item_0 value=\"size\" type=\"value\"/>\n <item_1 value=\"opacity\" type=\"value\"/>\n </properties_list>\n </hairybrush>\n <experimentbrush>\n <properties_list type=\"array\">\n <item_0 value=\"opacity\" type=\"value\"/>\n <item_1 value=\"shape_windingfill\" type=\"value\"/>\n </properties_list>\n </experimentbrush>\n <spraybrush>\n <properties_list type=\"array\">\n <item_0 value=\"size\" type=\"value\"/>\n <item_1 value=\"opacity\" type=\"value\"/>\n <item_2 value=\"spray_particlecount\" type=\"value\"/>\n <item_3 value=\"spray_density\" type=\"value\"/>\n </properties_list>\n </spraybrush>\n <hatchingbrush>\n <properties_list type=\"array\">\n <item_0 value=\"size\" type=\"value\"/>\n <item_1 value=\"opacity\" type=\"value\"/>\n <item_2 value=\"hatching_angle\" type=\"value\"/>\n <item_3 value=\"hatching_thickness\" type=\"value\"/>\n <item_4 value=\"hatching_separation\" type=\"value\"/>\n </properties_list>\n </hatchingbrush>\n <gridbrush>\n <properties_list type=\"array\">\n <item_0 value=\"size\" type=\"value\"/>\n <item_1 value=\"opacity\" type=\"value\"/>\n <item_2 value=\"grid_divisionlevel\" type=\"value\"/>\n </properties_list>\n </gridbrush>\n <curvebrush>\n <properties_list type=\"array\">\n <item_0 value=\"opacity\" type=\"value\"/>\n <item_1 value=\"curve_historysize\" type=\"value\"/>\n <item_2 value=\"curve_linewidth\" type=\"value\"/>\n <item_3 value=\"curve_lineopacity\" type=\"value\"/>\n <item_4 value=\"curve_connectionline\" type=\"value\"/>\n </properties_list>\n </curvebrush>\n <dynabrush>\n <properties_list type=\"array\">\n <item_0 value=\"dyna_diameter\" type=\"value\"/>\n <item_1 value=\"opacity\" type=\"value\"/>\n <item_2 value=\"dyna_mass\" type=\"value\"/>\n <item_3 value=\"dyna_drag\" type=\"value\"/>\n </properties_list>\n </dynabrush>\n <particlebrush>\n <properties_list type=\"array\">\n <item_0 value=\"opacity\" type=\"value\"/>\n <item_1 value=\"particle_particles\" type=\"value\"/>\n <item_2 value=\"particle_opecityweight\" type=\"value\"/>\n <item_3 value=\"particle_iterations\" type=\"value\"/>\n </properties_list>\n </particlebrush>\n <duplicate>\n <properties_list type=\"array\">\n <item_0 value=\"size\" type=\"value\"/>\n <item_1 value=\"opacity\" type=\"value\"/>\n <item_2 value=\"clone_healing\" type=\"value\"/>\n <item_3 value=\"clone_movesource\" type=\"value\"/>\n </properties_list>\n </duplicate>\n <deformbrush>\n <properties_list type=\"array\">\n <item_0 value=\"size\" type=\"value\"/>\n <item_1 value=\"opacity\" type=\"value\"/>\n <item_2 value=\"deform_amount\" type=\"value\"/>\n <item_3 value=\"deform_mode\" type=\"value\"/>\n </properties_list>\n </deformbrush>\n <tangentnormal>\n <properties_list type=\"array\">\n <item_0 value=\"size\" type=\"value\"/>\n <item_1 value=\"opacity\" type=\"value\"/>\n </properties_list>\n </tangentnormal>\n <filter>\n <properties_list type=\"array\">\n <item_0 value=\"size\" type=\"value\"/>\n <item_1 value=\"opacity\" type=\"value\"/>\n </properties_list>\n </filter>\n <roundmarker>\n <properties_list type=\"array\">\n <item_0 value=\"opacity\" type=\"value\"/>\n <item_1 value=\"size\" type=\"value\"/>\n </properties_list>\n </roundmarker>\n</hud_properties>\n";
return defaultValue ? defaultDoc : m_cfg.readEntry("brushHudSettings", defaultDoc);
}
void KisConfig::setBrushHudSetting(const QString &value) const
{
m_cfg.writeEntry("brushHudSettings", value);
}
bool KisConfig::calculateAnimationCacheInBackground(bool defaultValue) const
{
return defaultValue ? true : m_cfg.readEntry("calculateAnimationCacheInBackground", true);
}
void KisConfig::setCalculateAnimationCacheInBackground(bool value)
{
m_cfg.writeEntry("calculateAnimationCacheInBackground", value);
}
QColor KisConfig::defaultAssistantsColor(bool defaultValue) const
{
static const QColor defaultColor = QColor(176, 176, 176, 255);
return defaultValue ? defaultColor : m_cfg.readEntry("defaultAssistantsColor", defaultColor);
}
void KisConfig::setDefaultAssistantsColor(const QColor &color) const
{
m_cfg.writeEntry("defaultAssistantsColor", color);
}
bool KisConfig::autoSmoothBezierCurves(bool defaultValue) const
{
return defaultValue ? false : m_cfg.readEntry("autoSmoothBezierCurves", false);
}
void KisConfig::setAutoSmoothBezierCurves(bool value)
{
m_cfg.writeEntry("autoSmoothBezierCurves", value);
}
bool KisConfig::activateTransformToolAfterPaste(bool defaultValue) const
{
return defaultValue ? false : m_cfg.readEntry("activateTransformToolAfterPaste", false);
}
void KisConfig::setActivateTransformToolAfterPaste(bool value)
{
m_cfg.writeEntry("activateTransformToolAfterPaste", value);
}
KisConfig::RootSurfaceFormat KisConfig::rootSurfaceFormat(bool defaultValue) const
{
const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat);
return rootSurfaceFormat(&kritarc, defaultValue);
}
void KisConfig::setRootSurfaceFormat(KisConfig::RootSurfaceFormat value)
{
const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat);
setRootSurfaceFormat(&kritarc, value);
}
KisConfig::RootSurfaceFormat KisConfig::rootSurfaceFormat(QSettings *displayrc, bool defaultValue)
{
QString textValue = "bt709-g22";
if (!defaultValue) {
textValue = displayrc->value("rootSurfaceFormat", textValue).toString();
}
return textValue == "bt709-g10" ? BT709_G10 :
textValue == "bt2020-pq" ? BT2020_PQ :
BT709_G22;
}
void KisConfig::setRootSurfaceFormat(QSettings *displayrc, KisConfig::RootSurfaceFormat value)
{
const QString textValue =
value == BT709_G10 ? "bt709-g10" :
value == BT2020_PQ ? "bt2020-pq" :
"bt709-g22";
displayrc->setValue("rootSurfaceFormat", textValue);
}
+bool KisConfig::useZip64(bool defaultValue) const
+{
+ return defaultValue ? false : m_cfg.readEntry("UseZip64", false);
+}
+
+void KisConfig::setUseZip64(bool value)
+{
+ m_cfg.writeEntry("UseZip64", value);
+}
+
#include <QDomDocument>
#include <QDomElement>
void KisConfig::writeKoColor(const QString& name, const KoColor& color) const
{
QDomDocument doc = QDomDocument(name);
QDomElement el = doc.createElement(name);
doc.appendChild(el);
color.toXML(doc, el);
m_cfg.writeEntry(name, doc.toString());
}
//ported from kispropertiesconfig.
KoColor KisConfig::readKoColor(const QString& name, const KoColor& color) const
{
QDomDocument doc;
if (!m_cfg.readEntry(name).isNull()) {
doc.setContent(m_cfg.readEntry(name));
QDomElement e = doc.documentElement().firstChild().toElement();
return KoColor::fromXML(e, Integer16BitsColorDepthID.id());
} else {
QString blackColor = "<!DOCTYPE Color>\n<Color>\n <RGB r=\"0\" space=\"sRGB-elle-V2-srgbtrc.icc\" b=\"0\" g=\"0\"/>\n</Color>\n";
doc.setContent(blackColor);
QDomElement e = doc.documentElement().firstChild().toElement();
return KoColor::fromXML(e, Integer16BitsColorDepthID.id());
}
return color;
}
diff --git a/libs/ui/kis_config.h b/libs/ui/kis_config.h
index 01ae27ab13..cbbb252348 100644
--- a/libs/ui/kis_config.h
+++ b/libs/ui/kis_config.h
@@ -1,625 +1,629 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
*
* 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_CONFIG_H_
#define KIS_CONFIG_H_
#include <QString>
#include <QStringList>
#include <QList>
#include <QColor>
#include <ksharedconfig.h>
#include <kconfiggroup.h>
#include <kis_global.h>
#include <kis_properties_configuration.h>
#include "kritaui_export.h"
class KoColorProfile;
class KoColorSpace;
class KisSnapConfig;
class QSettings;
class KisOcioConfiguration;
class KRITAUI_EXPORT KisConfig
{
public:
/**
* @brief KisConfig create a kisconfig object
* @param readOnly if true, there will be no call to sync when the object is deleted.
* Any KisConfig object created in a thread must be read-only.
*/
KisConfig(bool readOnly);
~KisConfig();
bool disableTouchOnCanvas(bool defaultValue = false) const;
void setDisableTouchOnCanvas(bool value) const;
bool useProjections(bool defaultValue = false) const;
void setUseProjections(bool useProj) const;
bool undoEnabled(bool defaultValue = false) const;
void setUndoEnabled(bool undo) const;
int undoStackLimit(bool defaultValue = false) const;
void setUndoStackLimit(int limit) const;
bool useCumulativeUndoRedo(bool defaultValue = false) const;
void setCumulativeUndoRedo(bool value);
double stackT1(bool defaultValue = false) const;
void setStackT1(int T1);
double stackT2(bool defaultValue = false) const;
void setStackT2(int T2);
int stackN(bool defaultValue = false) const;
void setStackN(int N);
qint32 defImageWidth(bool defaultValue = false) const;
void defImageWidth(qint32 width) const;
qint32 defImageHeight(bool defaultValue = false) const;
void defImageHeight(qint32 height) const;
qreal defImageResolution(bool defaultValue = false) const;
void defImageResolution(qreal res) const;
int preferredVectorImportResolutionPPI(bool defaultValue = false) const;
void setPreferredVectorImportResolutionPPI(int value) const;
/**
* @return the id of the default color model used for creating new images.
*/
QString defColorModel(bool defaultValue = false) const;
/**
* set the id of the default color model used for creating new images.
*/
void defColorModel(const QString & model) const;
/**
* @return the id of the default color depth used for creating new images.
*/
QString defaultColorDepth(bool defaultValue = false) const;
/**
* set the id of the default color depth used for creating new images.
*/
void setDefaultColorDepth(const QString & depth) const;
/**
* @return the id of the default color profile used for creating new images.
*/
QString defColorProfile(bool defaultValue = false) const;
/**
* set the id of the default color profile used for creating new images.
*/
void defColorProfile(const QString & depth) const;
CursorStyle newCursorStyle(bool defaultValue = false) const;
void setNewCursorStyle(CursorStyle style);
QColor getCursorMainColor(bool defaultValue = false) const;
void setCursorMainColor(const QColor& v) const;
OutlineStyle newOutlineStyle(bool defaultValue = false) const;
void setNewOutlineStyle(OutlineStyle style);
QRect colorPreviewRect() const;
void setColorPreviewRect(const QRect &rect);
/// get the profile the user has selected for the given screen
QString monitorProfile(int screen) const;
void setMonitorProfile(int screen, const QString & monitorProfile, bool override) const;
QString monitorForScreen(int screen, const QString &defaultMonitor, bool defaultValue = true) const;
void setMonitorForScreen(int screen, const QString& monitor);
/// Get the actual profile to be used for the given screen, which is
/// either the screen profile set by the color management system or
/// the custom monitor profile set by the user, depending on the configuration
const KoColorProfile *displayProfile(int screen) const;
QString workingColorSpace(bool defaultValue = false) const;
void setWorkingColorSpace(const QString & workingColorSpace) const;
QString importProfile(bool defaultValue = false) const;
void setImportProfile(const QString & importProfile) const;
QString printerColorSpace(bool defaultValue = false) const;
void setPrinterColorSpace(const QString & printerColorSpace) const;
QString printerProfile(bool defaultValue = false) const;
void setPrinterProfile(const QString & printerProfile) const;
bool useBlackPointCompensation(bool defaultValue = false) const;
void setUseBlackPointCompensation(bool useBlackPointCompensation) const;
bool allowLCMSOptimization(bool defaultValue = false) const;
void setAllowLCMSOptimization(bool allowLCMSOptimization);
bool forcePaletteColors(bool defaultValue = false) const;
void setForcePaletteColors(bool forcePaletteColors);
void writeKoColor(const QString& name, const KoColor& color) const;
KoColor readKoColor(const QString& name, const KoColor& color = KoColor()) const;
bool showRulers(bool defaultValue = false) const;
void setShowRulers(bool rulers) const;
bool forceShowSaveMessages(bool defaultValue = true) const;
void setForceShowSaveMessages(bool value) const;
bool forceShowAutosaveMessages(bool defaultValue = true) const;
void setForceShowAutosaveMessages(bool ShowAutosaveMessages) const;
bool rulersTrackMouse(bool defaultValue = false) const;
void setRulersTrackMouse(bool value) const;
qint32 pasteBehaviour(bool defaultValue = false) const;
void setPasteBehaviour(qint32 behaviour) const;
qint32 monitorRenderIntent(bool defaultValue = false) const;
void setRenderIntent(qint32 monitorRenderIntent) const;
bool useOpenGL(bool defaultValue = false) const;
void setUseOpenGL(bool useOpenGL) const;
int openGLFilteringMode(bool defaultValue = false) const;
void setOpenGLFilteringMode(int filteringMode);
bool useOpenGLTextureBuffer(bool defaultValue = false) const;
void setUseOpenGLTextureBuffer(bool useBuffer);
bool disableVSync(bool defaultValue = false) const;
void setDisableVSync(bool disableVSync);
bool showAdvancedOpenGLSettings(bool defaultValue = false) const;
bool forceOpenGLFenceWorkaround(bool defaultValue = false) const;
int numMipmapLevels(bool defaultValue = false) const;
int openGLTextureSize(bool defaultValue = false) const;
int textureOverlapBorder() const;
quint32 getGridMainStyle(bool defaultValue = false) const;
void setGridMainStyle(quint32 v) const;
quint32 getGridSubdivisionStyle(bool defaultValue = false) const;
void setGridSubdivisionStyle(quint32 v) const;
QColor getGridMainColor(bool defaultValue = false) const;
void setGridMainColor(const QColor & v) const;
QColor getGridSubdivisionColor(bool defaultValue = false) const;
void setGridSubdivisionColor(const QColor & v) const;
QColor getPixelGridColor(bool defaultValue = false) const;
void setPixelGridColor(const QColor & v) const;
qreal getPixelGridDrawingThreshold(bool defaultValue = false) const;
void setPixelGridDrawingThreshold(qreal v) const;
bool pixelGridEnabled(bool defaultValue = false) const;
void enablePixelGrid(bool v) const;
quint32 guidesLineStyle(bool defaultValue = false) const;
void setGuidesLineStyle(quint32 v) const;
QColor guidesColor(bool defaultValue = false) const;
void setGuidesColor(const QColor & v) const;
void loadSnapConfig(KisSnapConfig *config, bool defaultValue = false) const;
void saveSnapConfig(const KisSnapConfig &config);
qint32 checkSize(bool defaultValue = false) const;
void setCheckSize(qint32 checkSize) const;
bool scrollCheckers(bool defaultValue = false) const;
void setScrollingCheckers(bool scollCheckers) const;
QColor checkersColor1(bool defaultValue = false) const;
void setCheckersColor1(const QColor & v) const;
QColor checkersColor2(bool defaultValue = false) const;
void setCheckersColor2(const QColor & v) const;
QColor canvasBorderColor(bool defaultValue = false) const;
void setCanvasBorderColor(const QColor &color) const;
bool hideScrollbars(bool defaultValue = false) const;
void setHideScrollbars(bool value) const;
bool antialiasCurves(bool defaultValue = false) const;
void setAntialiasCurves(bool v) const;
bool antialiasSelectionOutline(bool defaultValue = false) const;
void setAntialiasSelectionOutline(bool v) const;
bool showRootLayer(bool defaultValue = false) const;
void setShowRootLayer(bool showRootLayer) const;
bool showGlobalSelection(bool defaultValue = false) const;
void setShowGlobalSelection(bool showGlobalSelection) const;
bool showOutlineWhilePainting(bool defaultValue = false) const;
void setShowOutlineWhilePainting(bool showOutlineWhilePainting) const;
bool forceAlwaysFullSizedOutline(bool defaultValue = false) const;
void setForceAlwaysFullSizedOutline(bool value) const;
enum SessionOnStartup {
SOS_BlankSession,
SOS_PreviousSession,
SOS_ShowSessionManager
};
SessionOnStartup sessionOnStartup(bool defaultValue = false) const;
void setSessionOnStartup(SessionOnStartup value);
bool saveSessionOnQuit(bool defaultValue) const;
void setSaveSessionOnQuit(bool value);
qreal outlineSizeMinimum(bool defaultValue = false) const;
void setOutlineSizeMinimum(qreal outlineSizeMinimum) const;
qreal selectionViewSizeMinimum(bool defaultValue = false) const;
void setSelectionViewSizeMinimum(qreal outlineSizeMinimum) const;
int autoSaveInterval(bool defaultValue = false) const;
void setAutoSaveInterval(int seconds) const;
bool backupFile(bool defaultValue = false) const;
void setBackupFile(bool backupFile) const;
bool showFilterGallery(bool defaultValue = false) const;
void setShowFilterGallery(bool showFilterGallery) const;
bool showFilterGalleryLayerMaskDialog(bool defaultValue = false) const;
void setShowFilterGalleryLayerMaskDialog(bool showFilterGallery) const;
// OPENGL_SUCCESS, TRY_OPENGL, OPENGL_NOT_TRIED, OPENGL_FAILED
QString canvasState(bool defaultValue = false) const;
void setCanvasState(const QString& state) const;
bool toolOptionsPopupDetached(bool defaultValue = false) const;
void setToolOptionsPopupDetached(bool detached) const;
bool paintopPopupDetached(bool defaultValue = false) const;
void setPaintopPopupDetached(bool detached) const;
QString pressureTabletCurve(bool defaultValue = false) const;
void setPressureTabletCurve(const QString& curveString) const;
bool useWin8PointerInput(bool defaultValue = false) const;
void setUseWin8PointerInput(bool value) const;
qreal vastScrolling(bool defaultValue = false) const;
void setVastScrolling(const qreal factor) const;
int presetChooserViewMode(bool defaultValue = false) const;
void setPresetChooserViewMode(const int mode) const;
int presetIconSize(bool defaultValue = false) const;
void setPresetIconSize(const int value) const;
bool firstRun(bool defaultValue = false) const;
void setFirstRun(const bool firstRun) const;
bool clicklessSpacePan(bool defaultValue = false) const;
void setClicklessSpacePan(const bool toggle) const;
int horizontalSplitLines(bool defaultValue = false) const;
void setHorizontalSplitLines(const int numberLines) const;
int verticalSplitLines(bool defaultValue = false) const;
void setVerticalSplitLines(const int numberLines) const;
bool hideDockersFullscreen(bool defaultValue = false) const;
void setHideDockersFullscreen(const bool value) const;
bool showDockers(bool defaultValue = false) const;
void setShowDockers(const bool value) const;
bool showStatusBar(bool defaultValue = false) const;
void setShowStatusBar(const bool value) const;
bool hideMenuFullscreen(bool defaultValue = false) const;
void setHideMenuFullscreen(const bool value) const;
bool hideScrollbarsFullscreen(bool defaultValue = false) const;
void setHideScrollbarsFullscreen(const bool value) const;
bool hideStatusbarFullscreen(bool defaultValue = false) const;
void setHideStatusbarFullscreen(const bool value) const;
bool hideTitlebarFullscreen(bool defaultValue = false) const;
void setHideTitlebarFullscreen(const bool value) const;
bool hideToolbarFullscreen(bool defaultValue = false) const;
void setHideToolbarFullscreen(const bool value) const;
bool fullscreenMode(bool defaultValue = false) const;
void setFullscreenMode(const bool value) const;
QStringList favoriteCompositeOps(bool defaultValue = false) const;
void setFavoriteCompositeOps(const QStringList& compositeOps) const;
QString exportConfiguration(const QString &filterId, bool defaultValue = false) const;
void setExportConfiguration(const QString &filterId, KisPropertiesConfigurationSP properties) const;
QString importConfiguration(const QString &filterId, bool defaultValue = false) const;
void setImportConfiguration(const QString &filterId, KisPropertiesConfigurationSP properties) const;
bool useOcio(bool defaultValue = false) const;
void setUseOcio(bool useOCIO) const;
int favoritePresets(bool defaultValue = false) const;
void setFavoritePresets(const int value);
bool levelOfDetailEnabled(bool defaultValue = false) const;
void setLevelOfDetailEnabled(bool value);
KisOcioConfiguration ocioConfiguration(bool defaultValue = false) const;
void setOcioConfiguration(const KisOcioConfiguration &cfg);
enum OcioColorManagementMode {
INTERNAL = 0,
OCIO_CONFIG,
OCIO_ENVIRONMENT
};
OcioColorManagementMode ocioColorManagementMode(bool defaultValue = false) const;
void setOcioColorManagementMode(OcioColorManagementMode mode) const;
int ocioLutEdgeSize(bool defaultValue = false) const;
void setOcioLutEdgeSize(int value);
bool ocioLockColorVisualRepresentation(bool defaultValue = false) const;
void setOcioLockColorVisualRepresentation(bool value);
bool useSystemMonitorProfile(bool defaultValue = false) const;
void setUseSystemMonitorProfile(bool _useSystemMonitorProfile) const;
QString defaultPalette(bool defaultValue = false) const;
void setDefaultPalette(const QString& name) const;
QString toolbarSlider(int sliderNumber, bool defaultValue = false) const;
void setToolbarSlider(int sliderNumber, const QString &slider);
int layerThumbnailSize(bool defaultValue = false) const;
void setLayerThumbnailSize(int size);
bool sliderLabels(bool defaultValue = false) const;
void setSliderLabels(bool enabled);
QString currentInputProfile(bool defaultValue = false) const;
void setCurrentInputProfile(const QString& name);
bool presetStripVisible(bool defaultValue = false) const;
void setPresetStripVisible(bool visible);
bool scratchpadVisible(bool defaultValue = false) const;
void setScratchpadVisible(bool visible);
bool showSingleChannelAsColor(bool defaultValue = false) const;
void setShowSingleChannelAsColor(bool asColor);
bool hidePopups(bool defaultValue = false) const;
void setHidePopups(bool hidepopups);
int numDefaultLayers(bool defaultValue = false) const;
void setNumDefaultLayers(int num);
quint8 defaultBackgroundOpacity(bool defaultValue = false) const;
void setDefaultBackgroundOpacity(quint8 value);
QColor defaultBackgroundColor(bool defaultValue = false) const;
void setDefaultBackgroundColor(QColor value);
enum BackgroundStyle {
- LAYER = 0,
- PROJECTION = 1
+ RASTER_LAYER = 0,
+ CANVAS_COLOR = 1,
+ FILL_LAYER = 2
};
BackgroundStyle defaultBackgroundStyle(bool defaultValue = false) const;
void setDefaultBackgroundStyle(BackgroundStyle value);
int lineSmoothingType(bool defaultValue = false) const;
void setLineSmoothingType(int value);
qreal lineSmoothingDistance(bool defaultValue = false) const;
void setLineSmoothingDistance(qreal value);
qreal lineSmoothingTailAggressiveness(bool defaultValue = false) const;
void setLineSmoothingTailAggressiveness(qreal value);
bool lineSmoothingSmoothPressure(bool defaultValue = false) const;
void setLineSmoothingSmoothPressure(bool value);
bool lineSmoothingScalableDistance(bool defaultValue = false) const;
void setLineSmoothingScalableDistance(bool value);
qreal lineSmoothingDelayDistance(bool defaultValue = false) const;
void setLineSmoothingDelayDistance(qreal value);
bool lineSmoothingUseDelayDistance(bool defaultValue = false) const;
void setLineSmoothingUseDelayDistance(bool value);
bool lineSmoothingFinishStabilizedCurve(bool defaultValue = false) const;
void setLineSmoothingFinishStabilizedCurve(bool value);
bool lineSmoothingStabilizeSensors(bool defaultValue = false) const;
void setLineSmoothingStabilizeSensors(bool value);
int tabletEventsDelay(bool defaultValue = false) const;
void setTabletEventsDelay(int value);
bool trackTabletEventLatency(bool defaultValue = false) const;
void setTrackTabletEventLatency(bool value);
bool testingAcceptCompressedTabletEvents(bool defaultValue = false) const;
void setTestingAcceptCompressedTabletEvents(bool value);
bool shouldEatDriverShortcuts(bool defaultValue = false) const;
bool testingCompressBrushEvents(bool defaultValue = false) const;
void setTestingCompressBrushEvents(bool value);
const KoColorSpace* customColorSelectorColorSpace(bool defaultValue = false) const;
void setCustomColorSelectorColorSpace(const KoColorSpace *cs);
bool useDirtyPresets(bool defaultValue = false) const;
void setUseDirtyPresets(bool value);
bool useEraserBrushSize(bool defaultValue = false) const;
void setUseEraserBrushSize(bool value);
bool useEraserBrushOpacity(bool defaultValue = false) const;
void setUseEraserBrushOpacity(bool value);
QColor getMDIBackgroundColor(bool defaultValue = false) const;
void setMDIBackgroundColor(const QColor & v) const;
QString getMDIBackgroundImage(bool defaultValue = false) const;
void setMDIBackgroundImage(const QString & fileName) const;
int workaroundX11SmoothPressureSteps(bool defaultValue = false) const;
bool showCanvasMessages(bool defaultValue = false) const;
void setShowCanvasMessages(bool show);
bool compressKra(bool defaultValue = false) const;
void setCompressKra(bool compress);
bool toolOptionsInDocker(bool defaultValue = false) const;
void setToolOptionsInDocker(bool inDocker);
bool kineticScrollingEnabled(bool defaultValue = false) const;
void setKineticScrollingEnabled(bool enabled);
int kineticScrollingGesture(bool defaultValue = false) const;
void setKineticScrollingGesture(int kineticScroll);
int kineticScrollingSensitivity(bool defaultValue = false) const;
void setKineticScrollingSensitivity(int sensitivity);
bool kineticScrollingHiddenScrollbars(bool defaultValue = false) const;
void setKineticScrollingHideScrollbars(bool scrollbar);
void setEnableOpenGLFramerateLogging(bool value) const;
bool enableOpenGLFramerateLogging(bool defaultValue = false) const;
void setEnableBrushSpeedLogging(bool value) const;
bool enableBrushSpeedLogging(bool defaultValue = false) const;
void setEnableAmdVectorizationWorkaround(bool value);
bool enableAmdVectorizationWorkaround(bool defaultValue = false) const;
bool animationDropFrames(bool defaultValue = false) const;
void setAnimationDropFrames(bool value);
int scrubbingUpdatesDelay(bool defaultValue = false) const;
void setScrubbingUpdatesDelay(int value);
int scrubbingAudioUpdatesDelay(bool defaultValue = false) const;
void setScrubbingAudioUpdatesDelay(int value);
int audioOffsetTolerance(bool defaultValue = false) const;
void setAudioOffsetTolerance(int value);
bool switchSelectionCtrlAlt(bool defaultValue = false) const;
void setSwitchSelectionCtrlAlt(bool value);
bool convertToImageColorspaceOnImport(bool defaultValue = false) const;
void setConvertToImageColorspaceOnImport(bool value);
int stabilizerSampleSize(bool defaultValue = false) const;
void setStabilizerSampleSize(int value);
bool stabilizerDelayedPaint(bool defaultValue = false) const;
void setStabilizerDelayedPaint(bool value);
QString customFFMpegPath(bool defaultValue = false) const;
void setCustomFFMpegPath(const QString &value) const;
bool showBrushHud(bool defaultValue = false) const;
void setShowBrushHud(bool value);
QString brushHudSetting(bool defaultValue = false) const;
void setBrushHudSetting(const QString &value) const;
bool calculateAnimationCacheInBackground(bool defaultValue = false) const;
void setCalculateAnimationCacheInBackground(bool value);
QColor defaultAssistantsColor(bool defaultValue = false) const;
void setDefaultAssistantsColor(const QColor &color) const;
bool autoSmoothBezierCurves(bool defaultValue = false) const;
void setAutoSmoothBezierCurves(bool value);
bool activateTransformToolAfterPaste(bool defaultValue = false) const;
void setActivateTransformToolAfterPaste(bool value);
enum RootSurfaceFormat {
BT709_G22 = 0,
BT709_G10,
BT2020_PQ
};
RootSurfaceFormat rootSurfaceFormat(bool defaultValue = false) const;
void setRootSurfaceFormat(RootSurfaceFormat value);
static RootSurfaceFormat rootSurfaceFormat(QSettings *displayrc, bool defaultValue = false);
static void setRootSurfaceFormat(QSettings *displayrc, RootSurfaceFormat value);
+ bool useZip64(bool defaultValue = false) const;
+ void setUseZip64(bool value);
+
template<class T>
void writeEntry(const QString& name, const T& value) {
m_cfg.writeEntry(name, value);
}
template<class T>
void writeList(const QString& name, const QList<T>& value) {
m_cfg.writeEntry(name, value);
}
template<class T>
T readEntry(const QString& name, const T& defaultValue=T()) {
return m_cfg.readEntry(name, defaultValue);
}
template<class T>
QList<T> readList(const QString& name, const QList<T>& defaultValue=QList<T>()) {
return m_cfg.readEntry(name, defaultValue);
}
/// get the profile the color management system has stored for the given screen
static const KoColorProfile* getScreenProfile(int screen);
private:
KisConfig(const KisConfig&);
KisConfig& operator=(const KisConfig&) const;
private:
mutable KConfigGroup m_cfg;
bool m_readOnly;
};
#endif // KIS_CONFIG_H_
diff --git a/libs/ui/kis_deferred_signal.h b/libs/ui/kis_deferred_signal.h
index 9b8181a5ab..4ea8c67f3a 100644
--- a/libs/ui/kis_deferred_signal.h
+++ b/libs/ui/kis_deferred_signal.h
@@ -1,71 +1,72 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_DEFERRED_SIGNAL_H
#define __KIS_DEFERRED_SIGNAL_H
#include <QObject>
#include <functional>
/**
- * \class KisDeferredSignal is used for calling a specified callback
+ * \class KisDeferredSignal
+ * \brief This class is used for calling a specified callback
* function (which is a std::function) after a specified time
* delay. The callback is called from the QTimer event, so the
* usage of the class does not block the Qt's event loop.
*
* Usage:
*
* \code{.cpp}
*
* // prepare the callback function
* std::function<void ()> callback(
* std::bind(&KisCanvas2::setMonitorProfile, this,
* monitorProfile, renderingIntent, conversionFlags));
*
* // create the timer connected to the function
* KisDeferredSignal::deferSignal(1000, callback);
*
* \endcode
*
* TODO: rename KisDeferredSignal -> KisDeferredCallback
*/
class KisDeferredSignal : public QObject
{
Q_OBJECT
public:
using CallbackFunction = std::function<void ()>;
public:
/**
* Creates a timer which will call \p function after \p delay
* milliseconds
*/
static void deferSignal(int delay, CallbackFunction function);
private Q_SLOTS:
void timeout();
private:
KisDeferredSignal(int delay, CallbackFunction function);
private:
CallbackFunction m_function;
};
#endif /* __KIS_DEFERRED_SIGNAL_H */
diff --git a/libs/ui/kis_image_manager.h b/libs/ui/kis_image_manager.h
index ea4371345f..49d17d743b 100644
--- a/libs/ui/kis_image_manager.h
+++ b/libs/ui/kis_image_manager.h
@@ -1,75 +1,76 @@
/* This file is part of the KDE project
* Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2006
*
* 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_IMAGE_MANAGER
#define KIS_IMAGE_MANAGER
#include <QObject>
#include <QPointer>
#include <QUrl>
#include <kritaui_export.h>
class KisViewManager;
class KisFilterStrategy;
class KisActionManager;
class KisView;
class KRITAUI_EXPORT KisImageManager : public QObject
{
Q_OBJECT
public:
KisImageManager(KisViewManager * view);
~KisImageManager() override {}
void setView(QPointer<KisView>imageView);
void setup(KisActionManager *actionManager);
public Q_SLOTS:
void slotImportLayerFromFile();
void slotImportLayerAsTransparencyMask();
void slotImportLayerAsFilterMask();
void slotImportLayerAsSelectionMask();
/**
* Import an image as a layer. If there is more than
* one layer in the image, import all of them as separate
* layers.
*
* @param url the url to the image file
+ * @param layerType the layer type
* @return the number of layers added
*/
qint32 importImage(const QUrl &url, const QString &layerType = "KisPaintLayer");
void resizeCurrentImage(qint32 w, qint32 h, qint32 xOffset, qint32 yOffset);
void scaleCurrentImage(const QSize &size, qreal xres, qreal yres, KisFilterStrategy *filterStrategy);
void rotateCurrentImage(double radians);
void shearCurrentImage(double angleX, double angleY);
void slotImageProperties();
void slotImageColor();
private:
KisViewManager * m_view;
};
#endif // KIS_IMAGE_MANAGER
diff --git a/libs/ui/kis_layer_manager.cc b/libs/ui/kis_layer_manager.cc
index c34be1e3f9..06706e11c7 100644
--- a/libs/ui/kis_layer_manager.cc
+++ b/libs/ui/kis_layer_manager.cc
@@ -1,967 +1,961 @@
/*
* Copyright (C) 2006 Boudewijn Rempt <boud@valdyas.org>
*
* 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 <QRect>
#include <QApplication>
#include <QCursor>
#include <QString>
#include <QDialog>
#include <QVBoxLayout>
#include <QFileInfo>
#include <QStandardPaths>
#include <kactioncollection.h>
#include <klocalizedstring.h>
#include <QMessageBox>
#include <QUrl>
#include <kis_file_name_requester.h>
#include <kis_icon.h>
#include <KisImportExportManager.h>
#include <KisDocument.h>
#include <KoColorSpace.h>
#include <KoCompositeOpRegistry.h>
#include <KoPointerEvent.h>
#include <KoColorProfile.h>
#include <KoSelection.h>
#include <KisPart.h>
#include <KisMainWindow.h>
#include <filter/kis_filter_configuration.h>
#include <filter/kis_filter.h>
#include <kis_filter_strategy.h>
#include <generator/kis_generator_layer.h>
#include <kis_file_layer.h>
#include <kis_adjustment_layer.h>
#include <kis_mask.h>
#include <kis_clone_layer.h>
#include <kis_group_layer.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <kis_paint_device.h>
#include <kis_selection.h>
#include <flake/kis_shape_layer.h>
#include <kis_undo_adapter.h>
#include <kis_painter.h>
#include <kis_meta_data_store.h>
#include <kis_meta_data_merge_strategy_registry.h>
#include <kis_psd_layer_style.h>
#include <KisMimeDatabase.h>
#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 "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 "KisImportExportManager.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 "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(QPointer<KisView>view)
{
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->resourceProvider()->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"));
+ 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);
+ 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<KisNodeSP> selectedNodes = m_view->nodeManager()->selectedNodes();
const bool multipleLayersSelected = selectedNodes.size() > 1;
- KisAdjustmentLayerSP alayer = KisAdjustmentLayerSP(dynamic_cast<KisAdjustmentLayer*>(layer.data()));
- KisGeneratorLayerSP glayer = KisGeneratorLayerSP(dynamic_cast<KisGeneratorLayer*>(layer.data()));
- KisFileLayerSP flayer = KisFileLayerSP(dynamic_cast<KisFileLayer*>(layer.data()));
+ KisAdjustmentLayerSP adjustmentLayer = KisAdjustmentLayerSP(dynamic_cast<KisAdjustmentLayer*>(layer.data()));
+ KisGeneratorLayerSP groupLayer = KisGeneratorLayerSP(dynamic_cast<KisGeneratorLayer*>(layer.data()));
+ KisFileLayerSP filterLayer = KisFileLayerSP(dynamic_cast<KisFileLayer*>(layer.data()));
- if (alayer && !multipleLayersSelected) {
+ if (adjustmentLayer && !multipleLayersSelected) {
- KisPaintDeviceSP dev = alayer->projection();
+ KisPaintDeviceSP dev = adjustmentLayer->projection();
- KisDlgAdjLayerProps dlg(alayer, alayer.data(), dev, m_view, alayer->filter().data(), alayer->name(), i18n("Filter Layer Properties"), m_view->mainWindow(), "dlgadjlayerprops");
+ 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(alayer->filter());
+ KisFilterConfigurationSP configBefore(adjustmentLayer->filter());
KIS_ASSERT_RECOVER_RETURN(configBefore);
QString xmlBefore = configBefore->toXML();
if (dlg.exec() == QDialog::Accepted) {
- alayer->setName(dlg.layerName());
+ adjustmentLayer->setName(dlg.layerName());
KisFilterConfigurationSP configAfter(dlg.filterConfiguration());
Q_ASSERT(configAfter);
QString xmlAfter = configAfter->toXML();
if(xmlBefore != xmlAfter) {
KisChangeFilterCmd *cmd
- = new KisChangeFilterCmd(alayer,
- configBefore->name(),
- xmlBefore,
- configAfter->name(),
- xmlAfter,
- false);
+ = 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) {
- alayer->setFilter(KisFilterRegistry::instance()->cloneConfiguration(configBefore.data()));
- alayer->setDirty();
+ adjustmentLayer->setFilter(KisFilterRegistry::instance()->cloneConfiguration(configBefore.data()));
+ adjustmentLayer->setDirty();
}
}
}
- else if (glayer && !multipleLayersSelected) {
-
- KisDlgGeneratorLayer dlg(glayer->name(), m_view, m_view->mainWindow());
- dlg.setCaption(i18n("Fill Layer Properties"));
-
- KisFilterConfigurationSP configBefore(glayer->filter());
+ else if (groupLayer && !multipleLayersSelected) {
+ KisFilterConfigurationSP configBefore(groupLayer->filter());
Q_ASSERT(configBefore);
QString xmlBefore = configBefore->toXML();
- dlg.setConfiguration(configBefore.data());
- dlg.resize(dlg.minimumSizeHint());
-
- if (dlg.exec() == QDialog::Accepted) {
+ KisDlgGeneratorLayer *dlg = new KisDlgGeneratorLayer(groupLayer->name(), m_view, m_view->mainWindow(), groupLayer, configBefore);
+ dlg->setCaption(i18n("Fill Layer Properties"));
+ dlg->setAttribute(Qt::WA_DeleteOnClose);
- glayer->setName(dlg.layerName());
+ dlg->setConfiguration(configBefore.data());
+ dlg->resize(dlg->minimumSizeHint());
- KisFilterConfigurationSP configAfter(dlg.configuration());
- Q_ASSERT(configAfter);
- QString xmlAfter = configAfter->toXML();
-
- if(xmlBefore != xmlAfter) {
- KisChangeFilterCmd *cmd
- = new KisChangeFilterCmd(glayer,
- configBefore->name(),
- xmlBefore,
- configAfter->name(),
- xmlAfter,
- true);
- // FIXME: check whether is needed
- cmd->redo();
- m_view->undoAdapter()->addCommand(cmd);
- m_view->document()->setModified(true);
- }
+ Qt::WindowFlags flags = dlg->windowFlags();
+ dlg->setWindowFlags(flags | Qt::WindowStaysOnTopHint | Qt::Dialog);
+ dlg->show();
- }
- } else if (flayer && !multipleLayersSelected){
+ }
+ else if (filterLayer && !multipleLayersSelected){
QString basePath = QFileInfo(m_view->document()->url().toLocalFile()).absolutePath();
- QString fileNameOld = flayer->fileName();
- KisFileLayer::ScalingMethod scalingMethodOld = flayer->scalingMethod();
- KisDlgFileLayer dlg(basePath, flayer->name(), m_view->mainWindow());
+ 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;
}
- flayer->setName(dlg.layerName());
+ filterLayer->setName(dlg.layerName());
if (fileNameOld!= fileNameNew || scalingMethodOld != scalingMethodNew) {
KisChangeFileLayerCmd *cmd
- = new KisChangeFileLayerCmd(flayer,
- basePath,
- fileNameOld,
- scalingMethodOld,
- basePath,
- fileNameNew,
- scalingMethodNew);
+ = 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<KisNodeSP> 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::convertNodeToPaintLayer(KisNodeSP source)
{
KisImageWSP image = m_view->image();
if (!image) return;
KisLayer *srcLayer = qobject_cast<KisLayer*>(source.data());
if (srcLayer && (srcLayer->inherits("KisGroupLayer") || srcLayer->layerStyle() || srcLayer->childCount() > 0)) {
image->flattenLayer(srcLayer);
return;
}
KisPaintDeviceSP srcDevice =
- source->paintDevice() ? source->projection() : source->original();
+ source->paintDevice() ? source->projection() : source->original();
bool putBehind = false;
QString newCompositeOp = source->compositeOpId();
KisColorizeMask *colorizeMask = dynamic_cast<KisColorizeMask*>(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()) {
+ *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<KisGroupLayer*>(activeLayer().data());
if (group.isNull()) return;
KisPaintLayerSP animatedLayer = new KisPaintLayer(m_view->image(), group->name(), OPACITY_OPAQUE_U8);
animatedLayer->enableAnimation();
KisRasterKeyframeChannel *contentChannel = dynamic_cast<KisRasterKeyframeChannel*>(
- animatedLayer->getKeyframeChannel(KisKeyframeChannel::Content.id(), true));
+ 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<KisDocument> 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";
+ << "No node accepted newly created node";
parent = m_view->image()->root();
above = parent->lastChild();
}
}
void KisLayerManager::addLayerCommon(KisNodeSP activeNode, KisNodeSP layer, bool updateImage)
{
KisNodeSP parent;
KisNodeSP above;
adjustLayerPosition(layer, activeNode, parent, above);
KisGroupLayer *group = dynamic_cast<KisGroupLayer*>(parent.data());
const bool parentForceUpdate = group && !group->projectionIsValid();
updateImage |= parentForceUpdate;
m_commandsAdapter->addNode(layer, parent, above, updateImage, updateImage);
}
KisLayerSP KisLayerManager::addPaintLayer(KisNodeSP activeNode)
{
- KisLayerSP layer = KisLayerUtils::constructDefaultLayer(m_view->image());
+ KisImageWSP image = m_view->image();
+ KisLayerSP layer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, image->colorSpace());
addLayerCommon(activeNode, layer, false);
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);
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);
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);
return layer;
}
KisNodeSP KisLayerManager::addAdjustmentLayer(KisNodeSP activeNode)
{
KisImageWSP image = m_view->image();
KisSelectionSP selection = m_view->selection();
KisAdjustmentLayerSP adjl = addAdjustmentLayer(activeNode, QString(), 0, selection);
image->refreshGraph();
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!
m_commandsAdapter->undoLastCommand();
} else {
adjl->setName(dlg.layerName());
}
return adjl;
}
KisAdjustmentLayerSP KisLayerManager::addAdjustmentLayer(KisNodeSP activeNode, const QString & name,
KisFilterConfigurationSP filter, KisSelectionSP selection)
{
KisImageWSP image = m_view->image();
KisAdjustmentLayerSP layer = new KisAdjustmentLayer(image, name, filter, selection);
addLayerCommon(activeNode, layer);
return layer;
}
KisNodeSP KisLayerManager::addGeneratorLayer(KisNodeSP activeNode)
{
KisImageWSP image = m_view->image();
+ QColor currentForeground = m_view->resourceProvider()->fgColor().toQColor();
- KisDlgGeneratorLayer dlg(image->nextLayerName(), m_view, m_view->mainWindow());
- dlg.resize(dlg.minimumSizeHint());
+ 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 );
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<KisSelectionMask*>(node.data());
}
bool tryMergeSelectionMasks(KisNodeSP currentNode, KisImageSP image)
{
bool result = false;
KisNodeSP prevNode = currentNode->prevSibling();
if (isSelectionMask(currentNode) &&
- prevNode && isSelectionMask(prevNode)) {
+ prevNode && isSelectionMask(prevNode)) {
QList<KisNodeSP> 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<KisGroupLayer*>(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<KisNodeSP> selectedNodes = m_view->nodeManager()->selectedNodes();
- if (selectedNodes.size() > 1) {
+ if (selectedNodes.size() > 1) {
image->mergeMultipleLayers(selectedNodes, m_view->activeNode());
}
- else if (tryMergeSelectionMasks(m_view->activeNode(), image)) {
+ 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<KisLayer*>(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);
+ 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);
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->resourceProvider());
std::function<void ()> 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));
+ new KisSetLayerStyleCommand(layer, oldStyle, newStyle));
image->postExecutionUndoAdapter()->addCommand(command);
}
}
diff --git a/libs/ui/kis_model_index_converter_base.h b/libs/ui/kis_model_index_converter_base.h
index 92ceef1884..a36c7d8511 100644
--- a/libs/ui/kis_model_index_converter_base.h
+++ b/libs/ui/kis_model_index_converter_base.h
@@ -1,78 +1,82 @@
/*
* Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_MODEL_INDEX_CONVERTER_BASE_H
#define __KIS_MODEL_INDEX_CONVERTER_BASE_H
#include <QModelIndex>
#include "kritaui_export.h"
class KisNodeDummy;
/**
* The base class for converting objects to/from QModelIndex used
* in KisNodeModel and KisNodeDummy used in KisDummiesFacadeBase
* (KisShapeController).
*
* This is not a trivial task, because the indexing of nodes is
* reversed in KisNodeModel.
*/
class KRITAUI_EXPORT KisModelIndexConverterBase
{
public:
virtual ~KisModelIndexConverterBase();
/**
* Returns the dummy staying in the specified \p row of a \p parent
- * May return null in case of incosistency
+ * May return null in case of inconsistency
*/
virtual KisNodeDummy* dummyFromRow(int row, QModelIndex parent) = 0;
/**
* Returns the dummy associated with the \p index
* WARNING: \p index must be valid
- * NOTE: cannot return null
+ * \note cannot return null
*/
virtual KisNodeDummy* dummyFromIndex(QModelIndex index) = 0;
/**
* Returns the index corresponding to the position of the \p dummy
* in the model. Will return invalid index if the dummy should be hidden
*/
virtual QModelIndex indexFromDummy(KisNodeDummy *dummy) = 0;
/**
* Calculates the parent and the position in the model for newly created dummy
+ * \param parentDummy the dummy parent
+ * \param index the dummy index
* \param newNodeMetaObjectType is a class name of a newly added node
* This name is got from Qt's meta object system so you must
* compare this value against a corresponding staticMetaObject
* object only.
* We do not pass a pointer to a real node to limit the access to
* real nodes.
- * Return whether the new dummy will be shown in the model
+ * \param parentIndex the parent index
+ * \param row the dummy row
+ * \return whether the new dummy will be shown in the model
*/
virtual bool indexFromAddedDummy(KisNodeDummy *parentDummy, int index, const QString &newNodeMetaObjectType, QModelIndex &parentIndex, int &row) = 0;
/**
* Returns the number of children of the given index of the model
*/
virtual int rowCount(QModelIndex parent) = 0;
};
#endif /* __KIS_MODEL_INDEX_CONVERTER_BASE_H */
diff --git a/libs/ui/kis_node_view_color_scheme.cpp b/libs/ui/kis_node_view_color_scheme.cpp
index 384a8d23fa..f875165465 100644
--- a/libs/ui/kis_node_view_color_scheme.cpp
+++ b/libs/ui/kis_node_view_color_scheme.cpp
@@ -1,194 +1,199 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_view_color_scheme.h"
#include <QTreeView>
#include <QStyle>
#include "krita_utils.h"
#include <QApplication>
#include <QGlobalStatic>
#include <kis_config.h>
Q_GLOBAL_STATIC(KisNodeViewColorScheme, s_instance)
struct KisNodeViewColorScheme::Private
{
Private() {
if (colorLabels.isEmpty()) {
colorLabels << Qt::transparent;
colorLabels << QColor(91,173,220);
colorLabels << QColor(151,202,63);
colorLabels << QColor(247,229,61);
colorLabels << QColor(255,170,63);
colorLabels << QColor(177,102,63);
colorLabels << QColor(238,50,51);
colorLabels << QColor(191,106,209);
colorLabels << QColor(118,119,114);
const QColor noLabelSetColor = qApp->palette().color(QPalette::Highlight);
for (auto it = colorLabels.begin(); it != colorLabels.end(); ++it) {
KritaUtils::dragColor(&(*it), noLabelSetColor, 0.35);
}
}
}
static QVector<QColor> colorLabels;
};
QVector<QColor> KisNodeViewColorScheme::Private::colorLabels;
KisNodeViewColorScheme::KisNodeViewColorScheme()
: m_d(new Private)
{
}
KisNodeViewColorScheme::~KisNodeViewColorScheme()
{
}
KisNodeViewColorScheme* KisNodeViewColorScheme::instance()
{
return s_instance;
}
QColor KisNodeViewColorScheme::gridColor(const QStyleOptionViewItem &option, QTreeView *view)
{
const int gridHint = view->style()->styleHint(QStyle::SH_Table_GridLineColor, &option, view);
const QColor gridColor = static_cast<QRgb>(gridHint);
return gridColor;
}
int KisNodeViewColorScheme::visibilitySize() const
{
return 16;
}
int KisNodeViewColorScheme::visibilityMargin() const
{
return 2;
}
int KisNodeViewColorScheme::thumbnailSize() const
{
KisConfig cfg(true);
return cfg.layerThumbnailSize(false);
}
int KisNodeViewColorScheme::thumbnailMargin() const
{
return 3;
}
int KisNodeViewColorScheme::decorationSize() const
{
return 12;
}
int KisNodeViewColorScheme::decorationMargin() const
{
return 1;
}
int KisNodeViewColorScheme::textMargin() const
{
return 2;
}
int KisNodeViewColorScheme::iconSize() const
{
return 16;
}
int KisNodeViewColorScheme::iconMargin() const
{
return 1;
}
int KisNodeViewColorScheme::border() const
{
return 1;
}
int KisNodeViewColorScheme::rowHeight() const
{
return border() + 2 * thumbnailMargin() + thumbnailSize();
}
int KisNodeViewColorScheme::visibilityColumnWidth() const
{
return border() +
2 * visibilityMargin() + visibilitySize() +
border();
}
int KisNodeViewColorScheme::indentation() const
{
return
2 * thumbnailMargin() + thumbnailSize() +
border();
}
+QRect KisNodeViewColorScheme::relVisibilityRect() const
+{
+ return QRect(0, 0,
+ visibilitySize() + 2 * visibilityMargin() + 2 * border(),
+ visibilitySize() + 2 * visibilityMargin() + 1 * border());
+}
+
QRect KisNodeViewColorScheme::relThumbnailRect() const
{
- return QRect(-indentation(),
- border(),
- thumbnailSize() + 2 * thumbnailMargin(),
- thumbnailSize() + 2 * thumbnailMargin());
+ return QRect(0, 0,
+ thumbnailSize() + 2 * thumbnailMargin() + 2 * border(),
+ thumbnailSize() + 2 * thumbnailMargin() + 1 * border());
}
QRect KisNodeViewColorScheme::relDecorationRect() const
{
- return QRect(border() + decorationMargin(),
- border() + decorationMargin(),
- decorationSize(),
- decorationSize());
+ return QRect(0, 0,
+ decorationSize() + 2 * decorationMargin() + 2 * border(),
+ decorationSize() + 2 * decorationMargin() + 1 * border());
}
QRect KisNodeViewColorScheme::relExpandButtonRect() const
{
const int newY = rowHeight() - decorationMargin() - decorationSize();
QRect rc = relDecorationRect();
rc.moveTop(newY);
return rc;
}
QColor KisNodeViewColorScheme::colorLabel(int index) const
{
/**
* We should ensure that the index of the overflowing range
* will never be zero again.
*/
if (index >= m_d->colorLabels.size()) {
index = 1 + index % (m_d->colorLabels.size() - 1);
} else {
index = index % m_d->colorLabels.size();
}
return m_d->colorLabels[index];
}
QVector<QColor> KisNodeViewColorScheme::allColorLabels() const
{
return m_d->colorLabels;
}
diff --git a/libs/ui/kis_node_view_color_scheme.h b/libs/ui/kis_node_view_color_scheme.h
index ef96293a47..afb1de6945 100644
--- a/libs/ui/kis_node_view_color_scheme.h
+++ b/libs/ui/kis_node_view_color_scheme.h
@@ -1,73 +1,74 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_VIEW_COLOR_SCHEME_H
#define __KIS_NODE_VIEW_COLOR_SCHEME_H
#include <QScopedPointer>
#include <QColor>
#include "kritaui_export.h"
class QTreeView;
class QStyleOptionViewItem;
class QRect;
class KRITAUI_EXPORT KisNodeViewColorScheme
{
public:
KisNodeViewColorScheme();
~KisNodeViewColorScheme();
static KisNodeViewColorScheme* instance();
QColor gridColor(const QStyleOptionViewItem &option, QTreeView *view);
int visibilitySize() const;
int visibilityMargin() const;
int thumbnailSize() const;
int thumbnailMargin() const;
int decorationSize() const;
int decorationMargin() const;
int textMargin() const;
int iconSize() const;
int iconMargin() const;
int border() const;
int rowHeight() const;
int visibilityColumnWidth() const;
int indentation() const;
+ QRect relVisibilityRect() const;
QRect relThumbnailRect() const;
QRect relDecorationRect() const;
QRect relExpandButtonRect() const;
QColor colorLabel(int index) const;
QVector<QColor> allColorLabels() const;
private:
struct Private;
const QScopedPointer<Private> m_d;
};
#endif /* __KIS_NODE_VIEW_COLOR_SCHEME_H */
diff --git a/libs/ui/kis_painting_assistants_decoration.cpp b/libs/ui/kis_painting_assistants_decoration.cpp
index de5fe80866..a782900435 100644
--- a/libs/ui/kis_painting_assistants_decoration.cpp
+++ b/libs/ui/kis_painting_assistants_decoration.cpp
@@ -1,484 +1,490 @@
/*
* Copyright (c) 2009 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2017 Scott Petrovic <scottpetrovic@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_painting_assistants_decoration.h"
#include <cfloat>
#include <QList>
#include <QPointF>
#include <klocalizedstring.h>
#include <kactioncollection.h>
#include <ktoggleaction.h>
#include "kis_debug.h"
#include "KisDocument.h"
#include "kis_canvas2.h"
#include "kis_icon_utils.h"
#include "KisViewManager.h"
#include <QPainter>
#include <QApplication>
struct KisPaintingAssistantsDecoration::Private {
Private()
: assistantVisible(false)
, outlineVisible(false)
, snapOnlyOneAssistant(true)
, firstAssistant(0)
, aFirstStroke(false)
, m_handleSize(14)
{}
bool assistantVisible;
bool outlineVisible;
bool snapOnlyOneAssistant;
KisPaintingAssistantSP firstAssistant;
KisPaintingAssistantSP selectedAssistant;
bool aFirstStroke;
bool m_isEditingAssistants = false;
bool m_outlineVisible = false;
int m_handleSize; // size of editor handles on assistants
// move, visibility, delete icons for each assistant. These only display while the assistant tool is active
// these icons will be covered by the kis_paintint_assistant_decoration with things like the perspective assistant
AssistantEditorData toolData;
QPixmap m_iconDelete = KisIconUtils::loadIcon("dialog-cancel").pixmap(toolData.deleteIconSize, toolData.deleteIconSize);
QPixmap m_iconSnapOn = KisIconUtils::loadIcon("visible").pixmap(toolData.snapIconSize, toolData.snapIconSize);
QPixmap m_iconSnapOff = KisIconUtils::loadIcon("novisible").pixmap(toolData.snapIconSize, toolData.snapIconSize);
QPixmap m_iconMove = KisIconUtils::loadIcon("transform-move").pixmap(toolData.moveIconSize, toolData.moveIconSize);
KisCanvas2 * m_canvas = 0;
};
KisPaintingAssistantsDecoration::KisPaintingAssistantsDecoration(QPointer<KisView> parent) :
KisCanvasDecoration("paintingAssistantsDecoration", parent),
d(new Private)
{
setAssistantVisible(true);
setOutlineVisible(true);
setPriority(95);
d->snapOnlyOneAssistant = true; //turn on by default.
}
KisPaintingAssistantsDecoration::~KisPaintingAssistantsDecoration()
{
delete d;
}
void KisPaintingAssistantsDecoration::addAssistant(KisPaintingAssistantSP assistant)
{
QList<KisPaintingAssistantSP> assistants = view()->document()->assistants();
if (assistants.contains(assistant)) return;
assistants.append(assistant);
assistant->setAssistantGlobalColorCache(view()->document()->assistantsGlobalColor());
view()->document()->setAssistants(assistants);
setVisible(!assistants.isEmpty());
emit assistantChanged();
}
void KisPaintingAssistantsDecoration::removeAssistant(KisPaintingAssistantSP assistant)
{
QList<KisPaintingAssistantSP> assistants = view()->document()->assistants();
KIS_ASSERT_RECOVER_NOOP(assistants.contains(assistant));
if (assistants.removeAll(assistant)) {
view()->document()->setAssistants(assistants);
setVisible(!assistants.isEmpty());
emit assistantChanged();
}
}
void KisPaintingAssistantsDecoration::removeAll()
{
QList<KisPaintingAssistantSP> assistants = view()->document()->assistants();
assistants.clear();
view()->document()->setAssistants(assistants);
setVisible(!assistants.isEmpty());
emit assistantChanged();
}
QPointF KisPaintingAssistantsDecoration::adjustPosition(const QPointF& point, const QPointF& strokeBegin)
{
if (assistants().empty()) {
return point;
}
if (assistants().count() == 1) {
if(assistants().first()->isSnappingActive() == true){
QPointF newpoint = assistants().first()->adjustPosition(point, strokeBegin);
// check for NaN
if (newpoint.x() != newpoint.x()) return point;
return newpoint;
}
}
QPointF best = point;
double distance = DBL_MAX;
//the following tries to find the closest point to stroke-begin. It checks all assistants for the closest point//
if(!d->snapOnlyOneAssistant){
Q_FOREACH (KisPaintingAssistantSP assistant, assistants()) {
if(assistant->isSnappingActive() == true){//this checks if the assistant in question has it's snapping boolean turned on//
QPointF pt = assistant->adjustPosition(point, strokeBegin);
if (pt.x() != pt.x()) continue;
double dist = qAbs(pt.x() - point.x()) + qAbs(pt.y() - point.y());
if (dist < distance) {
best = pt;
distance = dist;
}
}
}
} else if (d->aFirstStroke==false) {
Q_FOREACH (KisPaintingAssistantSP assistant, assistants()) {
if(assistant->isSnappingActive() == true){//this checks if the assistant in question has it's snapping boolean turned on//
QPointF pt = assistant->adjustPosition(point, strokeBegin);
if (pt.x() != pt.x()) continue;
double dist = qAbs(pt.x() - point.x()) + qAbs(pt.y() - point.y());
if (dist < distance) {
best = pt;
distance = dist;
d->firstAssistant = assistant;
}
}
}
} else if(d->firstAssistant) {
//make sure there's a first assistant to begin with.//
- best = d->firstAssistant->adjustPosition(point, strokeBegin);
+ QPointF newpoint = d->firstAssistant->adjustPosition(point, strokeBegin);
+ // BUGFIX: 402535
+ // assistants might return (NaN,NaN), must always check for that
+ if (newpoint.x() == newpoint.x()) {
+ // not a NaN
+ best = newpoint;
+ }
} else {
d->aFirstStroke=false;
}
//this is here to be compatible with the movement in the perspective tool.
qreal dx = point.x() - strokeBegin.x(), dy = point.y() - strokeBegin.y();
if (dx * dx + dy * dy >= 4.0) {
// allow some movement before snapping
d->aFirstStroke=true;
}
return best;
}
void KisPaintingAssistantsDecoration::endStroke()
{
d->aFirstStroke = false;
Q_FOREACH (KisPaintingAssistantSP assistant, assistants()) {
assistant->endStroke();
}
}
void KisPaintingAssistantsDecoration::drawDecoration(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter,KisCanvas2* canvas)
{
if(assistants().length() == 0) {
return; // no assistants to worry about, ok to exit
}
if (!canvas) {
dbgFile<<"canvas does not exist in painting assistant decoration, you may have passed arguments incorrectly:"<<canvas;
} else {
d->m_canvas = canvas;
}
// the preview functionality for assistants. do not show while editing
if (d->m_isEditingAssistants) {
d->m_outlineVisible = false;
}
else {
d->m_outlineVisible = outlineVisibility();
}
Q_FOREACH (KisPaintingAssistantSP assistant, assistants()) {
assistant->drawAssistant(gc, updateRect, converter, true, canvas, assistantVisibility(), d->m_outlineVisible);
if (isEditingAssistants()) {
drawHandles(assistant, gc, converter);
}
}
// draw editor controls on top of all assistant lines (why this code is last)
if (isEditingAssistants()) {
Q_FOREACH (KisPaintingAssistantSP assistant, assistants()) {
drawEditorWidget(assistant, gc, converter);
}
}
}
void KisPaintingAssistantsDecoration::drawHandles(KisPaintingAssistantSP assistant, QPainter& gc, const KisCoordinatesConverter *converter)
{
QTransform initialTransform = converter->documentToWidgetTransform();
QColor colorToPaint = assistant->effectiveAssistantColor();
Q_FOREACH (const KisPaintingAssistantHandleSP handle, assistant->handles()) {
QPointF transformedHandle = initialTransform.map(*handle);
QRectF ellipse(transformedHandle - QPointF(handleSize() * 0.5, handleSize() * 0.5), QSizeF(handleSize(), handleSize()));
QPainterPath path;
path.addEllipse(ellipse);
gc.save();
gc.setPen(Qt::NoPen);
gc.setBrush(colorToPaint);
gc.drawPath(path);
gc.restore();
}
// some assistants have side handles like the vanishing point assistant
Q_FOREACH (const KisPaintingAssistantHandleSP handle, assistant->sideHandles()) {
QPointF transformedHandle = initialTransform.map(*handle);
QRectF ellipse(transformedHandle - QPointF(handleSize() * 0.5, handleSize() * 0.5), QSizeF(handleSize(), handleSize()));
QPainterPath path;
path.addEllipse(ellipse);
gc.save();
gc.setPen(Qt::NoPen);
gc.setBrush(colorToPaint);
gc.drawPath(path);
gc.restore();
}
}
int KisPaintingAssistantsDecoration::handleSize()
{
return d->m_handleSize;
}
void KisPaintingAssistantsDecoration::setHandleSize(int handleSize)
{
d->m_handleSize = handleSize;
}
QList<KisPaintingAssistantHandleSP> KisPaintingAssistantsDecoration::handles()
{
QList<KisPaintingAssistantHandleSP> hs;
Q_FOREACH (KisPaintingAssistantSP assistant, assistants()) {
Q_FOREACH (const KisPaintingAssistantHandleSP handle, assistant->handles()) {
if (!hs.contains(handle)) {
hs.push_back(handle);
}
}
Q_FOREACH (const KisPaintingAssistantHandleSP handle, assistant->sideHandles()) {
if (!hs.contains(handle)) {
hs.push_back(handle);
}
}
}
return hs;
}
QList<KisPaintingAssistantSP> KisPaintingAssistantsDecoration::assistants() const
{
QList<KisPaintingAssistantSP> assistants = view()->document()->assistants();
return assistants;
}
KisPaintingAssistantSP KisPaintingAssistantsDecoration::selectedAssistant()
{
return d->selectedAssistant;
}
void KisPaintingAssistantsDecoration::setSelectedAssistant(KisPaintingAssistantSP assistant)
{
d->selectedAssistant = assistant;
emit selectedAssistantChanged();
}
void KisPaintingAssistantsDecoration::deselectAssistant()
{
d->selectedAssistant.clear();
}
void KisPaintingAssistantsDecoration::setAssistantVisible(bool set)
{
d->assistantVisible=set;
}
void KisPaintingAssistantsDecoration::setOutlineVisible(bool set)
{
d->outlineVisible=set;
}
void KisPaintingAssistantsDecoration::setOnlyOneAssistantSnap(bool assistant)
{
d->snapOnlyOneAssistant = assistant;
}
bool KisPaintingAssistantsDecoration::assistantVisibility()
{
return d->assistantVisible;
}
bool KisPaintingAssistantsDecoration::outlineVisibility()
{
return d->outlineVisible;
}
void KisPaintingAssistantsDecoration::uncache()
{
Q_FOREACH (KisPaintingAssistantSP assistant, assistants()) {
assistant->uncache();
}
}
void KisPaintingAssistantsDecoration::toggleAssistantVisible()
{
setAssistantVisible(!assistantVisibility());
uncache();
}
void KisPaintingAssistantsDecoration::toggleOutlineVisible()
{
setOutlineVisible(!outlineVisibility());
}
QColor KisPaintingAssistantsDecoration::globalAssistantsColor()
{
return view()->document()->assistantsGlobalColor();
}
void KisPaintingAssistantsDecoration::setGlobalAssistantsColor(QColor color)
{
// view()->document() is referenced multiple times in this class
// it is used to later store things in the KRA file when saving.
view()->document()->setAssistantsGlobalColor(color);
Q_FOREACH (KisPaintingAssistantSP assistant, assistants()) {
assistant->setAssistantGlobalColorCache(color);
}
uncache();
}
void KisPaintingAssistantsDecoration::activateAssistantsEditor()
{
setVisible(true); // this turns on the decorations in general. we leave it on at this point
d->m_isEditingAssistants = true;
uncache(); // updates visuals when editing
}
void KisPaintingAssistantsDecoration::deactivateAssistantsEditor()
{
if (!d->m_canvas) {
return;
}
d->m_isEditingAssistants = false; // some elements are hidden when we aren't editing
uncache(); // updates visuals when not editing
}
bool KisPaintingAssistantsDecoration::isEditingAssistants()
{
return d->m_isEditingAssistants;
}
QPointF KisPaintingAssistantsDecoration::snapToGuide(KoPointerEvent *e, const QPointF &offset, bool useModifiers)
{
if (!d->m_canvas || !d->m_canvas->currentImage()) {
return e->point;
}
KoSnapGuide *snapGuide = d->m_canvas->snapGuide();
QPointF pos = snapGuide->snap(e->point, offset, useModifiers ? e->modifiers() : Qt::NoModifier);
return pos;
}
QPointF KisPaintingAssistantsDecoration::snapToGuide(const QPointF& pt, const QPointF &offset)
{
if (!d->m_canvas) {
return pt;
}
KoSnapGuide *snapGuide = d->m_canvas->snapGuide();
QPointF pos = snapGuide->snap(pt, offset, Qt::NoModifier);
return pos;
}
/*
* functions only used internally in this class
* we potentially could make some of these inline to speed up performance
*/
void KisPaintingAssistantsDecoration::drawEditorWidget(KisPaintingAssistantSP assistant, QPainter& gc, const KisCoordinatesConverter *converter)
{
if (!assistant->isAssistantComplete()) {
return;
}
QTransform initialTransform = converter->documentToWidgetTransform();
// We are going to put all of the assistant actions below the bounds of the assistant
// so they are out of the way
// assistant->buttonPosition() gets the center X/Y position point
QPointF actionsPosition = initialTransform.map(assistant->buttonPosition());
AssistantEditorData toolData; // shared const data for positioning and sizing
QPointF iconMovePosition(actionsPosition + toolData.moveIconPosition);
QPointF iconSnapPosition(actionsPosition + toolData.snapIconPosition);
QPointF iconDeletePosition(actionsPosition + toolData.deleteIconPosition);
// Background container for helpers
QBrush backgroundColor = d->m_canvas->viewManager()->mainWindow()->palette().window();
QPointF actionsBGRectangle(actionsPosition + QPointF(10, 10));
gc.setRenderHint(QPainter::Antialiasing);
QPainterPath bgPath;
bgPath.addRoundedRect(QRectF(actionsBGRectangle.x(), actionsBGRectangle.y(), 110, 40), 6, 6);
QPen stroke(QColor(60, 60, 60, 80), 2);
// if the assistant is selected, make outline stroke fatter and use theme's highlight color
// for better visual feedback
if (selectedAssistant()) { // there might not be a selected assistant, so do not seg fault
if (assistant->buttonPosition() == selectedAssistant()->buttonPosition()) {
stroke.setWidth(4);
stroke.setColor(qApp->palette().color(QPalette::Highlight));
}
}
// draw the final result
gc.setPen(stroke);
gc.fillPath(bgPath, backgroundColor);
gc.drawPath(bgPath);
// Move Assistant Tool helper
gc.drawPixmap(iconMovePosition, d->m_iconMove);
// active toggle
if (assistant->isSnappingActive() == true) {
gc.drawPixmap(iconSnapPosition, d->m_iconSnapOn);
}
else {
gc.drawPixmap(iconSnapPosition, d->m_iconSnapOff);
}
gc.drawPixmap(iconDeletePosition, d->m_iconDelete);
}
diff --git a/libs/ui/kis_paintop_box.cc b/libs/ui/kis_paintop_box.cc
index f580a74490..f63946d9ce 100644
--- a/libs/ui/kis_paintop_box.cc
+++ b/libs/ui/kis_paintop_box.cc
@@ -1,1346 +1,1348 @@
/*
* kis_paintop_box.cc - part of KImageShop/Krayon/Krita
*
* Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org)
* Copyright (c) 2009-2011 Sven Langkamp (sven.langkamp@gmail.com)
* Copyright (c) 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
* Copyright (C) 2011 Silvio Heinrich <plassy@web.de>
* Copyright (C) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
* Copyright (c) 2014 Mohit Goyal <mohit.bits2011@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_paintop_box.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QToolButton>
#include <QPixmap>
#include <QWidgetAction>
#include <QApplication>
#include <QMenu>
#include <QTime>
#include <kis_debug.h>
#include <kactioncollection.h>
#include <kacceleratormanager.h>
#include <QKeySequence>
#include <kis_icon.h>
#include <KoColorSpace.h>
#include <KoCompositeOpRegistry.h>
#include <KoResourceSelector.h>
#include <KoResourceServerAdapter.h>
#include <KoToolManager.h>
#include <KoColorSpaceRegistry.h>
#include <kis_paint_device.h>
#include <brushengine/kis_paintop_registry.h>
#include <brushengine/kis_paintop_preset.h>
#include <brushengine/kis_paintop_settings.h>
#include <brushengine/kis_paintop_settings_update_proxy.h>
#include <kis_config_widget.h>
#include <kis_image.h>
#include <kis_node.h>
#include <brushengine/kis_paintop_config_widget.h>
#include <kis_action.h>
#include "kis_canvas2.h"
#include "kis_node_manager.h"
#include "KisViewManager.h"
#include "kis_canvas_resource_provider.h"
#include "KisResourceServerProvider.h"
#include "kis_favorite_resource_manager.h"
#include "kis_config.h"
#include "kis_popup_button.h"
#include "widgets/kis_iconwidget.h"
#include "widgets/kis_tool_options_popup.h"
#include "widgets/kis_paintop_presets_popup.h"
#include "widgets/kis_paintop_presets_chooser_popup.h"
#include "widgets/kis_workspace_chooser.h"
#include "widgets/kis_paintop_list_widget.h"
#include "widgets/kis_slider_spin_box.h"
#include "widgets/kis_cmb_composite.h"
#include "widgets/kis_widget_chooser.h"
#include "tool/kis_tool.h"
#include "kis_signals_blocker.h"
#include "kis_action_manager.h"
#include "kis_highlighted_button.h"
typedef KoResourceServerSimpleConstruction<KisPaintOpPreset, SharedPointerStoragePolicy<KisPaintOpPresetSP> > KisPaintOpPresetResourceServer;
typedef KoResourceServerAdapter<KisPaintOpPreset, SharedPointerStoragePolicy<KisPaintOpPresetSP> > KisPaintOpPresetResourceServerAdapter;
KisPaintopBox::KisPaintopBox(KisViewManager *view, QWidget *parent, const char *name)
: QWidget(parent)
, m_resourceProvider(view->resourceProvider())
, m_optionWidget(0)
, m_toolOptionsPopupButton(0)
, m_brushEditorPopupButton(0)
, m_presetSelectorPopupButton(0)
, m_toolOptionsPopup(0)
, m_viewManager(view)
, m_previousNode(0)
, m_currTabletToolID(KoInputDevice::invalid())
, m_presetsEnabled(true)
, m_blockUpdate(false)
, m_dirtyPresetsEnabled(false)
, m_eraserBrushSizeEnabled(false)
, m_eraserBrushOpacityEnabled(false)
{
Q_ASSERT(view != 0);
setObjectName(name);
KisConfig cfg(true);
m_dirtyPresetsEnabled = cfg.useDirtyPresets();
m_eraserBrushSizeEnabled = cfg.useEraserBrushSize();
m_eraserBrushOpacityEnabled = cfg.useEraserBrushOpacity();
KAcceleratorManager::setNoAccel(this);
setWindowTitle(i18n("Painter's Toolchest"));
m_favoriteResourceManager = new KisFavoriteResourceManager(this);
KConfigGroup grp = KSharedConfig::openConfig()->group("krita").group("Toolbar BrushesAndStuff");
int iconsize = grp.readEntry("IconSize", 32);
if (!cfg.toolOptionsInDocker()) {
m_toolOptionsPopupButton = new KisPopupButton(this);
m_toolOptionsPopupButton->setIcon(KisIconUtils::loadIcon("configure"));
m_toolOptionsPopupButton->setToolTip(i18n("Tool Settings"));
m_toolOptionsPopupButton->setFixedSize(iconsize, iconsize);
}
m_brushEditorPopupButton = new KisIconWidget(this);
m_brushEditorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_02"));
m_brushEditorPopupButton->setToolTip(i18n("Edit brush settings"));
m_brushEditorPopupButton->setFixedSize(iconsize, iconsize);
m_presetSelectorPopupButton = new KisPopupButton(this);
m_presetSelectorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_01"));
m_presetSelectorPopupButton->setToolTip(i18n("Choose brush preset"));
m_presetSelectorPopupButton->setFixedSize(iconsize, iconsize);
m_eraseModeButton = new KisHighlightedToolButton(this);
m_eraseModeButton->setFixedSize(iconsize, iconsize);
m_eraseModeButton->setCheckable(true);
m_eraseAction = m_viewManager->actionManager()->createAction("erase_action");
m_eraseModeButton->setDefaultAction(m_eraseAction);
m_reloadButton = new QToolButton(this);
m_reloadButton->setFixedSize(iconsize, iconsize);
m_reloadAction = m_viewManager->actionManager()->createAction("reload_preset_action");
m_reloadButton->setDefaultAction(m_reloadAction);
m_alphaLockButton = new KisHighlightedToolButton(this);
m_alphaLockButton->setFixedSize(iconsize, iconsize);
m_alphaLockButton->setCheckable(true);
KisAction* alphaLockAction = m_viewManager->actionManager()->createAction("preserve_alpha");
m_alphaLockButton->setDefaultAction(alphaLockAction);
// horizontal and vertical mirror toolbar buttons
// mirror tool options for the X Mirror
QMenu *toolbarMenuXMirror = new QMenu();
hideCanvasDecorationsX = m_viewManager->actionManager()->createAction("mirrorX-hideDecorations");
toolbarMenuXMirror->addAction(hideCanvasDecorationsX);
lockActionX = m_viewManager->actionManager()->createAction("mirrorX-lock");
toolbarMenuXMirror->addAction(lockActionX);
moveToCenterActionX = m_viewManager->actionManager()->createAction("mirrorX-moveToCenter");
toolbarMenuXMirror->addAction(moveToCenterActionX);
// mirror tool options for the Y Mirror
QMenu *toolbarMenuYMirror = new QMenu();
hideCanvasDecorationsY = m_viewManager->actionManager()->createAction("mirrorY-hideDecorations");
toolbarMenuYMirror->addAction(hideCanvasDecorationsY);
lockActionY = m_viewManager->actionManager()->createAction("mirrorY-lock");
toolbarMenuYMirror->addAction(lockActionY);
moveToCenterActionY = m_viewManager->actionManager()->createAction("mirrorY-moveToCenter");
toolbarMenuYMirror->addAction(moveToCenterActionY);
// create horizontal and vertical mirror buttons
m_hMirrorButton = new KisHighlightedToolButton(this);
int menuPadding = 10;
m_hMirrorButton->setFixedSize(iconsize + menuPadding, iconsize);
m_hMirrorButton->setCheckable(true);
m_hMirrorAction = m_viewManager->actionManager()->createAction("hmirror_action");
m_hMirrorButton->setDefaultAction(m_hMirrorAction);
m_hMirrorButton->setMenu(toolbarMenuXMirror);
m_hMirrorButton->setPopupMode(QToolButton::MenuButtonPopup);
m_vMirrorButton = new KisHighlightedToolButton(this);
m_vMirrorButton->setFixedSize(iconsize + menuPadding, iconsize);
m_vMirrorButton->setCheckable(true);
m_vMirrorAction = m_viewManager->actionManager()->createAction("vmirror_action");
m_vMirrorButton->setDefaultAction(m_vMirrorAction);
m_vMirrorButton->setMenu(toolbarMenuYMirror);
m_vMirrorButton->setPopupMode(QToolButton::MenuButtonPopup);
// add connections for horizontal and mirrror buttons
connect(lockActionX, SIGNAL(toggled(bool)), this, SLOT(slotLockXMirrorToggle(bool)));
connect(lockActionY, SIGNAL(toggled(bool)), this, SLOT(slotLockYMirrorToggle(bool)));
connect(moveToCenterActionX, SIGNAL(triggered(bool)), this, SLOT(slotMoveToCenterMirrorX()));
connect(moveToCenterActionY, SIGNAL(triggered(bool)), this, SLOT(slotMoveToCenterMirrorY()));
connect(hideCanvasDecorationsX, SIGNAL(toggled(bool)), this, SLOT(slotHideDecorationMirrorX(bool)));
connect(hideCanvasDecorationsY, SIGNAL(toggled(bool)), this, SLOT(slotHideDecorationMirrorY(bool)));
const bool sliderLabels = cfg.sliderLabels();
int sliderWidth;
if (sliderLabels) {
sliderWidth = 150 * logicalDpiX() / 96;
}
else {
sliderWidth = 120 * logicalDpiX() / 96;
}
for (int i = 0; i < 3; ++i) {
m_sliderChooser[i] = new KisWidgetChooser(i + 1);
KisDoubleSliderSpinBox* slOpacity;
KisDoubleSliderSpinBox* slFlow;
KisDoubleSliderSpinBox* slSize;
if (sliderLabels) {
slOpacity = m_sliderChooser[i]->addWidget<KisDoubleSliderSpinBox>("opacity");
slFlow = m_sliderChooser[i]->addWidget<KisDoubleSliderSpinBox>("flow");
slSize = m_sliderChooser[i]->addWidget<KisDoubleSliderSpinBox>("size");
slOpacity->setPrefix(QString("%1 ").arg(i18n("Opacity:")));
slFlow->setPrefix(QString("%1 ").arg(i18n("Flow:")));
slSize->setPrefix(QString("%1 ").arg(i18n("Size:")));
}
else {
slOpacity = m_sliderChooser[i]->addWidget<KisDoubleSliderSpinBox>("opacity", i18n("Opacity:"));
slFlow = m_sliderChooser[i]->addWidget<KisDoubleSliderSpinBox>("flow", i18n("Flow:"));
slSize = m_sliderChooser[i]->addWidget<KisDoubleSliderSpinBox>("size", i18n("Size:"));
}
slOpacity->setRange(0, 100, 0);
slOpacity->setValue(100);
slOpacity->setSingleStep(5);
slOpacity->setSuffix("%");
slOpacity->setMinimumWidth(qMax(sliderWidth, slOpacity->sizeHint().width()));
slOpacity->setFixedHeight(iconsize);
slOpacity->setBlockUpdateSignalOnDrag(true);
slFlow->setRange(0, 100, 0);
slFlow->setValue(100);
slFlow->setSingleStep(5);
slFlow->setSuffix("%");
slFlow->setMinimumWidth(qMax(sliderWidth, slFlow->sizeHint().width()));
slFlow->setFixedHeight(iconsize);
slFlow->setBlockUpdateSignalOnDrag(true);
slSize->setRange(0, cfg.readEntry("maximumBrushSize", 1000), 2);
slSize->setValue(100);
slSize->setSingleStep(1);
slSize->setExponentRatio(3.0);
slSize->setSuffix(i18n(" px"));
slSize->setMinimumWidth(qMax(sliderWidth, slSize->sizeHint().width()));
slSize->setFixedHeight(iconsize);
slSize->setBlockUpdateSignalOnDrag(true);
m_sliderChooser[i]->chooseWidget(cfg.toolbarSlider(i + 1));
}
m_cmbCompositeOp = new KisCompositeOpComboBox();
m_cmbCompositeOp->setFixedHeight(iconsize);
Q_FOREACH (KisAction * a, m_cmbCompositeOp->blendmodeActions()) {
m_viewManager->actionManager()->addAction(a->text(), a);
}
m_workspaceWidget = new KisPopupButton(this);
m_workspaceWidget->setIcon(KisIconUtils::loadIcon("view-choose"));
m_workspaceWidget->setToolTip(i18n("Choose workspace"));
m_workspaceWidget->setFixedSize(iconsize, iconsize);
m_workspaceWidget->setPopupWidget(new KisWorkspaceChooser(view));
QHBoxLayout* baseLayout = new QHBoxLayout(this);
m_paintopWidget = new QWidget(this);
baseLayout->addWidget(m_paintopWidget);
baseLayout->setSpacing(4);
baseLayout->setContentsMargins(0, 0, 0, 0);
m_layout = new QHBoxLayout(m_paintopWidget);
if (!cfg.toolOptionsInDocker()) {
m_layout->addWidget(m_toolOptionsPopupButton);
}
m_layout->addWidget(m_brushEditorPopupButton);
m_layout->addWidget(m_presetSelectorPopupButton);
m_layout->setSpacing(4);
m_layout->setContentsMargins(0, 0, 0, 0);
QWidget* compositeActions = new QWidget(this);
QHBoxLayout* compositeLayout = new QHBoxLayout(compositeActions);
compositeLayout->addWidget(m_cmbCompositeOp);
compositeLayout->addWidget(m_eraseModeButton);
compositeLayout->addWidget(m_alphaLockButton);
compositeLayout->setSpacing(4);
compositeLayout->setContentsMargins(0, 0, 0, 0);
compositeLayout->addWidget(m_reloadButton);
QWidgetAction * action;
action = new QWidgetAction(this);
view->actionCollection()->addAction("composite_actions", action);
action->setText(i18n("Brush composite"));
action->setDefaultWidget(compositeActions);
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("brushslider1", action);
view->actionCollection()->addAction("brushslider1", action);
action->setDefaultWidget(m_sliderChooser[0]);
connect(action, SIGNAL(triggered()), m_sliderChooser[0], SLOT(showPopupWidget()));
connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_sliderChooser[0], SLOT(updateThemedIcons()));
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("brushslider2", action);
view->actionCollection()->addAction("brushslider2", action);
action->setDefaultWidget(m_sliderChooser[1]);
connect(action, SIGNAL(triggered()), m_sliderChooser[1], SLOT(showPopupWidget()));
connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_sliderChooser[1], SLOT(updateThemedIcons()));
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("brushslider3", action);
view->actionCollection()->addAction("brushslider3", action);
action->setDefaultWidget(m_sliderChooser[2]);
connect(action, SIGNAL(triggered()), m_sliderChooser[2], SLOT(showPopupWidget()));
connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_sliderChooser[2], SLOT(updateThemedIcons()));
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("next_favorite_preset", action);
view->actionCollection()->addAction("next_favorite_preset", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotNextFavoritePreset()));
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("previous_favorite_preset", action);
view->actionCollection()->addAction("previous_favorite_preset", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotPreviousFavoritePreset()));
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("previous_preset", action);
view->actionCollection()->addAction("previous_preset", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotSwitchToPreviousPreset()));
if (!cfg.toolOptionsInDocker()) {
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("show_tool_options", action);
view->actionCollection()->addAction("show_tool_options", action);
connect(action, SIGNAL(triggered()), m_toolOptionsPopupButton, SLOT(showPopupWidget()));
}
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("show_brush_editor", action);
view->actionCollection()->addAction("show_brush_editor", action);
connect(action, SIGNAL(triggered()), m_brushEditorPopupButton, SLOT(showPopupWidget()));
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("show_brush_presets", action);
view->actionCollection()->addAction("show_brush_presets", action);
connect(action, SIGNAL(triggered()), m_presetSelectorPopupButton, SLOT(showPopupWidget()));
QWidget* mirrorActions = new QWidget(this);
QHBoxLayout* mirrorLayout = new QHBoxLayout(mirrorActions);
mirrorLayout->addWidget(m_hMirrorButton);
mirrorLayout->addWidget(m_vMirrorButton);
mirrorLayout->setSpacing(4);
mirrorLayout->setContentsMargins(0, 0, 0, 0);
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("mirror_actions", action);
action->setDefaultWidget(mirrorActions);
view->actionCollection()->addAction("mirror_actions", action);
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("workspaces", action);
view->actionCollection()->addAction("workspaces", action);
action->setDefaultWidget(m_workspaceWidget);
if (!cfg.toolOptionsInDocker()) {
m_toolOptionsPopup = new KisToolOptionsPopup();
m_toolOptionsPopupButton->setPopupWidget(m_toolOptionsPopup);
m_toolOptionsPopup->switchDetached(false);
}
m_savePresetWidget = new KisPresetSaveWidget(this);
m_presetsPopup = new KisPaintOpPresetsPopup(m_resourceProvider, m_favoriteResourceManager, m_savePresetWidget);
m_brushEditorPopupButton->setPopupWidget(m_presetsPopup);
m_presetsPopup->parentWidget()->setWindowTitle(i18n("Brush Editor"));
connect(m_presetsPopup, SIGNAL(brushEditorShown()), SLOT(slotUpdateOptionsWidgetPopup()));
connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_presetsPopup, SLOT(updateThemedIcons()));
m_presetsChooserPopup = new KisPaintOpPresetsChooserPopup();
m_presetsChooserPopup->setMinimumHeight(550);
m_presetsChooserPopup->setMinimumWidth(450);
m_presetSelectorPopupButton->setPopupWidget(m_presetsChooserPopup);
m_currCompositeOpID = KoCompositeOpRegistry::instance().getDefaultCompositeOp().id();
slotNodeChanged(view->activeNode());
// Get all the paintops
QList<QString> keys = KisPaintOpRegistry::instance()->keys();
QList<KisPaintOpFactory*> factoryList;
Q_FOREACH (const QString & paintopId, keys) {
factoryList.append(KisPaintOpRegistry::instance()->get(paintopId));
}
m_presetsPopup->setPaintOpList(factoryList);
connect(m_presetsPopup , SIGNAL(paintopActivated(QString)) , SLOT(slotSetPaintop(QString)));
connect(m_presetsPopup , SIGNAL(defaultPresetClicked()) , SLOT(slotSetupDefaultPreset()));
connect(m_presetsPopup , SIGNAL(signalResourceSelected(KoResource*)), SLOT(resourceSelected(KoResource*)));
connect(m_presetsPopup , SIGNAL(reloadPresetClicked()) , SLOT(slotReloadPreset()));
connect(m_presetsPopup , SIGNAL(dirtyPresetToggled(bool)) , SLOT(slotDirtyPresetToggled(bool)));
connect(m_presetsPopup , SIGNAL(eraserBrushSizeToggled(bool)) , SLOT(slotEraserBrushSizeToggled(bool)));
connect(m_presetsPopup , SIGNAL(eraserBrushOpacityToggled(bool)) , SLOT(slotEraserBrushOpacityToggled(bool)));
connect(m_presetsPopup, SIGNAL(createPresetFromScratch(QString)), this, SLOT(slotCreatePresetFromScratch(QString)));
connect(m_presetsChooserPopup, SIGNAL(resourceSelected(KoResource*)) , SLOT(resourceSelected(KoResource*)));
connect(m_presetsChooserPopup, SIGNAL(resourceClicked(KoResource*)) , SLOT(resourceSelected(KoResource*)));
connect(m_resourceProvider , SIGNAL(sigNodeChanged(KisNodeSP)) , SLOT(slotNodeChanged(KisNodeSP)));
connect(m_cmbCompositeOp , SIGNAL(currentIndexChanged(int)) , SLOT(slotSetCompositeMode(int)));
connect(m_eraseAction , SIGNAL(toggled(bool)) , SLOT(slotToggleEraseMode(bool)));
connect(alphaLockAction , SIGNAL(toggled(bool)) , SLOT(slotToggleAlphaLockMode(bool)));
m_disablePressureAction = m_viewManager->actionManager()->createAction("disable_pressure");
connect(m_disablePressureAction , SIGNAL(toggled(bool)) , SLOT(slotDisablePressureMode(bool)));
m_disablePressureAction->setChecked(true);
connect(m_hMirrorAction , SIGNAL(toggled(bool)) , SLOT(slotHorizontalMirrorChanged(bool)));
connect(m_vMirrorAction , SIGNAL(toggled(bool)) , SLOT(slotVerticalMirrorChanged(bool)));
connect(m_reloadAction , SIGNAL(triggered()) , SLOT(slotReloadPreset()));
connect(m_sliderChooser[0]->getWidget<KisDoubleSliderSpinBox>("opacity"), SIGNAL(valueChanged(qreal)), SLOT(slotSlider1Changed()));
connect(m_sliderChooser[0]->getWidget<KisDoubleSliderSpinBox>("flow") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider1Changed()));
connect(m_sliderChooser[0]->getWidget<KisDoubleSliderSpinBox>("size") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider1Changed()));
connect(m_sliderChooser[1]->getWidget<KisDoubleSliderSpinBox>("opacity"), SIGNAL(valueChanged(qreal)), SLOT(slotSlider2Changed()));
connect(m_sliderChooser[1]->getWidget<KisDoubleSliderSpinBox>("flow") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider2Changed()));
connect(m_sliderChooser[1]->getWidget<KisDoubleSliderSpinBox>("size") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider2Changed()));
connect(m_sliderChooser[2]->getWidget<KisDoubleSliderSpinBox>("opacity"), SIGNAL(valueChanged(qreal)), SLOT(slotSlider3Changed()));
connect(m_sliderChooser[2]->getWidget<KisDoubleSliderSpinBox>("flow") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider3Changed()));
connect(m_sliderChooser[2]->getWidget<KisDoubleSliderSpinBox>("size") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider3Changed()));
//Needed to connect canvas to favorite resource manager
connect(m_viewManager->resourceProvider(), SIGNAL(sigFGColorChanged(KoColor)), SLOT(slotUnsetEraseMode()));
connect(m_resourceProvider, SIGNAL(sigFGColorUsed(KoColor)), m_favoriteResourceManager, SLOT(slotAddRecentColor(KoColor)));
connect(m_resourceProvider, SIGNAL(sigFGColorChanged(KoColor)), m_favoriteResourceManager, SLOT(slotChangeFGColorSelector(KoColor)));
connect(m_resourceProvider, SIGNAL(sigBGColorChanged(KoColor)), m_favoriteResourceManager, SLOT(slotSetBGColor(KoColor)));
// cold initialization
m_favoriteResourceManager->slotChangeFGColorSelector(m_resourceProvider->fgColor());
m_favoriteResourceManager->slotSetBGColor(m_resourceProvider->bgColor());
connect(m_favoriteResourceManager, SIGNAL(sigSetFGColor(KoColor)), m_resourceProvider, SLOT(slotSetFGColor(KoColor)));
connect(m_favoriteResourceManager, SIGNAL(sigSetBGColor(KoColor)), m_resourceProvider, SLOT(slotSetBGColor(KoColor)));
connect(m_favoriteResourceManager, SIGNAL(sigEnableChangeColor(bool)), m_resourceProvider, SLOT(slotResetEnableFGChange(bool)));
connect(view->mainWindow(), SIGNAL(themeChanged()), this, SLOT(slotUpdateSelectionIcon()));
connect(m_resourceProvider->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)),
this, SLOT(slotCanvasResourceChanged(int,QVariant)));
slotInputDeviceChanged(KoToolManager::instance()->currentInputDevice());
KisPaintOpPresetResourceServer *rserver = KisResourceServerProvider::instance()->paintOpPresetServer();
m_eraserName = "eraser_circle";
m_defaultPresetName = "basic_tip_default";
bool foundEraser = false;
bool foundTip = false;
for (int i=0; i<rserver->resourceCount(); i++) {
KisPaintOpPresetSP resource = rserver->resources().at(i);
if (resource->name().toLower().contains("eraser_circle")) {
m_eraserName = resource->name();
foundEraser = true;
} else if (foundEraser == false && (resource->name().toLower().contains("eraser") ||
resource->filename().toLower().contains("eraser"))) {
m_eraserName = resource->name();
foundEraser = true;
}
if (resource->name().toLower().contains("basic_tip_default")) {
m_defaultPresetName = resource->name();
foundTip = true;
} else if (foundTip == false && (resource->name().toLower().contains("default") ||
resource->filename().toLower().contains("default"))) {
m_defaultPresetName = resource->name();
foundTip = true;
}
}
}
KisPaintopBox::~KisPaintopBox()
{
KisConfig cfg(false);
QMapIterator<TabletToolID, TabletToolData> iter(m_tabletToolMap);
while (iter.hasNext()) {
iter.next();
//qDebug() << "Writing last used preset for" << iter.key().pointer << iter.key().uniqueID << iter.value().preset->name();
if ((iter.key().pointer) == QTabletEvent::Eraser) {
cfg.writeEntry(QString("LastEraser_%1").arg(iter.key().uniqueID) , iter.value().preset->name());
}
else {
cfg.writeEntry(QString("LastPreset_%1").arg(iter.key().uniqueID) , iter.value().preset->name());
}
}
// Do not delete the widget, since it is global to the application, not owned by the view
m_presetsPopup->setPaintOpSettingsWidget(0);
qDeleteAll(m_paintopOptionWidgets);
delete m_favoriteResourceManager;
for (int i = 0; i < 3; ++i) {
delete m_sliderChooser[i];
}
}
void KisPaintopBox::restoreResource(KoResource* resource)
{
KisPaintOpPreset* preset = dynamic_cast<KisPaintOpPreset*>(resource);
if (preset) {
setCurrentPaintop(preset);
m_presetsPopup->setPresetImage(preset->image());
m_presetsPopup->resourceSelected(resource);
}
}
void KisPaintopBox::newOptionWidgets(const QList<QPointer<QWidget> > &optionWidgetList)
{
if (m_toolOptionsPopup) {
m_toolOptionsPopup->newOptionWidgets(optionWidgetList);
}
}
void KisPaintopBox::resourceSelected(KoResource* resource)
{
+ KIS_SAFE_ASSERT_RECOVER_RETURN(m_optionWidget);
+
m_presetsPopup->setCreatingBrushFromScratch(false); // show normal UI elements when we are not creating
KisPaintOpPreset* preset = dynamic_cast<KisPaintOpPreset*>(resource);
if (preset && preset != m_resourceProvider->currentPreset()) {
if (!preset->settings()->isLoadable())
return;
if (!m_dirtyPresetsEnabled) {
KisSignalsBlocker blocker(m_optionWidget);
if (!preset->load()) {
warnKrita << "failed to load the preset.";
}
}
//qDebug() << "resourceSelected" << resource->name();
setCurrentPaintop(preset);
m_presetsPopup->setPresetImage(preset->image());
m_presetsPopup->resourceSelected(resource);
}
}
void KisPaintopBox::setCurrentPaintop(const KoID& paintop)
{
KisPaintOpPresetSP preset = activePreset(paintop);
Q_ASSERT(preset && preset->settings());
//qDebug() << "setCurrentPaintop();" << paintop << preset;
setCurrentPaintop(preset);
}
void KisPaintopBox::setCurrentPaintop(KisPaintOpPresetSP preset)
{
//qDebug() << "setCurrentPaintop(); " << preset->name();
if (preset == m_resourceProvider->currentPreset()) {
if (preset == m_tabletToolMap[m_currTabletToolID].preset) {
return;
}
}
Q_ASSERT(preset);
const KoID& paintop = preset->paintOp();
m_presetConnections.clear();
if (m_resourceProvider->currentPreset()) {
m_resourceProvider->setPreviousPaintOpPreset(m_resourceProvider->currentPreset());
if (m_optionWidget) {
m_optionWidget->hide();
}
}
if (!m_paintopOptionWidgets.contains(paintop))
m_paintopOptionWidgets[paintop] = KisPaintOpRegistry::instance()->get(paintop.id())->createConfigWidget(this);
m_optionWidget = m_paintopOptionWidgets[paintop];
KisSignalsBlocker b(m_optionWidget);
preset->setOptionsWidget(m_optionWidget);
m_optionWidget->setImage(m_viewManager->image());
m_optionWidget->setNode(m_viewManager->activeNode());
m_presetsPopup->setPaintOpSettingsWidget(m_optionWidget);
m_resourceProvider->setPaintOpPreset(preset);
Q_ASSERT(m_optionWidget && m_presetSelectorPopupButton);
m_presetConnections.addConnection(m_optionWidget, SIGNAL(sigConfigurationUpdated()), this, SLOT(slotGuiChangedCurrentPreset()));
m_presetConnections.addConnection(m_optionWidget, SIGNAL(sigSaveLockedConfig(KisPropertiesConfigurationSP)), this, SLOT(slotSaveLockedOptionToPreset(KisPropertiesConfigurationSP)));
m_presetConnections.addConnection(m_optionWidget, SIGNAL(sigDropLockedConfig(KisPropertiesConfigurationSP)), this, SLOT(slotDropLockedOption(KisPropertiesConfigurationSP)));
// load the current brush engine icon for the brush editor toolbar button
m_brushEditorPopupButton->slotSetItem(preset.data());
m_presetsPopup->setCurrentPaintOpId(paintop.id());
////qDebug() << "\tsetting the new preset for" << m_currTabletToolID.uniqueID << "to" << preset->name();
m_paintOpPresetMap[m_resourceProvider->currentPreset()->paintOp()] = preset;
m_tabletToolMap[m_currTabletToolID].preset = preset;
m_tabletToolMap[m_currTabletToolID].paintOpID = preset->paintOp();
if (m_presetsPopup->currentPaintOpId() != paintop.id()) {
// Must change the paintop as the current one is not supported
// by the new colorspace.
dbgKrita << "current paintop " << paintop.name() << " was not set, not supported by colorspace";
}
}
void KisPaintopBox::slotUpdateOptionsWidgetPopup()
{
KisPaintOpPresetSP preset = m_resourceProvider->currentPreset();
KIS_SAFE_ASSERT_RECOVER_RETURN(preset);
KIS_SAFE_ASSERT_RECOVER_RETURN(m_optionWidget);
m_optionWidget->setConfigurationSafe(preset->settings());
m_presetsPopup->resourceSelected(preset.data());
m_presetsPopup->updateViewSettings();
// the m_viewManager->image() is set earlier, but the reference will be missing when the stamp button is pressed
// need to later do some research on how and when we should be using weak shared pointers (WSP) that creates this situation
m_optionWidget->setImage(m_viewManager->image());
}
KisPaintOpPresetSP KisPaintopBox::defaultPreset(const KoID& paintOp)
{
QString defaultName = paintOp.id() + ".kpp";
QString path = KoResourcePaths::findResource("kis_defaultpresets", defaultName);
KisPaintOpPresetSP preset = new KisPaintOpPreset(path);
if (!preset->load()) {
preset = KisPaintOpRegistry::instance()->defaultPreset(paintOp);
}
Q_ASSERT(preset);
Q_ASSERT(preset->valid());
return preset;
}
KisPaintOpPresetSP KisPaintopBox::activePreset(const KoID& paintOp)
{
if (m_paintOpPresetMap[paintOp] == 0) {
m_paintOpPresetMap[paintOp] = defaultPreset(paintOp);
}
return m_paintOpPresetMap[paintOp];
}
void KisPaintopBox::updateCompositeOp(QString compositeOpID)
{
if (!m_optionWidget) return;
KisSignalsBlocker blocker(m_optionWidget);
KisNodeSP node = m_resourceProvider->currentNode();
if (node && node->paintDevice()) {
if (!node->paintDevice()->colorSpace()->hasCompositeOp(compositeOpID))
compositeOpID = KoCompositeOpRegistry::instance().getDefaultCompositeOp().id();
{
KisSignalsBlocker b1(m_cmbCompositeOp);
m_cmbCompositeOp->selectCompositeOp(KoID(compositeOpID));
}
if (compositeOpID != m_currCompositeOpID) {
m_currCompositeOpID = compositeOpID;
}
if (compositeOpID == COMPOSITE_ERASE || m_resourceProvider->eraserMode()) {
m_eraseModeButton->setChecked(true);
}
else {
m_eraseModeButton->setChecked(false);
}
}
}
void KisPaintopBox::setWidgetState(int flags)
{
if (flags & (ENABLE_COMPOSITEOP | DISABLE_COMPOSITEOP)) {
m_cmbCompositeOp->setEnabled(flags & ENABLE_COMPOSITEOP);
m_eraseModeButton->setEnabled(flags & ENABLE_COMPOSITEOP);
}
if (flags & (ENABLE_PRESETS | DISABLE_PRESETS)) {
m_presetSelectorPopupButton->setEnabled(flags & ENABLE_PRESETS);
m_brushEditorPopupButton->setEnabled(flags & ENABLE_PRESETS);
}
for (int i = 0; i < 3; ++i) {
if (flags & (ENABLE_OPACITY | DISABLE_OPACITY))
m_sliderChooser[i]->getWidget("opacity")->setEnabled(flags & ENABLE_OPACITY);
if (flags & (ENABLE_FLOW | DISABLE_FLOW))
m_sliderChooser[i]->getWidget("flow")->setEnabled(flags & ENABLE_FLOW);
if (flags & (ENABLE_SIZE | DISABLE_SIZE))
m_sliderChooser[i]->getWidget("size")->setEnabled(flags & ENABLE_SIZE);
}
}
void KisPaintopBox::setSliderValue(const QString& sliderID, qreal value)
{
for (int i = 0; i < 3; ++i) {
KisDoubleSliderSpinBox* slider = m_sliderChooser[i]->getWidget<KisDoubleSliderSpinBox>(sliderID);
KisSignalsBlocker b(slider);
if (sliderID == "opacity" || sliderID == "flow") { // opacity and flows UI stored at 0-100%
slider->setValue(value*100);
} else {
slider->setValue(value); // brush size
}
}
}
void KisPaintopBox::slotSetPaintop(const QString& paintOpId)
{
if (KisPaintOpRegistry::instance()->get(paintOpId) != 0) {
KoID id(paintOpId, KisPaintOpRegistry::instance()->get(paintOpId)->name());
//qDebug() << "slotsetpaintop" << id;
setCurrentPaintop(id);
}
}
void KisPaintopBox::slotInputDeviceChanged(const KoInputDevice& inputDevice)
{
TabletToolMap::iterator toolData = m_tabletToolMap.find(inputDevice);
//qDebug() << "slotInputDeviceChanged()" << inputDevice.device() << inputDevice.uniqueTabletId();
m_currTabletToolID = TabletToolID(inputDevice);
if (toolData == m_tabletToolMap.end()) {
KisConfig cfg(true);
KisPaintOpPresetResourceServer *rserver = KisResourceServerProvider::instance()->paintOpPresetServer();
KisPaintOpPresetSP preset;
if (inputDevice.pointer() == QTabletEvent::Eraser) {
preset = rserver->resourceByName(cfg.readEntry<QString>(QString("LastEraser_%1").arg(inputDevice.uniqueTabletId()), m_eraserName));
}
else {
preset = rserver->resourceByName(cfg.readEntry<QString>(QString("LastPreset_%1").arg(inputDevice.uniqueTabletId()), m_defaultPresetName));
//if (preset)
//qDebug() << "found stored preset " << preset->name() << "for" << inputDevice.uniqueTabletId();
//else
//qDebug() << "no preset found for" << inputDevice.uniqueTabletId();
}
if (!preset) {
preset = rserver->resourceByName(m_defaultPresetName);
}
if (preset) {
//qDebug() << "inputdevicechanged 1" << preset;
setCurrentPaintop(preset);
}
}
else {
if (toolData->preset) {
//qDebug() << "inputdevicechanged 2" << toolData->preset;
setCurrentPaintop(toolData->preset);
}
else {
//qDebug() << "inputdevicechanged 3" << toolData->paintOpID;
setCurrentPaintop(toolData->paintOpID);
}
}
}
void KisPaintopBox::slotCreatePresetFromScratch(QString paintop)
{
//First try to select an available default preset for that engine. If it doesn't exist, then
//manually set the engine to use a new preset.
KoID id(paintop, KisPaintOpRegistry::instance()->get(paintop)->name());
KisPaintOpPresetSP preset = defaultPreset(id);
slotSetPaintop(paintop); // change the paintop settings area and update the UI
if (!preset) {
m_presetsPopup->setCreatingBrushFromScratch(true); // disable UI elements while creating from scratch
preset = m_resourceProvider->currentPreset();
} else {
m_resourceProvider->setPaintOpPreset(preset);
preset->setOptionsWidget(m_optionWidget);
}
m_presetsPopup->resourceSelected(preset.data()); // this helps update the UI on the brush editor
}
void KisPaintopBox::slotCanvasResourceChanged(int key, const QVariant &value)
{
if (m_viewManager) {
sender()->blockSignals(true);
KisPaintOpPresetSP preset = m_viewManager->resourceProvider()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value<KisPaintOpPresetSP>();
if (preset && m_resourceProvider->currentPreset()->name() != preset->name()) {
QString compositeOp = preset->settings()->getString("CompositeOp");
updateCompositeOp(compositeOp);
resourceSelected(preset.data());
}
/**
* Update currently selected preset in both the popup widgets
*/
m_presetsChooserPopup->canvasResourceChanged(preset);
m_presetsPopup->currentPresetChanged(preset);
if (key == KisCanvasResourceProvider::CurrentCompositeOp) {
if (m_resourceProvider->currentCompositeOp() != m_currCompositeOpID) {
updateCompositeOp(m_resourceProvider->currentCompositeOp());
}
}
if (key == KisCanvasResourceProvider::Size) {
setSliderValue("size", m_resourceProvider->size());
}
if (key == KisCanvasResourceProvider::Opacity) {
setSliderValue("opacity", m_resourceProvider->opacity());
}
if (key == KisCanvasResourceProvider::Flow) {
setSliderValue("flow", m_resourceProvider->flow());
}
if (key == KisCanvasResourceProvider::EraserMode) {
m_eraseAction->setChecked(value.toBool());
}
if (key == KisCanvasResourceProvider::DisablePressure) {
m_disablePressureAction->setChecked(value.toBool());
}
sender()->blockSignals(false);
}
}
void KisPaintopBox::slotUpdatePreset()
{
if (!m_resourceProvider->currentPreset()) return;
// block updates of avoid some over updating of the option widget
m_blockUpdate = true;
setSliderValue("size", m_resourceProvider->size());
{
qreal opacity = m_resourceProvider->currentPreset()->settings()->paintOpOpacity();
m_resourceProvider->setOpacity(opacity);
setSliderValue("opacity", opacity);
setWidgetState(ENABLE_OPACITY);
}
{
setSliderValue("flow", m_resourceProvider->currentPreset()->settings()->paintOpFlow());
setWidgetState(ENABLE_FLOW);
}
{
updateCompositeOp(m_resourceProvider->currentPreset()->settings()->paintOpCompositeOp());
setWidgetState(ENABLE_COMPOSITEOP);
}
m_blockUpdate = false;
}
void KisPaintopBox::slotSetupDefaultPreset()
{
KisPaintOpPresetSP preset = defaultPreset(m_resourceProvider->currentPreset()->paintOp());
preset->setOptionsWidget(m_optionWidget);
m_resourceProvider->setPaintOpPreset(preset);
// tell the brush editor that the resource has changed
// so it can update everything
m_presetsPopup->resourceSelected(preset.data());
}
void KisPaintopBox::slotNodeChanged(const KisNodeSP node)
{
if (m_previousNode.isValid() && m_previousNode->paintDevice())
disconnect(m_previousNode->paintDevice().data(), SIGNAL(colorSpaceChanged(const KoColorSpace*)), this, SLOT(slotColorSpaceChanged(const KoColorSpace*)));
// Reconnect colorspace change of node
if (node && node->paintDevice()) {
connect(node->paintDevice().data(), SIGNAL(colorSpaceChanged(const KoColorSpace*)), this, SLOT(slotColorSpaceChanged(const KoColorSpace*)));
m_resourceProvider->setCurrentCompositeOp(m_currCompositeOpID);
m_previousNode = node;
slotColorSpaceChanged(node->colorSpace());
}
if (m_optionWidget) {
m_optionWidget->setNode(node);
}
}
void KisPaintopBox::slotColorSpaceChanged(const KoColorSpace* colorSpace)
{
m_cmbCompositeOp->validate(colorSpace);
}
void KisPaintopBox::slotToggleEraseMode(bool checked)
{
const bool oldEraserMode = m_resourceProvider->eraserMode();
m_resourceProvider->setEraserMode(checked);
if (oldEraserMode != checked && m_eraserBrushSizeEnabled) {
const qreal currentSize = m_resourceProvider->size();
KisPaintOpSettingsSP settings = m_resourceProvider->currentPreset()->settings();
// remember brush size. set the eraser size to the normal brush size if not set
if (checked) {
settings->setSavedBrushSize(currentSize);
if (qFuzzyIsNull(settings->savedEraserSize())) {
settings->setSavedEraserSize(currentSize);
}
} else {
settings->setSavedEraserSize(currentSize);
if (qFuzzyIsNull(settings->savedBrushSize())) {
settings->setSavedBrushSize(currentSize);
}
}
//update value in UI (this is the main place the value is 'stored' in memory)
qreal newSize = checked ? settings->savedEraserSize() : settings->savedBrushSize();
m_resourceProvider->setSize(newSize);
}
if (oldEraserMode != checked && m_eraserBrushOpacityEnabled) {
const qreal currentOpacity = m_resourceProvider->opacity();
KisPaintOpSettingsSP settings = m_resourceProvider->currentPreset()->settings();
// remember brush opacity. set the eraser opacity to the normal brush opacity if not set
if (checked) {
settings->setSavedBrushOpacity(currentOpacity);
if (qFuzzyIsNull(settings->savedEraserOpacity())) {
settings->setSavedEraserOpacity(currentOpacity);
}
} else {
settings->setSavedEraserOpacity(currentOpacity);
if (qFuzzyIsNull(settings->savedBrushOpacity())) {
settings->setSavedBrushOpacity(currentOpacity);
}
}
//update value in UI (this is the main place the value is 'stored' in memory)
qreal newOpacity = checked ? settings->savedEraserOpacity() : settings->savedBrushOpacity();
m_resourceProvider->setOpacity(newOpacity);
}
}
void KisPaintopBox::slotSetCompositeMode(int index)
{
Q_UNUSED(index);
QString compositeOp = m_cmbCompositeOp->selectedCompositeOp().id();
m_resourceProvider->setCurrentCompositeOp(compositeOp);
}
void KisPaintopBox::slotHorizontalMirrorChanged(bool value)
{
m_resourceProvider->setMirrorHorizontal(value);
}
void KisPaintopBox::slotVerticalMirrorChanged(bool value)
{
m_resourceProvider->setMirrorVertical(value);
}
void KisPaintopBox::sliderChanged(int n)
{
if (!m_optionWidget) // widget will not exist if the are no documents open
return;
KisSignalsBlocker blocker(m_optionWidget);
// flow and opacity are shown as 0-100% on the UI, but their data is actually 0-1. Convert those two values
// back for further work
qreal opacity = m_sliderChooser[n]->getWidget<KisDoubleSliderSpinBox>("opacity")->value()/100;
qreal flow = m_sliderChooser[n]->getWidget<KisDoubleSliderSpinBox>("flow")->value()/100;
qreal size = m_sliderChooser[n]->getWidget<KisDoubleSliderSpinBox>("size")->value();
setSliderValue("opacity", opacity);
setSliderValue("flow" , flow);
setSliderValue("size" , size);
if (m_presetsEnabled) {
// IMPORTANT: set the PaintOp size before setting the other properties
// it won't work the other way
// TODO: why?!
m_resourceProvider->setSize(size);
m_resourceProvider->setOpacity(opacity);
m_resourceProvider->setFlow(flow);
KisLockedPropertiesProxySP propertiesProxy = KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(m_resourceProvider->currentPreset()->settings());
propertiesProxy->setProperty("OpacityValue", opacity);
propertiesProxy->setProperty("FlowValue", flow);
m_optionWidget->setConfigurationSafe(m_resourceProvider->currentPreset()->settings().data());
} else {
m_resourceProvider->setOpacity(opacity);
}
m_presetsPopup->resourceSelected(m_resourceProvider->currentPreset().data());
}
void KisPaintopBox::slotSlider1Changed()
{
sliderChanged(0);
}
void KisPaintopBox::slotSlider2Changed()
{
sliderChanged(1);
}
void KisPaintopBox::slotSlider3Changed()
{
sliderChanged(2);
}
void KisPaintopBox::slotToolChanged(KoCanvasController* canvas, int toolId)
{
Q_UNUSED(canvas);
Q_UNUSED(toolId);
if (!m_viewManager->canvasBase()) return;
QString id = KoToolManager::instance()->activeToolId();
KisTool* tool = dynamic_cast<KisTool*>(KoToolManager::instance()->toolById(m_viewManager->canvasBase(), id));
if (tool) {
int flags = tool->flags();
if (flags & KisTool::FLAG_USES_CUSTOM_COMPOSITEOP) {
setWidgetState(ENABLE_COMPOSITEOP | ENABLE_OPACITY);
} else {
setWidgetState(DISABLE_COMPOSITEOP | DISABLE_OPACITY);
}
if (flags & KisTool::FLAG_USES_CUSTOM_PRESET) {
setWidgetState(ENABLE_PRESETS);
slotUpdatePreset();
m_presetsEnabled = true;
} else {
setWidgetState(DISABLE_PRESETS);
m_presetsEnabled = false;
}
if (flags & KisTool::FLAG_USES_CUSTOM_SIZE) {
setWidgetState(ENABLE_SIZE | ENABLE_FLOW);
} else {
setWidgetState(DISABLE_SIZE | DISABLE_FLOW);
}
} else setWidgetState(DISABLE_ALL);
}
void KisPaintopBox::slotPreviousFavoritePreset()
{
if (!m_favoriteResourceManager) return;
QVector<KisPaintOpPresetSP> presets = m_favoriteResourceManager->favoritePresetList();
for (int i=0; i < presets.size(); ++i) {
if (m_resourceProvider->currentPreset() &&
m_resourceProvider->currentPreset()->name() == presets[i]->name()) {
if (i > 0) {
m_favoriteResourceManager->slotChangeActivePaintop(i - 1);
} else {
m_favoriteResourceManager->slotChangeActivePaintop(m_favoriteResourceManager->numFavoritePresets() - 1);
}
//floating message should have least 2 lines, otherwise
//preset thumbnail will be too small to distinguish
//(because size of image on floating message depends on amount of lines in msg)
m_viewManager->showFloatingMessage(
i18n("%1\nselected",
m_resourceProvider->currentPreset()->name()),
QIcon(QPixmap::fromImage(m_resourceProvider->currentPreset()->image())));
return;
}
}
}
void KisPaintopBox::slotNextFavoritePreset()
{
if (!m_favoriteResourceManager) return;
QVector<KisPaintOpPresetSP> presets = m_favoriteResourceManager->favoritePresetList();
for(int i = 0; i < presets.size(); ++i) {
if (m_resourceProvider->currentPreset()->name() == presets[i]->name()) {
if (i < m_favoriteResourceManager->numFavoritePresets() - 1) {
m_favoriteResourceManager->slotChangeActivePaintop(i + 1);
} else {
m_favoriteResourceManager->slotChangeActivePaintop(0);
}
m_viewManager->showFloatingMessage(
i18n("%1\nselected",
m_resourceProvider->currentPreset()->name()),
QIcon(QPixmap::fromImage(m_resourceProvider->currentPreset()->image())));
return;
}
}
}
void KisPaintopBox::slotSwitchToPreviousPreset()
{
if (m_resourceProvider->previousPreset()) {
//qDebug() << "slotSwitchToPreviousPreset();" << m_resourceProvider->previousPreset();
setCurrentPaintop(m_resourceProvider->previousPreset());
m_viewManager->showFloatingMessage(
i18n("%1\nselected",
m_resourceProvider->currentPreset()->name()),
QIcon(QPixmap::fromImage(m_resourceProvider->currentPreset()->image())));
}
}
void KisPaintopBox::slotUnsetEraseMode()
{
m_eraseAction->setChecked(false);
}
void KisPaintopBox::slotToggleAlphaLockMode(bool checked)
{
if (checked) {
m_alphaLockButton->actions()[0]->setIcon(KisIconUtils::loadIcon("transparency-locked"));
} else {
m_alphaLockButton->actions()[0]->setIcon(KisIconUtils::loadIcon("transparency-unlocked"));
}
m_resourceProvider->setGlobalAlphaLock(checked);
}
void KisPaintopBox::slotDisablePressureMode(bool checked)
{
if (checked) {
m_disablePressureAction->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure"));
} else {
m_disablePressureAction->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure_locked"));
}
m_resourceProvider->setDisablePressure(checked);
}
void KisPaintopBox::slotReloadPreset()
{
KisSignalsBlocker blocker(m_optionWidget);
//Here using the name and fetching the preset from the server was the only way the load was working. Otherwise it was not loading.
KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer();
KisPaintOpPresetSP preset = rserver->resourceByName(m_resourceProvider->currentPreset()->name());
if (preset) {
preset->load();
}
}
void KisPaintopBox::slotGuiChangedCurrentPreset() // Called only when UI is changed and not when preset is changed
{
KisPaintOpPresetSP preset = m_resourceProvider->currentPreset();
{
/**
* Here we postpone all the settings updates events until the entire writing
* operation will be finished. As soon as it is finished, the updates will be
* emitted happily (if there were any).
*/
KisPaintOpPreset::UpdatedPostponer postponer(preset.data());
QStringList preserveProperties;
preserveProperties << "lodUserAllowed";
preserveProperties << "lodSizeThreshold";
// clear all the properties before dumping the stuff into the preset,
// some of the options add the values incrementally
// (e.g. KisPaintOpUtils::RequiredBrushFilesListTag), therefore they
// may add up if we pass the same preset multiple times
preset->settings()->resetSettings(preserveProperties);
m_optionWidget->writeConfigurationSafe(const_cast<KisPaintOpSettings*>(preset->settings().data()));
}
// we should also update the preset strip to update the status of the "dirty" mark
m_presetsPopup->resourceSelected(m_resourceProvider->currentPreset().data());
// TODO!!!!!!!!
//m_presetsPopup->updateViewSettings();
}
void KisPaintopBox::slotSaveLockedOptionToPreset(KisPropertiesConfigurationSP p)
{
QMapIterator<QString, QVariant> i(p->getProperties());
while (i.hasNext()) {
i.next();
m_resourceProvider->currentPreset()->settings()->setProperty(i.key(), QVariant(i.value()));
if (m_resourceProvider->currentPreset()->settings()->hasProperty(i.key() + "_previous")) {
m_resourceProvider->currentPreset()->settings()->removeProperty(i.key() + "_previous");
}
}
slotGuiChangedCurrentPreset();
}
void KisPaintopBox::slotDropLockedOption(KisPropertiesConfigurationSP p)
{
KisSignalsBlocker blocker(m_optionWidget);
KisPaintOpPresetSP preset = m_resourceProvider->currentPreset();
{
KisPaintOpPreset::DirtyStateSaver dirtySaver(preset.data());
QMapIterator<QString, QVariant> i(p->getProperties());
while (i.hasNext()) {
i.next();
if (preset->settings()->hasProperty(i.key() + "_previous")) {
preset->settings()->setProperty(i.key(), preset->settings()->getProperty(i.key() + "_previous"));
preset->settings()->removeProperty(i.key() + "_previous");
}
}
}
//slotUpdatePreset();
}
void KisPaintopBox::slotDirtyPresetToggled(bool value)
{
if (!value) {
slotReloadPreset();
m_presetsPopup->resourceSelected(m_resourceProvider->currentPreset().data());
m_presetsPopup->updateViewSettings();
}
m_dirtyPresetsEnabled = value;
KisConfig cfg(false);
cfg.setUseDirtyPresets(m_dirtyPresetsEnabled);
}
void KisPaintopBox::slotEraserBrushSizeToggled(bool value)
{
m_eraserBrushSizeEnabled = value;
KisConfig cfg(false);
cfg.setUseEraserBrushSize(m_eraserBrushSizeEnabled);
}
void KisPaintopBox::slotEraserBrushOpacityToggled(bool value)
{
m_eraserBrushOpacityEnabled = value;
KisConfig cfg(false);
cfg.setUseEraserBrushOpacity(m_eraserBrushOpacityEnabled);
}
void KisPaintopBox::slotUpdateSelectionIcon()
{
m_hMirrorAction->setIcon(KisIconUtils::loadIcon("symmetry-horizontal"));
m_vMirrorAction->setIcon(KisIconUtils::loadIcon("symmetry-vertical"));
KisConfig cfg(true);
if (!cfg.toolOptionsInDocker() && m_toolOptionsPopupButton) {
m_toolOptionsPopupButton->setIcon(KisIconUtils::loadIcon("configure"));
}
m_presetSelectorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_01"));
m_brushEditorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_02"));
m_workspaceWidget->setIcon(KisIconUtils::loadIcon("view-choose"));
m_eraseAction->setIcon(KisIconUtils::loadIcon("draw-eraser"));
m_reloadAction->setIcon(KisIconUtils::loadIcon("view-refresh"));
if (m_disablePressureAction->isChecked()) {
m_disablePressureAction->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure"));
} else {
m_disablePressureAction->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure_locked"));
}
}
void KisPaintopBox::slotLockXMirrorToggle(bool toggleLock) {
m_resourceProvider->setMirrorHorizontalLock(toggleLock);
}
void KisPaintopBox::slotLockYMirrorToggle(bool toggleLock) {
m_resourceProvider->setMirrorVerticalLock(toggleLock);
}
void KisPaintopBox::slotHideDecorationMirrorX(bool toggled) {
m_resourceProvider->setMirrorHorizontalHideDecorations(toggled);
}
void KisPaintopBox::slotHideDecorationMirrorY(bool toggled) {
m_resourceProvider->setMirrorVerticalHideDecorations(toggled);
}
void KisPaintopBox::slotMoveToCenterMirrorX() {
m_resourceProvider->mirrorHorizontalMoveCanvasToCenter();
}
void KisPaintopBox::slotMoveToCenterMirrorY() {
m_resourceProvider->mirrorVerticalMoveCanvasToCenter();
}
diff --git a/libs/ui/kis_paintop_option.h b/libs/ui/kis_paintop_option.h
index 925e24c2cc..e04d32bea7 100644
--- a/libs/ui/kis_paintop_option.h
+++ b/libs/ui/kis_paintop_option.h
@@ -1,133 +1,133 @@
/* This file is part of the KDE project
* Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2008
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KIS_PAINTOP_OPTION_H
#define KIS_PAINTOP_OPTION_H
#include <kis_types.h>
#include <kritaui_export.h>
#include <kis_properties_configuration.h>
#include <brushengine/kis_locked_properties_proxy.h>
#include <KisPaintopPropertiesBase.h>
class QWidget;
class QString;
class KisPaintopLodLimitations;
/**
* Base interface for paintop options. A paintop option
* can be enabled/disabled, has a configuration page
* (for example, a curve), a user-visible name and can
* be serialized and deserialized into KisPaintOpPresets
*
* Because KisPaintOpOption classes create a QWidget in
* their constructor (the configuration page) you CANNOT
* create those objects in a KisPaintOp. KisPaintOps are
* created in non-gui threads.
*
* Options are disabled by default.
*/
class KRITAUI_EXPORT KisPaintOpOption : public QObject
{
Q_OBJECT
public:
enum PaintopCategory {
GENERAL,
COLOR,
TEXTURE,
FILTER,
MASKING_BRUSH
};
KisPaintOpOption(KisPaintOpOption::PaintopCategory category, bool checked);
~KisPaintOpOption() override;
KisPaintOpOption::PaintopCategory category() const;
virtual bool isCheckable() const;
virtual bool isChecked() const;
virtual void setChecked(bool checked);
void setLocked(bool value);
bool isLocked() const;
/**
* Reimplement this to use the image in the option widget
*/
virtual void setImage(KisImageWSP image);
virtual void setNode(KisNodeWSP node);
void startReadOptionSetting(const KisPropertiesConfigurationSP setting);
void startWriteOptionSetting(KisPropertiesConfigurationSP setting) const;
QWidget *configurationPage() const;
virtual void lodLimitations(KisPaintopLodLimitations *l) const;
protected:
void setConfigurationPage(QWidget *page);
protected:
/**
* Re-implement this to save the configuration to the paint configuration.
*/
virtual void writeOptionSetting(KisPropertiesConfigurationSP setting) const {
Q_UNUSED(setting);
}
/**
- * Re-implement this to set te widgets with the values in @param setting.
+ * Re-implement this to set te widgets with the values in @p setting.
*/
virtual void readOptionSetting(const KisPropertiesConfigurationSP setting) {
Q_UNUSED(setting);
}
protected Q_SLOTS:
void emitSettingChanged();
void emitCheckedChanged();
Q_SIGNALS:
/**
* emit this whenever a setting has changed. It will update the preview
*/
void sigSettingChanged();
/**
* emit this whenever a checked state of the option has changed. It as always
* emitted *before* sigSettingChanged()
*/
void sigCheckedChanged(bool value);
protected:
bool m_checkable;
bool m_locked;
private:
struct Private;
Private* const m_d;
};
#endif
diff --git a/libs/ui/kis_png_converter.h b/libs/ui/kis_png_converter.h
index 27eafd80c7..0842928991 100644
--- a/libs/ui/kis_png_converter.h
+++ b/libs/ui/kis_png_converter.h
@@ -1,144 +1,146 @@
/*
* Copyright (c) 2005, 2007 Cyrille Berger <cberger@cberger.net>
*
* 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_PNG_CONVERTER_H_
#define _KIS_PNG_CONVERTER_H_
#include <png.h>
#include <QColor>
#include <QVector>
#include "kis_types.h"
#include "kis_global.h"
#include "kis_annotation.h"
#include <kritaui_export.h>
#include <KisImageBuilderResult.h>
class KoStore;
class KisDocument;
class KoColorSpace;
namespace KisMetaData
{
class Filter;
class Store;
}
struct KisPNGOptions {
KisPNGOptions()
: compression(0)
, interlace(false)
, alpha(true)
, exif(true)
, iptc(true)
, xmp(true)
, tryToSaveAsIndexed(true)
, saveSRGBProfile(false)
, forceSRGB(false)
, storeMetaData(false)
, storeAuthor(false)
, saveAsHDR(false)
, transparencyFillColor(Qt::white)
{}
int compression;
bool interlace;
bool alpha;
bool exif;
bool iptc;
bool xmp;
bool tryToSaveAsIndexed;
bool saveSRGBProfile;
bool forceSRGB;
bool storeMetaData;
bool storeAuthor;
bool saveAsHDR;
QList<const KisMetaData::Filter*> filters;
QColor transparencyFillColor;
};
/**
* This class allows to import/export a PNG from either a file or a QIODevice.
*/
// XXX_PROGRESS (pass KoUpdater to the png converter)
class KRITAUI_EXPORT KisPNGConverter : public QObject
{
Q_OBJECT
public:
/**
* Initialize the converter.
* @param doc the KisDocument related to the image, can be null if you don't have a KisDocument
- * @param adapter the undo adapter to be used by the image, can be null if you don't want to use an undo adapter
+ * @param batchMode whether to use the batch mode
*/
KisPNGConverter(KisDocument *doc, bool batchMode = false);
~KisPNGConverter() override;
public:
/**
* Load an image from an URL. If the image is not on a local drive, the image is first downloaded to a
* temporary location.
- * @param uri the url of the image
+ * @param filename the file name of the image
*/
KisImageBuilder_Result buildImage(const QString &filename);
/**
* Load an image from a QIODevice.
* @param iod device to access the data
*/
KisImageBuilder_Result buildImage(QIODevice* iod);
/**
* Save a layer to a PNG
- * @param uri the url of the destination file
+ * @param filename the name of the destination file
+ * @param imageRect the image rectangle to save
+ * @param xRes resolution along x axis
+ * @param yRes resolution along y axis
* @param device the paint device to save
* @param annotationsStart an iterator on the first annotation
* @param annotationsEnd an iterator on the last annotation
- * @param compression a number between 0 and 9 to specify the compression rate (9 is most compressed)
- * @param interlace set to true if you want to generate an interlaced png
- * @param alpha set to true if you want to save the alpha channel
+ * @param options PNG formatting options
+ * @param metaData image metadata
*/
KisImageBuilder_Result buildFile(const QString &filename, const QRect &imageRect, const qreal xRes, const qreal yRes, KisPaintDeviceSP device, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, KisPNGOptions options, KisMetaData::Store* metaData);
KisImageBuilder_Result buildFile(QIODevice*, const QRect &imageRect, const qreal xRes, const qreal yRes, KisPaintDeviceSP device, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, KisPNGOptions options, KisMetaData::Store* metaData);
/**
* Retrieve the constructed image
*/
KisImageSP image();
/**
* @brief saveDeviceToStore saves the given paint device to the KoStore. If the device is not 8 bits sRGB, it will be converted to 8 bits sRGB.
* @return true if the saving succeeds
*/
static bool saveDeviceToStore(const QString &filename, const QRect &imageRect, const qreal xRes, const qreal yRes, KisPaintDeviceSP dev, KoStore *store, KisMetaData::Store* metaData = 0);
static bool isColorSpaceSupported(const KoColorSpace *cs);
public Q_SLOTS:
virtual void cancel();
private:
void progress(png_structp png_ptr, png_uint_32 row_number, int pass);
private:
png_uint_32 m_max_row;
KisImageSP m_image;
KisDocument *m_doc;
bool m_stop;
bool m_batchMode;
QString m_path;
};
#endif
diff --git a/libs/ui/kis_safe_document_loader.cpp b/libs/ui/kis_safe_document_loader.cpp
index 583b465633..2b240ccf8a 100644
--- a/libs/ui/kis_safe_document_loader.cpp
+++ b/libs/ui/kis_safe_document_loader.cpp
@@ -1,249 +1,272 @@
/*
* Copyright (c) 2013 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_safe_document_loader.h"
#include <QTimer>
#include <QFileSystemWatcher>
#include <QApplication>
#include <QFileInfo>
#include <QDir>
#include <QUrl>
+#include <KoStore.h>
+
+#include <kis_paint_layer.h>
+#include <kis_group_layer.h>
#include "KisDocument.h"
-#include "kis_image.h"
+#include <kis_image.h>
#include "kis_signal_compressor.h"
#include "KisPart.h"
class FileSystemWatcherWrapper : public QObject
{
Q_OBJECT
public:
FileSystemWatcherWrapper() {
connect(&m_watcher, SIGNAL(fileChanged(QString)), SIGNAL(fileChanged(QString)));
connect(&m_watcher, SIGNAL(fileChanged(QString)), SLOT(slotFileChanged(QString)));
}
bool addPath(const QString &file) {
bool result = true;
const QString ufile = unifyFilePath(file);
if (m_pathCount.contains(ufile)) {
m_pathCount[ufile]++;
} else {
m_pathCount.insert(ufile, 1);
result = m_watcher.addPath(ufile);
}
return result;
}
bool removePath(const QString &file) {
bool result = true;
const QString ufile = unifyFilePath(file);
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(m_pathCount.contains(ufile), false);
if (m_pathCount[ufile] == 1) {
m_pathCount.remove(ufile);
result = m_watcher.removePath(ufile);
} else {
m_pathCount[ufile]--;
}
return result;
}
QStringList files() const {
return m_watcher.files();
}
private Q_SLOTS:
void slotFileChanged(const QString &path) {
// re-add the file after QSaveFile optimization
if (!m_watcher.files().contains(path) && QFileInfo(path).exists()) {
m_watcher.addPath(path);
}
}
Q_SIGNALS:
void fileChanged(const QString &path);
private:
QString unifyFilePath(const QString &path) {
return QFileInfo(path).absoluteFilePath();
}
private:
QFileSystemWatcher m_watcher;
QHash<QString, int> m_pathCount;
};
Q_GLOBAL_STATIC(FileSystemWatcherWrapper, s_fileSystemWatcher)
struct KisSafeDocumentLoader::Private
{
Private()
: fileChangedSignalCompressor(500 /* ms */, KisSignalCompressor::POSTPONE)
{
}
QScopedPointer<KisDocument> doc;
KisSignalCompressor fileChangedSignalCompressor;
QTimer delayedLoadTimer;
bool isLoading = false;
bool fileChangedFlag = false;
QString path;
QString temporaryPath;
qint64 initialFileSize = 0;
QDateTime initialFileTimeStamp;
};
KisSafeDocumentLoader::KisSafeDocumentLoader(const QString &path, QObject *parent)
: QObject(parent),
m_d(new Private())
{
connect(s_fileSystemWatcher, SIGNAL(fileChanged(QString)),
SLOT(fileChanged(QString)));
connect(&m_d->fileChangedSignalCompressor, SIGNAL(timeout()),
SLOT(fileChangedCompressed()));
connect(&m_d->delayedLoadTimer, SIGNAL(timeout()),
SLOT(delayedLoadStart()));
m_d->delayedLoadTimer.setSingleShot(true);
m_d->delayedLoadTimer.setInterval(100 /* ms */);
setPath(path);
}
KisSafeDocumentLoader::~KisSafeDocumentLoader()
{
if (!m_d->path.isEmpty()) {
s_fileSystemWatcher->removePath(m_d->path);
}
delete m_d;
}
void KisSafeDocumentLoader::setPath(const QString &path)
{
if (path.isEmpty()) return;
if (!m_d->path.isEmpty()) {
s_fileSystemWatcher->removePath(m_d->path);
}
m_d->path = path;
s_fileSystemWatcher->addPath(m_d->path);
}
void KisSafeDocumentLoader::reloadImage()
{
fileChangedCompressed(true);
}
void KisSafeDocumentLoader::fileChanged(QString path)
{
if (path == m_d->path) {
if (s_fileSystemWatcher->files().contains(path) == false && QFileInfo(path).exists()) {
//When a path is renamed it is removed, so we ought to readd it.
s_fileSystemWatcher->addPath(path);
}
m_d->fileChangedFlag = true;
m_d->fileChangedSignalCompressor.start();
}
}
void KisSafeDocumentLoader::fileChangedCompressed(bool sync)
{
if (m_d->isLoading) return;
QFileInfo initialFileInfo(m_d->path);
m_d->initialFileSize = initialFileInfo.size();
m_d->initialFileTimeStamp = initialFileInfo.lastModified();
// it may happen when the file is flushed by
// so other application
if (!m_d->initialFileSize) return;
m_d->isLoading = true;
m_d->fileChangedFlag = false;
m_d->temporaryPath =
- QDir::tempPath() + QDir::separator() +
- QString("krita_file_layer_copy_%1_%2.%3")
- .arg(QApplication::applicationPid())
- .arg(qrand())
- .arg(initialFileInfo.suffix());
+ QDir::tempPath() + QDir::separator() +
+ QString("krita_file_layer_copy_%1_%2.%3")
+ .arg(QApplication::applicationPid())
+ .arg(qrand())
+ .arg(initialFileInfo.suffix());
QFile::copy(m_d->path, m_d->temporaryPath);
if (!sync) {
m_d->delayedLoadTimer.start();
} else {
QApplication::processEvents();
delayedLoadStart();
}
}
void KisSafeDocumentLoader::delayedLoadStart()
{
QFileInfo originalInfo(m_d->path);
QFileInfo tempInfo(m_d->temporaryPath);
bool successfullyLoaded = false;
if (!m_d->fileChangedFlag &&
- originalInfo.size() == m_d->initialFileSize &&
- originalInfo.lastModified() == m_d->initialFileTimeStamp &&
- tempInfo.size() == m_d->initialFileSize) {
+ originalInfo.size() == m_d->initialFileSize &&
+ originalInfo.lastModified() == m_d->initialFileTimeStamp &&
+ tempInfo.size() == m_d->initialFileSize) {
m_d->doc.reset(KisPart::instance()->createDocument());
- successfullyLoaded = m_d->doc->openUrl(QUrl::fromLocalFile(m_d->temporaryPath),
- KisDocument::DontAddToRecent);
+
+ if (m_d->path.toLower().endsWith("ora") || m_d->path.toLower().endsWith("kra")) {
+ QScopedPointer<KoStore> store(KoStore::createStore(m_d->temporaryPath, KoStore::Read));
+ if (store) {
+ if (store->open(QString("mergedimage.png"))) {
+ QByteArray bytes = store->read(store->size());
+ store->close();
+ QImage mergedImage;
+ mergedImage.loadFromData(bytes);
+ KisImageSP image = new KisImage(0, mergedImage.width(), mergedImage.height(), KoColorSpaceRegistry::instance()->rgb8(), "");
+ KisPaintLayerSP layer = new KisPaintLayer(image, "", OPACITY_OPAQUE_U8);
+ layer->paintDevice()->convertFromQImage(mergedImage, 0);
+ image->addNode(layer, image->rootLayer());
+ m_d->doc->setCurrentImage(image);
+ }
+ }
+ }
+ else {
+ successfullyLoaded = m_d->doc->openUrl(QUrl::fromLocalFile(m_d->temporaryPath),
+ KisDocument::DontAddToRecent);
+ }
} else {
dbgKrita << "File was modified externally. Restarting.";
dbgKrita << ppVar(m_d->fileChangedFlag);
dbgKrita << ppVar(m_d->initialFileSize);
dbgKrita << ppVar(m_d->initialFileTimeStamp);
dbgKrita << ppVar(originalInfo.size());
dbgKrita << ppVar(originalInfo.lastModified());
dbgKrita << ppVar(tempInfo.size());
}
QFile::remove(m_d->temporaryPath);
m_d->isLoading = false;
if (!successfullyLoaded) {
// Restart the attempt
m_d->fileChangedSignalCompressor.start();
}
else {
KisPaintDeviceSP paintDevice = new KisPaintDevice(m_d->doc->image()->colorSpace());
KisPaintDeviceSP projection = m_d->doc->image()->projection();
paintDevice->makeCloneFrom(projection, projection->extent());
emit loadingFinished(paintDevice, m_d->doc->image()->xRes(), m_d->doc->image()->yRes());
}
m_d->doc.reset();
}
#include "kis_safe_document_loader.moc"
diff --git a/libs/ui/kis_safe_document_loader.h b/libs/ui/kis_safe_document_loader.h
index eb202a6984..b955a4635f 100644
--- a/libs/ui/kis_safe_document_loader.h
+++ b/libs/ui/kis_safe_document_loader.h
@@ -1,48 +1,49 @@
/*
* Copyright (c) 2013 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_SAFE_DOCUMENT_LOADER_H
#define __KIS_SAFE_DOCUMENT_LOADER_H
#include <QObject>
#include "kis_types.h"
class KisSafeDocumentLoader : public QObject
{
Q_OBJECT
-public:
+private:
+ friend class KisFileLayer;
+
KisSafeDocumentLoader(const QString &path = "", QObject *parent = 0);
~KisSafeDocumentLoader() override;
-public:
void setPath(const QString &path);
void reloadImage();
private Q_SLOTS:
void fileChanged(QString);
void fileChangedCompressed(bool sync = false);
void delayedLoadStart();
Q_SIGNALS:
void loadingFinished(KisPaintDeviceSP paintDevice, int xRes, int yRes);
private:
struct Private;
Private * const m_d;
};
#endif /* __KIS_SAFE_DOCUMENT_LOADER_H */
diff --git a/libs/ui/kis_selection_manager.cc b/libs/ui/kis_selection_manager.cc
index f1bcb15ec9..a6f68155ee 100644
--- a/libs/ui/kis_selection_manager.cc
+++ b/libs/ui/kis_selection_manager.cc
@@ -1,746 +1,746 @@
/*
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2007 Sven Langkamp <sven.langkamp@gmail.com>
*
* The outline algorithm uses the limn algorithm of fontutils by
* Karl Berry <karl@cs.umb.edu> and Kathryn Hargreaves <letters@cs.umb.edu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_selection_manager.h"
#include <QApplication>
#include <QClipboard>
#include <QColor>
#include <QTimer>
#include <QMimeData>
#include <QAction>
#include <ktoggleaction.h>
#include <klocalizedstring.h>
#include <kstandardaction.h>
#include <kactioncollection.h>
#include "KoCanvasController.h"
#include "KoChannelInfo.h"
#include "KoIntegerMaths.h"
#include <KisDocument.h>
#include <KisMainWindow.h>
#include <KoViewConverter.h>
#include <KoSelection.h>
#include <KoShapeManager.h>
#include <KoSelectedShapesProxy.h>
#include <KoShapeStroke.h>
#include <KoColorSpace.h>
#include <KoCompositeOp.h>
#include <KoToolProxy.h>
#include <KoSvgPaste.h>
#include <kis_icon.h>
#include "kis_adjustment_layer.h"
#include "kis_node_manager.h"
#include "canvas/kis_canvas2.h"
#include "kis_config.h"
#include "kis_convolution_painter.h"
#include "kis_convolution_kernel.h"
#include "kis_debug.h"
#include "kis_fill_painter.h"
#include "kis_group_layer.h"
#include "kis_layer.h"
#include "kis_statusbar.h"
#include "kis_paint_device.h"
#include "kis_paint_layer.h"
#include "kis_painter.h"
#include "kis_transaction.h"
#include "kis_selection.h"
#include "kis_types.h"
#include "kis_canvas_resource_provider.h"
#include "kis_undo_adapter.h"
#include "kis_pixel_selection.h"
#include "flake/kis_shape_selection.h"
#include "commands/kis_selection_commands.h"
#include "kis_selection_mask.h"
#include "flake/kis_shape_layer.h"
#include "kis_selection_decoration.h"
#include "canvas/kis_canvas_decoration.h"
#include "kis_node_commands_adapter.h"
#include "kis_iterator_ng.h"
#include "kis_clipboard.h"
#include "KisViewManager.h"
#include "kis_selection_filters.h"
#include "kis_figure_painting_tool_helper.h"
#include "KisView.h"
#include "dialogs/kis_dlg_stroke_selection_properties.h"
#include "actions/kis_selection_action_factories.h"
#include "actions/KisPasteActionFactory.h"
#include "kis_action.h"
#include "kis_action_manager.h"
#include "operations/kis_operation_configuration.h"
//new
#include "kis_node_query_path.h"
#include "kis_tool_shape.h"
KisSelectionManager::KisSelectionManager(KisViewManager * view)
: m_view(view),
m_doc(0),
m_imageView(0),
m_adapter(new KisNodeCommandsAdapter(view)),
m_copy(0),
m_copyMerged(0),
m_cut(0),
m_paste(0),
m_pasteNew(0),
m_cutToNewLayer(0),
m_selectAll(0),
m_deselect(0),
m_clear(0),
m_reselect(0),
m_invert(0),
m_copyToNewLayer(0),
m_fillForegroundColor(0),
m_fillBackgroundColor(0),
m_fillPattern(0),
m_imageResizeToSelection(0),
m_selectionDecoration(0)
{
m_clipboard = KisClipboard::instance();
}
KisSelectionManager::~KisSelectionManager()
{
}
void KisSelectionManager::setup(KisActionManager* actionManager)
{
m_cut = actionManager->createStandardAction(KStandardAction::Cut, this, SLOT(cut()));
m_copy = actionManager->createStandardAction(KStandardAction::Copy, this, SLOT(copy()));
m_paste = actionManager->createStandardAction(KStandardAction::Paste, this, SLOT(paste()));
KisAction *action = actionManager->createAction("copy_sharp");
connect(action, SIGNAL(triggered()), this, SLOT(copySharp()));
action = actionManager->createAction("cut_sharp");
connect(action, SIGNAL(triggered()), this, SLOT(cutSharp()));
m_pasteNew = actionManager->createAction("paste_new");
connect(m_pasteNew, SIGNAL(triggered()), this, SLOT(pasteNew()));
m_pasteAt = actionManager->createAction("paste_at");
connect(m_pasteAt, SIGNAL(triggered()), this, SLOT(pasteAt()));
m_copyMerged = actionManager->createAction("copy_merged");
connect(m_copyMerged, SIGNAL(triggered()), this, SLOT(copyMerged()));
m_selectAll = actionManager->createAction("select_all");
connect(m_selectAll, SIGNAL(triggered()), this, SLOT(selectAll()));
m_deselect = actionManager->createAction("deselect");
connect(m_deselect, SIGNAL(triggered()), this, SLOT(deselect()));
m_clear = actionManager->createAction("clear");
connect(m_clear, SIGNAL(triggered()), SLOT(clear()));
m_reselect = actionManager->createAction("reselect");
connect(m_reselect, SIGNAL(triggered()), this, SLOT(reselect()));
m_invert = actionManager->createAction("invert_selection");
m_invert->setOperationID("invertselection");
actionManager->registerOperation(new KisInvertSelectionOperation);
m_copyToNewLayer = actionManager->createAction("copy_selection_to_new_layer");
connect(m_copyToNewLayer, SIGNAL(triggered()), this, SLOT(copySelectionToNewLayer()));
m_cutToNewLayer = actionManager->createAction("cut_selection_to_new_layer");
connect(m_cutToNewLayer, SIGNAL(triggered()), this, SLOT(cutToNewLayer()));
m_fillForegroundColor = actionManager->createAction("fill_selection_foreground_color");
connect(m_fillForegroundColor, SIGNAL(triggered()), this, SLOT(fillForegroundColor()));
m_fillBackgroundColor = actionManager->createAction("fill_selection_background_color");
connect(m_fillBackgroundColor, SIGNAL(triggered()), this, SLOT(fillBackgroundColor()));
m_fillPattern = actionManager->createAction("fill_selection_pattern");
connect(m_fillPattern, SIGNAL(triggered()), this, SLOT(fillPattern()));
m_fillForegroundColorOpacity = actionManager->createAction("fill_selection_foreground_color_opacity");
connect(m_fillForegroundColorOpacity, SIGNAL(triggered()), this, SLOT(fillForegroundColorOpacity()));
m_fillBackgroundColorOpacity = actionManager->createAction("fill_selection_background_color_opacity");
connect(m_fillBackgroundColorOpacity, SIGNAL(triggered()), this, SLOT(fillBackgroundColorOpacity()));
m_fillPatternOpacity = actionManager->createAction("fill_selection_pattern_opacity");
connect(m_fillPatternOpacity, SIGNAL(triggered()), this, SLOT(fillPatternOpacity()));
m_strokeShapes = actionManager->createAction("stroke_shapes");
connect(m_strokeShapes, SIGNAL(triggered()), this, SLOT(paintSelectedShapes()));
m_toggleDisplaySelection = actionManager->createAction("toggle_display_selection");
connect(m_toggleDisplaySelection, SIGNAL(triggered()), this, SLOT(toggleDisplaySelection()));
m_toggleDisplaySelection->setChecked(true);
m_imageResizeToSelection = actionManager->createAction("resizeimagetoselection");
connect(m_imageResizeToSelection, SIGNAL(triggered()), this, SLOT(imageResizeToSelection()));
action = actionManager->createAction("edit_selection");
connect(action, SIGNAL(triggered()), SLOT(editSelection()));
action = actionManager->createAction("convert_to_vector_selection");
connect(action, SIGNAL(triggered()), SLOT(convertToVectorSelection()));
action = actionManager->createAction("convert_to_raster_selection");
connect(action, SIGNAL(triggered()), SLOT(convertToRasterSelection()));
action = actionManager->createAction("convert_shapes_to_vector_selection");
connect(action, SIGNAL(triggered()), SLOT(convertShapesToVectorSelection()));
action = actionManager->createAction("convert_selection_to_shape");
connect(action, SIGNAL(triggered()), SLOT(convertToShape()));
m_toggleSelectionOverlayMode = actionManager->createAction("toggle-selection-overlay-mode");
connect(m_toggleSelectionOverlayMode, SIGNAL(triggered()), SLOT(slotToggleSelectionDecoration()));
m_strokeSelected = actionManager->createAction("stroke_selection");
connect(m_strokeSelected, SIGNAL(triggered()), SLOT(slotStrokeSelection()));
QClipboard *cb = QApplication::clipboard();
connect(cb, SIGNAL(dataChanged()), SLOT(clipboardDataChanged()));
}
void KisSelectionManager::setView(QPointer<KisView>imageView)
{
if (m_imageView && m_imageView->canvasBase()) {
disconnect(m_imageView->canvasBase()->toolProxy(), SIGNAL(toolChanged(QString)), this, SLOT(clipboardDataChanged()));
KoSelection *selection = m_imageView->canvasBase()->globalShapeManager()->selection();
selection->disconnect(this, SLOT(shapeSelectionChanged()));
KisSelectionDecoration *decoration = qobject_cast<KisSelectionDecoration*>(m_imageView->canvasBase()->decoration("selection").data());
if (decoration) {
disconnect(SIGNAL(currentSelectionChanged()), decoration);
}
m_imageView->image()->undoAdapter()->disconnect(this);
m_selectionDecoration = 0;
}
m_imageView = imageView;
if (m_imageView) {
- connect(m_imageView->canvasBase()->selectedShapesProxy(), SIGNAL(selectionChanged()), this, SLOT(shapeSelectionChanged()));
+ connect(m_imageView->canvasBase()->selectedShapesProxy(), SIGNAL(selectionChanged()), this, SLOT(shapeSelectionChanged()), Qt::UniqueConnection);
KisSelectionDecoration* decoration = qobject_cast<KisSelectionDecoration*>(m_imageView->canvasBase()->decoration("selection").data());
if (!decoration) {
decoration = new KisSelectionDecoration(m_imageView);
decoration->setVisible(true);
m_imageView->canvasBase()->addDecoration(decoration);
}
m_selectionDecoration = decoration;
connect(this, SIGNAL(currentSelectionChanged()), decoration, SLOT(selectionChanged()));
connect(m_imageView->image()->undoAdapter(), SIGNAL(selectionChanged()), SLOT(selectionChanged()));
connect(m_imageView->canvasBase()->toolProxy(), SIGNAL(toolChanged(QString)), SLOT(clipboardDataChanged()));
}
}
void KisSelectionManager::clipboardDataChanged()
{
m_view->updateGUI();
}
bool KisSelectionManager::havePixelsSelected()
{
KisSelectionSP activeSelection = m_view->selection();
return activeSelection && !activeSelection->selectedRect().isEmpty();
}
bool KisSelectionManager::havePixelsInClipboard()
{
return m_clipboard->hasClip();
}
bool KisSelectionManager::haveShapesSelected()
{
if (m_view && m_view->canvasBase()) {
return m_view->canvasBase()->selectedShapesProxy()->selection()->count() > 0;
}
return false;
}
bool KisSelectionManager::haveShapesInClipboard()
{
KoSvgPaste paste;
return paste.hasShapes();
}
bool KisSelectionManager::haveAnySelectionWithPixels()
{
KisSelectionSP selection = m_view->selection();
return selection && selection->hasPixelSelection();
}
bool KisSelectionManager::haveShapeSelectionWithShapes()
{
KisSelectionSP selection = m_view->selection();
return selection && selection->hasShapeSelection();
}
bool KisSelectionManager::haveRasterSelectionWithPixels()
{
KisSelectionSP selection = m_view->selection();
return selection && selection->hasPixelSelection() && !selection->hasShapeSelection();
}
void KisSelectionManager::updateGUI()
{
Q_ASSERT(m_view);
Q_ASSERT(m_clipboard);
if (!m_view || !m_clipboard) return;
bool havePixelsSelected = this->havePixelsSelected();
bool havePixelsInClipboard = this->havePixelsInClipboard();
bool haveShapesSelected = this->haveShapesSelected();
bool haveShapesInClipboard = this->haveShapesInClipboard();
bool haveDevice = m_view->activeDevice();
KisLayerSP activeLayer = m_view->activeLayer();
KisImageWSP image = activeLayer ? activeLayer->image() : 0;
bool canReselect = image && image->canReselectGlobalSelection();
bool canDeselect = image && image->globalSelection();
m_clear->setEnabled(haveDevice || havePixelsSelected || haveShapesSelected);
m_cut->setEnabled(havePixelsSelected || haveShapesSelected);
m_copy->setEnabled(havePixelsSelected || haveShapesSelected);
m_paste->setEnabled(havePixelsInClipboard || haveShapesInClipboard);
m_pasteAt->setEnabled(havePixelsInClipboard || haveShapesInClipboard);
// FIXME: how about pasting shapes?
m_pasteNew->setEnabled(havePixelsInClipboard);
m_selectAll->setEnabled(true);
m_deselect->setEnabled(canDeselect);
m_reselect->setEnabled(canReselect);
// m_load->setEnabled(true);
// m_save->setEnabled(havePixelsSelected);
updateStatusBar();
emit signalUpdateGUI();
}
void KisSelectionManager::updateStatusBar()
{
if (m_view && m_view->statusBar()) {
m_view->statusBar()->setSelection(m_view->image());
}
}
void KisSelectionManager::selectionChanged()
{
m_view->updateGUI();
emit currentSelectionChanged();
}
void KisSelectionManager::cut()
{
KisCutCopyActionFactory factory;
factory.run(true, false, m_view);
}
void KisSelectionManager::copy()
{
KisCutCopyActionFactory factory;
factory.run(false, false, m_view);
}
void KisSelectionManager::cutSharp()
{
KisCutCopyActionFactory factory;
factory.run(true, true, m_view);
}
void KisSelectionManager::copySharp()
{
KisCutCopyActionFactory factory;
factory.run(false, true, m_view);
}
void KisSelectionManager::copyMerged()
{
KisCopyMergedActionFactory factory;
factory.run(m_view);
}
void KisSelectionManager::paste()
{
KisPasteActionFactory factory;
factory.run(false, m_view);
}
void KisSelectionManager::pasteAt()
{
KisPasteActionFactory factory;
factory.run(true, m_view);
}
void KisSelectionManager::pasteNew()
{
KisPasteNewActionFactory factory;
factory.run(m_view);
}
void KisSelectionManager::selectAll()
{
KisSelectAllActionFactory factory;
factory.run(m_view);
}
void KisSelectionManager::deselect()
{
KisDeselectActionFactory factory;
factory.run(m_view);
}
void KisSelectionManager::invert()
{
if(m_invert)
m_invert->trigger();
}
void KisSelectionManager::reselect()
{
KisReselectActionFactory factory;
factory.run(m_view);
}
#include <KoToolManager.h>
#include <KoInteractionTool.h>
void KisSelectionManager::editSelection()
{
KisSelectionSP selection = m_view->selection();
if (!selection) return;
KisAction *action = m_view->actionManager()->actionByName("show-global-selection-mask");
KIS_SAFE_ASSERT_RECOVER_RETURN(action);
if (!action->isChecked()) {
action->setChecked(true);
emit action->toggled(true);
emit action->triggered(true);
}
KisNodeSP node = selection->parentNode();
KIS_SAFE_ASSERT_RECOVER_RETURN(node);
m_view->nodeManager()->slotNonUiActivatedNode(node);
if (selection->hasShapeSelection()) {
KisShapeSelection *shapeSelection = dynamic_cast<KisShapeSelection*>(selection->shapeSelection());
KIS_SAFE_ASSERT_RECOVER_RETURN(shapeSelection);
KoToolManager::instance()->switchToolRequested(KoInteractionTool_ID);
QList<KoShape*> shapes = shapeSelection->shapes();
if (shapes.isEmpty()) {
KIS_SAFE_ASSERT_RECOVER_NOOP(0 && "no shapes");
return;
}
Q_FOREACH (KoShape *shape, shapes) {
m_view->canvasBase()->selectedShapesProxy()->selection()->select(shape);
}
} else {
KoToolManager::instance()->switchToolRequested("KisToolTransform");
}
}
void KisSelectionManager::convertToVectorSelection()
{
KisSelectionToVectorActionFactory factory;
factory.run(m_view);
}
void KisSelectionManager::convertToRasterSelection()
{
KisSelectionToRasterActionFactory factory;
factory.run(m_view);
}
void KisSelectionManager::convertShapesToVectorSelection()
{
KisShapesToVectorSelectionActionFactory factory;
factory.run(m_view);
}
void KisSelectionManager::convertToShape()
{
KisSelectionToShapeActionFactory factory;
factory.run(m_view);
}
void KisSelectionManager::clear()
{
KisClearActionFactory factory;
factory.run(m_view);
}
void KisSelectionManager::fillForegroundColor()
{
KisFillActionFactory factory;
factory.run("fg", m_view);
}
void KisSelectionManager::fillBackgroundColor()
{
KisFillActionFactory factory;
factory.run("bg", m_view);
}
void KisSelectionManager::fillPattern()
{
KisFillActionFactory factory;
factory.run("pattern", m_view);
}
void KisSelectionManager::fillForegroundColorOpacity()
{
KisFillActionFactory factory;
factory.run("fg_opacity", m_view);
}
void KisSelectionManager::fillBackgroundColorOpacity()
{
KisFillActionFactory factory;
factory.run("bg_opacity", m_view);
}
void KisSelectionManager::fillPatternOpacity()
{
KisFillActionFactory factory;
factory.run("pattern_opacity", m_view);
}
void KisSelectionManager::copySelectionToNewLayer()
{
copy();
paste();
}
void KisSelectionManager::cutToNewLayer()
{
cut();
paste();
}
void KisSelectionManager::toggleDisplaySelection()
{
KIS_ASSERT_RECOVER_RETURN(m_selectionDecoration);
m_selectionDecoration->toggleVisibility();
m_toggleDisplaySelection->blockSignals(true);
m_toggleDisplaySelection->setChecked(m_selectionDecoration->visible());
m_toggleDisplaySelection->blockSignals(false);
emit displaySelectionChanged();
}
bool KisSelectionManager::displaySelection()
{
return m_toggleDisplaySelection->isChecked();
}
void KisSelectionManager::shapeSelectionChanged()
{
KoShapeManager* shapeManager = m_view->canvasBase()->globalShapeManager();
KoSelection * selection = shapeManager->selection();
QList<KoShape*> selectedShapes = selection->selectedShapes();
KoShapeStrokeSP border(new KoShapeStroke(0, Qt::lightGray));
Q_FOREACH (KoShape* shape, shapeManager->shapes()) {
if (dynamic_cast<KisShapeSelection*>(shape->parent())) {
if (selectedShapes.contains(shape))
shape->setStroke(border);
else
shape->setStroke(KoShapeStrokeSP());
}
}
m_view->updateGUI();
}
void KisSelectionManager::imageResizeToSelection()
{
KisImageResizeToSelectionActionFactory factory;
factory.run(m_view);
}
void KisSelectionManager::paintSelectedShapes()
{
KisImageWSP image = m_view->image();
if (!image) return;
KisLayerSP layer = m_view->activeLayer();
if (!layer) return;
QList<KoShape*> shapes = m_view->canvasBase()->shapeManager()->selection()->selectedShapes();
KisPaintLayerSP paintLayer = new KisPaintLayer(image, i18n("Stroked Shapes"), OPACITY_OPAQUE_U8);
KUndo2MagicString actionName = kundo2_i18n("Stroke Shapes");
m_adapter->beginMacro(actionName);
m_adapter->addNode(paintLayer.data(), layer->parent().data(), layer.data());
KisFigurePaintingToolHelper helper(actionName,
image,
paintLayer.data(),
m_view->resourceProvider()->resourceManager(),
KisPainter::StrokeStyleBrush,
KisPainter::FillStyleNone);
Q_FOREACH (KoShape* shape, shapes) {
QTransform matrix = shape->absoluteTransformation(0) * QTransform::fromScale(image->xRes(), image->yRes());
QPainterPath mapedOutline = matrix.map(shape->outline());
helper.paintPainterPath(mapedOutline);
}
m_adapter->endMacro();
}
void KisSelectionManager::slotToggleSelectionDecoration()
{
KIS_ASSERT_RECOVER_RETURN(m_selectionDecoration);
KisSelectionDecoration::Mode mode =
m_selectionDecoration->mode() ?
KisSelectionDecoration::Ants : KisSelectionDecoration::Mask;
m_selectionDecoration->setMode(mode);
emit displaySelectionChanged();
}
bool KisSelectionManager::showSelectionAsMask() const
{
if (m_selectionDecoration) {
return m_selectionDecoration->mode() == KisSelectionDecoration::Mask;
}
return false;
}
void KisSelectionManager::slotStrokeSelection()
{
KisImageWSP image = m_view->image();
if (!image ) {
return;
}
KisNodeSP currentNode = m_view->resourceProvider()->resourceManager()->resource(KisCanvasResourceProvider::CurrentKritaNode).value<KisNodeWSP>();
bool isVectorLayer = false;
if (currentNode->inherits("KisShapeLayer")) {
isVectorLayer = true;
}
QPointer<KisDlgStrokeSelection> dlg = new KisDlgStrokeSelection(image, m_view, isVectorLayer);
if (dlg->exec() == QDialog::Accepted) {
StrokeSelectionOptions params = dlg->getParams();
if (params.brushSelected){
KisStrokeBrushSelectionActionFactory factory;
factory.run(m_view, params);
}
else {
KisStrokeSelectionActionFactory factory;
factory.run(m_view, params);
}
}
delete dlg;
}
#include "kis_image_barrier_locker.h"
#include "kis_selection_tool_helper.h"
void KisSelectionManager::selectOpaqueOnNode(KisNodeSP node, SelectionAction action)
{
KisImageSP image = m_view->image();
if (!m_view->blockUntilOperationsFinished(image)) {
return;
}
KUndo2MagicString actionName;
KisPixelSelectionSP tmpSel = KisPixelSelectionSP(new KisPixelSelection());
KisCanvas2 *canvas = m_view->canvasBase();
{
KisImageBarrierLocker locker(image);
KisPaintDeviceSP device = node->projection();
if (!device) device = node->paintDevice();
if (!device) device = node->original();
KIS_ASSERT_RECOVER_RETURN(canvas && device);
QRect rc = device->exactBounds();
if (rc.isEmpty()) return;
/**
* If there is nothing selected, just create a new selection
*/
if (!canvas->imageView()->selection()) {
action = SELECTION_REPLACE;
}
switch (action) {
case SELECTION_ADD:
actionName = kundo2_i18n("Select Opaque (Add)");
break;
case SELECTION_SUBTRACT:
actionName = kundo2_i18n("Select Opaque (Subtract)");
break;
case SELECTION_INTERSECT:
actionName = kundo2_i18n("Select Opaque (Intersect)");
break;
case SELECTION_SYMMETRICDIFFERENCE:
actionName = kundo2_i18n("Select Opaque (Symmetric Difference)");
break;
default:
actionName = kundo2_i18n("Select Opaque");
break;
}
qint32 x, y, w, h;
rc.getRect(&x, &y, &w, &h);
const KoColorSpace * cs = device->colorSpace();
KisHLineConstIteratorSP deviter = device->createHLineConstIteratorNG(x, y, w);
KisHLineIteratorSP selIter = tmpSel ->createHLineIteratorNG(x, y, w);
for (int row = y; row < h + y; ++row) {
do {
*selIter->rawData() = cs->opacityU8(deviter->oldRawData());
} while (deviter->nextPixel() && selIter->nextPixel());
deviter->nextRow();
selIter->nextRow();
}
}
KisSelectionToolHelper helper(canvas, actionName);
tmpSel->invalidateOutlineCache();
helper.selectPixelSelection(tmpSel, action);
}
diff --git a/libs/ui/kis_statusbar.cc b/libs/ui/kis_statusbar.cc
index c30348ba0f..991beb8245 100644
--- a/libs/ui/kis_statusbar.cc
+++ b/libs/ui/kis_statusbar.cc
@@ -1,384 +1,394 @@
/* This file is part of KimageShop^WKrayon^WKrita
*
* Copyright (c) 2006 Boudewijn Rempt <boud@valdyas.org>
*
* 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_statusbar.h"
#include <QLabel>
#include <QFontMetrics>
#include <QToolButton>
#include <QPushButton>
#include <QAction>
#include <QToolTip>
#include <QStatusBar>
#include <ksqueezedtextlabel.h>
#include <klocalizedstring.h>
#include <kformat.h>
#include <KoColorProfile.h>
#include <KoColorSpace.h>
#include <KoToolManager.h>
#include <KoViewConverter.h>
+#include <KisUsageLogger.h>
+
#include <kis_icon_utils.h>
#include <kis_types.h>
#include <kis_image.h>
#include <kis_selection.h>
#include <kis_paint_device.h>
#include <kis_selection_manager.h>
#include "kis_memory_statistics_server.h"
#include "KisView.h"
+#include "KisDocument.h"
#include "KisViewManager.h"
#include "canvas/kis_canvas2.h"
#include "kis_progress_widget.h"
#include "kis_zoom_manager.h"
#include "KisMainWindow.h"
#include "kis_config.h"
enum {
IMAGE_SIZE_ID,
POINTER_POSITION_ID
};
KisStatusBar::KisStatusBar(KisViewManager *viewManager)
: m_viewManager(viewManager)
, m_imageView(0)
, m_statusBar(0)
{
}
void KisStatusBar::setup()
{
m_selectionStatus = new QToolButton();
m_selectionStatus->setObjectName("selection status");
m_selectionStatus->setIconSize(QSize(16,16));
m_selectionStatus->setAutoRaise(true);
m_selectionStatus->setEnabled(false);
updateSelectionIcon();
m_statusBar = m_viewManager->mainWindow()->statusBar();
connect(m_selectionStatus, SIGNAL(clicked()), m_viewManager->selectionManager(), SLOT(slotToggleSelectionDecoration()));
connect(m_viewManager->selectionManager(), SIGNAL(displaySelectionChanged()), SLOT(updateSelectionToolTip()));
connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), this, SLOT(updateSelectionIcon()));
addStatusBarItem(m_selectionStatus);
m_selectionStatus->setVisible(false);
m_statusBarStatusLabel = new KSqueezedTextLabel();
m_statusBarStatusLabel->setObjectName("statsBarStatusLabel");
m_statusBarStatusLabel->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
m_statusBarStatusLabel->setContentsMargins(5, 5, 5, 5);
connect(KoToolManager::instance(), SIGNAL(changedStatusText(QString)),
m_statusBarStatusLabel, SLOT(setText(QString)));
addStatusBarItem(m_statusBarStatusLabel, 2);
m_statusBarStatusLabel->setVisible(false);
m_statusBarProfileLabel = new KSqueezedTextLabel();
m_statusBarProfileLabel->setObjectName("statsBarProfileLabel");
m_statusBarProfileLabel->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
m_statusBarProfileLabel->setContentsMargins(5, 5, 5, 5);
addStatusBarItem(m_statusBarProfileLabel, 3);
m_statusBarProfileLabel->setVisible(false);
m_progress = new KisProgressWidget();
m_progress->setObjectName("ProgressBar");
addStatusBarItem(m_progress);
m_progress->setVisible(false);
connect(m_progress, SIGNAL(sigCancellationRequested()), this, SIGNAL(sigCancellationRequested()));
m_progressUpdater.reset(new KisProgressUpdater(m_progress, m_progress->progressProxy()));
m_progressUpdater->setAutoNestNames(true);
m_memoryReportBox = new QPushButton();
m_memoryReportBox->setObjectName("memoryReportBox");
m_memoryReportBox->setFlat(true);
m_memoryReportBox->setContentsMargins(5, 5, 5, 5);
m_memoryReportBox->setMinimumWidth(120);
addStatusBarItem(m_memoryReportBox);
m_memoryReportBox->setVisible(false);
connect(m_memoryReportBox, SIGNAL(clicked()), SLOT(showMemoryInfoToolTip()));
m_pointerPositionLabel = new QLabel(QString());
m_pointerPositionLabel->setObjectName("pointerPositionLabel");
m_pointerPositionLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
m_pointerPositionLabel->setMinimumWidth(100);
m_pointerPositionLabel->setContentsMargins(5,5, 5, 5);
addStatusBarItem(m_pointerPositionLabel);
m_pointerPositionLabel->setVisible(false);
connect(KisMemoryStatisticsServer::instance(),
SIGNAL(sigUpdateMemoryStatistics()),
SLOT(imageSizeChanged()));
}
KisStatusBar::~KisStatusBar()
{
}
void KisStatusBar::setView(QPointer<KisView> imageView)
{
if (m_imageView == imageView) {
return;
}
if (m_imageView) {
m_imageView->disconnect(this);
removeStatusBarItem(m_imageView->zoomManager()->zoomActionWidget());
m_imageView = 0;
}
if (imageView) {
m_imageView = imageView;
connect(m_imageView, SIGNAL(sigColorSpaceChanged(const KoColorSpace*)),
this, SLOT(updateStatusBarProfileLabel()));
connect(m_imageView, SIGNAL(sigProfileChanged(const KoColorProfile*)),
this, SLOT(updateStatusBarProfileLabel()));
connect(m_imageView, SIGNAL(sigSizeChanged(QPointF,QPointF)),
this, SLOT(imageSizeChanged()));
updateStatusBarProfileLabel();
addStatusBarItem(m_imageView->zoomManager()->zoomActionWidget());
}
imageSizeChanged();
}
void KisStatusBar::addStatusBarItem(QWidget *widget, int stretch, bool permanent)
{
StatusBarItem sbItem(widget);
if (permanent) {
m_statusBar->addPermanentWidget(widget, stretch);
}
else {
m_statusBar->addWidget(widget, stretch);
}
widget->setVisible(true);
m_statusBarItems.append(sbItem);
}
void KisStatusBar::removeStatusBarItem(QWidget *widget)
{
int i = 0;
Q_FOREACH(const StatusBarItem& sbItem, m_statusBarItems) {
if (sbItem.widget() == widget) {
break;
}
i++;
}
if (i < m_statusBarItems.count()) {
m_statusBar->removeWidget(m_statusBarItems[i].widget());
m_statusBarItems.remove(i);
}
}
void KisStatusBar::hideAllStatusBarItems()
{
Q_FOREACH(const StatusBarItem& sbItem, m_statusBarItems) {
sbItem.hide();
}
}
void KisStatusBar::showAllStatusBarItems()
{
Q_FOREACH(const StatusBarItem& sbItem, m_statusBarItems) {
sbItem.show();
}
}
void KisStatusBar::documentMousePositionChanged(const QPointF &pos)
{
if (!m_imageView) return;
QPoint pixelPos = m_imageView->image()->documentToImagePixelFloored(pos);
pixelPos.setX(qBound(0, pixelPos.x(), m_viewManager->image()->width() - 1));
pixelPos.setY(qBound(0, pixelPos.y(), m_viewManager->image()->height() - 1));
m_pointerPositionLabel->setText(QString("%1, %2").arg(pixelPos.x()).arg(pixelPos.y()));
}
void KisStatusBar::imageSizeChanged()
{
updateMemoryStatus();
QString sizeText;
KisImageWSP image = m_imageView ? m_imageView->image() : 0;
if (image) {
qint32 w = image->width();
qint32 h = image->height();
sizeText = QString("%1 x %2 (%3)").arg(w).arg(h).arg(m_shortMemoryTag);
} else {
sizeText = m_shortMemoryTag;
}
m_memoryReportBox->setIcon(m_memoryStatusIcon);
m_memoryReportBox->setText(sizeText);
m_memoryReportBox->setToolTip(m_longMemoryTag);
}
void KisStatusBar::updateSelectionIcon()
{
QIcon icon;
if (!m_viewManager->selectionManager()->displaySelection()) {
icon = KisIconUtils::loadIcon("selection-mode_invisible");
} else if (m_viewManager->selectionManager()->showSelectionAsMask()) {
icon = KisIconUtils::loadIcon("selection-mode_mask");
} else /* if (!m_view->selectionManager()->showSelectionAsMask()) */ {
icon = KisIconUtils::loadIcon("selection-mode_ants");
}
m_selectionStatus->setIcon(icon);
}
void KisStatusBar::updateMemoryStatus()
{
KisMemoryStatisticsServer::Statistics stats =
KisMemoryStatisticsServer::instance()
->fetchMemoryStatistics(m_imageView ? m_imageView->image() : 0);
const KFormat format;
const QString imageStatsMsg =
i18nc("tooltip on statusbar memory reporting button (image stats)",
"Image size:\t %1\n"
" - layers:\t\t %2\n"
" - projections:\t %3\n"
" - instant preview:\t %4\n",
format.formatByteSize(stats.imageSize),
format.formatByteSize(stats.layersSize),
format.formatByteSize(stats.projectionsSize),
format.formatByteSize(stats.lodSize));
const QString memoryStatsMsg =
i18nc("tooltip on statusbar memory reporting button (total stats)",
"Memory used:\t %1 / %2\n"
" image data:\t %3 / %4\n"
" pool:\t\t %5 / %6\n"
" undo data:\t %7\n"
"\n"
"Swap used:\t %8",
format.formatByteSize(stats.totalMemorySize),
format.formatByteSize(stats.totalMemoryLimit),
format.formatByteSize(stats.realMemorySize),
format.formatByteSize(stats.tilesHardLimit),
format.formatByteSize(stats.poolSize),
format.formatByteSize(stats.tilesPoolLimit),
format.formatByteSize(stats.historicalMemorySize),
format.formatByteSize(stats.swapSize));
QString longStats = imageStatsMsg + "\n" + memoryStatsMsg;
QString shortStats = format.formatByteSize(stats.imageSize);
QIcon icon;
const qint64 warnLevel = stats.tilesHardLimit - stats.tilesHardLimit / 8;
if (stats.imageSize > warnLevel ||
stats.realMemorySize > warnLevel) {
+ if (!m_memoryWarningLogged) {
+ m_memoryWarningLogged = true;
+ KisUsageLogger::log(QString("WARNING: %1 is running out of memory:%2\n").arg(m_imageView->document()->url().toLocalFile()).arg(longStats));
+ }
+
icon = KisIconUtils::loadIcon("dialog-warning");
QString suffix =
i18nc("tooltip on statusbar memory reporting button",
"\n\nWARNING:\tOut of memory! Swapping has been started.\n"
"\t\tPlease configure more RAM for Krita in Settings dialog");
longStats += suffix;
+
+
}
m_shortMemoryTag = shortStats;
m_longMemoryTag = longStats;
m_memoryStatusIcon = icon;
emit memoryStatusUpdated();
}
void KisStatusBar::showMemoryInfoToolTip()
{
QToolTip::showText(QCursor::pos(), m_memoryReportBox->toolTip(), m_memoryReportBox);
}
void KisStatusBar::updateSelectionToolTip()
{
updateSelectionIcon();
KisSelectionSP selection = m_viewManager->selection();
if (selection) {
m_selectionStatus->setEnabled(true);
QRect r = selection->selectedExactRect();
QString displayMode =
!m_viewManager->selectionManager()->displaySelection() ?
i18n("Hidden") :
(m_viewManager->selectionManager()->showSelectionAsMask() ?
i18n("Mask") : i18n("Ants"));
m_selectionStatus->setToolTip(
i18n("Selection: x = %1 y = %2 width = %3 height = %4\n"
"Display Mode: %5",
r.x(), r.y(), r.width(), r.height(), displayMode));
} else {
m_selectionStatus->setEnabled(false);
m_selectionStatus->setToolTip(i18n("No Selection"));
}
}
void KisStatusBar::setSelection(KisImageWSP image)
{
Q_UNUSED(image);
updateSelectionToolTip();
}
void KisStatusBar::setProfile(KisImageWSP image)
{
if (m_statusBarProfileLabel == 0) {
return;
}
if (!image) return;
if (image->profile() == 0) {
m_statusBarProfileLabel->setText(i18n("No profile"));
} else {
m_statusBarProfileLabel->setText(image->colorSpace()->name() + " " + image->profile()->name());
}
}
void KisStatusBar::setHelp(const QString &t)
{
Q_UNUSED(t);
}
void KisStatusBar::updateStatusBarProfileLabel()
{
if (!m_imageView) return;
setProfile(m_imageView->image());
}
KoProgressUpdater *KisStatusBar::progressUpdater()
{
return m_progressUpdater.data();
}
diff --git a/libs/ui/kis_statusbar.h b/libs/ui/kis_statusbar.h
index b890f64906..14467e9180 100644
--- a/libs/ui/kis_statusbar.h
+++ b/libs/ui/kis_statusbar.h
@@ -1,138 +1,140 @@
/* This file is part of KimageShop^WKrayon^WKrita
*
* Copyright (c) 2003-200^ Boudewijn Rempt <boud@valdyas.org>
*
* 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_STATUSBAR_H
#define KIS_STATUSBAR_H
#include <QObject>
#include <QPointer>
#include <QIcon>
#include <QStatusBar>
#include <kis_types.h>
#include "KisView.h"
class QLabel;
class QToolButton;
class QPushButton;
class KSqueezedTextLabel;
class KisViewManager;
class KisProgressWidget;
class KoProgressUpdater;
#include "kritaui_export.h"
class KRITAUI_EXPORT KisStatusBar : public QObject
{
class StatusBarItem
{
public:
StatusBarItem()
: m_widget(0) {}
StatusBarItem(QWidget * widget)
: m_widget(widget) {}
bool operator==(const StatusBarItem& rhs) {
return m_widget == rhs.m_widget;
}
bool operator!=(const StatusBarItem& rhs) {
return m_widget != rhs.m_widget;
}
QWidget * widget() const {
return m_widget;
}
void show() const {
m_widget->show();
}
void hide() const {
m_widget->hide();
}
private:
QPointer<QWidget> m_widget;
};
Q_OBJECT
public:
explicit KisStatusBar(KisViewManager *view);
~KisStatusBar() override;
void setup();
void setView(QPointer<KisView> imageView);
void hideAllStatusBarItems();
void showAllStatusBarItems();
KoProgressUpdater *progressUpdater();
public Q_SLOTS:
void documentMousePositionChanged(const QPointF &p);
void imageSizeChanged();
void setSelection(KisImageWSP image);
void setProfile(KisImageWSP image);
void setHelp(const QString &t);
void updateStatusBarProfileLabel();
void updateSelectionToolTip();
private Q_SLOTS:
void updateSelectionIcon();
void showMemoryInfoToolTip();
Q_SIGNALS:
void sigCancellationRequested();
/// tell the listener that the memory usage has changed
/// and it needs to update its stats
void memoryStatusUpdated();
private:
void removeStatusBarItem(QWidget *widget);
void addStatusBarItem(QWidget *widget, int stretch = 0, bool permanent = false);
void updateMemoryStatus();
private:
QPointer<KisViewManager> m_viewManager;
QPointer<KisView> m_imageView;
QPointer<QStatusBar> m_statusBar;
KisProgressWidget *m_progress;
QScopedPointer<KoProgressUpdater> m_progressUpdater;
QToolButton *m_selectionStatus;
QPushButton *m_memoryReportBox;
QLabel *m_pointerPositionLabel;
KSqueezedTextLabel *m_statusBarStatusLabel;
KSqueezedTextLabel *m_statusBarProfileLabel;
QString m_shortMemoryTag;
QString m_longMemoryTag;
QIcon m_memoryStatusIcon;
QVector<StatusBarItem> m_statusBarItems;
+
+ bool m_memoryWarningLogged {false};
};
#endif
diff --git a/libs/ui/kisexiv2/kis_exif_io.cpp b/libs/ui/kisexiv2/kis_exif_io.cpp
index 1a01fedf33..4a7857aa9c 100644
--- a/libs/ui/kisexiv2/kis_exif_io.cpp
+++ b/libs/ui/kisexiv2/kis_exif_io.cpp
@@ -1,639 +1,637 @@
/*
* Copyright (c) 2007 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_exif_io.h"
#include <exiv2/exif.hpp>
#include <exiv2/error.hpp>
#include <qendian.h>
#include <QIODevice>
#include <QByteArray>
#include <QVariant>
#include <QDateTime>
#include <QDate>
#include <QTime>
#include <QTextCodec>
#include <kis_debug.h>
#include "kis_exiv2.h"
#include <kis_meta_data_store.h>
#include <kis_meta_data_entry.h>
#include <kis_meta_data_value.h>
#include <kis_meta_data_schema.h>
#include <kis_meta_data_schema_registry.h>
struct KisExifIO::Private {
};
// ---- Exception conversion functions ---- //
// convert ExifVersion and FlashpixVersion to a KisMetaData value
KisMetaData::Value exifVersionToKMDValue(const Exiv2::Value::AutoPtr value)
{
const Exiv2::DataValue* dvalue = dynamic_cast<const Exiv2::DataValue*>(&*value);
if (dvalue) {
Q_ASSERT(dvalue);
QByteArray array(dvalue->count(), 0);
dvalue->copy((Exiv2::byte*)array.data());
return KisMetaData::Value(QString(array));
}
else {
Q_ASSERT(value->typeId() == Exiv2::asciiString);
return KisMetaData::Value(QString::fromLatin1(value->toString().c_str()));
}
}
// convert from KisMetaData value to ExifVersion and FlashpixVersion
Exiv2::Value* kmdValueToExifVersion(const KisMetaData::Value& value)
{
Exiv2::DataValue* dvalue = new Exiv2::DataValue;
QString ver = value.asVariant().toString();
dvalue->read((const Exiv2::byte*)ver.toLatin1().constData(), ver.size());
return dvalue;
}
// Convert an exif array of integer string to a KisMetaData array of integer
KisMetaData::Value exifArrayToKMDIntOrderedArray(const Exiv2::Value::AutoPtr value)
{
QList<KisMetaData::Value> v;
const Exiv2::DataValue* dvalue = dynamic_cast<const Exiv2::DataValue*>(&*value);
if (dvalue) {
QByteArray array(dvalue->count(), 0);
dvalue->copy((Exiv2::byte*)array.data());
for (int i = 0; i < array.size(); i++) {
QChar c((char)array[i]);
v.push_back(KisMetaData::Value(QString(c).toInt(0)));
}
} else {
Q_ASSERT(value->typeId() == Exiv2::asciiString);
QString str = QString::fromLatin1(value->toString().c_str());
v.push_back(KisMetaData::Value(str.toInt()));
}
return KisMetaData::Value(v, KisMetaData::Value::OrderedArray);
}
// Convert a KisMetaData array of integer to an exif array of integer string
Exiv2::Value* kmdIntOrderedArrayToExifArray(const KisMetaData::Value& value)
{
QList<KisMetaData::Value> v = value.asArray();
QByteArray s;
for (QList<KisMetaData::Value>::iterator it = v.begin();
it != v.end(); ++it) {
int val = it->asVariant().toInt(0);
s += QByteArray::number(val);
}
return new Exiv2::DataValue((const Exiv2::byte*)s.data(), s.size());
}
QDateTime exivValueToDateTime(const Exiv2::Value::AutoPtr value)
{
return QDateTime::fromString(value->toString().c_str(), Qt::ISODate);
}
template<typename T>
inline T fixEndianess(T v, Exiv2::ByteOrder order)
{
switch (order) {
case Exiv2::invalidByteOrder:
return v;
case Exiv2::littleEndian:
return qFromLittleEndian<T>(v);
case Exiv2::bigEndian:
return qFromBigEndian<T>(v);
}
warnKrita << "KisExifIO: unknown byte order";
return v;
}
Exiv2::ByteOrder invertByteOrder(Exiv2::ByteOrder order)
{
switch (order) {
case Exiv2::littleEndian:
return Exiv2::bigEndian;
case Exiv2::bigEndian:
return Exiv2::littleEndian;
case Exiv2::invalidByteOrder:
warnKrita << "KisExifIO: Can't invert Exiv2::invalidByteOrder";
return Exiv2::invalidByteOrder;
}
return Exiv2::invalidByteOrder;
}
KisMetaData::Value exifOECFToKMDOECFStructure(const Exiv2::Value::AutoPtr value, Exiv2::ByteOrder order)
{
QMap<QString, KisMetaData::Value> oecfStructure;
const Exiv2::DataValue* dvalue = dynamic_cast<const Exiv2::DataValue*>(&*value);
Q_ASSERT(dvalue);
QByteArray array(dvalue->count(), 0);
dvalue->copy((Exiv2::byte*)array.data());
int columns = fixEndianess<quint16>((reinterpret_cast<quint16*>(array.data()))[0], order);
int rows = fixEndianess<quint16>((reinterpret_cast<quint16*>(array.data()))[1], order);
if ((columns * rows + 4) > dvalue->count()) { // Sometime byteOrder get messed up (especially if metadata got saved with kexiv2 library, or any library that doesn't save back with the same byte order as the camera)
order = invertByteOrder(order);
columns = fixEndianess<quint16>((reinterpret_cast<quint16*>(array.data()))[0], order);
rows = fixEndianess<quint16>((reinterpret_cast<quint16*>(array.data()))[1], order);
Q_ASSERT((columns * rows + 4) > dvalue->count());
}
oecfStructure["Columns"] = KisMetaData::Value(columns);
oecfStructure["Rows"] = KisMetaData::Value(rows);
int index = 4;
QList<KisMetaData::Value> names;
for (int i = 0; i < columns; i++) {
int lastIndex = array.indexOf((char)0, index);
QString name = array.mid(index, lastIndex - index);
if (index != lastIndex) {
index = lastIndex + 1;
dbgMetaData << "Name [" << i << "] =" << name;
names.append(KisMetaData::Value(name));
} else {
names.append(KisMetaData::Value(""));
}
}
oecfStructure["Names"] = KisMetaData::Value(names, KisMetaData::Value::OrderedArray);
QList<KisMetaData::Value> values;
qint32* dataIt = reinterpret_cast<qint32*>(array.data() + index);
for (int i = 0; i < columns; i++) {
for (int j = 0; j < rows; j++) {
values.append(KisMetaData::Value(KisMetaData::Rational(fixEndianess<qint32>(dataIt[0], order), fixEndianess<qint32>(dataIt[1], order))));
dataIt += 2;
}
}
oecfStructure["Values"] = KisMetaData::Value(values, KisMetaData::Value::OrderedArray);
dbgMetaData << "OECF: " << ppVar(columns) << ppVar(rows) << ppVar(dvalue->count());
return KisMetaData::Value(oecfStructure);
}
Exiv2::Value* kmdOECFStructureToExifOECF(const KisMetaData::Value& value)
{
QMap<QString, KisMetaData::Value> oecfStructure = value.asStructure();
quint16 columns = oecfStructure["Columns"].asVariant().toInt(0);
quint16 rows = oecfStructure["Rows"].asVariant().toInt(0);
QList<KisMetaData::Value> names = oecfStructure["Names"].asArray();
QList<KisMetaData::Value> values = oecfStructure["Values"].asArray();
Q_ASSERT(columns*rows == values.size());
int length = 4 + rows * columns * 8; // The 4 byte for storing rows/columns and the rows*columns*sizeof(rational)
bool saveNames = (!names.empty() && names[0].asVariant().toString().size() > 0);
if (saveNames) {
for (int i = 0; i < columns; i++) {
length += names[i].asVariant().toString().size() + 1;
}
}
QByteArray array(length, 0);
(reinterpret_cast<quint16*>(array.data()))[0] = columns;
(reinterpret_cast<quint16*>(array.data()))[1] = rows;
int index = 4;
if (saveNames) {
for (int i = 0; i < columns; i++) {
QByteArray name = names[i].asVariant().toString().toLatin1();
name.append((char)0);
memcpy(array.data() + index, name.data(), name.size());
index += name.size();
}
}
qint32* dataIt = reinterpret_cast<qint32*>(array.data() + index);
for (QList<KisMetaData::Value>::iterator it = values.begin();
it != values.end(); ++it) {
dataIt[0] = it->asRational().numerator;
dataIt[1] = it->asRational().denominator;
dataIt += 2;
}
return new Exiv2::DataValue((const Exiv2::byte*)array.data(), array.size());
}
KisMetaData::Value deviceSettingDescriptionExifToKMD(const Exiv2::Value::AutoPtr value)
{
QMap<QString, KisMetaData::Value> deviceSettingStructure;
QByteArray array;
const Exiv2::DataValue* dvalue = dynamic_cast<const Exiv2::DataValue*>(&*value);
if(dvalue)
{
array.resize(dvalue->count());
dvalue->copy((Exiv2::byte*)array.data());
} else {
Q_ASSERT(value->typeId() == Exiv2::unsignedShort);
array.resize(2 * value->count());
value->copy((Exiv2::byte*)array.data(), Exiv2::littleEndian);
}
int columns = (reinterpret_cast<quint16*>(array.data()))[0];
int rows = (reinterpret_cast<quint16*>(array.data()))[1];
deviceSettingStructure["Columns"] = KisMetaData::Value(columns);
deviceSettingStructure["Rows"] = KisMetaData::Value(rows);
QList<KisMetaData::Value> settings;
QByteArray null(2, 0);
for (int index = 4; index < array.size(); )
{
const int lastIndex = array.indexOf(null, index);
const int numChars = (lastIndex - index) / 2; // including trailing zero
QString setting = QString::fromUtf16((ushort*)(void*)( array.data() + index), numChars);
index = lastIndex + 2;
dbgMetaData << "Setting << " << setting;
settings.append(KisMetaData::Value(setting));
}
deviceSettingStructure["Settings"] = KisMetaData::Value(settings, KisMetaData::Value::OrderedArray);
return KisMetaData::Value(deviceSettingStructure);
}
Exiv2::Value* deviceSettingDescriptionKMDToExif(const KisMetaData::Value& value)
{
QMap<QString, KisMetaData::Value> deviceSettingStructure = value.asStructure();
quint16 columns = deviceSettingStructure["Columns"].asVariant().toInt(0);
quint16 rows = deviceSettingStructure["Rows"].asVariant().toInt(0);
QTextCodec* codec = QTextCodec::codecForName("UTF-16");
QList<KisMetaData::Value> settings = deviceSettingStructure["Settings"].asArray();
QByteArray array(4, 0);
(reinterpret_cast<quint16*>(array.data()))[0] = columns;
(reinterpret_cast<quint16*>(array.data()))[1] = rows;
for (int i = 0; i < settings.count(); i++) {
QString str = settings[i].asVariant().toString();
QByteArray setting = codec->fromUnicode(str);
array.append(setting);
}
return new Exiv2::DataValue((const Exiv2::byte*)array.data(), array.size());
}
KisMetaData::Value cfaPatternExifToKMD(const Exiv2::Value::AutoPtr value, Exiv2::ByteOrder order)
{
QMap<QString, KisMetaData::Value> cfaPatternStructure;
const Exiv2::DataValue* dvalue = dynamic_cast<const Exiv2::DataValue*>(&*value);
Q_ASSERT(dvalue);
QByteArray array(dvalue->count(), 0);
dvalue->copy((Exiv2::byte*)array.data());
int columns = fixEndianess<quint16>((reinterpret_cast<quint16*>(array.data()))[0], order);
int rows = fixEndianess<quint16>((reinterpret_cast<quint16*>(array.data()))[1], order);
if ((columns * rows + 4) != dvalue->count()) { // Sometime byteOrder get messed up (especially if metadata got saved with kexiv2 library, or any library that doesn't save back with the same byte order as the camera)
order = invertByteOrder(order);
columns = fixEndianess<quint16>((reinterpret_cast<quint16*>(array.data()))[0], order);
rows = fixEndianess<quint16>((reinterpret_cast<quint16*>(array.data()))[1], order);
Q_ASSERT((columns * rows + 4) == dvalue->count());
}
cfaPatternStructure["Columns"] = KisMetaData::Value(columns);
cfaPatternStructure["Rows"] = KisMetaData::Value(rows);
QList<KisMetaData::Value> values;
int index = 4;
for (int i = 0; i < columns * rows; i++) {
values.append(KisMetaData::Value(*(array.data() + index)));
index++;
}
cfaPatternStructure["Values"] = KisMetaData::Value(values, KisMetaData::Value::OrderedArray);
dbgMetaData << "CFAPattern " << ppVar(columns) << " " << ppVar(rows) << ppVar(values.size()) << ppVar(dvalue->count());
return KisMetaData::Value(cfaPatternStructure);
}
Exiv2::Value* cfaPatternKMDToExif(const KisMetaData::Value& value)
{
QMap<QString, KisMetaData::Value> cfaStructure = value.asStructure();
quint16 columns = cfaStructure["Columns"].asVariant().toInt(0);
quint16 rows = cfaStructure["Rows"].asVariant().toInt(0);
QList<KisMetaData::Value> values = cfaStructure["Values"].asArray();
Q_ASSERT(columns*rows == values.size());
QByteArray array(4 + columns*rows, 0);
(reinterpret_cast<quint16*>(array.data()))[0] = columns;
(reinterpret_cast<quint16*>(array.data()))[1] = rows;
for (int i = 0; i < columns * rows; i++) {
quint8 val = values[i].asVariant().toUInt();
*(array.data() + 4 + i) = val;
}
dbgMetaData << "Cfa Array " << ppVar(columns) << ppVar(rows) << ppVar(array.size());
return new Exiv2::DataValue((const Exiv2::byte*)array.data(), array.size());
}
// Read and write Flash //
KisMetaData::Value flashExifToKMD(const Exiv2::Value::AutoPtr value)
{
uint16_t v = value->toLong();
QMap<QString, KisMetaData::Value> flashStructure;
bool fired = (v & 0x01); // bit 1 is whether flash was fired or not
flashStructure["Fired"] = QVariant(fired);
int ret = ((v >> 1) & 0x03); // bit 2 and 3 are Return
flashStructure["Return"] = QVariant(ret);
int mode = ((v >> 3) & 0x03); // bit 4 and 5 are Mode
flashStructure["Mode"] = QVariant(mode);
bool function = ((v >> 5) & 0x01); // bit 6 if function
flashStructure["Function"] = QVariant(function);
bool redEye = ((v >> 6) & 0x01); // bit 7 if function
flashStructure["RedEyeMode"] = QVariant(redEye);
return KisMetaData::Value(flashStructure);
}
Exiv2::Value* flashKMDToExif(const KisMetaData::Value& value)
{
uint16_t v = 0;
QMap<QString, KisMetaData::Value> flashStructure = value.asStructure();
v = flashStructure["Fired"].asVariant().toBool();
v |= ((flashStructure["Return"].asVariant().toInt() & 0x03) << 1);
v |= ((flashStructure["Mode"].asVariant().toInt() & 0x03) << 3);
v |= ((flashStructure["Function"].asVariant().toInt() & 0x03) << 5);
v |= ((flashStructure["RedEyeMode"].asVariant().toInt() & 0x03) << 6);
return new Exiv2::ValueType<uint16_t>(v);
}
// ---- Implementation of KisExifIO ----//
KisExifIO::KisExifIO() : d(new Private)
{
}
KisExifIO::~KisExifIO()
{
delete d;
}
bool KisExifIO::saveTo(KisMetaData::Store* store, QIODevice* ioDevice, HeaderType headerType) const
{
ioDevice->open(QIODevice::WriteOnly);
Exiv2::ExifData exifData;
if (headerType == KisMetaData::IOBackend::JpegHeader) {
QByteArray header(6, 0);
header[0] = 0x45;
header[1] = 0x78;
header[2] = 0x69;
header[3] = 0x66;
header[4] = 0x00;
header[5] = 0x00;
ioDevice->write(header);
}
for (QHash<QString, KisMetaData::Entry>::const_iterator it = store->begin();
it != store->end(); ++it) {
try {
const KisMetaData::Entry& entry = *it;
dbgMetaData << "Trying to save: " << entry.name() << " of " << entry.schema()->prefix() << ":" << entry.schema()->uri();
QString exivKey;
if (entry.schema()->uri() == KisMetaData::Schema::TIFFSchemaUri) {
exivKey = "Exif.Image." + entry.name();
} else if (entry.schema()->uri() == KisMetaData::Schema::EXIFSchemaUri) { // Distinguish between exif and gps
if (entry.name().left(3) == "GPS") {
exivKey = "Exif.GPS." + entry.name();
} else {
exivKey = "Exif.Photo." + entry.name();
}
} else if (entry.schema()->uri() == KisMetaData::Schema::DublinCoreSchemaUri) {
if (entry.name() == "description") {
exivKey = "Exif.Image.ImageDescription";
} else if (entry.name() == "creator") {
exivKey = "Exif.Image.Artist";
} else if (entry.name() == "rights") {
exivKey = "Exif.Image.Copyright";
}
} else if (entry.schema()->uri() == KisMetaData::Schema::XMPSchemaUri) {
if (entry.name() == "ModifyDate") {
exivKey = "Exif.Image.DateTime";
} else if (entry.name() == "CreatorTool") {
exivKey = "Exif.Image.Software";
}
} else if (entry.schema()->uri() == KisMetaData::Schema::MakerNoteSchemaUri) {
if (entry.name() == "RawData") {
exivKey = "Exif.Photo.MakerNote";
}
}
dbgMetaData << "Saving " << entry.name() << " to " << exivKey;
if (exivKey.isEmpty()) {
dbgMetaData << entry.qualifiedName() << " is unsavable to EXIF";
} else {
Exiv2::ExifKey exifKey(qPrintable(exivKey));
Exiv2::Value* v = 0;
if (exivKey == "Exif.Photo.ExifVersion" || exivKey == "Exif.Photo.FlashpixVersion") {
v = kmdValueToExifVersion(entry.value());
} else if (exivKey == "Exif.Photo.FileSource") {
char s[] = { 0x03 };
v = new Exiv2::DataValue((const Exiv2::byte*)s, 1);
} else if (exivKey == "Exif.Photo.SceneType") {
char s[] = { 0x01 };
v = new Exiv2::DataValue((const Exiv2::byte*)s, 1);
} else if (exivKey == "Exif.Photo.ComponentsConfiguration") {
v = kmdIntOrderedArrayToExifArray(entry.value());
} else if (exivKey == "Exif.Image.Artist") { // load as dc:creator
KisMetaData::Value creator = entry.value();
if (entry.value().asArray().size() > 0) {
creator = entry.value().asArray()[0];
}
-#if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 20
+#if !EXIV2_TEST_VERSION(0,21,0)
v = kmdValueToExivValue(creator, Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId()));
#else
v = kmdValueToExivValue(creator, exifKey.defaultTypeId());
-
#endif
} else if (exivKey == "Exif.Photo.OECF") {
v = kmdOECFStructureToExifOECF(entry.value());
} else if (exivKey == "Exif.Photo.DeviceSettingDescription") {
v = deviceSettingDescriptionKMDToExif(entry.value());
} else if (exivKey == "Exif.Photo.CFAPattern") {
v = cfaPatternKMDToExif(entry.value());
} else if (exivKey == "Exif.Photo.Flash") {
v = flashKMDToExif(entry.value());
} else if (exivKey == "Exif.Photo.UserComment") {
Q_ASSERT(entry.value().type() == KisMetaData::Value::LangArray);
QMap<QString, KisMetaData::Value> langArr = entry.value().asLangArray();
if (langArr.contains("x-default")) {
-#if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 20
+#if !EXIV2_TEST_VERSION(0,21,0)
v = kmdValueToExivValue(langArr.value("x-default"), Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId()));
#else
v = kmdValueToExivValue(langArr.value("x-default"), exifKey.defaultTypeId());
#endif
} else if (langArr.size() > 0) {
-#if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 20
+#if !EXIV2_TEST_VERSION(0,21,0)
v = kmdValueToExivValue(langArr.begin().value(), Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId()));
#else
v = kmdValueToExivValue(langArr.begin().value(), exifKey.defaultTypeId());
#endif
}
} else {
dbgMetaData << exifKey.tag();
-#if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 20
+#if !EXIV2_TEST_VERSION(0,21,0)
v = kmdValueToExivValue(entry.value(), Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId()));
#else
v = kmdValueToExivValue(entry.value(), exifKey.defaultTypeId());
#endif
}
if (v && v->typeId() != Exiv2::invalidTypeId) {
dbgMetaData << "Saving key" << exivKey << " of KMD value" << entry.value();
exifData.add(exifKey, v);
} else {
dbgMetaData << "No exif value was created for" << entry.qualifiedName() << " as" << exivKey;// << " of KMD value" << entry.value();
}
}
} catch (Exiv2::AnyError& e) {
dbgMetaData << "exiv error " << e.what();
}
}
-#if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 17
+#if !EXIV2_TEST_VERSION(0,18,0)
Exiv2::DataBuf rawData = exifData.copy();
ioDevice->write((const char*) rawData.pData_, rawData.size_);
#else
Exiv2::Blob rawData;
Exiv2::ExifParser::encode(rawData, Exiv2::littleEndian, exifData);
ioDevice->write((const char*) &*rawData.begin(), rawData.size());
#endif
ioDevice->close();
return true;
}
bool KisExifIO::canSaveAllEntries(KisMetaData::Store* /*store*/) const
{
return false; // It's a known fact that exif can't save all information, but TODO: write the check
}
bool KisExifIO::loadFrom(KisMetaData::Store* store, QIODevice* ioDevice) const
{
ioDevice->open(QIODevice::ReadOnly);
if (!ioDevice->isOpen()) {
return false;
}
QByteArray arr = ioDevice->readAll();
Exiv2::ExifData exifData;
Exiv2::ByteOrder byteOrder;
-#if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 17
+#if !EXIV2_TEST_VERSION(0,18,0)
exifData.load((const Exiv2::byte*)arr.data(), arr.size());
byteOrder = exifData.byteOrder();
#else
try {
byteOrder = Exiv2::ExifParser::decode(exifData, (const Exiv2::byte*)arr.data(), arr.size());
}
catch (const std::exception& ex) {
warnKrita << "Received exception trying to parse exiv data" << ex.what();
return false;
}
catch (...) {
dbgKrita << "Received unknown exception trying to parse exiv data";
return false;
}
-
#endif
dbgMetaData << "Byte order = " << byteOrder << ppVar(Exiv2::bigEndian) << ppVar(Exiv2::littleEndian);
dbgMetaData << "There are" << exifData.count() << " entries in the exif section";
const KisMetaData::Schema* tiffSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::TIFFSchemaUri);
Q_ASSERT(tiffSchema);
const KisMetaData::Schema* exifSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::EXIFSchemaUri);
Q_ASSERT(exifSchema);
const KisMetaData::Schema* dcSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::DublinCoreSchemaUri);
Q_ASSERT(dcSchema);
const KisMetaData::Schema* xmpSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::XMPSchemaUri);
Q_ASSERT(xmpSchema);
for (Exiv2::ExifMetadata::const_iterator it = exifData.begin();
it != exifData.end(); ++it) {
if (it->key() == "Exif.Photo.StripOffsets"
|| it->key() == "RowsPerStrip"
|| it->key() == "StripByteCounts"
|| it->key() == "JPEGInterchangeFormat"
|| it->key() == "JPEGInterchangeFormatLength"
|| it->tagName() == "0x0000" ) {
dbgMetaData << it->key().c_str() << " is ignored";
} else if (it->key() == "Exif.Photo.MakerNote") {
const KisMetaData::Schema* makerNoteSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::MakerNoteSchemaUri);
store->addEntry(KisMetaData::Entry(makerNoteSchema, "RawData", exivValueToKMDValue(it->getValue(), false)));
} else if (it->key() == "Exif.Image.DateTime") { // load as xmp:ModifyDate
store->addEntry(KisMetaData::Entry(xmpSchema, "ModifyDate", KisMetaData::Value(exivValueToDateTime(it->getValue()))));
} else if (it->key() == "Exif.Image.ImageDescription") { // load as "dc:description"
store->addEntry(KisMetaData::Entry(dcSchema, "description", exivValueToKMDValue(it->getValue(), false)));
} else if (it->key() == "Exif.Image.Software") { // load as "xmp:CreatorTool"
store->addEntry(KisMetaData::Entry(xmpSchema, "CreatorTool", exivValueToKMDValue(it->getValue(), false)));
} else if (it->key() == "Exif.Image.Artist") { // load as dc:creator
QList<KisMetaData::Value> creators;
creators.push_back(exivValueToKMDValue(it->getValue(), false));
store->addEntry(KisMetaData::Entry(dcSchema, "creator", KisMetaData::Value(creators, KisMetaData::Value::OrderedArray)));
} else if (it->key() == "Exif.Image.Copyright") { // load as dc:rights
store->addEntry(KisMetaData::Entry(dcSchema, "rights", exivValueToKMDValue(it->getValue(), false)));
} else if (it->groupName() == "Image") {
// Tiff tags
QString fixedTN = it->tagName().c_str();
if (it->key() == "Exif.Image.ExifTag") {
dbgMetaData << "Ignoring " << it->key().c_str();
} else if (KisMetaData::Entry::isValidName(fixedTN)) {
store->addEntry(KisMetaData::Entry(tiffSchema, fixedTN, exivValueToKMDValue(it->getValue(), false))) ;
} else {
dbgMetaData << "Invalid tag name: " << fixedTN;
}
} else if (it->groupName() == "Photo" || (it->groupName() == "GPS")) {
// Exif tags (and GPS tags)
KisMetaData::Value v;
if (it->key() == "Exif.Photo.ExifVersion" || it->key() == "Exif.Photo.FlashpixVersion") {
v = exifVersionToKMDValue(it->getValue());
} else if (it->key() == "Exif.Photo.FileSource") {
v = KisMetaData::Value(3);
} else if (it->key() == "Exif.Photo.SceneType") {
v = KisMetaData::Value(1);
} else if (it->key() == "Exif.Photo.ComponentsConfiguration") {
v = exifArrayToKMDIntOrderedArray(it->getValue());
} else if (it->key() == "Exif.Photo.OECF") {
v = exifOECFToKMDOECFStructure(it->getValue(), byteOrder);
} else if (it->key() == "Exif.Photo.DateTimeDigitized" || it->key() == "Exif.Photo.DateTimeOriginal") {
v = KisMetaData::Value(exivValueToDateTime(it->getValue()));
} else if (it->key() == "Exif.Photo.DeviceSettingDescription") {
v = deviceSettingDescriptionExifToKMD(it->getValue());
} else if (it->key() == "Exif.Photo.CFAPattern") {
v = cfaPatternExifToKMD(it->getValue(), byteOrder);
} else if (it->key() == "Exif.Photo.Flash") {
v = flashExifToKMD(it->getValue());
} else if (it->key() == "Exif.Photo.UserComment") {
KisMetaData::Value vUC = exivValueToKMDValue(it->getValue(), false);
Q_ASSERT(vUC.type() == KisMetaData::Value::Variant);
QVariant commentVar = vUC.asVariant();
QString comment;
if (commentVar.type() == QVariant::String) {
comment = commentVar.toString();
} else if (commentVar.type() == QVariant::ByteArray) {
const QByteArray commentString = commentVar.toByteArray();
comment = QString::fromLatin1(commentString.constData(), commentString.size());
} else {
warnKrita << "KisExifIO: Unhandled UserComment value type.";
}
KisMetaData::Value vcomment(comment);
vcomment.addPropertyQualifier("xml:lang", KisMetaData::Value("x-default"));
QList<KisMetaData::Value> alt;
alt.append(vcomment);
v = KisMetaData::Value(alt, KisMetaData::Value::LangArray);
} else {
bool forceSeq = false;
KisMetaData::Value::ValueType arrayType = KisMetaData::Value::UnorderedArray;
if (it->key() == "Exif.Photo.ISOSpeedRatings") {
forceSeq = true;
arrayType = KisMetaData::Value::OrderedArray;
}
v = exivValueToKMDValue(it->getValue(), forceSeq, arrayType);
}
if (it->key() == "Exif.Photo.InteroperabilityTag" || it->key() == "Exif.Photo.0xea1d") { // InteroperabilityTag isn't useful for XMP, 0xea1d isn't a valid Exif tag
dbgMetaData << "Ignoring " << it->key().c_str();
} else {
store->addEntry(KisMetaData::Entry(exifSchema, it->tagName().c_str(), v));
}
} else if (it->groupName() == "Thumbnail") {
dbgMetaData << "Ignoring thumbnail tag :" << it->key().c_str();
} else {
dbgMetaData << "Unknown exif tag, cannot load:" << it->key().c_str();
}
}
ioDevice->close();
return true;
}
diff --git a/libs/ui/kisexiv2/kis_exiv2.cpp b/libs/ui/kisexiv2/kis_exiv2.cpp
index 87b6fa88a0..7635e21847 100644
--- a/libs/ui/kisexiv2/kis_exiv2.cpp
+++ b/libs/ui/kisexiv2/kis_exiv2.cpp
@@ -1,296 +1,296 @@
/*
* Copyright (c) 2007 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_exiv2.h"
#include <QDateTime>
#include "kis_iptc_io.h"
#include "kis_exif_io.h"
#include "kis_xmp_io.h"
#include <kis_meta_data_value.h>
#include <kis_debug.h>
// ---- Generic conversion functions ---- //
// Convert an exiv value to a KisMetaData value
KisMetaData::Value exivValueToKMDValue(const Exiv2::Value::AutoPtr value, bool forceSeq, KisMetaData::Value::ValueType arrayType)
{
switch (value->typeId()) {
case Exiv2::signedByte:
case Exiv2::invalidTypeId:
case Exiv2::lastTypeId:
case Exiv2::directory:
dbgMetaData << "Invalid value :" << value->typeId() << " value =" << value->toString().c_str();
return KisMetaData::Value();
case Exiv2::undefined: {
dbgMetaData << "Undefined value :" << value->typeId() << " value =" << value->toString().c_str();
QByteArray array(value->count() , 0);
value->copy((Exiv2::byte*)array.data(), Exiv2::invalidByteOrder);
return KisMetaData::Value(QString(array.toBase64()));
}
case Exiv2::unsignedByte:
case Exiv2::unsignedShort:
case Exiv2::unsignedLong:
case Exiv2::signedShort:
case Exiv2::signedLong: {
if (value->count() == 1 && !forceSeq) {
return KisMetaData::Value((int)value->toLong());
} else {
QList<KisMetaData::Value> array;
for (int i = 0; i < value->count(); i++)
array.push_back(KisMetaData::Value((int)value->toLong(i)));
return KisMetaData::Value(array, arrayType);
}
}
case Exiv2::asciiString:
case Exiv2::string:
case Exiv2::comment: // look at kexiv2 for the problem about decoding correctly that tag
return KisMetaData::Value(value->toString().c_str());
case Exiv2::unsignedRational:
if(value->size() < 2)
{
dbgMetaData << "Invalid size :" << value->size() << " value =" << value->toString().c_str();
return KisMetaData::Value();
}
return KisMetaData::Value(KisMetaData::Rational(value->toRational().first , value->toRational().second));
case Exiv2::signedRational:
if(value->size() < 2)
{
dbgMetaData << "Invalid size :" << value->size() << " value =" << value->toString().c_str();
return KisMetaData::Value();
}
return KisMetaData::Value(KisMetaData::Rational(value->toRational().first , value->toRational().second));
case Exiv2::date:
case Exiv2::time:
return KisMetaData::Value(QDateTime::fromString(value->toString().c_str(), Qt::ISODate));
case Exiv2::xmpText:
case Exiv2::xmpAlt:
case Exiv2::xmpBag:
case Exiv2::xmpSeq:
case Exiv2::langAlt:
default: {
dbgMetaData << "Unknown type id :" << value->typeId() << " value =" << value->toString().c_str();
//Q_ASSERT(false); // This point must never be reached !
return KisMetaData::Value();
}
}
dbgMetaData << "Unknown type id :" << value->typeId() << " value =" << value->toString().c_str();
//Q_ASSERT(false); // This point must never be reached !
return KisMetaData::Value();
}
// Convert a QtVariant to an Exiv value
Exiv2::Value* variantToExivValue(const QVariant& variant, Exiv2::TypeId type)
{
switch (type) {
case Exiv2::undefined: {
QByteArray arr = QByteArray::fromBase64(variant.toString().toLatin1());
return new Exiv2::DataValue((Exiv2::byte*)arr.data(), arr.size());
}
case Exiv2::unsignedByte:
return new Exiv2::ValueType<uint16_t>(variant.toUInt(0));
case Exiv2::unsignedShort:
return new Exiv2::ValueType<uint16_t>(variant.toUInt(0));
case Exiv2::unsignedLong:
return new Exiv2::ValueType<uint32_t>(variant.toUInt(0));
case Exiv2::signedShort:
return new Exiv2::ValueType<int16_t>(variant.toInt(0));
case Exiv2::signedLong:
return new Exiv2::ValueType<int32_t>(variant.toInt(0));
case Exiv2::date: {
QDate date = variant.toDate();
return new Exiv2::DateValue(date.year(), date.month(), date.day());
}
case Exiv2::asciiString:
if (variant.type() == QVariant::DateTime) {
return new Exiv2::AsciiValue(qPrintable(variant.toDateTime().toString("yyyy:MM:dd hh:mm:ss")));
} else
return new Exiv2::AsciiValue(qPrintable(variant.toString()));
case Exiv2::string: {
if (variant.type() == QVariant::DateTime) {
return new Exiv2::StringValue(qPrintable(variant.toDateTime().toString("yyyy:MM:dd hh:mm:ss")));
} else
return new Exiv2::StringValue(qPrintable(variant.toString()));
}
case Exiv2::comment:
return new Exiv2::CommentValue(qPrintable(variant.toString()));
default:
dbgMetaData << "Unhandled type:" << type;
//Q_ASSERT(false);
return 0;
}
}
template<typename _TYPE_>
Exiv2::Value* arrayToExivValue(const KisMetaData::Value& value)
{
Exiv2::ValueType<_TYPE_>* ev = new Exiv2::ValueType<_TYPE_>();
for (int i = 0; i < value.asArray().size(); ++i) {
ev->value_.push_back(qvariant_cast<_TYPE_>(value.asArray()[i].asVariant()));
}
return ev;
}
// Convert a KisMetaData to an Exiv value
Exiv2::Value* kmdValueToExivValue(const KisMetaData::Value& value, Exiv2::TypeId type)
{
switch (value.type()) {
case KisMetaData::Value::Invalid:
return &*Exiv2::Value::create(Exiv2::invalidTypeId);
case KisMetaData::Value::Variant: {
return variantToExivValue(value.asVariant(), type);
}
case KisMetaData::Value::Rational:
//Q_ASSERT(type == Exiv2::signedRational || type == Exiv2::unsignedRational);
if (type == Exiv2::signedRational) {
return new Exiv2::ValueType<Exiv2::Rational>(Exiv2::Rational(value.asRational().numerator, value.asRational().denominator));
} else {
return new Exiv2::ValueType<Exiv2::URational>(Exiv2::URational(value.asRational().numerator, value.asRational().denominator));
}
case KisMetaData::Value::OrderedArray:
- /* Falls through */
+ Q_FALLTHROUGH();
case KisMetaData::Value::UnorderedArray:
- /* Falls through */
+ Q_FALLTHROUGH();
case KisMetaData::Value::AlternativeArray: {
switch (type) {
case Exiv2::unsignedByte:
return arrayToExivValue<uint16_t>(value);
case Exiv2::unsignedShort:
return arrayToExivValue<uint16_t>(value);
case Exiv2::unsignedLong:
return arrayToExivValue<uint32_t>(value);
case Exiv2::signedShort:
return arrayToExivValue<int16_t>(value);
case Exiv2::signedLong:
return arrayToExivValue<int32_t>(value);
case Exiv2::string: {
Exiv2::StringValue* ev = new Exiv2::StringValue();
for (int i = 0; i < value.asArray().size(); ++i) {
ev->value_ += qvariant_cast<QString>(value.asArray()[i].asVariant()).toLatin1().constData();
if (i != value.asArray().size() - 1) ev->value_ += ',';
}
return ev;
}
break;
default:
dbgMetaData << type << " " << value;
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(0 && "Unknown alternative array type", 0);
break;
}
break;
}
default:
dbgMetaData << type << " " << value;
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(0 && "Unknown array type", 0);
break;
}
return 0;
}
Exiv2::Value* kmdValueToExivXmpValue(const KisMetaData::Value& value)
{
//Q_ASSERT(value.type() != KisMetaData::Value::Structure);
switch (value.type()) {
case KisMetaData::Value::Invalid:
return new Exiv2::DataValue(Exiv2::invalidTypeId);
case KisMetaData::Value::Variant: {
QVariant var = value.asVariant();
if (var.type() == QVariant::Bool) {
if (var.toBool()) {
return new Exiv2::XmpTextValue("True");
} else {
return new Exiv2::XmpTextValue("False");
}
} else {
//Q_ASSERT(var.canConvert(QVariant::String));
return new Exiv2::XmpTextValue(var.toString().toLatin1().constData());
}
}
case KisMetaData::Value::Rational: {
QString rat = "%1 / %2";
rat = rat.arg(value.asRational().numerator);
rat = rat.arg(value.asRational().denominator);
return new Exiv2::XmpTextValue(rat.toLatin1().constData());
}
case KisMetaData::Value::AlternativeArray:
case KisMetaData::Value::OrderedArray:
case KisMetaData::Value::UnorderedArray: {
Exiv2::XmpArrayValue* arrV = new Exiv2::XmpArrayValue;
switch (value.type()) {
case KisMetaData::Value::OrderedArray:
arrV->setXmpArrayType(Exiv2::XmpValue::xaSeq);
break;
case KisMetaData::Value::UnorderedArray:
arrV->setXmpArrayType(Exiv2::XmpValue::xaBag);
break;
case KisMetaData::Value::AlternativeArray:
arrV->setXmpArrayType(Exiv2::XmpValue::xaAlt);
break;
default:
// Cannot happen
;
}
Q_FOREACH (const KisMetaData::Value& v, value.asArray()) {
Exiv2::Value* ev = kmdValueToExivXmpValue(v);
if (ev) {
arrV->read(ev->toString());
delete ev;
}
}
return arrV;
}
case KisMetaData::Value::LangArray: {
Exiv2::Value* arrV = new Exiv2::LangAltValue;
QMap<QString, KisMetaData::Value> langArray = value.asLangArray();
for (QMap<QString, KisMetaData::Value>::iterator it = langArray.begin();
it != langArray.end(); ++it) {
QString exivVal;
if (it.key() != "x-default") {
exivVal = "lang=" + it.key() + ' ';
}
//Q_ASSERT(it.value().type() == KisMetaData::Value::Variant);
QVariant var = it.value().asVariant();
//Q_ASSERT(var.type() == QVariant::String);
exivVal += var.toString();
arrV->read(exivVal.toLatin1().constData());
}
return arrV;
}
case KisMetaData::Value::Structure:
default: {
warnKrita << "KisExiv2: Unhandled value type";
return 0;
}
}
warnKrita << "KisExiv2: Unhandled value type";
return 0;
}
void KisExiv2::initialize()
{
// XXX_EXIV: make the exiv io backends real plugins
KisMetaData::IOBackendRegistry* ioreg = KisMetaData::IOBackendRegistry::instance();
ioreg->add(new KisIptcIO);
ioreg->add(new KisExifIO);
ioreg->add(new KisXMPIO);
}
diff --git a/libs/ui/kisexiv2/kis_exiv2.h b/libs/ui/kisexiv2/kis_exiv2.h
index 9343265f1e..6b66aa7852 100644
--- a/libs/ui/kisexiv2/kis_exiv2.h
+++ b/libs/ui/kisexiv2/kis_exiv2.h
@@ -1,44 +1,44 @@
/*
* Copyright (c) 2007 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _KIS_EXIV2_H_
#define _KIS_EXIV2_H_
#include <kis_meta_data_value.h>
-#include <exiv2/value.hpp>
+#include <exiv2/exiv2.hpp>
#include "kritaui_export.h"
/// Convert an exiv value to a KisMetaData value
KisMetaData::Value exivValueToKMDValue(const Exiv2::Value::AutoPtr value, bool forceSeq, KisMetaData::Value::ValueType arrayType = KisMetaData::Value::UnorderedArray);
/// Convert a KisMetaData to an Exiv value
Exiv2::Value* kmdValueToExivValue(const KisMetaData::Value& value, Exiv2::TypeId type);
/**
* Convert a KisMetaData to an Exiv value, without knowing the targeted Exiv2::TypeId
* This function should be used for saving to XMP.
*/
Exiv2::Value* kmdValueToExivXmpValue(const KisMetaData::Value& value);
struct KRITAUI_EXPORT KisExiv2 {
static void initialize();
};
#endif
diff --git a/libs/ui/kisexiv2/kis_iptc_io.cpp b/libs/ui/kisexiv2/kis_iptc_io.cpp
index d2eb7c9b2f..0ac881f61b 100644
--- a/libs/ui/kisexiv2/kis_iptc_io.cpp
+++ b/libs/ui/kisexiv2/kis_iptc_io.cpp
@@ -1,200 +1,200 @@
/*
* Copyright (c) 2007 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_iptc_io.h"
#include <kis_debug.h>
#include <exiv2/iptc.hpp>
#include "kis_exiv2.h"
#include <kis_meta_data_store.h>
#include <kis_meta_data_entry.h>
#include <kis_meta_data_value.h>
#include <kis_meta_data_schema.h>
#include <kis_meta_data_schema_registry.h>
const char photoshopMarker[] = "Photoshop 3.0\0";
const char photoshopBimId_[] = "8BIM";
const uint16_t photoshopIptc = 0x0404;
const QByteArray photoshopIptc_((char*)&photoshopIptc, 2);
struct IPTCToKMD {
QString exivTag;
QString namespaceUri;
QString name;
};
static const IPTCToKMD mappings[] = {
{ "Iptc.Application2.City", KisMetaData::Schema::PhotoshopSchemaUri, "City" },
{ "Iptc.Application2.Copyright", KisMetaData::Schema::DublinCoreSchemaUri, "rights" },
{ "Iptc.Application2.CountryName", KisMetaData::Schema::PhotoshopSchemaUri, "Country" },
{ "Iptc.Application2.CountryCode", KisMetaData::Schema::IPTCSchemaUri, "CountryCode" },
{ "Iptc.Application2.Byline", KisMetaData::Schema::DublinCoreSchemaUri, "creator" },
{ "Iptc.Application2.BylineTitle", KisMetaData::Schema::PhotoshopSchemaUri, "AuthorsPosition" },
{ "Iptc.Application2.DateCreated", KisMetaData::Schema::PhotoshopSchemaUri, "DateCreated" },
{ "Iptc.Application2.Caption", KisMetaData::Schema::DublinCoreSchemaUri, "description" },
{ "Iptc.Application2.Writer", KisMetaData::Schema::PhotoshopSchemaUri, "CaptionWriter" },
{ "Iptc.Application2.Headline", KisMetaData::Schema::PhotoshopSchemaUri, "Headline" },
{ "Iptc.Application2.SpecialInstructions", KisMetaData::Schema::PhotoshopSchemaUri, "Instructions" },
{ "Iptc.Application2.ObjectAttribute", KisMetaData::Schema::IPTCSchemaUri, "IntellectualGenre" },
{ "Iptc.Application2.TransmissionReference", KisMetaData::Schema::PhotoshopSchemaUri, "JobID" },
{ "Iptc.Application2.Keywords", KisMetaData::Schema::DublinCoreSchemaUri, "subject" },
{ "Iptc.Application2.SubLocation", KisMetaData::Schema::IPTCSchemaUri, "Location" },
{ "Iptc.Application2.Credit", KisMetaData::Schema::PhotoshopSchemaUri, "Credit" },
{ "Iptc.Application2.ProvinceState", KisMetaData::Schema::PhotoshopSchemaUri, "State" },
{ "Iptc.Application2.Source", KisMetaData::Schema::PhotoshopSchemaUri, "Source" },
{ "Iptc.Application2.Subject", KisMetaData::Schema::IPTCSchemaUri, "SubjectCode" },
{ "Iptc.Application2.ObjectName", KisMetaData::Schema::DublinCoreSchemaUri, "title" },
{ "Iptc.Application2.Urgency", KisMetaData::Schema::PhotoshopSchemaUri, "Urgency" },
{ "Iptc.Application2.Category", KisMetaData::Schema::PhotoshopSchemaUri, "Category" },
{ "Iptc.Application2.SuppCategory", KisMetaData::Schema::PhotoshopSchemaUri, "SupplementalCategory" },
{ "", "", "" } // indicates the end of the array
};
struct KisIptcIO::Private {
QHash<QString, IPTCToKMD> iptcToKMD;
QHash<QString, IPTCToKMD> kmdToIPTC;
};
// ---- Implementation of KisExifIO ----//
KisIptcIO::KisIptcIO() : d(new Private)
{
}
KisIptcIO::~KisIptcIO()
{
delete d;
}
void KisIptcIO::initMappingsTable() const
{
// For some reason, initializing the tables in the constructor makes the it crash
if (d->iptcToKMD.size() == 0) {
for (int i = 0; !mappings[i].exivTag.isEmpty(); i++) {
dbgKrita << "mapping[i] = " << mappings[i].exivTag << " " << mappings[i].namespaceUri << " " << mappings[i].name;
d->iptcToKMD[mappings[i].exivTag] = mappings[i];
d->kmdToIPTC[
KisMetaData::SchemaRegistry::instance()
->schemaFromUri(mappings[i].namespaceUri)
->generateQualifiedName(mappings[i].name)] = mappings[i];
}
}
}
bool KisIptcIO::saveTo(KisMetaData::Store* store, QIODevice* ioDevice, HeaderType headerType) const
{
QStringList blockedEntries = QStringList() << "photoshop:DateCreated";
initMappingsTable();
ioDevice->open(QIODevice::WriteOnly);
Exiv2::IptcData iptcData;
for (QHash<QString, KisMetaData::Entry>::const_iterator it = store->begin();
it != store->end(); ++it) {
const KisMetaData::Entry& entry = *it;
if (d->kmdToIPTC.contains(entry.qualifiedName())) {
if (blockedEntries.contains(entry.qualifiedName())) {
warnKrita << "skipping" << entry.qualifiedName() << entry.value();
continue;
}
try {
QString iptcKeyStr = d->kmdToIPTC[ entry.qualifiedName()].exivTag;
Exiv2::IptcKey iptcKey(qPrintable(iptcKeyStr));
Exiv2::Value *v = kmdValueToExivValue(entry.value(),
Exiv2::IptcDataSets::dataSetType(iptcKey.tag(), iptcKey.record()));
if (v && v->typeId() != Exiv2::invalidTypeId) {
iptcData.add(iptcKey, v);
}
} catch (Exiv2::AnyError& e) {
dbgMetaData << "exiv error " << e.what();
}
}
}
-#if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 17
+#if !EXIV2_TEST_VERSION(0,18,0)
Exiv2::DataBuf rawData = iptcData.copy();
#else
Exiv2::DataBuf rawData = Exiv2::IptcParser::encode(iptcData);
#endif
if (headerType == KisMetaData::IOBackend::JpegHeader) {
QByteArray header;
header.append(photoshopMarker);
header.append(QByteArray(1, 0)); // Null terminated string
header.append(photoshopBimId_);
header.append(photoshopIptc_);
header.append(QByteArray(2, 0));
qint32 size = rawData.size_;
QByteArray sizeArray(4, 0);
sizeArray[0] = (char)((size & 0xff000000) >> 24);
sizeArray[1] = (char)((size & 0x00ff0000) >> 16);
sizeArray[2] = (char)((size & 0x0000ff00) >> 8);
sizeArray[3] = (char)(size & 0x000000ff);
header.append(sizeArray);
ioDevice->write(header);
}
ioDevice->write((const char*) rawData.pData_, rawData.size_);
ioDevice->close();
return true;
}
bool KisIptcIO::canSaveAllEntries(KisMetaData::Store* store) const
{
Q_UNUSED(store);
return false;
}
bool KisIptcIO::loadFrom(KisMetaData::Store* store, QIODevice* ioDevice) const
{
initMappingsTable();
dbgMetaData << "Loading IPTC Tags";
ioDevice->open(QIODevice::ReadOnly);
QByteArray arr = ioDevice->readAll();
Exiv2::IptcData iptcData;
-#if EXIV2_MAJOR_VERSION == 0 && EXIV2_MINOR_VERSION <= 17
+#if !EXIV2_TEST_VERSION(0,18,0)
iptcData.load((const Exiv2::byte*)arr.data(), arr.size());
#else
Exiv2::IptcParser::decode(iptcData, (const Exiv2::byte*)arr.data(), arr.size());
#endif
dbgMetaData << "There are" << iptcData.count() << " entries in the IPTC section";
for (Exiv2::IptcMetadata::const_iterator it = iptcData.begin();
it != iptcData.end(); ++it) {
dbgMetaData << "Reading info for key" << it->key().c_str();
if (d->iptcToKMD.contains(it->key().c_str())) {
const IPTCToKMD& iptcToKMd = d->iptcToKMD[it->key().c_str()];
const KisMetaData::Schema* schema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(iptcToKMd.namespaceUri);
KisMetaData::Value value;
if (iptcToKMd.exivTag == "Iptc.Application2.Keywords") {
Q_ASSERT(it->getValue()->typeId() == Exiv2::string);
QString data = it->getValue()->toString().c_str();
QStringList list = data.split(',');
QList<KisMetaData::Value> values;
Q_FOREACH (const QString &entry, list) {
values.push_back(KisMetaData::Value(entry));
}
value = KisMetaData::Value(values, KisMetaData::Value::UnorderedArray);
} else {
value = exivValueToKMDValue(it->getValue(), false);
}
store->addEntry(KisMetaData::Entry(schema, iptcToKMd.name, value));
}
}
return false;
}
diff --git a/libs/ui/kisexiv2/kis_xmp_io.cpp b/libs/ui/kisexiv2/kis_xmp_io.cpp
index c4663efe7d..97e0d70af3 100644
--- a/libs/ui/kisexiv2/kis_xmp_io.cpp
+++ b/libs/ui/kisexiv2/kis_xmp_io.cpp
@@ -1,401 +1,399 @@
/*
* Copyright (c) 2008-2010 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_xmp_io.h"
#include <string>
-#include <exiv2/xmp.hpp>
#include "kis_exiv2.h"
#include <kis_meta_data_store.h>
#include <kis_meta_data_entry.h>
#include <kis_meta_data_parser.h>
#include <kis_meta_data_value.h>
#include <kis_meta_data_schema.h>
#include <kis_meta_data_schema_registry.h>
#include <kis_meta_data_type_info.h>
#include <kis_debug.h>
KisXMPIO::KisXMPIO()
{
}
KisXMPIO::~KisXMPIO()
{
}
inline std::string exiv2Prefix(const KisMetaData::Schema* _schema)
{
const QByteArray latin1SchemaUri = _schema->uri().toLatin1();
std::string prefix = Exiv2::XmpProperties::prefix(latin1SchemaUri.constData());
if (prefix.empty()) {
dbgMetaData << "Unknown namespace " << ppVar(_schema->uri()) << ppVar(_schema->prefix());
prefix = _schema->prefix().toLatin1().constData();
Exiv2::XmpProperties::registerNs(latin1SchemaUri.constData(), prefix);
}
return prefix;
}
namespace
{
void saveStructure(Exiv2::XmpData& xmpData_, const QString& name, const std::string& prefix, const QMap<QString, KisMetaData::Value>& structure, const KisMetaData::Schema* structureSchema)
{
std::string structPrefix = exiv2Prefix(structureSchema);
for (QMap<QString, KisMetaData::Value>::const_iterator it = structure.begin();
it != structure.end(); ++it) {
Q_ASSERT(it.value().type() != KisMetaData::Value::Structure); // Can't nest structure
QString key = QString("%1/%2:%3").arg(name).arg(structPrefix.c_str()).arg(it.key());
Exiv2::XmpKey ekey(prefix, key.toLatin1().constData());
dbgMetaData << ppVar(key) << ppVar(ekey.key().c_str());
Exiv2::Value *v = kmdValueToExivXmpValue(it.value());
if (v) {
xmpData_.add(ekey, v);
}
}
}
}
bool KisXMPIO::saveTo(KisMetaData::Store* store, QIODevice* ioDevice, HeaderType headerType) const
{
dbgMetaData << "Save XMP Data";
Exiv2::XmpData xmpData_;
for (QHash<QString, KisMetaData::Entry>::const_iterator it = store->begin();
it != store->end(); ++it) {
const KisMetaData::Entry& entry = *it;
// Check whether the prefix and namespace are know to exiv2
std::string prefix = exiv2Prefix(entry.schema());
dbgMetaData << "Saving " << entry.name();
const KisMetaData::Value& value = entry.value();
const KisMetaData::TypeInfo* typeInfo = entry.schema()->propertyType(entry.name());
if (value.type() == KisMetaData::Value::Structure) {
QMap<QString, KisMetaData::Value> structure = value.asStructure();
const KisMetaData::Schema* structureSchema = 0;
if (typeInfo) {
structureSchema = typeInfo->structureSchema();
}
if (!structureSchema) {
dbgMetaData << "Unknown schema for " << entry.name();
structureSchema = entry.schema();
}
Q_ASSERT(structureSchema);
saveStructure(xmpData_, entry.name(), prefix, structure, structureSchema);
} else {
Exiv2::XmpKey key(prefix, entry.name().toLatin1().constData());
if (typeInfo && (typeInfo->propertyType() == KisMetaData::TypeInfo::OrderedArrayType
|| typeInfo->propertyType() == KisMetaData::TypeInfo::UnorderedArrayType
|| typeInfo->propertyType() == KisMetaData::TypeInfo::AlternativeArrayType)
&& typeInfo->embeddedPropertyType()->propertyType() == KisMetaData::TypeInfo::StructureType) {
// Here is the bad part, again we need to do it by hand
Exiv2::XmpTextValue tv;
switch (typeInfo->propertyType()) {
case KisMetaData::TypeInfo::OrderedArrayType:
tv.setXmpArrayType(Exiv2::XmpValue::xaSeq);
break;
case KisMetaData::TypeInfo::UnorderedArrayType:
tv.setXmpArrayType(Exiv2::XmpValue::xaBag);
break;
case KisMetaData::TypeInfo::AlternativeArrayType:
tv.setXmpArrayType(Exiv2::XmpValue::xaAlt);
break;
default:
// Cannot happen
;
}
xmpData_.add(key, &tv); // set the arrya type
const KisMetaData::TypeInfo* stuctureTypeInfo = typeInfo->embeddedPropertyType();
const KisMetaData::Schema* structureSchema = 0;
if (stuctureTypeInfo) {
structureSchema = stuctureTypeInfo->structureSchema();
}
if (!structureSchema) {
dbgMetaData << "Unknown schema for " << entry.name();
structureSchema = entry.schema();
}
Q_ASSERT(structureSchema);
QList<KisMetaData::Value> array = value.asArray();
for (int idx = 0; idx < array.size(); ++idx) {
saveStructure(xmpData_, QString("%1[%2]").arg(entry.name()).arg(idx + 1), prefix, array[idx].asStructure(), structureSchema);
}
} else {
dbgMetaData << ppVar(key.key().c_str());
Exiv2::Value *v = kmdValueToExivXmpValue(value);
if (v) {
xmpData_.add(key, v);
}
}
}
// TODO property qualifier
}
// Serialize data
std::string xmpPacket_;
Exiv2::XmpParser::encode(xmpPacket_, xmpData_);
// Save data into the IO device
ioDevice->open(QIODevice::WriteOnly);
if (headerType == KisMetaData::IOBackend::JpegHeader) {
xmpPacket_ = "http://ns.adobe.com/xap/1.0/\0" + xmpPacket_;
}
ioDevice->write(xmpPacket_.c_str(), xmpPacket_.length());
return true;
}
bool parseTagName(const QString &tagString,
QString &structName,
int &arrayIndex,
QString &tagName,
const KisMetaData::TypeInfo** typeInfo,
const KisMetaData::Schema *schema)
{
arrayIndex = -1;
*typeInfo = 0;
int numSubNames = tagString.count('/') + 1;
if (numSubNames == 1) {
structName.clear();
tagName = tagString;
*typeInfo = schema->propertyType(tagName);
return true;
}
if (numSubNames == 2) {
QRegExp regexp("([A-Za-z]\\w+)/([A-Za-z]\\w+):([A-Za-z]\\w+)");
if (regexp.indexIn(tagString) != -1) {
structName = regexp.capturedTexts()[1];
tagName = regexp.capturedTexts()[3];
*typeInfo = schema->propertyType(structName);
if (*typeInfo && (*typeInfo)->propertyType() == KisMetaData::TypeInfo::StructureType) {
*typeInfo = (*typeInfo)->structureSchema()->propertyType(tagName);
}
return true;
}
QRegExp regexp2("([A-Za-z]\\w+)\\[(\\d+)\\]/([A-Za-z]\\w+):([A-Za-z]\\w+)");
if (regexp2.indexIn(tagString) != -1) {
structName = regexp2.capturedTexts()[1];
arrayIndex = regexp2.capturedTexts()[2].toInt() - 1;
tagName = regexp2.capturedTexts()[4];
if (schema->propertyType(structName)) {
*typeInfo = schema->propertyType(structName)->embeddedPropertyType();
Q_ASSERT(*typeInfo);
if ((*typeInfo)->propertyType() == KisMetaData::TypeInfo::StructureType) {
*typeInfo = (*typeInfo)->structureSchema()->propertyType(tagName);
}
}
return true;
}
}
warnKrita << "WARNING: Unsupported tag. We do not yet support nested tags. The tag will be dropped!";
warnKrita << " Failing tag:" << tagString;
return false;
}
bool KisXMPIO::loadFrom(KisMetaData::Store* store, QIODevice* ioDevice) const
{
ioDevice->open(QIODevice::ReadOnly);
dbgMetaData << "Load XMP Data";
std::string xmpPacket_;
QByteArray arr = ioDevice->readAll();
xmpPacket_.assign(arr.data(), arr.length());
dbgMetaData << xmpPacket_.length();
// dbgMetaData << xmpPacket_.c_str();
Exiv2::XmpData xmpData_;
Exiv2::XmpParser::decode(xmpData_, xmpPacket_);
QMap< const KisMetaData::Schema*, QMap<QString, QMap<QString, KisMetaData::Value> > > structures;
QMap< const KisMetaData::Schema*, QMap<QString, QVector< QMap<QString, KisMetaData::Value> > > > arraysOfStructures;
for (Exiv2::XmpData::iterator it = xmpData_.begin(); it != xmpData_.end(); ++it) {
dbgMetaData << "Start iteration" << it->key().c_str();
Exiv2::XmpKey key(it->key());
dbgMetaData << key.groupName().c_str() << " " << key.tagName().c_str() << " " << key.ns().c_str();
if ((key.groupName() == "exif" || key.groupName() == "tiff") && key.tagName() == "NativeDigest") { // TODO: someone who has time to lose can look in adding support for NativeDigest, it's undocumented use by the XMP SDK to check if exif data has been changed while XMP hasn't been updated
dbgMetaData << "dropped";
} else {
const KisMetaData::Schema* schema = KisMetaData::SchemaRegistry::instance()->schemaFromPrefix(key.groupName().c_str());
if (!schema) {
schema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(key.ns().c_str());
if (!schema) {
schema = KisMetaData::SchemaRegistry::instance()->create(key.ns().c_str(), key.groupName().c_str());
Q_ASSERT(schema);
}
}
const Exiv2::Value::AutoPtr value = it->getValue();
QString structName;
int arrayIndex = -1;
QString tagName;
const KisMetaData::TypeInfo* typeInfo = 0;
if (!parseTagName(key.tagName().c_str(),
structName, arrayIndex, tagName,
&typeInfo, schema)) continue;
bool isStructureEntry = !structName.isEmpty() && arrayIndex == -1;
bool isStructureInArrayEntry = !structName.isEmpty() && arrayIndex != -1;
Q_ASSERT(isStructureEntry != isStructureInArrayEntry || !isStructureEntry);
KisMetaData::Value v;
bool ignoreValue = false;
// Compute the value
if (value->typeId() == Exiv2::xmpBag
|| value->typeId() == Exiv2::xmpSeq
|| value->typeId() == Exiv2::xmpAlt) {
const KisMetaData::TypeInfo* embeddedTypeInfo = 0;
if (typeInfo) {
embeddedTypeInfo = typeInfo->embeddedPropertyType();
}
const KisMetaData::Parser* parser = 0;
if (embeddedTypeInfo) {
parser = embeddedTypeInfo->parser();
}
const Exiv2::XmpArrayValue* xav = dynamic_cast<const Exiv2::XmpArrayValue*>(value.get());
Q_ASSERT(xav);
QList<KisMetaData::Value> array;
- for (std::vector< std::string >::const_iterator it = xav->value_.begin();
- it != xav->value_.end(); ++it) {
- QString value = it->c_str();
+ for (int i = 0; i < xav->count(); ++i) {
+ QString value = QString::fromStdString(xav->toString(i));
if (parser) {
array.push_back(parser->parse(value));
} else {
dbgImage << "No parser " << tagName;
array.push_back(KisMetaData::Value(value));
}
}
KisMetaData::Value::ValueType vt = KisMetaData::Value::Invalid;
switch (xav->xmpArrayType()) {
case Exiv2::XmpValue::xaNone:
warnKrita << "KisXMPIO: Unsupported array";
break;
case Exiv2::XmpValue::xaAlt:
vt = KisMetaData::Value::AlternativeArray;
break;
case Exiv2::XmpValue::xaBag:
vt = KisMetaData::Value::UnorderedArray;
break;
case Exiv2::XmpValue::xaSeq:
vt = KisMetaData::Value::OrderedArray;
break;
}
v = KisMetaData::Value(array, vt);
} else if (value->typeId() == Exiv2::langAlt) {
const Exiv2::LangAltValue* xav = dynamic_cast<const Exiv2::LangAltValue*>(value.get());
QList<KisMetaData::Value> alt;
for (std::map< std::string, std::string>::const_iterator it = xav->value_.begin();
it != xav->value_.end(); ++it) {
KisMetaData::Value valt(it->second.c_str());
valt.addPropertyQualifier("xml:lang", KisMetaData::Value(it->first.c_str()));
alt.push_back(valt);
}
v = KisMetaData::Value(alt, KisMetaData::Value::LangArray);
} else {
QString valTxt = value->toString().c_str();
if (typeInfo && typeInfo->parser()) {
v = typeInfo->parser()->parse(valTxt);
} else {
dbgMetaData << "No parser " << tagName;
v = KisMetaData::Value(valTxt);
}
if (valTxt == "type=\"Struct\"") {
if (!typeInfo || typeInfo->propertyType() == KisMetaData::TypeInfo::StructureType) {
ignoreValue = true;
}
}
}
// set the value
if (isStructureEntry) {
structures[schema][structName][tagName] = v;
} else if (isStructureInArrayEntry) {
if (arraysOfStructures[schema][structName].size() <= arrayIndex) {
arraysOfStructures[schema][structName].resize(arrayIndex + 1);
}
if (!arraysOfStructures[schema][structName][arrayIndex].contains(tagName)) {
arraysOfStructures[schema][structName][arrayIndex][tagName] = v;
} else {
warnKrita << "WARNING: trying to overwrite tag" << tagName << "in" << structName << arrayIndex;
}
} else {
if (!ignoreValue) {
store->addEntry(KisMetaData::Entry(schema, tagName, v));
} else {
dbgMetaData << "Ignoring value for " << tagName << " " << v;
}
}
}
}
for (QMap< const KisMetaData::Schema*, QMap<QString, QMap<QString, KisMetaData::Value> > >::iterator it = structures.begin();
it != structures.end(); ++it) {
const KisMetaData::Schema* schema = it.key();
for (QMap<QString, QMap<QString, KisMetaData::Value> >::iterator it2 = it.value().begin();
it2 != it.value().end(); ++it2) {
store->addEntry(KisMetaData::Entry(schema, it2.key(), KisMetaData::Value(it2.value())));
}
}
for (QMap< const KisMetaData::Schema*, QMap<QString, QVector< QMap<QString, KisMetaData::Value> > > >::iterator it = arraysOfStructures.begin(); it != arraysOfStructures.end(); ++it) {
const KisMetaData::Schema* schema = it.key();
for (QMap<QString, QVector<QMap<QString, KisMetaData::Value> > >::iterator it2 = it.value().begin();
it2 != it.value().end(); ++it2) {
KisMetaData::Value::ValueType type = KisMetaData::Value::OrderedArray;
QString entryName = it2.key();
if (schema->propertyType(entryName)) {
switch (schema->propertyType(entryName)->propertyType()) {
case KisMetaData::TypeInfo::OrderedArrayType:
type = KisMetaData::Value::OrderedArray;
break;
case KisMetaData::TypeInfo::UnorderedArrayType:
type = KisMetaData::Value::OrderedArray;
break;
case KisMetaData::TypeInfo::AlternativeArrayType:
type = KisMetaData::Value::AlternativeArray;
break;
default:
type = KisMetaData::Value::Invalid;
break;
}
} else if (store->containsEntry(schema, entryName)) {
KisMetaData::Value value = store->getEntry(schema, entryName).value();
if (value.isArray()) {
type = value.type();
}
}
store->removeEntry(schema, entryName);
if (type != KisMetaData::Value::Invalid) {
QList< KisMetaData::Value > valueList;
for (int i = 0; i < it2.value().size(); ++i) {
valueList.append(it2.value()[i]);
}
store->addEntry(KisMetaData::Entry(schema, entryName, KisMetaData::Value(valueList, type)));
}
}
}
return true;
}
diff --git a/libs/ui/layerstyles/WdgGradientOverlay.ui b/libs/ui/layerstyles/WdgGradientOverlay.ui
index 5fb0c6e772..0bf1218d5a 100644
--- a/libs/ui/layerstyles/WdgGradientOverlay.ui
+++ b/libs/ui/layerstyles/WdgGradientOverlay.ui
@@ -1,286 +1,284 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WdgGradientOverlay</class>
<widget class="QWidget" name="WdgGradientOverlay">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
- <width>411</width>
- <height>294</height>
+ <width>473</width>
+ <height>358</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Gradient Overlay</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Gradient</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Ble&amp;nd Mode:</string>
</property>
<property name="buddy">
<cstring>cmbCompositeOp</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="KisCompositeOpComboBox" name="cmbCompositeOp">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Set the blend mode for the layer</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Opac&amp;ity:</string>
</property>
<property name="buddy">
<cstring>intOpacity</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="KisSliderSpinBox" name="intOpacity" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>10</width>
<height>15</height>
</size>
</property>
<property name="toolTip">
<string>Set the master opacity for the layer</string>
</property>
<property name="whatsThis">
<string>Adjust the transparency of the layer</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>&amp;Gradient:</string>
</property>
<property name="buddy">
<cstring>cmbGradient</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="KisCmbGradient" name="cmbGradient">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>11</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="chkReverse">
<property name="text">
<string>&amp;Reverse</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>St&amp;yle:</string>
</property>
<property name="buddy">
<cstring>cmbStyle</cstring>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QComboBox" name="cmbStyle">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>Linear</string>
</property>
</item>
<item>
<property name="text">
<string>Radial</string>
</property>
</item>
<item>
<property name="text">
<string comment="@item:inlistbox Gradient style">Angle</string>
</property>
</item>
<item>
<property name="text">
<string>Reflected</string>
</property>
</item>
<item>
<property name="text">
<string>Diamond</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QCheckBox" name="chkAlignWithLayer">
<property name="text">
<string>Ali&amp;gn with Layer</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>&amp;Angle:</string>
</property>
<property name="buddy">
- <cstring>dialAngle</cstring>
+ <cstring>angleSelector</cstring>
</property>
</widget>
</item>
- <item row="4" column="1">
- <layout class="QHBoxLayout" name="horizontalLayout_2">
- <item>
- <widget class="QDial" name="dialAngle">
- <property name="toolTip">
- <string>Set the angle of the light source</string>
- </property>
- <property name="minimum">
- <number>-179</number>
- </property>
- <property name="maximum">
- <number>180</number>
- </property>
- <property name="wrapping">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="KisIntParseSpinBox" name="intAngle">
- <property name="minimum">
- <number>-179</number>
- </property>
- <property name="maximum">
- <number>180</number>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="5" column="0">
+ <item row="6" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>S&amp;cale:</string>
</property>
<property name="buddy">
<cstring>intScale</cstring>
</property>
</widget>
</item>
- <item row="5" column="1">
+ <item row="6" column="1">
<widget class="KisSliderSpinBox" name="intScale" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Set size of gradation</string>
</property>
</widget>
</item>
+ <item row="4" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="KisLayerStyleAngleSelector" name="angleSelector" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
</layout>
<zorder>label_6</zorder>
<zorder>intScale</zorder>
<zorder>label_9</zorder>
<zorder>label_7</zorder>
<zorder>label_8</zorder>
<zorder>cmbCompositeOp</zorder>
<zorder>intOpacity</zorder>
<zorder>label_13</zorder>
<zorder>label_14</zorder>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
- <customwidget>
- <class>KisIntParseSpinBox</class>
- <extends>QSpinBox</extends>
- <header>kis_int_parse_spin_box.h</header>
- </customwidget>
<customwidget>
<class>KisSliderSpinBox</class>
<extends>QWidget</extends>
<header location="global">kis_slider_spin_box.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>KisCompositeOpComboBox</class>
<extends>QComboBox</extends>
<header location="global">kis_cmb_composite.h</header>
</customwidget>
+ <customwidget>
+ <class>KisLayerStyleAngleSelector</class>
+ <extends>QWidget</extends>
+ <header>KisLayerStyleAngleSelector.h</header>
+ <container>1</container>
+ </customwidget>
<customwidget>
<class>KisCmbGradient</class>
<extends>QToolButton</extends>
<header>kis_cmb_gradient.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
diff --git a/libs/ui/layerstyles/WdgSatin.ui b/libs/ui/layerstyles/WdgSatin.ui
index 31d2567bc3..1f40d07151 100644
--- a/libs/ui/layerstyles/WdgSatin.ui
+++ b/libs/ui/layerstyles/WdgSatin.ui
@@ -1,278 +1,260 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WdgSatin</class>
<widget class="QWidget" name="WdgSatin">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
- <height>345</height>
+ <height>429</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Satin</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Structure</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Ble&amp;nd Mode:</string>
</property>
<property name="buddy">
<cstring>cmbCompositeOp</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="KisCompositeOpComboBox" name="cmbCompositeOp">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Set the blend mode for the layer</string>
</property>
</widget>
</item>
<item>
<widget class="KisColorButton" name="bnColor">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Opaci&amp;ty:</string>
</property>
<property name="buddy">
<cstring>intOpacity</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="KisSliderSpinBox" name="intOpacity" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>10</width>
<height>15</height>
</size>
</property>
<property name="toolTip">
<string>Set the master opacity for the layer</string>
</property>
<property name="whatsThis">
<string>Adjust the transparency of the layer</string>
</property>
</widget>
</item>
- <item row="2" column="1">
- <layout class="QHBoxLayout" name="horizontalLayout_2"/>
- </item>
- <item row="3" column="0">
+ <item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>An&amp;gle:</string>
</property>
<property name="buddy">
- <cstring>dialAngle</cstring>
+ <cstring>angleSelector</cstring>
</property>
</widget>
</item>
- <item row="3" column="1">
- <layout class="QHBoxLayout" name="horizontalLayout_3">
- <item>
- <widget class="QDial" name="dialAngle">
- <property name="toolTip">
- <string>Set the angle of the light source</string>
- </property>
- <property name="minimum">
- <number>-179</number>
- </property>
- <property name="maximum">
- <number>180</number>
- </property>
- <property name="wrapping">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="KisIntParseSpinBox" name="intAngle">
- <property name="minimum">
- <number>-179</number>
- </property>
- <property name="maximum">
- <number>180</number>
- </property>
- </widget>
- </item>
- </layout>
- </item>
<item row="4" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>&amp;Distance:</string>
</property>
<property name="buddy">
<cstring>intDistance</cstring>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="KisSliderSpinBox" name="intDistance" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>S&amp;ize:</string>
</property>
<property name="buddy">
<cstring>intSize</cstring>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="KisSliderSpinBox" name="intSize" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Contour:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="KisCmbContour" name="cmbContour" native="true"/>
</item>
<item row="7" column="1">
<widget class="QCheckBox" name="chkAntiAliased">
<property name="toolTip">
<string>Smooth the contour</string>
</property>
<property name="text">
<string>Anti-aliased</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QCheckBox" name="chkInvert">
<property name="text">
<string comment="@option:check In Layer Style">&amp;Invert</string>
</property>
</widget>
</item>
+ <item row="2" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="KisLayerStyleAngleSelector" name="angleSelector" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
</layout>
<zorder>cmbContour</zorder>
<zorder>chkAntiAliased</zorder>
<zorder>intOpacity</zorder>
<zorder>label_13</zorder>
<zorder>label_14</zorder>
<zorder>label_4</zorder>
<zorder>intDistance</zorder>
<zorder>intSize</zorder>
<zorder>label</zorder>
<zorder>label_2</zorder>
<zorder>label_6</zorder>
<zorder>chkInvert</zorder>
</widget>
</item>
- <item row="2" column="0">
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
<item row="1" column="0">
- <spacer name="verticalSpacer_2">
+ <spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KisColorButton</class>
<extends>QPushButton</extends>
<header>kis_color_button.h</header>
</customwidget>
- <customwidget>
- <class>KisIntParseSpinBox</class>
- <extends>QSpinBox</extends>
- <header>kis_int_parse_spin_box.h</header>
- </customwidget>
<customwidget>
<class>KisSliderSpinBox</class>
<extends>QWidget</extends>
<header location="global">kis_slider_spin_box.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>KisCompositeOpComboBox</class>
<extends>QComboBox</extends>
<header location="global">kis_cmb_composite.h</header>
</customwidget>
+ <customwidget>
+ <class>KisLayerStyleAngleSelector</class>
+ <extends>QWidget</extends>
+ <header>KisLayerStyleAngleSelector.h</header>
+ <container>1</container>
+ </customwidget>
<customwidget>
<class>KisCmbContour</class>
<extends>QWidget</extends>
<header>kis_cmb_contour.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
diff --git a/libs/ui/layerstyles/WdgStroke.ui b/libs/ui/layerstyles/WdgStroke.ui
index bc7a5531b2..761fc4d488 100644
--- a/libs/ui/layerstyles/WdgStroke.ui
+++ b/libs/ui/layerstyles/WdgStroke.ui
@@ -1,440 +1,451 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WdgStroke</class>
<widget class="QWidget" name="WdgStroke">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
- <width>406</width>
- <height>642</height>
+ <width>457</width>
+ <height>781</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Stroke</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Structure</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>S&amp;ize:</string>
</property>
<property name="buddy">
<cstring>intSize</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="KisSliderSpinBox" name="intSize" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Positio&amp;n:</string>
</property>
<property name="buddy">
<cstring>cmbPosition</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="cmbPosition">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string comment="@item:inlistbox Layer Style: Stroke position">Outside</string>
</property>
</item>
<item>
<property name="text">
<string comment="@item:inlistbox Layer Style: Stroke position">Inside</string>
</property>
</item>
<item>
<property name="text">
<string comment="@item:inlistbox Layer Style: Stroke position">Center</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>&amp;Blend Mode:</string>
</property>
<property name="buddy">
<cstring>cmbCompositeOp</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="KisCompositeOpComboBox" name="cmbCompositeOp">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Opacit&amp;y:</string>
</property>
<property name="buddy">
<cstring>intOpacity</cstring>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="KisSliderSpinBox" name="intOpacity" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string comment="@title:group In Layer Style">Fill</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QComboBox" name="cmbFillType">
<item>
<property name="text">
<string>Color</string>
</property>
</item>
<item>
<property name="text">
<string>Gradient</string>
</property>
</item>
<item>
<property name="text">
<string comment="@title:group In Layer Style">Pattern</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QStackedWidget" name="fillStack">
<property name="currentIndex">
- <number>0</number>
+ <number>1</number>
</property>
<widget class="QWidget" name="page_3">
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Color:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="KisColorButton" name="bnColor">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_4">
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>&amp;Gradient:</string>
</property>
<property name="buddy">
<cstring>cmbGradient</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="KisCmbGradient" name="cmbGradient">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="chkReverse">
<property name="text">
<string>&amp;Reverse</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>St&amp;yle:</string>
</property>
<property name="buddy">
<cstring>cmbStyle</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QComboBox" name="cmbStyle">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>Linear</string>
</property>
</item>
<item>
<property name="text">
<string>Radial</string>
</property>
</item>
<item>
<property name="text">
<string comment="@item:inlistbox Gradient style">Angle</string>
</property>
</item>
<item>
<property name="text">
<string>Reflected</string>
</property>
</item>
<item>
<property name="text">
<string>Diamond</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QCheckBox" name="chkAlignWithLayer">
<property name="text">
<string>Ali&amp;gn with Layer</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>&amp;Angle:</string>
</property>
<property name="buddy">
- <cstring>dialAngle</cstring>
+ <cstring>angleSelector</cstring>
</property>
</widget>
</item>
- <item row="2" column="1">
- <layout class="QHBoxLayout" name="horizontalLayout_2">
- <item>
- <widget class="QDial" name="dialAngle">
- <property name="toolTip">
- <string>Set the angle of the light source</string>
- </property>
- <property name="minimum">
- <number>-179</number>
- </property>
- <property name="maximum">
- <number>180</number>
- </property>
- <property name="wrapping">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="KisIntParseSpinBox" name="intAngle">
- <property name="minimum">
- <number>-179</number>
- </property>
- <property name="maximum">
- <number>180</number>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="3" column="0">
+ <item row="4" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>S&amp;cale:</string>
</property>
<property name="buddy">
<cstring>intScale</cstring>
</property>
</widget>
</item>
- <item row="3" column="1">
+ <item row="4" column="1">
<widget class="KisSliderSpinBox" name="intScale" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Set size of gradation</string>
</property>
</widget>
</item>
+ <item row="2" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="KisLayerStyleAngleSelector" name="angleSelector" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
</layout>
</widget>
<widget class="QWidget" name="page_2">
<layout class="QFormLayout" name="formLayout_4">
<item row="0" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Pattern:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="KisPatternChooser" name="patternChooser">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>250</width>
<height>250</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="bnSnapToOrigin">
<property name="text">
<string>Sn&amp;ap to Origin</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>S&amp;cale:</string>
</property>
<property name="buddy">
<cstring>intScale</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="KisSliderSpinBox" name="intScale_2" native="true">
<property name="toolTip">
<string>Set size of gradation</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="chkLinkWithLayer">
<property name="text">
<string>Lin&amp;k with Layer</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
+ <item row="1" column="0">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KisColorButton</class>
<extends>QPushButton</extends>
<header>kis_color_button.h</header>
</customwidget>
- <customwidget>
- <class>KisIntParseSpinBox</class>
- <extends>QSpinBox</extends>
- <header>kis_int_parse_spin_box.h</header>
- </customwidget>
<customwidget>
<class>KisSliderSpinBox</class>
<extends>QWidget</extends>
<header location="global">kis_slider_spin_box.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>KisCompositeOpComboBox</class>
<extends>QComboBox</extends>
<header location="global">kis_cmb_composite.h</header>
</customwidget>
+ <customwidget>
+ <class>KisLayerStyleAngleSelector</class>
+ <extends>QWidget</extends>
+ <header>KisLayerStyleAngleSelector.h</header>
+ <container>1</container>
+ </customwidget>
<customwidget>
<class>KisCmbGradient</class>
<extends>QToolButton</extends>
<header>kis_cmb_gradient.h</header>
</customwidget>
<customwidget>
<class>KisPatternChooser</class>
<extends>QFrame</extends>
<header location="global">kis_pattern_chooser.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
diff --git a/libs/ui/layerstyles/wdgBevelAndEmboss.ui b/libs/ui/layerstyles/wdgBevelAndEmboss.ui
index 1bd4462532..39cb1071c0 100644
--- a/libs/ui/layerstyles/wdgBevelAndEmboss.ui
+++ b/libs/ui/layerstyles/wdgBevelAndEmboss.ui
@@ -1,481 +1,437 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WdgBevelAndEmboss</class>
<widget class="QWidget" name="WdgBevelAndEmboss">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
- <width>399</width>
- <height>541</height>
+ <width>453</width>
+ <height>669</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Bevel and Emboss</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Structure</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>St&amp;yle:</string>
</property>
<property name="buddy">
<cstring>cmbStyle</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="cmbStyle">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>Outer Bevel</string>
</property>
</item>
<item>
<property name="text">
<string>Inner Bevel</string>
</property>
</item>
<item>
<property name="text">
<string comment="@item:inlistbox Bevel/emboss style">Emboss</string>
</property>
</item>
<item>
<property name="text">
<string>Pillow Emboss</string>
</property>
</item>
<item>
<property name="text">
<string>Stroke Emboss</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>&amp;Technique:</string>
</property>
<property name="buddy">
<cstring>cmbTechnique</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="cmbTechnique">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>Smooth</string>
</property>
</item>
<item>
<property name="text">
<string>Chisel Hard</string>
</property>
</item>
<item>
<property name="text">
<string>Chisel Soft</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>&amp;Depth:</string>
</property>
<property name="buddy">
<cstring>intDepth</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="KisSliderSpinBox" name="intDepth" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string comment="@label:listbox Bevel direction">Direction:</string>
</property>
+ <property name="buddy">
+ <cstring>cmbDirection</cstring>
+ </property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="cmbDirection">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string comment="@item:inlistbox Bevel direction">Up</string>
</property>
</item>
<item>
<property name="text">
<string comment="@item:inlistbox Bevel direction">Down</string>
</property>
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Si&amp;ze:</string>
</property>
<property name="buddy">
<cstring>intSize</cstring>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="KisSliderSpinBox" name="intSize" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>So&amp;ften:</string>
</property>
<property name="buddy">
<cstring>intSoften</cstring>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="KisSliderSpinBox" name="intSoften" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Shading</string>
</property>
- <layout class="QGridLayout" name="gridLayout_3">
- <item row="0" column="0" colspan="3">
- <layout class="QGridLayout" name="gridLayout">
- <item row="0" column="0">
- <widget class="QLabel" name="label_7">
- <property name="text">
- <string>&amp;Angle:</string>
- </property>
- <property name="buddy">
- <cstring>dialAngle</cstring>
- </property>
- </widget>
- </item>
- <item row="0" column="1" rowspan="2">
- <widget class="QDial" name="dialAngle">
- <property name="toolTip">
- <string>Set the angle of the light source</string>
- </property>
- <property name="minimum">
- <number>-179</number>
- </property>
- <property name="maximum">
- <number>180</number>
- </property>
- <property name="wrapping">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item row="0" column="2">
- <widget class="KisIntParseSpinBox" name="intAngle">
- <property name="minimum">
- <number>-179</number>
- </property>
- <property name="maximum">
- <number>180</number>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>18</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="2">
- <widget class="QCheckBox" name="chkUseGlobalLight">
- <property name="text">
- <string>Use &amp;Global Light</string>
- </property>
- </widget>
- </item>
- </layout>
+ <layout class="QFormLayout" name="formLayout_2">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>&amp;Angle:</string>
+ </property>
+ <property name="buddy">
+ <cstring>angleSelector</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="KisLayerStyleAngleSelector" name="angleSelector" native="true"/>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>A&amp;ltitude:</string>
+ </property>
+ <property name="buddy">
+ <cstring>intAltitude</cstring>
+ </property>
+ </widget>
</item>
- <item row="1" column="2">
+ <item row="1" column="1">
<widget class="KisIntParseSpinBox" name="intAltitude">
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
<property name="minimum">
<number>-179</number>
</property>
<property name="maximum">
<number>180</number>
</property>
</widget>
</item>
- <item row="2" column="1">
+ <item row="2" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>&amp;Gloss Contour:</string>
</property>
<property name="buddy">
<cstring>cmbContour</cstring>
</property>
</widget>
</item>
- <item row="2" column="2">
+ <item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QComboBox" name="cmbContour"/>
</item>
<item>
<widget class="QCheckBox" name="chkAntiAliased">
<property name="text">
<string>Anti-aliased</string>
</property>
</widget>
</item>
</layout>
</item>
- <item row="3" column="0" colspan="2">
+ <item row="3" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>H&amp;ighlight Mode:</string>
</property>
<property name="buddy">
<cstring>cmbHighlightMode</cstring>
</property>
</widget>
</item>
- <item row="3" column="2">
+ <item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="KisCompositeOpComboBox" name="cmbHighlightMode">
<property name="toolTip">
<string>Set the blend mode for the layer</string>
</property>
</widget>
</item>
<item>
<widget class="KisColorButton" name="bnHighlightColor">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
- <item row="4" column="0" colspan="2">
+ <item row="4" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>&amp;Opacity:</string>
</property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
<property name="buddy">
<cstring>intOpacity</cstring>
</property>
</widget>
</item>
- <item row="4" column="2">
+ <item row="4" column="1">
<widget class="KisSliderSpinBox" name="intOpacity" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>10</width>
<height>15</height>
</size>
</property>
<property name="toolTip">
<string>Set the master opacity for the layer</string>
</property>
<property name="whatsThis">
<string>Adjust the transparency of the layer</string>
</property>
</widget>
</item>
- <item row="5" column="2">
+ <item row="5" column="0">
+ <widget class="QLabel" name="label_11">
+ <property name="text">
+ <string>Sh&amp;adow Mode:</string>
+ </property>
+ <property name="buddy">
+ <cstring>cmbShadowMode</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="KisCompositeOpComboBox" name="cmbShadowMode">
<property name="toolTip">
<string>Set the blend mode for the layer</string>
</property>
</widget>
</item>
<item>
<widget class="KisColorButton" name="bnShadowColor">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
- <item row="6" column="0" colspan="2">
+ <item row="6" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>&amp;Opacity:</string>
</property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
<property name="buddy">
<cstring>intOpacity2</cstring>
</property>
</widget>
</item>
- <item row="6" column="2">
+ <item row="6" column="1">
<widget class="KisSliderSpinBox" name="intOpacity2" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>10</width>
<height>15</height>
</size>
</property>
<property name="toolTip">
<string>Set the master opacity for the layer</string>
</property>
<property name="whatsThis">
<string>Adjust the transparency of the layer</string>
</property>
</widget>
</item>
- <item row="1" column="0" colspan="2">
- <widget class="QLabel" name="label_8">
- <property name="text">
- <string>Altitude:</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- <property name="buddy">
- <cstring>dialAngle</cstring>
- </property>
- </widget>
- </item>
- <item row="5" column="0" colspan="2">
- <widget class="QLabel" name="label_11">
- <property name="text">
- <string>Sh&amp;adow Mode:</string>
- </property>
- <property name="buddy">
- <cstring>cmbShadowMode</cstring>
- </property>
- </widget>
- </item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
- <customwidget>
- <class>KisColorButton</class>
- <extends>QPushButton</extends>
- <header>kis_color_button.h</header>
- </customwidget>
<customwidget>
<class>KisIntParseSpinBox</class>
<extends>QSpinBox</extends>
<header>kis_int_parse_spin_box.h</header>
</customwidget>
+ <customwidget>
+ <class>KisColorButton</class>
+ <extends>QPushButton</extends>
+ <header>kis_color_button.h</header>
+ </customwidget>
<customwidget>
<class>KisSliderSpinBox</class>
<extends>QWidget</extends>
<header location="global">kis_slider_spin_box.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>KisCompositeOpComboBox</class>
<extends>QComboBox</extends>
<header location="global">kis_cmb_composite.h</header>
</customwidget>
+ <customwidget>
+ <class>KisLayerStyleAngleSelector</class>
+ <extends>QWidget</extends>
+ <header>KisLayerStyleAngleSelector.h</header>
+ <container>1</container>
+ </customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
diff --git a/libs/ui/layerstyles/wdgKisLayerStyleAngleSelector.ui b/libs/ui/layerstyles/wdgKisLayerStyleAngleSelector.ui
new file mode 100644
index 0000000000..ed36eb0807
--- /dev/null
+++ b/libs/ui/layerstyles/wdgKisLayerStyleAngleSelector.ui
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>WdgKisLayerStyleAngleSelector</class>
+ <widget class="QWidget" name="WdgKisLayerStyleAngleSelector">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>273</width>
+ <height>76</height>
+ </rect>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QDial" name="dialAngle">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>Set the angle of the light source</string>
+ </property>
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>360</number>
+ </property>
+ <property name="value">
+ <number>0</number>
+ </property>
+ <property name="invertedAppearance">
+ <bool>true</bool>
+ </property>
+ <property name="wrapping">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="KisIntParseSpinBox" name="intAngle">
+ <property name="wrapping">
+ <bool>true</bool>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="suffix">
+ <string>˚</string>
+ </property>
+ <property name="minimum">
+ <number>-179</number>
+ </property>
+ <property name="maximum">
+ <number>180</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="chkUseGlobalLight">
+ <property name="text">
+ <string>Use global light</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>KisIntParseSpinBox</class>
+ <extends>QSpinBox</extends>
+ <header>kis_int_parse_spin_box.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/libs/ui/layerstyles/wdgdropshadow.ui b/libs/ui/layerstyles/wdgdropshadow.ui
index 3074d05833..5572b503a6 100644
--- a/libs/ui/layerstyles/wdgdropshadow.ui
+++ b/libs/ui/layerstyles/wdgdropshadow.ui
@@ -1,318 +1,282 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WdgDropShadow</class>
<widget class="QWidget" name="WdgDropShadow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
- <width>435</width>
- <height>390</height>
+ <width>491</width>
+ <height>495</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QGroupBox" name="grpMain">
<property name="title">
<string>Drop Shadow</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
+ <item row="3" column="0">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
<item row="2" column="0">
<widget class="QCheckBox" name="chkLayerKnocksOutDropShadow">
<property name="toolTip">
<string>Use to obscure the shadow when fill is transparent</string>
</property>
<property name="text">
<string>Layer knocks O&amp;ut Drop Shadow</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Quality</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Contour:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="KisCmbContour" name="cmbContour" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="chkAntiAliased">
<property name="toolTip">
<string>Smooth the contour</string>
</property>
<property name="text">
<string>Anti-aliased</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>&amp;Noise:</string>
</property>
<property name="buddy">
<cstring>intNoise</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="KisSliderSpinBox" name="intNoise" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Add noise to shadow</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Structure</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
- <item row="0" column="0">
- <widget class="QLabel" name="label_13">
- <property name="text">
- <string>&amp;Blend Mode:</string>
- </property>
- <property name="buddy">
- <cstring>cmbCompositeOp</cstring>
- </property>
- </widget>
- </item>
<item row="0" column="1">
<widget class="KisCompositeOpComboBox" name="cmbCompositeOp">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Set the blend mode for the layer</string>
</property>
</widget>
</item>
- <item row="0" column="2">
- <widget class="KisColorButton" name="bnColor"/>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="label_14">
- <property name="text">
- <string>Opaci&amp;ty:</string>
- </property>
- <property name="buddy">
- <cstring>intOpacity</cstring>
+ <item row="4" column="1" colspan="2">
+ <widget class="KisSliderSpinBox" name="intDistance" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
</property>
</widget>
</item>
+ <item row="0" column="2">
+ <widget class="KisColorButton" name="bnColor"/>
+ </item>
<item row="1" column="1" colspan="2">
<widget class="KisSliderSpinBox" name="intOpacity" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>10</width>
<height>15</height>
</size>
</property>
<property name="toolTip">
<string>Set the master opacity for the layer</string>
</property>
<property name="whatsThis">
<string>Adjust the transparency of the layer</string>
</property>
</widget>
</item>
- <item row="2" column="0">
- <widget class="QLabel" name="label">
- <property name="text">
- <string>An&amp;gle:</string>
- </property>
- <property name="buddy">
- <cstring>dialAngle</cstring>
- </property>
- </widget>
- </item>
- <item row="2" column="1" colspan="2">
- <layout class="QHBoxLayout" name="horizontalLayout">
- <item>
- <widget class="QDial" name="dialAngle">
- <property name="toolTip">
- <string>Set the angle of the light source</string>
- </property>
- <property name="minimum">
- <number>-179</number>
- </property>
- <property name="maximum">
- <number>180</number>
- </property>
- <property name="wrapping">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="KisIntParseSpinBox" name="intAngle">
- <property name="minimum">
- <number>-179</number>
- </property>
- <property name="maximum">
- <number>180</number>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QCheckBox" name="chkUseGlobalLight">
- <property name="toolTip">
- <string>Use current setting for global light source</string>
- </property>
- <property name="text">
- <string>Use &amp;Global light</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="3" column="0">
+ <item row="4" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>&amp;Distance:</string>
</property>
<property name="buddy">
<cstring>intDistance</cstring>
</property>
</widget>
</item>
- <item row="3" column="1" colspan="2">
- <widget class="KisSliderSpinBox" name="intDistance" native="true">
+ <item row="6" column="1" colspan="2">
+ <widget class="KisSliderSpinBox" name="intSize" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
- <item row="4" column="0">
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_14">
+ <property name="text">
+ <string>Opaci&amp;ty:</string>
+ </property>
+ <property name="buddy">
+ <cstring>intOpacity</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>An&amp;gle:</string>
+ </property>
+ <property name="buddy">
+ <cstring>angleSelector</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
<widget class="QLabel" name="lblSpread">
<property name="text">
<string>Sp&amp;read:</string>
</property>
<property name="buddy">
<cstring>intSpread</cstring>
</property>
</widget>
</item>
- <item row="4" column="1" colspan="2">
+ <item row="5" column="1" colspan="2">
<widget class="KisSliderSpinBox" name="intSpread" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
- <item row="5" column="0">
+ <item row="6" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>S&amp;ize:</string>
</property>
<property name="buddy">
<cstring>intSize</cstring>
</property>
</widget>
</item>
- <item row="5" column="1" colspan="2">
- <widget class="KisSliderSpinBox" name="intSize" native="true">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_13">
+ <property name="text">
+ <string>&amp;Blend Mode:</string>
+ </property>
+ <property name="buddy">
+ <cstring>cmbCompositeOp</cstring>
</property>
</widget>
</item>
+ <item row="3" column="1" colspan="2">
+ <widget class="KisLayerStyleAngleSelector" name="angleSelector" native="true"/>
+ </item>
</layout>
</widget>
</item>
- <item row="3" column="0">
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KisColorButton</class>
<extends>QPushButton</extends>
<header>kis_color_button.h</header>
</customwidget>
- <customwidget>
- <class>KisIntParseSpinBox</class>
- <extends>QSpinBox</extends>
- <header>kis_int_parse_spin_box.h</header>
- </customwidget>
<customwidget>
<class>KisSliderSpinBox</class>
<extends>QWidget</extends>
<header location="global">kis_slider_spin_box.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>KisCompositeOpComboBox</class>
<extends>QComboBox</extends>
<header location="global">kis_cmb_composite.h</header>
</customwidget>
+ <customwidget>
+ <class>KisLayerStyleAngleSelector</class>
+ <extends>QWidget</extends>
+ <header>KisLayerStyleAngleSelector.h</header>
+ <container>1</container>
+ </customwidget>
<customwidget>
<class>KisCmbContour</class>
<extends>QWidget</extends>
<header>kis_cmb_contour.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
diff --git a/libs/ui/opengl/kis_opengl.cpp b/libs/ui/opengl/kis_opengl.cpp
index 89fb1763ab..6e0f9b5578 100644
--- a/libs/ui/opengl/kis_opengl.cpp
+++ b/libs/ui/opengl/kis_opengl.cpp
@@ -1,732 +1,734 @@
/*
* Copyright (c) 2007 Adrian Page <adrian@pagenet.plus.com>
*
* 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 <config-hdr.h>
#include "opengl/kis_opengl.h"
#include "opengl/kis_opengl_p.h"
#include <QOpenGLContext>
#include <QOpenGLDebugLogger>
#include <QOpenGLFunctions>
#include <QApplication>
#include <QDesktopWidget>
#include <QPixmapCache>
#include <QDir>
#include <QFile>
#include <QStandardPaths>
#include <QVector>
#include <QWindow>
#include <klocalizedstring.h>
#include <kis_debug.h>
#include <kis_config.h>
#include "KisOpenGLModeProber.h"
+#include <KisUsageLogger.h>
#include <boost/optional.hpp>
#include "kis_assert.h"
#include <QRegularExpression>
#include <QSettings>
#ifndef GL_RENDERER
# define GL_RENDERER 0x1F01
#endif
using namespace KisOpenGLPrivate;
namespace
{
// config option, set manually by main()
bool g_isDebugSynchronous = false;
bool g_sanityDefaultFormatIsSet = false;
boost::optional<KisOpenGLModeProber::Result> openGLCheckResult;
bool g_needsFenceWorkaround = false;
bool g_needsPixmapCacheWorkaround = false;
QString g_surfaceFormatDetectionLog;
QString g_debugText("OpenGL Info\n **OpenGL not initialized**");
QVector<KLocalizedString> g_openglWarningStrings;
KisOpenGL::OpenGLRenderers g_supportedRenderers;
KisOpenGL::OpenGLRenderer g_rendererPreferredByQt;
void overrideSupportedRenderers(KisOpenGL::OpenGLRenderers supportedRenderers, KisOpenGL::OpenGLRenderer preferredByQt) {
g_supportedRenderers = supportedRenderers;
g_rendererPreferredByQt = preferredByQt;
}
void openglOnMessageLogged(const QOpenGLDebugMessage& debugMessage) {
qDebug() << "OpenGL:" << debugMessage;
}
}
KisOpenGLPrivate::OpenGLCheckResult::OpenGLCheckResult(QOpenGLContext &context) {
if (!context.isValid()) {
return;
}
QOpenGLFunctions *funcs = context.functions(); // funcs is ready to be used
m_rendererString = QString(reinterpret_cast<const char *>(funcs->glGetString(GL_RENDERER)));
m_driverVersionString = QString(reinterpret_cast<const char *>(funcs->glGetString(GL_VERSION)));
m_glMajorVersion = context.format().majorVersion();
m_glMinorVersion = context.format().minorVersion();
m_supportsDeprecatedFunctions = (context.format().options() & QSurfaceFormat::DeprecatedFunctions);
m_isOpenGLES = context.isOpenGLES();
}
void KisOpenGLPrivate::appendOpenGLWarningString(KLocalizedString warning)
{
g_openglWarningStrings << warning;
}
void KisOpenGLPrivate::overrideOpenGLWarningString(QVector<KLocalizedString> warnings)
{
g_openglWarningStrings = warnings;
}
void KisOpenGL::initialize()
{
if (openGLCheckResult) return;
KIS_SAFE_ASSERT_RECOVER_NOOP(g_sanityDefaultFormatIsSet);
openGLCheckResult =
KisOpenGLModeProber::instance()->probeFormat(QSurfaceFormat::defaultFormat(), false);
g_debugText.clear();
QDebug debugOut(&g_debugText);
- debugOut << "OpenGL Info";
+ debugOut << "OpenGL Info\n";
debugOut << "\n Vendor: " << openGLCheckResult->vendorString();
debugOut << "\n Renderer: " << openGLCheckResult->rendererString();
debugOut << "\n Version: " << openGLCheckResult->driverVersionString();
debugOut << "\n Shading language: " << openGLCheckResult->shadingLanguageString();
debugOut << "\n Requested format: " << QSurfaceFormat::defaultFormat();
debugOut << "\n Current format: " << openGLCheckResult->format();
debugOut.nospace();
debugOut << "\n Version: " << openGLCheckResult->glMajorVersion() << "." << openGLCheckResult->glMinorVersion();
debugOut.resetFormat();
debugOut << "\n Supports deprecated functions" << openGLCheckResult->supportsDeprecatedFunctions();
debugOut << "\n is OpenGL ES:" << openGLCheckResult->isOpenGLES();
debugOut << "\n\nQPA OpenGL Detection Info";
debugOut << "\n supportsDesktopGL:" << bool(g_supportedRenderers & RendererDesktopGL);
#ifdef Q_OS_WIN
debugOut << "\n supportsAngleD3D11:" << bool(g_supportedRenderers & RendererOpenGLES);
debugOut << "\n isQtPreferAngle:" << bool(g_rendererPreferredByQt == RendererOpenGLES);
#else
debugOut << "\n supportsOpenGLES:" << bool(g_supportedRenderers & RendererOpenGLES);
debugOut << "\n isQtPreferOpenGLES:" << bool(g_rendererPreferredByQt == RendererOpenGLES);
#endif
debugOut << "\n== log ==\n";
debugOut.noquote();
debugOut << g_surfaceFormatDetectionLog;
debugOut.resetFormat();
debugOut << "\n== end log ==";
dbgOpenGL.noquote().nospace() << g_debugText;
+ KisUsageLogger::write(g_debugText);
// Check if we have a bugged driver that needs fence workaround
bool isOnX11 = false;
#ifdef HAVE_X11
isOnX11 = true;
#endif
KisConfig cfg(true);
if ((isOnX11 && openGLCheckResult->rendererString().startsWith("AMD")) || cfg.forceOpenGLFenceWorkaround()) {
g_needsFenceWorkaround = true;
}
/**
* NVidia + Qt's openGL don't play well together and one cannot
* draw a pixmap on a widget more than once in one rendering cycle.
*
* It can be workarounded by drawing strictly via QPixmapCache and
* only when the pixmap size in bigger than doubled size of the
* display framebuffer. That is for 8-bit HD display, you should have
* a cache bigger than 16 MiB. Don't ask me why. (DK)
*
* See bug: https://bugs.kde.org/show_bug.cgi?id=361709
*
* TODO: check if this workaround is still needed after merging
* Qt5+openGL3 branch.
*/
if (openGLCheckResult->vendorString().toUpper().contains("NVIDIA")) {
g_needsPixmapCacheWorkaround = true;
const QRect screenSize = QApplication::desktop()->screenGeometry();
const int minCacheSize = 20 * 1024;
const int cacheSize = 2048 + 2 * 4 * screenSize.width() * screenSize.height() / 1024; //KiB
QPixmapCache::setCacheLimit(qMax(minCacheSize, cacheSize));
}
}
void KisOpenGL::initializeContext(QOpenGLContext *ctx)
{
KisConfig cfg(true);
initialize();
const bool isDebugEnabled = ctx->format().testOption(QSurfaceFormat::DebugContext);
dbgUI << "OpenGL: Opening new context";
if (isDebugEnabled) {
// Passing ctx for ownership management only, not specifying context.
// QOpenGLDebugLogger only function on the current active context.
// FIXME: Do we need to make sure ctx is the active context?
QOpenGLDebugLogger* openglLogger = new QOpenGLDebugLogger(ctx);
if (openglLogger->initialize()) {
qDebug() << "QOpenGLDebugLogger is initialized. Check whether you get a message below.";
QObject::connect(openglLogger, &QOpenGLDebugLogger::messageLogged, &openglOnMessageLogged);
openglLogger->startLogging(g_isDebugSynchronous ? QOpenGLDebugLogger::SynchronousLogging : QOpenGLDebugLogger::AsynchronousLogging);
openglLogger->logMessage(QOpenGLDebugMessage::createApplicationMessage(QStringLiteral("QOpenGLDebugLogger is logging.")));
} else {
qDebug() << "QOpenGLDebugLogger cannot be initialized.";
delete openglLogger;
}
}
// Double check we were given the version we requested
QSurfaceFormat format = ctx->format();
QOpenGLFunctions *f = ctx->functions();
f->initializeOpenGLFunctions();
QFile log(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/krita-opengl.txt");
log.open(QFile::WriteOnly);
QString vendor((const char*)f->glGetString(GL_VENDOR));
log.write(vendor.toLatin1());
log.write(", ");
log.write(openGLCheckResult->rendererString().toLatin1());
log.write(", ");
QString version((const char*)f->glGetString(GL_VERSION));
log.write(version.toLatin1());
log.close();
}
const QString &KisOpenGL::getDebugText()
{
initialize();
return g_debugText;
}
QStringList KisOpenGL::getOpenGLWarnings() {
QStringList strings;
Q_FOREACH (const KLocalizedString &item, g_openglWarningStrings) {
strings << item.toString();
}
return strings;
}
// XXX Temporary function to allow LoD on OpenGL3 without triggering
// all of the other 3.2 functionality, can be removed once we move to Qt5.7
bool KisOpenGL::supportsLoD()
{
initialize();
return openGLCheckResult->supportsLoD();
}
bool KisOpenGL::hasOpenGL3()
{
initialize();
return openGLCheckResult->hasOpenGL3();
}
bool KisOpenGL::hasOpenGLES()
{
initialize();
return openGLCheckResult->isOpenGLES();
}
bool KisOpenGL::supportsFenceSync()
{
initialize();
return openGLCheckResult->supportsFenceSync();
}
bool KisOpenGL::needsFenceWorkaround()
{
initialize();
return g_needsFenceWorkaround;
}
bool KisOpenGL::needsPixmapCacheWorkaround()
{
initialize();
return g_needsPixmapCacheWorkaround;
}
void KisOpenGL::testingInitializeDefaultSurfaceFormat()
{
setDefaultSurfaceFormat(selectSurfaceFormat(KisOpenGL::RendererAuto, KisConfig::BT709_G22, false));
}
void KisOpenGL::setDebugSynchronous(bool value)
{
g_isDebugSynchronous = value;
}
KisOpenGL::OpenGLRenderer KisOpenGL::getCurrentOpenGLRenderer()
{
const QSurfaceFormat::RenderableType renderer = QSurfaceFormat::defaultFormat().renderableType();
return renderer == QSurfaceFormat::OpenGLES ? RendererOpenGLES :
renderer == QSurfaceFormat::OpenGL ? RendererDesktopGL :
RendererAuto;
}
KisOpenGL::OpenGLRenderer KisOpenGL::getQtPreferredOpenGLRenderer()
{
return g_rendererPreferredByQt;
}
KisOpenGL::OpenGLRenderers KisOpenGL::getSupportedOpenGLRenderers()
{
return g_supportedRenderers;
}
KisOpenGL::OpenGLRenderer KisOpenGL::getUserPreferredOpenGLRendererConfig()
{
const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat);
return convertConfigToOpenGLRenderer(kritarc.value("OpenGLRenderer", "auto").toString());
}
void KisOpenGL::setUserPreferredOpenGLRendererConfig(KisOpenGL::OpenGLRenderer renderer)
{
const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat);
kritarc.setValue("OpenGLRenderer", KisOpenGL::convertOpenGLRendererToConfig(renderer));
}
QString KisOpenGL::convertOpenGLRendererToConfig(KisOpenGL::OpenGLRenderer renderer)
{
switch (renderer) {
case RendererDesktopGL:
return QStringLiteral("desktop");
case RendererOpenGLES:
return QStringLiteral("angle");
default:
return QStringLiteral("auto");
}
}
KisOpenGL::OpenGLRenderer KisOpenGL::convertConfigToOpenGLRenderer(QString renderer)
{
if (renderer == "desktop") {
return RendererDesktopGL;
} else if (renderer == "angle") {
return RendererOpenGLES;
} else {
return RendererAuto;
}
}
namespace {
QSurfaceFormat generateSurfaceFormat(QSurfaceFormat::RenderableType renderer,
KisConfig::RootSurfaceFormat rootSurfaceFormat,
bool debugContext)
{
QSurfaceFormat format;
#ifdef Q_OS_OSX
format.setVersion(3, 2);
format.setProfile(QSurfaceFormat::CoreProfile);
#else
// XXX This can be removed once we move to Qt5.7
format.setVersion(3, 0);
format.setProfile(QSurfaceFormat::CompatibilityProfile);
format.setOptions(QSurfaceFormat::DeprecatedFunctions);
#endif
format.setDepthBufferSize(24);
format.setStencilBufferSize(8);
KisOpenGLModeProber::initSurfaceFormatFromConfig(rootSurfaceFormat, &format);
format.setRenderableType(renderer);
format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
format.setSwapInterval(0); // Disable vertical refresh syncing
if (debugContext) {
format.setOption(QSurfaceFormat::DebugContext, true);
}
return format;
}
bool isOpenGLRendererBlacklisted(const QString &rendererString,
const QString &driverVersionString,
QVector<KLocalizedString> *warningMessage)
{
bool isBlacklisted = false;
// Special blacklisting of OpenGL/ANGLE is tracked on:
// https://phabricator.kde.org/T7411
// HACK: Specifically detect for Intel driver build number
// See https://www.intel.com/content/www/us/en/support/articles/000005654/graphics-drivers.html
if (rendererString.startsWith("Intel")) {
KLocalizedString knownBadIntelWarning = ki18n("The Intel graphics driver in use is known to have issues with OpenGL.");
KLocalizedString grossIntelWarning = ki18n(
"Intel graphics drivers tend to have issues with OpenGL so ANGLE will be used by default. "
"You may manually switch to OpenGL but it is not guaranteed to work properly."
);
QRegularExpression regex("\\b\\d{2}\\.\\d{2}\\.\\d{2}\\.(\\d{4})\\b");
QRegularExpressionMatch match = regex.match(driverVersionString);
if (match.hasMatch()) {
int driverBuild = match.captured(1).toInt();
if (driverBuild > 4636 && driverBuild < 4729) {
// Make ANGLE the preferred renderer for Intel driver versions
// between build 4636 and 4729 (exclusive) due to an UI offset bug.
// See https://communities.intel.com/thread/116003
// (Build 4636 is known to work from some test results)
qDebug() << "Detected Intel driver build between 4636 and 4729, making ANGLE the preferred renderer";
isBlacklisted = true;
*warningMessage << knownBadIntelWarning;
} else if (driverBuild == 4358) {
// There are several reports on a bug where the canvas is not being
// updated properly which has debug info pointing to this build.
qDebug() << "Detected Intel driver build 4358, making ANGLE the preferred renderer";
isBlacklisted = true;
*warningMessage << knownBadIntelWarning;
} else {
// Intel tends to randomly break OpenGL in some of their new driver
// builds, therefore we just shouldn't use OpenGL by default to
// reduce bug report noises.
qDebug() << "Detected Intel driver, making ANGLE the preferred renderer";
isBlacklisted = true;
*warningMessage << grossIntelWarning;
}
}
}
return isBlacklisted;
}
boost::optional<bool> orderPreference(bool lhs, bool rhs)
{
if (lhs == rhs) return boost::none;
if (lhs && !rhs) return true;
if (!lhs && rhs) return false;
return false;
}
#define ORDER_BY(lhs, rhs) if (auto res = orderPreference((lhs), (rhs))) { return *res; }
class FormatPositionLess
{
public:
FormatPositionLess()
{
}
bool operator()(const QSurfaceFormat &lhs, const QSurfaceFormat &rhs) const {
KIS_SAFE_ASSERT_RECOVER_NOOP(m_preferredColorSpace != QSurfaceFormat::DefaultColorSpace);
ORDER_BY(isPreferredColorSpace(lhs.colorSpace()),
isPreferredColorSpace(rhs.colorSpace()));
if (doPreferHDR()) {
ORDER_BY(isHDRFormat(lhs), isHDRFormat(rhs));
} else {
ORDER_BY(!isHDRFormat(lhs), !isHDRFormat(rhs));
}
if (m_preferredRendererByUser != QSurfaceFormat::DefaultRenderableType) {
ORDER_BY(lhs.renderableType() == m_preferredRendererByUser,
rhs.renderableType() == m_preferredRendererByUser);
}
ORDER_BY(!isBlacklisted(lhs), !isBlacklisted(rhs));
if (doPreferHDR() &&
m_preferredRendererByHDR != QSurfaceFormat::DefaultRenderableType) {
ORDER_BY(lhs.renderableType() == m_preferredRendererByHDR,
rhs.renderableType() == m_preferredRendererByHDR);
}
KIS_SAFE_ASSERT_RECOVER_NOOP(m_preferredRendererByQt != QSurfaceFormat::DefaultRenderableType);
ORDER_BY(lhs.renderableType() == m_preferredRendererByQt,
rhs.renderableType() == m_preferredRendererByQt);
return false;
}
public:
void setPreferredColorSpace(const QSurfaceFormat::ColorSpace &preferredColorSpace) {
m_preferredColorSpace = preferredColorSpace;
}
void setPreferredRendererByQt(const QSurfaceFormat::RenderableType &preferredRendererByQt) {
m_preferredRendererByQt = preferredRendererByQt;
}
void setPreferredRendererByUser(const QSurfaceFormat::RenderableType &preferredRendererByUser) {
m_preferredRendererByUser = preferredRendererByUser;
}
void setPreferredRendererByHDR(const QSurfaceFormat::RenderableType &preferredRendererByHDR) {
m_preferredRendererByHDR = preferredRendererByHDR;
}
void setOpenGLBlacklisted(bool openGLBlacklisted) {
m_openGLBlacklisted = openGLBlacklisted;
}
void setOpenGLESBlacklisted(bool openGLESBlacklisted) {
m_openGLESBlacklisted = openGLESBlacklisted;
}
QSurfaceFormat::ColorSpace preferredColorSpace() const {
return m_preferredColorSpace;
}
QSurfaceFormat::RenderableType preferredRendererByUser() const {
return m_preferredRendererByUser;
}
private:
bool isHDRFormat(const QSurfaceFormat &f) const {
#ifdef HAVE_HDR
return f.colorSpace() == QSurfaceFormat::bt2020PQColorSpace ||
f.colorSpace() == QSurfaceFormat::scRGBColorSpace;
#else
Q_UNUSED(f);
return false;
#endif
}
bool isBlacklisted(const QSurfaceFormat &f) const {
KIS_SAFE_ASSERT_RECOVER_NOOP(f.renderableType() == QSurfaceFormat::OpenGL ||
f.renderableType() == QSurfaceFormat::OpenGLES);
return (f.renderableType() == QSurfaceFormat::OpenGL && m_openGLBlacklisted) ||
(f.renderableType() == QSurfaceFormat::OpenGLES && m_openGLESBlacklisted);
}
bool doPreferHDR() const {
#ifdef HAVE_HDR
return m_preferredColorSpace == QSurfaceFormat::bt2020PQColorSpace ||
m_preferredColorSpace == QSurfaceFormat::scRGBColorSpace;
#else
return false;
#endif
}
bool isPreferredColorSpace(const QSurfaceFormat::ColorSpace cs) const {
return KisOpenGLModeProber::fuzzyCompareColorSpaces(m_preferredColorSpace, cs);
return false;
}
private:
QSurfaceFormat::ColorSpace m_preferredColorSpace = QSurfaceFormat::DefaultColorSpace;
QSurfaceFormat::RenderableType m_preferredRendererByQt = QSurfaceFormat::OpenGL;
QSurfaceFormat::RenderableType m_preferredRendererByUser = QSurfaceFormat::DefaultRenderableType;
QSurfaceFormat::RenderableType m_preferredRendererByHDR = QSurfaceFormat::DefaultRenderableType;
bool m_openGLBlacklisted = false;
bool m_openGLESBlacklisted = false;
};
struct DetectionDebug : public QDebug
{
DetectionDebug(QString *string)
: QDebug(string),
m_string(string),
m_originalSize(string->size())
{}
~DetectionDebug() { dbgOpenGL << m_string->right(m_string->size() - m_originalSize); *this << endl; }
QString *m_string;
int m_originalSize;
};
}
#define dbgDetection() DetectionDebug(&g_surfaceFormatDetectionLog)
QSurfaceFormat KisOpenGL::selectSurfaceFormat(KisOpenGL::OpenGLRenderer preferredRenderer,
KisConfig::RootSurfaceFormat preferredRootSurfaceFormat,
bool enableDebug)
{
QVector<KLocalizedString> warningMessages;
using Info = boost::optional<KisOpenGLModeProber::Result>;
QVector<QSurfaceFormat::RenderableType> renderers({QSurfaceFormat::OpenGLES, QSurfaceFormat::OpenGL});
#ifdef HAVE_HDR
QVector<KisConfig::RootSurfaceFormat> formatSymbols({KisConfig::BT709_G22, KisConfig::BT709_G10, KisConfig::BT2020_PQ});
#else
QVector<KisConfig::RootSurfaceFormat> formatSymbols({KisConfig::BT709_G22});
#endif
QVector<QSurfaceFormat> preferredFormats;
Q_FOREACH (const QSurfaceFormat::RenderableType renderer, renderers) {
Q_FOREACH (const KisConfig::RootSurfaceFormat formatSymbol, formatSymbols) {
preferredFormats << generateSurfaceFormat(renderer, formatSymbol, enableDebug);
}
}
QSurfaceFormat defaultFormat = generateSurfaceFormat(QSurfaceFormat::DefaultRenderableType,
KisConfig::BT709_G22, false);
Info info = KisOpenGLModeProber::instance()->probeFormat(defaultFormat);
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(info, QSurfaceFormat());
FormatPositionLess compareOp;
compareOp.setPreferredRendererByQt(info->isOpenGLES() ? QSurfaceFormat::OpenGLES : QSurfaceFormat::OpenGL);
#ifdef HAVE_HDR
compareOp.setPreferredColorSpace(
preferredRootSurfaceFormat == KisConfig::BT709_G22 ? QSurfaceFormat::sRGBColorSpace :
preferredRootSurfaceFormat == KisConfig::BT709_G10 ? QSurfaceFormat::scRGBColorSpace :
QSurfaceFormat::bt2020PQColorSpace);
#else
Q_UNUSED(preferredRootSurfaceFormat);
compareOp.setPreferredColorSpace(QSurfaceFormat::sRGBColorSpace);
#endif
#ifdef Q_OS_WIN
compareOp.setPreferredRendererByHDR(QSurfaceFormat::OpenGLES);
#endif
compareOp.setPreferredRendererByUser(preferredRenderer == KisOpenGL::RendererDesktopGL ? QSurfaceFormat::OpenGL :
preferredRenderer == KisOpenGL::RendererOpenGLES ? QSurfaceFormat::OpenGLES :
QSurfaceFormat::DefaultRenderableType);
compareOp.setOpenGLESBlacklisted(false); // We cannot blacklist ES drivers atm
OpenGLRenderers supportedRenderers = RendererNone;
OpenGLRenderer preferredByQt = info->isOpenGLES() ? RendererOpenGLES : RendererDesktopGL;
if (!info->isOpenGLES()) {
compareOp.setOpenGLBlacklisted(isOpenGLRendererBlacklisted(info->rendererString(),
info->driverVersionString(),
&warningMessages));
supportedRenderers |= RendererDesktopGL;
info = KisOpenGLModeProber::instance()->
probeFormat(generateSurfaceFormat(QSurfaceFormat::OpenGLES,
KisConfig::BT709_G22, false));
if (info) {
supportedRenderers |= RendererOpenGLES;
}
} else {
supportedRenderers |= RendererOpenGLES;
info = KisOpenGLModeProber::instance()->
probeFormat(generateSurfaceFormat(QSurfaceFormat::OpenGL,
KisConfig::BT709_G22, false));
if (!info || info->isOpenGLES()) {
compareOp.setOpenGLBlacklisted(true);
} else {
compareOp.setOpenGLBlacklisted(isOpenGLRendererBlacklisted(info->rendererString(),
info->driverVersionString(),
&warningMessages));
supportedRenderers |= RendererDesktopGL;
}
}
std::stable_sort(preferredFormats.begin(), preferredFormats.end(), compareOp);
dbgDetection() << "Supported renderers:" << supportedRenderers;
dbgDetection() << "Surface format preference list:";
Q_FOREACH (const QSurfaceFormat &format, preferredFormats) {
dbgDetection() << "*" << format;
dbgDetection() << " " << format.renderableType();
}
QSurfaceFormat resultFormat = defaultFormat;
Q_FOREACH (const QSurfaceFormat &format, preferredFormats) {
dbgDetection() <<"Probing format..." << format.colorSpace() << format.renderableType();
Info info = KisOpenGLModeProber::instance()->probeFormat(format);
if (info && info->isSupportedVersion()) {
#ifdef Q_OS_WIN
// HACK: Block ANGLE with Direct3D9
// Direct3D9 does not give OpenGL ES 3.0
// Some versions of ANGLE returns OpenGL version 3.0 incorrectly
if (info->isUsingAngle() &&
info->rendererString().contains("Direct3D9", Qt::CaseInsensitive)) {
dbgDetection() << "Skipping Direct3D 9 Angle implementation, it shouldn't have happened.";
continue;
}
#endif
dbgDetection() << "Found format:" << format;
dbgDetection() << " " << format.renderableType();
resultFormat = format;
break;
}
}
{
const bool colorSpaceIsCorrect =
KisOpenGLModeProber::fuzzyCompareColorSpaces(compareOp.preferredColorSpace(),
resultFormat.colorSpace());
const bool rendererIsCorrect =
compareOp.preferredRendererByUser() == QSurfaceFormat::DefaultRenderableType ||
compareOp.preferredRendererByUser() == resultFormat.renderableType();
if (!rendererIsCorrect && colorSpaceIsCorrect) {
warningMessages << ki18n("Preferred renderer doesn't support requested surface format. Another renderer has been selected.");
} else if (!colorSpaceIsCorrect) {
warningMessages << ki18n("Preferred output format is not supported by available renderers");
}
}
overrideSupportedRenderers(supportedRenderers, preferredByQt);
overrideOpenGLWarningString(warningMessages);
return resultFormat;
}
void KisOpenGL::setDefaultSurfaceFormat(const QSurfaceFormat &format)
{
KIS_SAFE_ASSERT_RECOVER_NOOP(!g_sanityDefaultFormatIsSet);
g_sanityDefaultFormatIsSet = true;
QSurfaceFormat::setDefaultFormat(format);
}
bool KisOpenGL::hasOpenGL()
{
return openGLCheckResult->isSupportedVersion();
}
diff --git a/libs/ui/opengl/kis_opengl_image_textures.h b/libs/ui/opengl/kis_opengl_image_textures.h
index e4571b2911..c8d4e55d8b 100644
--- a/libs/ui/opengl/kis_opengl_image_textures.h
+++ b/libs/ui/opengl/kis_opengl_image_textures.h
@@ -1,203 +1,207 @@
/*
* Copyright (c) 2005-2007 Adrian Page <adrian@pagenet.plus.com>
*
* 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_OPENGL_IMAGE_TEXTURES_H_
#define KIS_OPENGL_IMAGE_TEXTURES_H_
#include <QVector>
#include <QMap>
#include <QOpenGLFunctions>
#include "kritaui_export.h"
#include "kis_shared.h"
#include "canvas/kis_update_info.h"
#include "opengl/kis_texture_tile.h"
#include "KisOpenGLUpdateInfoBuilder.h"
class KisOpenGLImageTextures;
typedef KisSharedPtr<KisOpenGLImageTextures> KisOpenGLImageTexturesSP;
class KoColorProfile;
class KisTextureTileUpdateInfoPoolCollection;
typedef QSharedPointer<KisTextureTileInfoPool> KisTextureTileInfoPoolSP;
class KisProofingConfiguration;
typedef QSharedPointer<KisProofingConfiguration> KisProofingConfigurationSP;
/**
* A set of OpenGL textures that contains the projection of a KisImage.
*/
class KRITAUI_EXPORT KisOpenGLImageTextures : public KisShared
{
public:
/**
* Obtain a KisOpenGLImageTextures object for the given image.
* @param image The image
* @param monitorProfile The profile of the display device
+ * @param renderingIntent The rendering intent
+ * @param conversionFlags The color conversion flags
*/
static KisOpenGLImageTexturesSP getImageTextures(KisImageWSP image,
const KoColorProfile *monitorProfile, KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags);
/**
* Default constructor.
*/
KisOpenGLImageTextures();
/**
* Destructor.
*/
virtual ~KisOpenGLImageTextures();
/**
* \return the image associated with the textures
*/
KisImageSP image() const;
/**
* Set the color profile of the display device.
- * @param profile The color profile of the display device
+ * @param monitorProfile The color profile of the display device
+ * @param renderingIntent The rendering intent
+ * @param conversionFlags The color conversion flags
*/
void setMonitorProfile(const KoColorProfile *monitorProfile,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags);
/**
* Update the textures when the color space of the image changes.
* @return true when a full data refetch should be initiated by the caller
*/
bool setImageColorSpace(const KoColorSpace *cs);
/**
* Complete initialization can only happen once an OpenGL context has been created.
- * @param f Pointer to OpenGL functions. They must already be ininitialized.
+ * @param f Pointer to OpenGL functions. They must already be initialized.
*/
void initGL(QOpenGLFunctions *f);
void setChannelFlags(const QBitArray &channelFlags);
void setProofingConfig(KisProofingConfigurationSP);
bool internalColorManagementActive() const;
bool setInternalColorManagementActive(bool value);
/**
* The background checkers texture.
*/
static const int BACKGROUND_TEXTURE_CHECK_SIZE = 32;
static const int BACKGROUND_TEXTURE_SIZE = BACKGROUND_TEXTURE_CHECK_SIZE * 2;
/**
* Generate a background texture from the given QImage. This is used for the checker
* pattern on which the image is rendered.
*/
void generateCheckerTexture(const QImage & checkImage);
GLuint checkerTexture();
void updateConfig(bool useBuffer, int NumMipmapLevels);
public:
inline QRect storedImageBounds() {
return m_storedImageBounds;
}
inline int xToCol(int x) {
return x / m_texturesInfo.effectiveWidth;
}
inline int yToRow(int y) {
return y / m_texturesInfo.effectiveHeight;
}
inline KisTextureTile* getTextureTileCR(int col, int row) {
if (m_initialized) {
int tile = row * m_numCols + col;
KIS_ASSERT_RECOVER_RETURN_VALUE(m_textureTiles.size() > tile, 0);
return m_textureTiles[tile];
}
return 0;
}
inline qreal texelSize() const {
Q_ASSERT(m_texturesInfo.width == m_texturesInfo.height);
return 1.0 / m_texturesInfo.width;
}
KisOpenGLUpdateInfoSP updateCache(const QRect& rect, KisImageSP srcImage);
KisOpenGLUpdateInfoSP updateCacheNoConversion(const QRect& rect);
void recalculateCache(KisUpdateInfoSP info, bool blockMipmapRegeneration);
void slotImageSizeChanged(qint32 w, qint32 h);
KisOpenGLUpdateInfoBuilder& updateInfoBuilder();
protected:
KisOpenGLImageTextures(KisImageWSP image, const KoColorProfile *monitorProfile,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags);
void recreateImageTextureTiles();
void destroyImageTextureTiles();
static bool imageCanShareTextures();
private:
void getTextureSize(KisGLTexturesInfo *texturesInfo);
void updateTextureFormat();
KisOpenGLUpdateInfoSP updateCacheImpl(const QRect& rect, KisImageSP srcImage, bool convertColorSpace);
private:
KisImageWSP m_image;
QRect m_storedImageBounds;
const KoColorProfile *m_monitorProfile;
KoColorConversionTransformation::Intent m_renderingIntent;
KoColorConversionTransformation::ConversionFlags m_conversionFlags;
/**
* Shows whether the internal color management should be enabled or not.
* Please note that if you disable color management, *but* your image color
* space will not be supported (non-RGB), then it will be enabled anyway.
* And this valiable will hold the real state of affairs!
*/
bool m_internalColorManagementActive;
GLuint m_checkerTexture;
KisGLTexturesInfo m_texturesInfo;
int m_numCols;
QVector<KisTextureTile*> m_textureTiles;
QOpenGLFunctions *m_glFuncs;
bool m_useOcio;
bool m_initialized;
KisOpenGLUpdateInfoBuilder m_updateInfoBuilder;
private:
typedef QMap<KisImageWSP, KisOpenGLImageTextures*> ImageTexturesMap;
static ImageTexturesMap imageTexturesMap;
};
#endif // KIS_OPENGL_IMAGE_TEXTURES_H_
diff --git a/libs/ui/opengl/kis_opengl_shader_loader.cpp b/libs/ui/opengl/kis_opengl_shader_loader.cpp
index 9073b464ba..60074732c4 100644
--- a/libs/ui/opengl/kis_opengl_shader_loader.cpp
+++ b/libs/ui/opengl/kis_opengl_shader_loader.cpp
@@ -1,212 +1,211 @@
/* This file is part of the KDE project
* Copyright (C) Julian Thijssen <julianthijssen@gmail.com>, (C) 2016
*
* 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_opengl_shader_loader.h"
#include "opengl/kis_opengl.h"
#include "kis_config.h"
#include <QFile>
#include <QMessageBox>
#include <KLocalizedString>
#define PROGRAM_VERTEX_ATTRIBUTE 0
#define PROGRAM_TEXCOORD_ATTRIBUTE 1
// Mapping of uniforms to uniform names
std::map<Uniform, const char *> KisShaderProgram::names = {
{ModelViewProjection, "modelViewProjection"},
{TextureMatrix, "textureMatrix"},
{ViewportScale, "viewportScale"},
{TexelSize, "texelSize"},
{Texture0, "texture0"},
{Texture1, "texture1"},
{FixedLodLevel, "fixedLodLevel"},
{FragmentColor, "fragColor"}
};
/**
* Generic shader loading function that will compile a shader program given
* a vertex shader and fragment shader resource path. Extra code can be prepended
* to each shader respectively using the header parameters.
*
* @param vertPath Resource path to a vertex shader
* @param fragPath Resource path to a fragment shader
* @param vertHeader Extra code which will be prepended to the vertex shader
* @param fragHeader Extra code which will be prepended to the fragment shader
*/
KisShaderProgram *KisOpenGLShaderLoader::loadShader(QString vertPath, QString fragPath,
QByteArray vertHeader, QByteArray fragHeader)
{
bool result;
KisShaderProgram *shader = new KisShaderProgram();
// Load vertex shader
QByteArray vertSource;
// XXX Check can be removed and set to the MAC version after we move to Qt5.7
#ifdef Q_OS_OSX
vertSource.append(KisOpenGL::hasOpenGL3() ? "#version 150 core\n" : "#version 120\n");
// OpenColorIO doesn't support the new GLSL version yet.
vertSource.append("#define texture2D texture\n");
vertSource.append("#define texture3D texture\n");
#else
if (KisOpenGL::hasOpenGLES()) {
vertSource.append("#version 300 es\n");
} else {
vertSource.append(KisOpenGL::supportsLoD() ? "#version 130\n" : "#version 120\n");
}
#endif
vertSource.append(vertHeader);
QFile vertexShaderFile(":/" + vertPath);
vertexShaderFile.open(QIODevice::ReadOnly);
vertSource.append(vertexShaderFile.readAll());
result = shader->addShaderFromSourceCode(QOpenGLShader::Vertex, vertSource);
if (!result)
throw ShaderLoaderException(QString("%1: %2 - Cause: %3").arg("Failed to add vertex shader source from file", vertPath, shader->log()));
// Load fragment shader
QByteArray fragSource;
// XXX Check can be removed and set to the MAC version after we move to Qt5.7
#ifdef Q_OS_OSX
fragSource.append(KisOpenGL::hasOpenGL3() ? "#version 150 core\n" : "#version 120\n");
// OpenColorIO doesn't support the new GLSL version yet.
fragSource.append("#define texture2D texture\n");
fragSource.append("#define texture3D texture\n");
#else
if (KisOpenGL::hasOpenGLES()) {
fragSource.append(
"#version 300 es\n"
"precision mediump float;\n"
"precision mediump sampler3D;\n");
// OpenColorIO doesn't support the new GLSL version yet.
fragSource.append("#define texture2D texture\n");
fragSource.append("#define texture3D texture\n");
-
} else {
fragSource.append(KisOpenGL::supportsLoD() ? "#version 130\n" : "#version 120\n");
}
#endif
fragSource.append(fragHeader);
QFile fragmentShaderFile(":/" + fragPath);
fragmentShaderFile.open(QIODevice::ReadOnly);
fragSource.append(fragmentShaderFile.readAll());
result = shader->addShaderFromSourceCode(QOpenGLShader::Fragment, fragSource);
if (!result)
throw ShaderLoaderException(QString("%1: %2 - Cause: %3").arg("Failed to add fragment shader source from file", fragPath, shader->log()));
// Bind attributes
shader->bindAttributeLocation("a_vertexPosition", PROGRAM_VERTEX_ATTRIBUTE);
shader->bindAttributeLocation("a_textureCoordinate", PROGRAM_TEXCOORD_ATTRIBUTE);
// Link
result = shader->link();
if (!result)
throw ShaderLoaderException(QString("Failed to link shader: ").append(vertPath));
Q_ASSERT(shader->isLinked());
return shader;
}
/**
* Specific display shader loading function. It adds the appropriate extra code
* to the fragment shader depending on what is available on the target machine.
* Additionally, it picks the appropriate shader files depending on the availability
* of OpenGL3.
*/
KisShaderProgram *KisOpenGLShaderLoader::loadDisplayShader(QSharedPointer<KisDisplayFilter> displayFilter, bool useHiQualityFiltering)
{
QByteArray fragHeader;
if (KisOpenGL::supportsLoD()) {
fragHeader.append("#define DIRECT_LOD_FETCH\n");
if (useHiQualityFiltering) {
fragHeader.append("#define HIGHQ_SCALING\n");
}
}
// If we have an OCIO display filter and it contains a function we add
// it to our shader header which will sit on top of the fragment code.
bool haveDisplayFilter = displayFilter && !displayFilter->program().isEmpty();
if (haveDisplayFilter) {
fragHeader.append("#define USE_OCIO\n");
fragHeader.append(displayFilter->program().toLatin1());
}
QString vertPath, fragPath;
// Select appropriate shader files
if (KisOpenGL::supportsLoD()) {
vertPath = "matrix_transform.vert";
fragPath = "highq_downscale.frag";
} else {
vertPath = "matrix_transform_legacy.vert";
fragPath = "simple_texture_legacy.frag";
}
KisShaderProgram *shader = loadShader(vertPath, fragPath, QByteArray(), fragHeader);
return shader;
}
/**
* Specific checker shader loading function. It picks the appropriate shader
* files depending on the availability of OpenGL3 on the target machine.
*/
KisShaderProgram *KisOpenGLShaderLoader::loadCheckerShader()
{
QString vertPath, fragPath;
// Select appropriate shader files
if (KisOpenGL::supportsLoD()) {
vertPath = "matrix_transform.vert";
fragPath = "simple_texture.frag";
} else {
vertPath = "matrix_transform_legacy.vert";
fragPath = "simple_texture_legacy.frag";
}
KisShaderProgram *shader = loadShader(vertPath, fragPath, QByteArray(), QByteArray());
return shader;
}
/**
* Specific uniform shader loading function. It picks the appropriate shader
* files depending on the availability of OpenGL3 on the target machine.
*/
KisShaderProgram *KisOpenGLShaderLoader::loadSolidColorShader()
{
QString vertPath, fragPath;
// Select appropriate shader files
if (KisOpenGL::supportsLoD()) {
vertPath = "matrix_transform.vert";
fragPath = "solid_color.frag";
} else {
vertPath = "matrix_transform_legacy.vert";
fragPath = "solid_color_legacy.frag";
}
KisShaderProgram *shader = loadShader(vertPath, fragPath, QByteArray(), QByteArray());
return shader;
}
diff --git a/libs/ui/qtlockedfile/qtlockedfile.cpp b/libs/ui/qtlockedfile/qtlockedfile.cpp
index cb666622f2..d3b7e85213 100644
--- a/libs/ui/qtlockedfile/qtlockedfile.cpp
+++ b/libs/ui/qtlockedfile/qtlockedfile.cpp
@@ -1,157 +1,157 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://www.qt.io/licensing. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "qtlockedfile.h"
/*!
\class QtLockedFile
\brief The QtLockedFile class extends QFile with advisory locking functions.
A file may be locked in read or write mode. Multiple instances of
\e QtLockedFile, created in multiple processes running on the same
machine, may have a file locked in read mode. Exactly one instance
may have it locked in write mode. A read and a write lock cannot
exist simultaneously on the same file.
The file locks are advisory. This means that nothing prevents
another process from manipulating a locked file using QFile or
file system functions offered by the OS. Serialization is only
guaranteed if all processes that access the file use
QtLockedFile. Also, while holding a lock on a file, a process
must not open the same file again (through any API), or locks
can be unexpectedly lost.
The lock provided by an instance of \e QtLockedFile is released
whenever the program terminates. This is true even when the
program crashes and no destructors are called.
*/
/*! \enum QtLockedFile::LockMode
This enum describes the available lock modes.
- \value ReadLock A read lock.
- \value WriteLock A write lock.
- \value NoLock Neither a read lock nor a write lock.
+ \var ReadLock A read lock.
+ \var WriteLock A write lock.
+ \var NoLock Neither a read lock nor a write lock.
*/
/*!
Constructs an unlocked \e QtLockedFile object. This constructor behaves in the same way
as \e QFile::QFile().
\sa QFile::QFile()
*/
QtLockedFile::QtLockedFile()
: QFile()
{
#ifdef Q_OS_WIN
m_semaphore_hnd = 0;
m_mutex_hnd = 0;
#endif
m_lock_mode = NoLock;
}
/*!
Constructs an unlocked QtLockedFile object with file \a name. This constructor behaves in
the same way as \e QFile::QFile(const QString&).
\sa QFile::QFile()
*/
QtLockedFile::QtLockedFile(const QString &name)
: QFile(name)
{
#ifdef Q_OS_WIN
m_semaphore_hnd = 0;
m_mutex_hnd = 0;
#endif
m_lock_mode = NoLock;
}
/*!
Returns \e true if this object has a in read or write lock;
otherwise returns \e false.
\sa lockMode()
*/
bool QtLockedFile::isLocked() const
{
return m_lock_mode != NoLock;
}
/*!
Returns the type of lock currently held by this object, or \e QtLockedFile::NoLock.
\sa isLocked()
*/
QtLockedFile::LockMode QtLockedFile::lockMode() const
{
return m_lock_mode;
}
/*!
\fn bool QtLockedFile::lock(LockMode mode, bool block = true)
Obtains a lock of type \a mode.
If \a block is true, this
function will block until the lock is acquired. If \a block is
false, this function returns \e false immediately if the lock cannot
be acquired.
If this object already has a lock of type \a mode, this function returns \e true immediately. If this object has a lock of a different type than \a mode, the lock
is first released and then a new lock is obtained.
This function returns \e true if, after it executes, the file is locked by this object,
and \e false otherwise.
\sa unlock(), isLocked(), lockMode()
*/
/*!
\fn bool QtLockedFile::unlock()
Releases a lock.
If the object has no lock, this function returns immediately.
This function returns \e true if, after it executes, the file is not locked by
this object, and \e false otherwise.
\sa lock(), isLocked(), lockMode()
*/
/*!
\fn QtLockedFile::~QtLockedFile()
Destroys the \e QtLockedFile object. If any locks were held, they are released.
*/
diff --git a/libs/ui/tests/kis_shape_selection_test.cpp b/libs/ui/tests/kis_shape_selection_test.cpp
index e4637deaff..880825468b 100644
--- a/libs/ui/tests/kis_shape_selection_test.cpp
+++ b/libs/ui/tests/kis_shape_selection_test.cpp
@@ -1,85 +1,85 @@
/*
* Copyright (c) 2008 Sven Langkamp <sven.langkamp@gmail.com>
*
* 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_shape_selection_test.h"
#include <QTest>
#include <kis_debug.h>
#include <QRect>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoPathShape.h>
#include "kis_selection.h"
#include "kis_pixel_selection.h"
#include "flake/kis_shape_selection.h"
#include "kis_image.h"
#include "testutil.h"
#include "kistest.h"
#include <KisPart.h>
#include <KisDocument.h>
void KisShapeSelectionTest::testAddChild()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
QScopedPointer<KisDocument> doc(KisPart::instance()->createDocument());
QColor qc(Qt::white);
qc.setAlpha(0);
KoColor bgColor(qc, cs);
- doc->newImage("test", 300, 300, cs, bgColor, true, 1, "test", 100);
+ doc->newImage("test", 300, 300, cs, bgColor, KisConfig::CANVAS_COLOR, 1, "test", 100);
KisImageSP image = doc->image();
KisSelectionSP selection = new KisSelection();
QVERIFY(selection->hasPixelSelection() == false);
QVERIFY(selection->hasShapeSelection() == false);
KisPixelSelectionSP pixelSelection = selection->pixelSelection();
pixelSelection->select(QRect(0, 0, 100, 100));
QCOMPARE(TestUtil::alphaDevicePixel(pixelSelection, 25, 25), MAX_SELECTED);
QCOMPARE(selection->selectedExactRect(), QRect(0, 0, 100, 100));
QRectF rect(50, 50, 100, 100);
QTransform matrix;
matrix.scale(1 / image->xRes(), 1 / image->yRes());
rect = matrix.mapRect(rect);
KoPathShape* shape = new KoPathShape();
shape->setShapeId(KoPathShapeId);
shape->moveTo(rect.topLeft());
shape->lineTo(rect.topRight());
shape->lineTo(rect.bottomRight());
shape->lineTo(rect.bottomLeft());
shape->close();
KisShapeSelection * shapeSelection = new KisShapeSelection(doc->shapeController(), image, selection);
selection->setShapeSelection(shapeSelection);
shapeSelection->addShape(shape);
QVERIFY(selection->hasShapeSelection());
selection->updateProjection();
image->waitForDone();
QCOMPARE(selection->selectedExactRect(), QRect(50, 50, 100, 100));
}
KISTEST_MAIN(KisShapeSelectionTest)
diff --git a/libs/ui/tool/KisSelectionToolFactoryBase.cpp b/libs/ui/tool/KisSelectionToolFactoryBase.cpp
index 2e33e31593..59eda09e53 100644
--- a/libs/ui/tool/KisSelectionToolFactoryBase.cpp
+++ b/libs/ui/tool/KisSelectionToolFactoryBase.cpp
@@ -1,64 +1,63 @@
/*
* Copyright (c) 2018 Boudewijn Rempt <boud@valdyas.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "KisSelectionToolFactoryBase.h"
#include <kis_action_registry.h>
KisSelectionToolFactoryBase::KisSelectionToolFactoryBase(const QString &id)
: KisToolPaintFactoryBase(id)
{
}
KisSelectionToolFactoryBase::~KisSelectionToolFactoryBase()
{
}
QList<QAction *> KisSelectionToolFactoryBase::createActionsImpl()
{
KisActionRegistry *actionRegistry = KisActionRegistry::instance();
QList<QAction *> actions = KisToolPaintFactoryBase::createActionsImpl();
actions << actionRegistry->makeQAction("selection_tool_mode_add");
actions << actionRegistry->makeQAction("selection_tool_mode_replace");
actions << actionRegistry->makeQAction("selection_tool_mode_subtract");
actions << actionRegistry->makeQAction("selection_tool_mode_intersect");
return actions;
}
KisToolPolyLineFactoryBase::KisToolPolyLineFactoryBase(const QString &id)
- : KisSelectionToolFactoryBase(id)
+ : KisToolPaintFactoryBase(id)
{
-
}
KisToolPolyLineFactoryBase::~KisToolPolyLineFactoryBase()
{
}
QList<QAction *> KisToolPolyLineFactoryBase::createActionsImpl()
{
KisActionRegistry *actionRegistry = KisActionRegistry::instance();
- QList<QAction *> actions = KisSelectionToolFactoryBase::createActionsImpl();
+ QList<QAction *> actions = KisToolPaintFactoryBase::createActionsImpl();
actions << actionRegistry->makeQAction("undo_polygon_selection");
actions << actionRegistry->makeQAction("selection_tool_mode_add");
return actions;
}
diff --git a/libs/ui/tool/KisSelectionToolFactoryBase.h b/libs/ui/tool/KisSelectionToolFactoryBase.h
index a2e73a0ff9..983f8c7b5a 100644
--- a/libs/ui/tool/KisSelectionToolFactoryBase.h
+++ b/libs/ui/tool/KisSelectionToolFactoryBase.h
@@ -1,44 +1,44 @@
/*
* Copyright (c) 2018 Boudewijn Rempt <boud@valdyas.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KISSELECTIONTOOLFACTORYBASE_H
#define KISSELECTIONTOOLFACTORYBASE_H
#include "KisToolPaintFactoryBase.h"
#include "kritaui_export.h"
class KRITAUI_EXPORT KisSelectionToolFactoryBase : public KisToolPaintFactoryBase
{
public:
explicit KisSelectionToolFactoryBase(const QString &id);
~KisSelectionToolFactoryBase() override;
protected:
QList<QAction *> createActionsImpl() override;
};
-class KRITAUI_EXPORT KisToolPolyLineFactoryBase : public KisSelectionToolFactoryBase
+class KRITAUI_EXPORT KisToolPolyLineFactoryBase : public KisToolPaintFactoryBase
{
public:
explicit KisToolPolyLineFactoryBase(const QString &id);
~KisToolPolyLineFactoryBase() override;
protected:
QList<QAction *> createActionsImpl() override;
};
#endif
diff --git a/libs/ui/tool/kis_tool.h b/libs/ui/tool/kis_tool.h
index df338dbdd3..cf9cb45b28 100644
--- a/libs/ui/tool/kis_tool.h
+++ b/libs/ui/tool/kis_tool.h
@@ -1,318 +1,319 @@
/*
* Copyright (c) 2006 Boudewijn Rempt <boud@valdyas.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_TOOL_H_
#define KIS_TOOL_H_
#include <QCursor>
#include <KoColor.h>
#include <KoToolBase.h>
#include <KoID.h>
#include <KoCanvasResourceProvider.h>
#include <kritaui_export.h>
#include <kis_types.h>
#ifdef __GNUC__
#define WARN_WRONG_MODE(_mode) warnKrita << "Unexpected tool event has come to" << __func__ << "while being mode" << _mode << "!"
#else
#define WARN_WRONG_MODE(_mode) warnKrita << "Unexpected tool event has come while being mode" << _mode << "!"
#endif
#define CHECK_MODE_SANITY_OR_RETURN(_mode) if (mode() != _mode) { WARN_WRONG_MODE(mode()); return; }
class KoCanvasBase;
class KoPattern;
class KoAbstractGradient;
class KisFilterConfiguration;
class QPainter;
class QPainterPath;
class QPolygonF;
/// Definitions of the toolgroups of Krita
static const QString TOOL_TYPE_SHAPE = "0 Krita/Shape"; // Geometric shapes like ellipses and lines
static const QString TOOL_TYPE_TRANSFORM = "2 Krita/Transform"; // Tools that transform the layer;
static const QString TOOL_TYPE_FILL = "3 Krita/Fill"; // Tools that fill parts of the canvas
static const QString TOOL_TYPE_VIEW = "4 Krita/View"; // Tools that affect the canvas: pan, zoom, etc.
static const QString TOOL_TYPE_SELECTION = "5 Krita/Select"; // Tools that select pixels
//activation id for Krita tools, Krita tools are always active and handle locked and invisible layers by themself
static const QString KRITA_TOOL_ACTIVATION_ID = "flake/always";
class KRITAUI_EXPORT KisTool
: public KoToolBase
{
Q_OBJECT
Q_PROPERTY(bool isActive READ isActive NOTIFY isActiveChanged)
public:
enum { FLAG_USES_CUSTOM_PRESET=0x01, FLAG_USES_CUSTOM_COMPOSITEOP=0x02, FLAG_USES_CUSTOM_SIZE=0x04 };
KisTool(KoCanvasBase * canvas, const QCursor & cursor);
~KisTool() override;
virtual int flags() const { return 0; }
void deleteSelection() override;
// KoToolBase Implementation.
public:
/**
* Called by KisToolProxy when the primary action of the tool is
* going to be started now, that is when all the modifiers are
* pressed and the only thing left is just to press the mouse
* button. On coming of this callback the tool is supposed to
* prepare the cursor and/or the outline to show the user shat is
* going to happen next
*/
virtual void activatePrimaryAction();
/**
* Called by KisToolProxy when the primary is no longer possible
* to be started now, e.g. when its modifiers and released. The
* tool is supposed revert all the preparetions it has doen in
* activatePrimaryAction().
*/
virtual void deactivatePrimaryAction();
/**
* Called by KisToolProxy when a primary action for the tool is
* started. The \p event stores the original event that
* started the stroke. The \p event is _accepted_ by default. If
* the tool decides to ignore this particular action (e.g. when
* the node is not editable), it should call event->ignore(). Then
* no further continuePrimaryAction() or endPrimaryAction() will
* be called until the next user action.
*/
virtual void beginPrimaryAction(KoPointerEvent *event);
/**
* Called by KisToolProxy when the primary action is in progress
* of pointer movement. If the tool has ignored the event in
* beginPrimaryAction(), this method will not be called.
*/
virtual void continuePrimaryAction(KoPointerEvent *event);
/**
* Called by KisToolProxy when the primary action is being
* finished, that is while mouseRelease or tabletRelease event.
* If the tool has ignored the event in beginPrimaryAction(), this
* method will not be called.
*/
virtual void endPrimaryAction(KoPointerEvent *event);
/**
* The same as beginPrimaryAction(), but called when the stroke is
* started by a double-click
*
* \see beginPrimaryAction()
*/
virtual void beginPrimaryDoubleClickAction(KoPointerEvent *event);
/**
* Returns true if the tool can handle (and wants to handle) a
* very tight flow of input events from the tablet
*/
virtual bool primaryActionSupportsHiResEvents() const;
enum ToolAction {
Primary,
AlternateChangeSize,
AlternatePickFgNode,
AlternatePickBgNode,
AlternatePickFgImage,
AlternatePickBgImage,
AlternateSecondary,
AlternateThird,
AlternateFourth,
AlternateFifth,
Alternate_NONE = 10000
};
// Technically users are allowed to configure this, but nobody ever would do that.
// So these can basically be thought of as aliases to ctrl+click, etc.
enum AlternateAction {
ChangeSize = AlternateChangeSize, // Default: Shift+Left click
PickFgNode = AlternatePickFgNode, // Default: Ctrl+Alt+Left click
PickBgNode = AlternatePickBgNode, // Default: Ctrl+Alt+Right click
PickFgImage = AlternatePickFgImage, // Default: Ctrl+Left click
PickBgImage = AlternatePickBgImage, // Default: Ctrl+Right click
Secondary = AlternateSecondary,
Third = AlternateThird,
Fourth = AlternateFourth,
Fifth = AlternateFifth,
NONE = 10000
};
static AlternateAction actionToAlternateAction(ToolAction action);
virtual void activateAlternateAction(AlternateAction action);
virtual void deactivateAlternateAction(AlternateAction action);
virtual void beginAlternateAction(KoPointerEvent *event, AlternateAction action);
virtual void continueAlternateAction(KoPointerEvent *event, AlternateAction action);
virtual void endAlternateAction(KoPointerEvent *event, AlternateAction action);
virtual void beginAlternateDoubleClickAction(KoPointerEvent *event, AlternateAction action);
void mousePressEvent(KoPointerEvent *event) override;
void mouseDoubleClickEvent(KoPointerEvent *event) override;
void mouseTripleClickEvent(KoPointerEvent *event) override;
void mouseReleaseEvent(KoPointerEvent *event) override;
void mouseMoveEvent(KoPointerEvent *event) override;
bool isActive() const;
public Q_SLOTS:
void activate(ToolActivation activation, const QSet<KoShape*> &shapes) override;
void deactivate() override;
void canvasResourceChanged(int key, const QVariant & res) override;
// Implement this slot in case there are any widgets or properties which need
// to be updated after certain operations, to reflect the inner state correctly.
// At the moment this is used for smoothing options in the freehand brush, but
// this will likely be expanded.
virtual void updateSettingsViews();
Q_SIGNALS:
void isActiveChanged(bool isActivated);
protected:
// conversion methods are also needed by the paint information builder
friend class KisToolPaintingInformationBuilder;
/// Convert from native (postscript points) to image pixel
/// coordinates.
QPointF convertToPixelCoord(KoPointerEvent *e);
QPointF convertToPixelCoord(const QPointF& pt);
QPointF convertToPixelCoordAndSnap(KoPointerEvent *e, const QPointF &offset = QPointF(), bool useModifiers = true);
QPointF convertToPixelCoordAndSnap(const QPointF& pt, const QPointF &offset = QPointF());
protected:
QPointF widgetCenterInWidgetPixels();
QPointF convertDocumentToWidget(const QPointF& pt);
/// Convert from native (postscript points) to integer image pixel
/// coordinates. This rounds down (not truncate) the pixel coordinates and
/// should be used in preference to QPointF::toPoint(), which rounds,
/// to ensure the cursor acts on the pixel it is visually over.
QPoint convertToImagePixelCoordFloored(KoPointerEvent *e);
QRectF convertToPt(const QRectF &rect);
qreal convertToPt(qreal value);
QPointF viewToPixel(const QPointF &viewCoord) const;
/// Convert an integer pixel coordinate into a view coordinate.
/// The view coordinate is at the centre of the pixel.
QPointF pixelToView(const QPoint &pixelCoord) const;
/// Convert a floating point pixel coordinate into a view coordinate.
QPointF pixelToView(const QPointF &pixelCoord) const;
/// Convert a pixel rectangle into a view rectangle.
QRectF pixelToView(const QRectF &pixelRect) const;
/// Convert a pixel path into a view path
QPainterPath pixelToView(const QPainterPath &pixelPath) const;
/// Convert a pixel polygon into a view path
QPolygonF pixelToView(const QPolygonF &pixelPolygon) const;
/// Update the canvas for the given rectangle in image pixel coordinates.
void updateCanvasPixelRect(const QRectF &pixelRect);
/// Update the canvas for the given rectangle in view coordinates.
void updateCanvasViewRect(const QRectF &viewRect);
QWidget* createOptionWidget() override;
/**
* To determine whether this tool will change its behavior when
* modifier keys are pressed
*/
virtual bool listeningToModifiers();
/**
* Request that this tool no longer listen to modifier keys
* (Responding to the request is optional)
*/
virtual void listenToModifiers(bool listen);
protected:
KisImageWSP image() const;
QCursor cursor() const;
/// Call this to set the document modified
void notifyModified() const;
KisImageWSP currentImage();
KoPattern* currentPattern();
KoAbstractGradient *currentGradient();
KisNodeSP currentNode() const;
KisNodeList selectedNodes() const;
KoColor currentFgColor();
KoColor currentBgColor();
KisPaintOpPresetSP currentPaintOpPreset();
KisFilterConfigurationSP currentGenerator();
/// paint the path which is in view coordinates, default paint mode is XOR_MODE, BW_MODE is also possible
/// never apply transformations to the painter, they would be useless, if drawing in OpenGL mode. The coordinates in the path should be in view coordinates.
void paintToolOutline(QPainter * painter, const QPainterPath &path);
/// Checks checks if the current node is editable
bool nodeEditable();
/// Checks checks if the selection is editable, only applies to local selection as global selection is always editable
bool selectionEditable();
/// Override the cursor appropriately if current node is not editable
bool overrideCursorIfNotEditable();
bool blockUntilOperationsFinished();
void blockUntilOperationsFinishedForced();
protected:
- enum ToolMode {
+ enum ToolMode: int {
HOVER_MODE,
PAINT_MODE,
SECONDARY_PAINT_MODE,
MIRROR_AXIS_SETUP_MODE,
GESTURE_MODE,
PAN_MODE,
- OTHER // not used now
+ OTHER, // tool-specific modes, like multibrush's symmetry axis setup
+ OTHER_1
};
virtual void setMode(ToolMode mode);
virtual ToolMode mode() const;
void setCursor(const QCursor &cursor);
protected Q_SLOTS:
/**
* Called whenever the configuration settings change.
*/
virtual void resetCursorStyle();
private:
struct Private;
Private* const d;
};
#endif // KIS_TOOL_H_
diff --git a/libs/ui/tool/kis_tool_freehand_helper.h b/libs/ui/tool/kis_tool_freehand_helper.h
index da4f912f37..39c3c56b9d 100644
--- a/libs/ui/tool/kis_tool_freehand_helper.h
+++ b/libs/ui/tool/kis_tool_freehand_helper.h
@@ -1,159 +1,166 @@
/*
* Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_TOOL_FREEHAND_HELPER_H
#define __KIS_TOOL_FREEHAND_HELPER_H
#include <QObject>
#include <QVector>
#include "kis_types.h"
#include "kritaui_export.h"
#include <brushengine/kis_paint_information.h>
#include "kis_default_bounds.h"
#include <brushengine/kis_paintop_settings.h>
#include "kis_smoothing_options.h"
#include "kundo2magicstring.h"
class KoPointerEvent;
class KoCanvasResourceProvider;
class KisPaintingInformationBuilder;
class KisStrokesFacade;
class KisPostExecutionUndoAdapter;
class KisPaintOp;
class KisFreehandStrokeInfo;
class KRITAUI_EXPORT KisToolFreehandHelper : public QObject
{
Q_OBJECT
public:
KisToolFreehandHelper(KisPaintingInformationBuilder *infoBuilder,
const KUndo2MagicString &transactionText = KUndo2MagicString(),
KisSmoothingOptions *smoothingOptions = 0);
~KisToolFreehandHelper() override;
void setSmoothness(KisSmoothingOptionsSP smoothingOptions);
KisSmoothingOptionsSP smoothingOptions() const;
bool isRunning() const;
void cursorMoved(const QPointF &cursorPos);
/**
- * @param pixelCoords - The position of the KoPointerEvent, in pixel coordinates.
+ * @param event The event
+ * @param pixelCoords The position of the KoPointerEvent, in pixel coordinates.
+ * @param resourceManager The canvas resource manager
+ * @param image The image
+ * @param currentNode The current node
+ * @param strokesFacade The strokes facade
+ * @param overrideNode The override node
+ * @param bounds The bounds
*/
void initPaint(KoPointerEvent *event,
const QPointF &pixelCoords,
KoCanvasResourceProvider *resourceManager,
KisImageWSP image,
KisNodeSP currentNode,
KisStrokesFacade *strokesFacade,
KisNodeSP overrideNode = 0,
KisDefaultBoundsBaseSP bounds = 0);
void paintEvent(KoPointerEvent *event);
void endPaint();
QPainterPath paintOpOutline(const QPointF &savedCursorPos,
const KoPointerEvent *event,
const KisPaintOpSettingsSP globalSettings,
KisPaintOpSettings::OutlineMode mode) const;
int canvasRotation();
void setCanvasRotation(int rotation = 0);
bool canvasMirroredH();
void setCanvasHorizontalMirrorState (bool mirrored = false);
Q_SIGNALS:
/**
* The signal is emitted when the outline should be updated
* explicitly by the tool. Used by Stabilizer option, because it
* paints on internal timer events instead of the on every paint()
* event
*/
void requestExplicitUpdateOutline();
protected:
void cancelPaint();
int elapsedStrokeTime() const;
void initPaintImpl(qreal startAngle,
const KisPaintInformation &pi,
KoCanvasResourceProvider *resourceManager,
KisImageWSP image,
KisNodeSP node,
KisStrokesFacade *strokesFacade,
KisNodeSP overrideNode = 0,
KisDefaultBoundsBaseSP bounds = 0);
protected:
virtual void createPainters(QVector<KisFreehandStrokeInfo*> &strokeInfos,
const KisDistanceInformation &startDist);
// lo-level methods for painting primitives
void paintAt(int strokeInfoId, const KisPaintInformation &pi);
void paintLine(int strokeInfoId,
const KisPaintInformation &pi1,
const KisPaintInformation &pi2);
void paintBezierCurve(int strokeInfoId,
const KisPaintInformation &pi1,
const QPointF &control1,
const QPointF &control2,
const KisPaintInformation &pi2);
// hi-level methods for painting primitives
virtual void paintAt(const KisPaintInformation &pi);
virtual void paintLine(const KisPaintInformation &pi1,
const KisPaintInformation &pi2);
virtual void paintBezierCurve(const KisPaintInformation &pi1,
const QPointF &control1,
const QPointF &control2,
const KisPaintInformation &pi2);
private:
void paint(KisPaintInformation &info);
void paintBezierSegment(KisPaintInformation pi1, KisPaintInformation pi2,
QPointF tangent1, QPointF tangent2);
void stabilizerStart(KisPaintInformation firstPaintInfo);
void stabilizerEnd();
KisPaintInformation getStabilizedPaintInfo(const QQueue<KisPaintInformation> &queue,
const KisPaintInformation &lastPaintInfo);
int computeAirbrushTimerInterval() const;
private Q_SLOTS:
void finishStroke();
void doAirbrushing();
void doAsynchronousUpdate(bool forceUpdate = false);
void stabilizerPollAndPaint();
void slotSmoothingTypeChanged();
private:
struct Private;
Private * const m_d;
};
#endif /* __KIS_TOOL_FREEHAND_HELPER_H */
diff --git a/libs/ui/tool/kis_tool_paint.cc b/libs/ui/tool/kis_tool_paint.cc
index 9a3e72b9b1..c8cc16d39c 100644
--- a/libs/ui/tool/kis_tool_paint.cc
+++ b/libs/ui/tool/kis_tool_paint.cc
@@ -1,782 +1,782 @@
/*
* Copyright (c) 2003-2009 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2015 Moritz Molch <kde@moritzmolch.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_tool_paint.h"
#include <algorithm>
#include <QWidget>
#include <QRect>
#include <QLayout>
#include <QLabel>
#include <QPushButton>
#include <QWhatsThis>
#include <QCheckBox>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QKeyEvent>
#include <QEvent>
#include <QVariant>
#include <QAction>
#include <kis_debug.h>
#include <QPoint>
#include <klocalizedstring.h>
#include <kactioncollection.h>
#include <kis_icon.h>
#include <KoShape.h>
#include <KoCanvasResourceProvider.h>
#include <KoColorSpace.h>
#include <KoPointerEvent.h>
#include <KoColor.h>
#include <KoCanvasBase.h>
#include <KoCanvasController.h>
#include <kis_types.h>
#include <kis_global.h>
#include <kis_image.h>
#include <kis_paint_device.h>
#include <kis_layer.h>
#include <KisViewManager.h>
#include <kis_canvas2.h>
#include <kis_cubic_curve.h>
#include "kis_display_color_converter.h"
#include <KisDocument.h>
#include <KisReferenceImagesLayer.h>
#include "kis_config.h"
#include "kis_config_notifier.h"
#include "kis_cursor.h"
#include "widgets/kis_cmb_composite.h"
#include "widgets/kis_slider_spin_box.h"
#include "kis_canvas_resource_provider.h"
#include "kis_tool_utils.h"
#include <brushengine/kis_paintop.h>
#include <brushengine/kis_paintop_preset.h>
#include <kis_action_manager.h>
#include <kis_action.h>
#include "strokes/kis_color_picker_stroke_strategy.h"
KisToolPaint::KisToolPaint(KoCanvasBase *canvas, const QCursor &cursor)
: KisTool(canvas, cursor),
m_showColorPreview(false),
m_colorPreviewShowComparePlate(false),
m_colorPickerDelayTimer(),
m_isOutlineEnabled(true)
{
m_specialHoverModifier = false;
m_optionsWidgetLayout = 0;
m_opacity = OPACITY_OPAQUE_U8;
m_supportOutline = false;
{
int maxSize = KisConfig(true).readEntry("maximumBrushSize", 1000);
int brushSize = 1;
do {
m_standardBrushSizes.push_back(brushSize);
int increment = qMax(1, int(std::ceil(qreal(brushSize) / 15)));
brushSize += increment;
} while (brushSize < maxSize);
m_standardBrushSizes.push_back(maxSize);
}
KisCanvas2 *kiscanvas = dynamic_cast<KisCanvas2*>(canvas);
connect(this, SIGNAL(sigPaintingFinished()), kiscanvas->viewManager()->resourceProvider(), SLOT(slotPainting()));
m_colorPickerDelayTimer.setSingleShot(true);
connect(&m_colorPickerDelayTimer, SIGNAL(timeout()), this, SLOT(activatePickColorDelayed()));
using namespace std::placeholders; // For _1 placeholder
std::function<void(PickingJob)> callback =
std::bind(&KisToolPaint::addPickerJob, this, _1);
m_colorPickingCompressor.reset(
new PickingCompressor(100, callback, KisSignalCompressor::FIRST_ACTIVE));
}
KisToolPaint::~KisToolPaint()
{
}
int KisToolPaint::flags() const
{
return KisTool::FLAG_USES_CUSTOM_COMPOSITEOP;
}
void KisToolPaint::canvasResourceChanged(int key, const QVariant& v)
{
KisTool::canvasResourceChanged(key, v);
switch(key) {
case(KisCanvasResourceProvider::Opacity):
setOpacity(v.toDouble());
break;
default: //nothing
break;
}
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(resetCursorStyle()), Qt::UniqueConnection);
}
void KisToolPaint::activate(ToolActivation toolActivation, const QSet<KoShape*> &shapes)
{
if (currentPaintOpPreset()) {
QString formattedBrushName = currentPaintOpPreset()->name().replace("_", " ");
emit statusTextChanged(formattedBrushName);
}
KisTool::activate(toolActivation, shapes);
if (flags() & KisTool::FLAG_USES_CUSTOM_SIZE) {
connect(action("increase_brush_size"), SIGNAL(triggered()), SLOT(increaseBrushSize()), Qt::UniqueConnection);
connect(action("decrease_brush_size"), SIGNAL(triggered()), SLOT(decreaseBrushSize()), Qt::UniqueConnection);
}
KisCanvasResourceProvider *provider = qobject_cast<KisCanvas2*>(canvas())->viewManager()->resourceProvider();
m_oldOpacity = provider->opacity();
provider->setOpacity(m_localOpacity);
}
void KisToolPaint::deactivate()
{
if (flags() & KisTool::FLAG_USES_CUSTOM_SIZE) {
disconnect(action("increase_brush_size"), 0, this, 0);
disconnect(action("decrease_brush_size"), 0, this, 0);
}
KisCanvasResourceProvider *provider = qobject_cast<KisCanvas2*>(canvas())->viewManager()->resourceProvider();
m_localOpacity = provider->opacity();
provider->setOpacity(m_oldOpacity);
KisTool::deactivate();
}
QPainterPath KisToolPaint::tryFixBrushOutline(const QPainterPath &originalOutline)
{
KisConfig cfg(true);
if (cfg.newOutlineStyle() == OUTLINE_NONE) return originalOutline;
const qreal minThresholdSize = cfg.outlineSizeMinimum();
/**
* If the brush outline is bigger than the canvas itself (which
* would make it invisible for a user in most of the cases) just
* add a cross in the center of it
*/
QSize widgetSize = canvas()->canvasWidget()->size();
const int maxThresholdSum = widgetSize.width() + widgetSize.height();
QPainterPath outline = originalOutline;
QRectF boundingRect = outline.boundingRect();
const qreal sum = boundingRect.width() + boundingRect.height();
QPointF center = boundingRect.center();
if (sum > maxThresholdSum) {
const int hairOffset = 7;
outline.moveTo(center.x(), center.y() - hairOffset);
outline.lineTo(center.x(), center.y() + hairOffset);
outline.moveTo(center.x() - hairOffset, center.y());
outline.lineTo(center.x() + hairOffset, center.y());
} else if (sum < minThresholdSize && !outline.isEmpty()) {
outline = QPainterPath();
outline.addEllipse(center, 0.5 * minThresholdSize, 0.5 * minThresholdSize);
}
return outline;
}
void KisToolPaint::paint(QPainter &gc, const KoViewConverter &converter)
{
Q_UNUSED(converter);
QPainterPath path = tryFixBrushOutline(pixelToView(m_currentOutline));
paintToolOutline(&gc, path);
if (m_showColorPreview) {
QRectF viewRect = converter.documentToView(m_oldColorPreviewRect);
gc.fillRect(viewRect, m_colorPreviewCurrentColor);
if (m_colorPreviewShowComparePlate) {
QRectF baseColorRect = viewRect.translated(viewRect.width(), 0);
gc.fillRect(baseColorRect, m_colorPreviewBaseColor);
}
}
}
void KisToolPaint::setMode(ToolMode mode)
{
if(this->mode() == KisTool::PAINT_MODE &&
mode != KisTool::PAINT_MODE) {
// Let's add history information about recently used colors
emit sigPaintingFinished();
}
KisTool::setMode(mode);
}
void KisToolPaint::activatePickColor(AlternateAction action)
{
m_showColorPreview = true;
requestUpdateOutline(m_outlineDocPoint, 0);
int resource = colorPreviewResourceId(action);
KoColor color = canvas()->resourceManager()->koColorResource(resource);
KisCanvas2 * kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
KIS_ASSERT_RECOVER_RETURN(kisCanvas);
m_colorPreviewCurrentColor = kisCanvas->displayColorConverter()->toQColor(color);
if (!m_colorPreviewBaseColor.isValid()) {
m_colorPreviewBaseColor = m_colorPreviewCurrentColor;
}
}
void KisToolPaint::deactivatePickColor(AlternateAction action)
{
Q_UNUSED(action);
m_showColorPreview = false;
m_oldColorPreviewRect = QRect();
m_oldColorPreviewUpdateRect = QRect();
m_colorPreviewCurrentColor = QColor();
}
void KisToolPaint::pickColorWasOverridden()
{
m_colorPreviewShowComparePlate = false;
m_colorPreviewBaseColor = QColor();
}
void KisToolPaint::activateAlternateAction(AlternateAction action)
{
switch (action) {
case PickFgNode:
- /* Falls through */
+ Q_FALLTHROUGH();
case PickBgNode:
- /* Falls through */
+ Q_FALLTHROUGH();
case PickFgImage:
- /* Falls through */
+ Q_FALLTHROUGH();
case PickBgImage:
delayedAction = action;
m_colorPickerDelayTimer.start(100);
- /* Falls through */
+ Q_FALLTHROUGH();
default:
pickColorWasOverridden();
KisTool::activateAlternateAction(action);
};
}
void KisToolPaint::activatePickColorDelayed()
{
switch (delayedAction) {
case PickFgNode:
useCursor(KisCursor::pickerLayerForegroundCursor());
activatePickColor(delayedAction);
break;
case PickBgNode:
useCursor(KisCursor::pickerLayerBackgroundCursor());
activatePickColor(delayedAction);
break;
case PickFgImage:
useCursor(KisCursor::pickerImageForegroundCursor());
activatePickColor(delayedAction);
break;
case PickBgImage:
useCursor(KisCursor::pickerImageBackgroundCursor());
activatePickColor(delayedAction);
break;
default:
break;
};
repaintDecorations();
}
bool KisToolPaint::isPickingAction(AlternateAction action) {
return action == PickFgNode ||
action == PickBgNode ||
action == PickFgImage ||
action == PickBgImage;
}
void KisToolPaint::deactivateAlternateAction(AlternateAction action)
{
if (!isPickingAction(action)) {
KisTool::deactivateAlternateAction(action);
return;
}
delayedAction = KisTool::NONE;
m_colorPickerDelayTimer.stop();
resetCursorStyle();
deactivatePickColor(action);
}
void KisToolPaint::addPickerJob(const PickingJob &pickingJob)
{
/**
* The actual picking is delayed by a compressor, so we can get this
* event when the stroke is already closed
*/
if (!m_pickerStrokeId) return;
KIS_ASSERT_RECOVER_RETURN(isPickingAction(pickingJob.action));
const QPoint imagePoint = image()->documentToImagePixelFloored(pickingJob.documentPixel);
const bool fromCurrentNode = pickingJob.action == PickFgNode || pickingJob.action == PickBgNode;
m_pickingResource = colorPreviewResourceId(pickingJob.action);
if (!fromCurrentNode) {
auto *kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
KIS_SAFE_ASSERT_RECOVER_RETURN(kisCanvas);
KisSharedPtr<KisReferenceImagesLayer> referencesLayer = kisCanvas->imageView()->document()->referenceImagesLayer();
if (referencesLayer && kisCanvas->referenceImagesDecoration()->visible()) {
QColor color = referencesLayer->getPixel(imagePoint);
if (color.isValid() && color.alpha() != 0) {
slotColorPickingFinished(KoColor(color, image()->colorSpace()));
return;
}
}
}
KisPaintDeviceSP device = fromCurrentNode ?
currentNode()->colorPickSourceDevice() : image()->projection();
// Used for color picker blending.
KoColor currentColor = canvas()->resourceManager()->foregroundColor();
if( pickingJob.action == PickBgNode || pickingJob.action == PickBgImage ){
currentColor = canvas()->resourceManager()->backgroundColor();
}
image()->addJob(m_pickerStrokeId,
new KisColorPickerStrokeStrategy::Data(device, imagePoint, currentColor));
}
void KisToolPaint::beginAlternateAction(KoPointerEvent *event, AlternateAction action)
{
if (isPickingAction(action)) {
KIS_ASSERT_RECOVER_RETURN(!m_pickerStrokeId);
setMode(SECONDARY_PAINT_MODE);
KisColorPickerStrokeStrategy *strategy = new KisColorPickerStrokeStrategy();
connect(strategy, &KisColorPickerStrokeStrategy::sigColorUpdated,
this, &KisToolPaint::slotColorPickingFinished);
m_pickerStrokeId = image()->startStroke(strategy);
m_colorPickingCompressor->start(PickingJob(event->point, action));
requestUpdateOutline(event->point, event);
} else {
KisTool::beginAlternateAction(event, action);
}
}
void KisToolPaint::continueAlternateAction(KoPointerEvent *event, AlternateAction action)
{
if (isPickingAction(action)) {
KIS_ASSERT_RECOVER_RETURN(m_pickerStrokeId);
m_colorPickingCompressor->start(PickingJob(event->point, action));
requestUpdateOutline(event->point, event);
} else {
KisTool::continueAlternateAction(event, action);
}
}
void KisToolPaint::endAlternateAction(KoPointerEvent *event, AlternateAction action)
{
if (isPickingAction(action)) {
KIS_ASSERT_RECOVER_RETURN(m_pickerStrokeId);
image()->endStroke(m_pickerStrokeId);
m_pickerStrokeId.clear();
requestUpdateOutline(event->point, event);
setMode(HOVER_MODE);
} else {
KisTool::endAlternateAction(event, action);
}
}
int KisToolPaint::colorPreviewResourceId(AlternateAction action)
{
bool toForegroundColor = action == PickFgNode || action == PickFgImage;
int resource = toForegroundColor ?
KoCanvasResourceProvider::ForegroundColor : KoCanvasResourceProvider::BackgroundColor;
return resource;
}
void KisToolPaint::slotColorPickingFinished(const KoColor &color)
{
canvas()->resourceManager()->setResource(m_pickingResource, color);
if (!m_showColorPreview) return;
KisCanvas2 * kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
KIS_ASSERT_RECOVER_RETURN(kisCanvas);
QColor previewColor = kisCanvas->displayColorConverter()->toQColor(color);
m_colorPreviewShowComparePlate = true;
m_colorPreviewCurrentColor = previewColor;
requestUpdateOutline(m_outlineDocPoint, 0);
}
void KisToolPaint::mousePressEvent(KoPointerEvent *event)
{
KisTool::mousePressEvent(event);
if (mode() == KisTool::HOVER_MODE) {
requestUpdateOutline(event->point, event);
}
}
void KisToolPaint::mouseMoveEvent(KoPointerEvent *event)
{
KisTool::mouseMoveEvent(event);
if (mode() == KisTool::HOVER_MODE) {
requestUpdateOutline(event->point, event);
}
}
void KisToolPaint::mouseReleaseEvent(KoPointerEvent *event)
{
KisTool::mouseReleaseEvent(event);
if (mode() == KisTool::HOVER_MODE) {
requestUpdateOutline(event->point, event);
}
}
QWidget * KisToolPaint::createOptionWidget()
{
QWidget *optionWidget = new QWidget();
optionWidget->setObjectName(toolId());
QVBoxLayout *verticalLayout = new QVBoxLayout(optionWidget);
verticalLayout->setObjectName("KisToolPaint::OptionWidget::VerticalLayout");
verticalLayout->setContentsMargins(0,0,0,0);
verticalLayout->setSpacing(5);
// See https://bugs.kde.org/show_bug.cgi?id=316896
QWidget *specialSpacer = new QWidget(optionWidget);
specialSpacer->setObjectName("SpecialSpacer");
specialSpacer->setFixedSize(0, 0);
verticalLayout->addWidget(specialSpacer);
verticalLayout->addWidget(specialSpacer);
m_optionsWidgetLayout = new QGridLayout();
m_optionsWidgetLayout->setColumnStretch(1, 1);
verticalLayout->addLayout(m_optionsWidgetLayout);
m_optionsWidgetLayout->setContentsMargins(0,0,0,0);
m_optionsWidgetLayout->setSpacing(5);
if (!quickHelp().isEmpty()) {
QPushButton *push = new QPushButton(KisIconUtils::loadIcon("help-contents"), QString(), optionWidget);
connect(push, SIGNAL(clicked()), this, SLOT(slotPopupQuickHelp()));
QHBoxLayout *hLayout = new QHBoxLayout();
hLayout->addWidget(push);
hLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed));
verticalLayout->addLayout(hLayout);
}
return optionWidget;
}
QWidget* findLabelWidget(QGridLayout *layout, QWidget *control)
{
QWidget *result = 0;
int index = layout->indexOf(control);
int row, col, rowSpan, colSpan;
layout->getItemPosition(index, &row, &col, &rowSpan, &colSpan);
if (col > 0) {
QLayoutItem *item = layout->itemAtPosition(row, col - 1);
if (item) {
result = item->widget();
}
} else {
QLayoutItem *item = layout->itemAtPosition(row, col + 1);
if (item) {
result = item->widget();
}
}
return result;
}
void KisToolPaint::showControl(QWidget *control, bool value)
{
control->setVisible(value);
QWidget *label = findLabelWidget(m_optionsWidgetLayout, control);
if (label) {
label->setVisible(value);
}
}
void KisToolPaint::enableControl(QWidget *control, bool value)
{
control->setEnabled(value);
QWidget *label = findLabelWidget(m_optionsWidgetLayout, control);
if (label) {
label->setEnabled(value);
}
}
void KisToolPaint::addOptionWidgetLayout(QLayout *layout)
{
Q_ASSERT(m_optionsWidgetLayout != 0);
int rowCount = m_optionsWidgetLayout->rowCount();
m_optionsWidgetLayout->addLayout(layout, rowCount, 0, 1, 2);
}
void KisToolPaint::addOptionWidgetOption(QWidget *control, QWidget *label)
{
Q_ASSERT(m_optionsWidgetLayout != 0);
if (label) {
m_optionsWidgetLayout->addWidget(label, m_optionsWidgetLayout->rowCount(), 0);
m_optionsWidgetLayout->addWidget(control, m_optionsWidgetLayout->rowCount() - 1, 1);
}
else {
m_optionsWidgetLayout->addWidget(control, m_optionsWidgetLayout->rowCount(), 0, 1, 2);
}
}
void KisToolPaint::setOpacity(qreal opacity)
{
m_opacity = quint8(opacity * OPACITY_OPAQUE_U8);
}
const KoCompositeOp* KisToolPaint::compositeOp()
{
if (currentNode()) {
KisPaintDeviceSP device = currentNode()->paintDevice();
if (device) {
QString op = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentCompositeOp).toString();
return device->colorSpace()->compositeOp(op);
}
}
return 0;
}
void KisToolPaint::slotPopupQuickHelp()
{
QWhatsThis::showText(QCursor::pos(), quickHelp());
}
KisToolPaint::NodePaintAbility KisToolPaint::nodePaintAbility()
{
KisNodeSP node = currentNode();
if (!node) {
return NONE;
}
if (node->inherits("KisShapeLayer")) {
return VECTOR;
}
if (node->inherits("KisCloneLayer")) {
return CLONE;
}
if (node->paintDevice()) {
return PAINT;
}
return NONE;
}
void KisToolPaint::activatePrimaryAction()
{
pickColorWasOverridden();
setOutlineEnabled(true);
KisTool::activatePrimaryAction();
}
void KisToolPaint::deactivatePrimaryAction()
{
setOutlineEnabled(false);
KisTool::deactivatePrimaryAction();
}
bool KisToolPaint::isOutlineEnabled() const
{
return m_isOutlineEnabled;
}
void KisToolPaint::setOutlineEnabled(bool value)
{
m_isOutlineEnabled = value;
requestUpdateOutline(m_outlineDocPoint, 0);
}
void KisToolPaint::increaseBrushSize()
{
qreal paintopSize = currentPaintOpPreset()->settings()->paintOpSize();
std::vector<int>::iterator result =
std::upper_bound(m_standardBrushSizes.begin(),
m_standardBrushSizes.end(),
qRound(paintopSize));
int newValue = result != m_standardBrushSizes.end() ? *result : m_standardBrushSizes.back();
currentPaintOpPreset()->settings()->setPaintOpSize(newValue);
requestUpdateOutline(m_outlineDocPoint, 0);
}
void KisToolPaint::decreaseBrushSize()
{
qreal paintopSize = currentPaintOpPreset()->settings()->paintOpSize();
std::vector<int>::reverse_iterator result =
std::upper_bound(m_standardBrushSizes.rbegin(),
m_standardBrushSizes.rend(),
(int)paintopSize,
std::greater<int>());
int newValue = result != m_standardBrushSizes.rend() ? *result : m_standardBrushSizes.front();
currentPaintOpPreset()->settings()->setPaintOpSize(newValue);
requestUpdateOutline(m_outlineDocPoint, 0);
}
QRectF KisToolPaint::colorPreviewDocRect(const QPointF &outlineDocPoint)
{
if (!m_showColorPreview) return QRect();
KisConfig cfg(true);
const QRectF colorPreviewViewRect = cfg.colorPreviewRect();
const QRectF colorPreviewDocumentRect = canvas()->viewConverter()->viewToDocument(colorPreviewViewRect);
return colorPreviewDocumentRect.translated(outlineDocPoint);
}
void KisToolPaint::requestUpdateOutline(const QPointF &outlineDocPoint, const KoPointerEvent *event)
{
if (!m_supportOutline) return;
KisConfig cfg(true);
KisPaintOpSettings::OutlineMode outlineMode;
if (isOutlineEnabled() &&
(mode() == KisTool::GESTURE_MODE ||
((cfg.newOutlineStyle() == OUTLINE_FULL ||
cfg.newOutlineStyle() == OUTLINE_CIRCLE ||
cfg.newOutlineStyle() == OUTLINE_TILT) &&
((mode() == HOVER_MODE) ||
(mode() == PAINT_MODE && cfg.showOutlineWhilePainting()))))) { // lisp forever!
outlineMode.isVisible = true;
if (cfg.newOutlineStyle() == OUTLINE_CIRCLE) {
outlineMode.forceCircle = true;
} else if(cfg.newOutlineStyle() == OUTLINE_TILT) {
outlineMode.forceCircle = true;
outlineMode.showTiltDecoration = true;
} else {
// noop
}
}
outlineMode.forceFullSize = cfg.forceAlwaysFullSizedOutline();
m_outlineDocPoint = outlineDocPoint;
m_currentOutline = getOutlinePath(m_outlineDocPoint, event, outlineMode);
QRectF outlinePixelRect = m_currentOutline.boundingRect();
QRectF outlineDocRect = currentImage()->pixelToDocument(outlinePixelRect);
// This adjusted call is needed as we paint with a 3 pixel wide brush and the pen is outside the bounds of the path
// Pen uses view coordinates so we have to zoom the document value to match 2 pixel in view coordinates
// See BUG 275829
qreal zoomX;
qreal zoomY;
canvas()->viewConverter()->zoom(&zoomX, &zoomY);
qreal xoffset = 2.0/zoomX;
qreal yoffset = 2.0/zoomY;
if (!outlineDocRect.isEmpty()) {
outlineDocRect.adjust(-xoffset,-yoffset,xoffset,yoffset);
}
QRectF colorPreviewDocRect = this->colorPreviewDocRect(m_outlineDocPoint);
QRectF colorPreviewDocUpdateRect;
if (!colorPreviewDocRect.isEmpty()) {
colorPreviewDocUpdateRect.adjust(-xoffset,-yoffset,xoffset,yoffset);
}
// DIRTY HACK ALERT: we should fetch the assistant's dirty rect when requesting
// the update, instead of just dumbly update the entire canvas!
KisCanvas2 * kiscanvas = dynamic_cast<KisCanvas2*>(canvas());
KisPaintingAssistantsDecorationSP decoration = kiscanvas->paintingAssistantsDecoration();
if (decoration && decoration->visible()) {
kiscanvas->updateCanvas();
} else {
// TODO: only this branch should be present!
if (!m_oldColorPreviewUpdateRect.isEmpty()) {
canvas()->updateCanvas(m_oldColorPreviewUpdateRect);
}
if (!m_oldOutlineRect.isEmpty()) {
canvas()->updateCanvas(m_oldOutlineRect);
}
if (!outlineDocRect.isEmpty()) {
canvas()->updateCanvas(outlineDocRect);
}
if (!colorPreviewDocUpdateRect.isEmpty()) {
canvas()->updateCanvas(colorPreviewDocUpdateRect);
}
}
m_oldOutlineRect = outlineDocRect;
m_oldColorPreviewRect = colorPreviewDocRect;
m_oldColorPreviewUpdateRect = colorPreviewDocUpdateRect;
}
QPainterPath KisToolPaint::getOutlinePath(const QPointF &documentPos,
const KoPointerEvent *event,
KisPaintOpSettings::OutlineMode outlineMode)
{
Q_UNUSED(event);
QPointF imagePos = currentImage()->documentToPixel(documentPos);
QPainterPath path = currentPaintOpPreset()->settings()->
brushOutline(KisPaintInformation(imagePos), outlineMode);
return path;
}
diff --git a/libs/ui/utils/kis_document_aware_spin_box_unit_manager.h b/libs/ui/utils/kis_document_aware_spin_box_unit_manager.h
index ec2143be8c..4431a84afa 100644
--- a/libs/ui/utils/kis_document_aware_spin_box_unit_manager.h
+++ b/libs/ui/utils/kis_document_aware_spin_box_unit_manager.h
@@ -1,70 +1,70 @@
/*
* Copyright (c) 2017 Laurent Valentin Jospin <laurent.valentin@famillejospin.ch>
*
* 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 KISDOCUMENTAWARESPINBOXUNITMANAGER_H
#define KISDOCUMENTAWARESPINBOXUNITMANAGER_H
#include "kis_spin_box_unit_manager.h"
#include "kis_double_parse_unit_spin_box.h"
#include "kritaui_export.h"
class KisDocumentAwareSpinBoxUnitManagerBuilder : public KisSpinBoxUnitManagerBuilder
{
public:
KisSpinBoxUnitManager* buildUnitManager(QObject* parent) override;
};
/*!
* \brief The KisDocumentAwareSpinBoxUnitManager class is a KisSpinBoxUnitManager that is able to connect to the current document to compute transformation for document relative units (the ones that depend of the resolution, or the size in pixels of the image).
* \see KisSpinBoxUnitManager
*/
class KRITAUI_EXPORT KisDocumentAwareSpinBoxUnitManager : public KisSpinBoxUnitManager
{
Q_OBJECT
public:
enum PixDir {
PIX_DIR_X,
PIX_DIR_Y
}; //in case the image has not the same x and y resolution, indicate on which direction get the resolution.
//! \brief configure a KisDocumentAwareSpinBoxUnitManager for the given spinbox (make the manager a child of the spinbox and attach it to the spinbox).
static void setDocumentAwarnessToExistingUnitSpinBox(KisDoubleParseUnitSpinBox* spinBox, bool setUnitFromOutsideToggle = false);
//! \brief create a unitSpinBox that is already document aware.
static KisDoubleParseUnitSpinBox* createUnitSpinBoxWithDocumentAwarness(QWidget* parent = 0);
KisDocumentAwareSpinBoxUnitManager(QObject *parent = 0, int pPixDir = PIX_DIR_X);
- //! \reimp \see KisSpinBoxUnitManager
+ //! \see KisSpinBoxUnitManager
qreal getConversionFactor(int dim, QString psymbol) const override;
- //! \reimp \see KisSpinBoxUnitManager
+ //! \see KisSpinBoxUnitManager
qreal getConversionConstant(int dim, QString symbol) const override;
protected:
- //! \reimp \see KisSpinBoxUnitManager
+ //! \see KisSpinBoxUnitManager
virtual bool hasPercent(int unitDim) const override;
PixDir pixDir;
};
#endif // KISDOCUMENTAWARESPINBOXUNITMANAGER_H
diff --git a/libs/ui/widgets/KisGamutMaskToolbar.cpp b/libs/ui/widgets/KisGamutMaskToolbar.cpp
index 88201939b3..ef8ca6cc06 100644
--- a/libs/ui/widgets/KisGamutMaskToolbar.cpp
+++ b/libs/ui/widgets/KisGamutMaskToolbar.cpp
@@ -1,115 +1,115 @@
/*
* Copyright (c) 2018 Anna Medonosova <anna.medonosova@gmail.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <QWidget>
#include "KisGamutMaskToolbar.h"
#include <kis_icon_utils.h>
#include <kis_canvas_resource_provider.h>
KisGamutMaskToolbar::KisGamutMaskToolbar(QWidget* parent) : QWidget(parent)
, m_selectedMask(nullptr)
{
m_ui = new Ui_wdgGamutMaskToolbar();
m_ui->setupUi(this);
m_iconMaskOff = KisIconUtils::loadIcon("gamut-mask-off");
m_iconMaskOn = KisIconUtils::loadIcon("gamut-mask-on");
m_textNoMask = i18n("Select a mask in \"Gamut Masks\" docker");
m_textMaskDisabled = i18n("Mask is disabled");
m_ui->bnToggleMask->setChecked(false);
m_ui->bnToggleMask->setIcon(m_iconMaskOff);
m_ui->rotationSlider->setRange(0, 360);
m_ui->rotationSlider->setPrefix(i18n("Rotation: "));
m_ui->rotationSlider->setSuffix("°");
m_ui->rotationSlider->setFastSliderStep(30); // TODO: test for usability
m_ui->rotationSlider->hide();
// gamut mask connections
connect(m_ui->bnToggleMask, SIGNAL(toggled(bool)), SLOT(slotGamutMaskToggle(bool)));
connect(m_ui->rotationSlider, SIGNAL(valueChanged(int)), SLOT(slotGamutMaskRotate(int)));
}
void KisGamutMaskToolbar::connectMaskSignals(KisCanvasResourceProvider* resourceProvider)
{
connect(resourceProvider, SIGNAL(sigGamutMaskChanged(KoGamutMask*)),
- this, SLOT(slotGamutMaskSet(KoGamutMask*)));
+ this, SLOT(slotGamutMaskSet(KoGamutMask*)), Qt::UniqueConnection);
connect(resourceProvider, SIGNAL(sigGamutMaskUnset()),
- this, SLOT(slotGamutMaskUnset()));
+ this, SLOT(slotGamutMaskUnset()), Qt::UniqueConnection);
connect(this, SIGNAL(sigGamutMaskChanged(KoGamutMask*)),
- resourceProvider, SLOT(slotGamutMaskActivated(KoGamutMask*)));
+ resourceProvider, SLOT(slotGamutMaskActivated(KoGamutMask*)), Qt::UniqueConnection);
}
void KisGamutMaskToolbar::slotGamutMaskSet(KoGamutMask *mask)
{
if (!mask) {
return;
}
m_selectedMask = mask;
if (m_selectedMask) {
slotGamutMaskToggle(true);
} else {
slotGamutMaskToggle(false);
}
}
void KisGamutMaskToolbar::slotGamutMaskUnset()
{
m_ui->rotationSlider->hide();
m_ui->labelMaskName->show();
m_ui->labelMaskName->setText(m_textNoMask);
}
void KisGamutMaskToolbar::slotGamutMaskToggle(bool state)
{
bool b = (!m_selectedMask) ? false : state;
m_ui->bnToggleMask->setChecked(b);
if (b == true) {
m_ui->bnToggleMask->setIcon(m_iconMaskOn);
m_ui->labelMaskName->hide();
m_ui->rotationSlider->show();
m_ui->rotationSlider->blockSignals(true);
m_ui->rotationSlider->setValue(m_selectedMask->rotation());
m_ui->rotationSlider->blockSignals(false);
} else {
m_ui->bnToggleMask->setIcon(m_iconMaskOff);
m_ui->rotationSlider->hide();
m_ui->labelMaskName->show();
m_ui->labelMaskName->setText(m_textMaskDisabled);
}
emit sigGamutMaskToggle(state);
}
void KisGamutMaskToolbar::slotGamutMaskRotate(int angle)
{
if (!m_selectedMask) {
return;
}
m_selectedMask->setRotation(angle);
emit sigGamutMaskChanged(m_selectedMask);
}
diff --git a/libs/ui/widgets/KisLayerStyleAngleSelector.cpp b/libs/ui/widgets/KisLayerStyleAngleSelector.cpp
new file mode 100644
index 0000000000..e576080b0a
--- /dev/null
+++ b/libs/ui/widgets/KisLayerStyleAngleSelector.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2018 Anna Medonosova <anna.medonosova@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "KisLayerStyleAngleSelector.h"
+
+#include <QWidget>
+#include <QDial>
+
+#include <kis_signals_blocker.h>
+
+KisLayerStyleAngleSelector::KisLayerStyleAngleSelector(QWidget *parent)
+ : QWidget(parent)
+ , m_enableGlobalLight(false)
+{
+ ui = new Ui_WdgKisLayerStyleAngleSelector();
+ ui->setupUi(this);
+
+ ui->chkUseGlobalLight->hide();
+
+ connect(ui->dialAngle, SIGNAL(valueChanged(int)), SLOT(slotDialAngleChanged(int)));
+ connect(ui->intAngle, SIGNAL(valueChanged(int)), SLOT(slotIntAngleChanged(int)));
+}
+
+int KisLayerStyleAngleSelector::value()
+{
+ return ui->intAngle->value();
+}
+
+void KisLayerStyleAngleSelector::setValue(int value)
+{
+ KisSignalsBlocker intB(ui->intAngle);
+ KisSignalsBlocker dialB(ui->dialAngle);
+
+ ui->intAngle->setValue(value);
+ ui->dialAngle->setValue(value + m_dialValueShift);
+}
+
+void KisLayerStyleAngleSelector::enableGlobalLight(bool enable)
+{
+ m_enableGlobalLight = enable;
+
+ if (enable) {
+ ui->chkUseGlobalLight->show();
+ connect(ui->chkUseGlobalLight, SIGNAL(toggled(bool)), SLOT(slotGlobalLightToggled()));
+ } else {
+ ui->chkUseGlobalLight->hide();
+ disconnect(ui->chkUseGlobalLight, SIGNAL(toggled(bool)), this, SLOT(slotGlobalLightToggled()));
+ }
+}
+
+bool KisLayerStyleAngleSelector::useGlobalLight()
+{
+ return m_enableGlobalLight && ui->chkUseGlobalLight->isChecked();
+}
+
+void KisLayerStyleAngleSelector::setUseGlobalLight(bool state)
+{
+ ui->chkUseGlobalLight->setChecked(state);
+}
+
+void KisLayerStyleAngleSelector::slotDialAngleChanged(int value)
+{
+ KisSignalsBlocker b(ui->intAngle);
+
+ int normalizedValue = 0;
+ if (value >= 270 && value <= 360) {
+ // Due to the mismatch between the domain of the dial (0°,360°)
+ // and the spinbox (-179°,180°), the shift in the third quadrant
+ // of the dial is different
+ normalizedValue = value - 360 - m_dialValueShift;
+ } else {
+ normalizedValue = value - m_dialValueShift;
+ }
+
+ ui->intAngle->setValue(normalizedValue);
+ emit valueChanged(normalizedValue);
+ emitChangeSignals();
+}
+
+void KisLayerStyleAngleSelector::slotIntAngleChanged(int value)
+{
+ KisSignalsBlocker b(ui->dialAngle);
+
+ int angleDialValue = value + m_dialValueShift;
+ ui->dialAngle->setValue(angleDialValue);
+
+ emit valueChanged(value);
+ emitChangeSignals();
+}
+
+void KisLayerStyleAngleSelector::slotGlobalLightToggled()
+{
+ emitChangeSignals();
+}
+
+void KisLayerStyleAngleSelector::emitChangeSignals()
+{
+ if (useGlobalLight()) {
+ emit globalAngleChanged(value());
+ }
+
+ emit configChanged();
+}
diff --git a/libs/ui/widgets/KisLayerStyleAngleSelector.h b/libs/ui/widgets/KisLayerStyleAngleSelector.h
new file mode 100644
index 0000000000..35eb382641
--- /dev/null
+++ b/libs/ui/widgets/KisLayerStyleAngleSelector.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2018 Anna Medonosova <anna.medonosova@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef KISLAYERSTYLEANGLESELECTOR_H
+#define KISLAYERSTYLEANGLESELECTOR_H
+
+#include <QObject>
+#include <QWidget>
+#include <QDial>
+
+#include <ui_wdgKisLayerStyleAngleSelector.h>
+
+class KisLayerStyleAngleSelector : public QWidget
+{
+ Q_OBJECT
+
+public:
+ KisLayerStyleAngleSelector(QWidget* parent);
+
+ int value();
+ void setValue(int value);
+
+ void enableGlobalLight(bool enable);
+
+ bool useGlobalLight();
+ void setUseGlobalLight(bool state);
+
+Q_SIGNALS:
+ void valueChanged(int);
+ void configChanged();
+ void globalAngleChanged(int);
+
+private Q_SLOTS:
+ void slotDialAngleChanged(int value);
+ void slotIntAngleChanged(int value);
+ void slotGlobalLightToggled();
+
+private:
+ void emitChangeSignals();
+
+ Ui_WdgKisLayerStyleAngleSelector* ui;
+
+ // BUG: 372169
+ // Adobe's dial widget differs from QDial by 90 degrees,
+ // therefore we need to apply this magic constant
+ // to this widget's QDial for consistency between
+ // the settings dialogs and on-canvas effects.
+ // Wrapping is handled by QDial itself.
+ const static int m_dialValueShift = 90;
+
+ bool m_enableGlobalLight;
+};
+
+#endif // KISLAYERSTYLEANGLESELECTOR_H
diff --git a/libs/ui/widgets/kis_curve_widget.cpp b/libs/ui/widgets/kis_curve_widget.cpp
index 3a7a0c9ddb..9f18242dbc 100644
--- a/libs/ui/widgets/kis_curve_widget.cpp
+++ b/libs/ui/widgets/kis_curve_widget.cpp
@@ -1,517 +1,517 @@
/*
* Copyright (c) 2005 C. Boemann <cbo@boemann.dk>
* Copyright (c) 2009 Dmitry Kazakov <dimula73@gmail.com>
*
* 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.
*/
// C++ includes.
#include <cmath>
#include <cstdlib>
// Qt includes.
#include <QPixmap>
#include <QPainter>
#include <QPoint>
#include <QPen>
#include <QEvent>
#include <QRect>
#include <QFont>
#include <QFontMetrics>
#include <QMouseEvent>
#include <QKeyEvent>
#include <QPaintEvent>
#include <QList>
#include <QApplication>
#include <QSpinBox>
// KDE includes.
#include <kis_debug.h>
#include <kis_config.h>
#include <klocalizedstring.h>
// Local includes.
#include "widgets/kis_curve_widget.h"
#define bounds(x,a,b) (x<a ? a : (x>b ? b :x))
#define MOUSE_AWAY_THRES 15
#define POINT_AREA 1E-4
#define CURVE_AREA 1E-4
#include "kis_curve_widget_p.h"
KisCurveWidget::KisCurveWidget(QWidget *parent, Qt::WindowFlags f)
: QWidget(parent, f), d(new KisCurveWidget::Private(this))
{
setObjectName("KisCurveWidget");
d->m_grab_point_index = -1;
d->m_readOnlyMode = false;
d->m_guideVisible = false;
d->m_pixmapDirty = true;
d->m_pixmapCache = 0;
d->setState(ST_NORMAL);
d->m_intIn = 0;
d->m_intOut = 0;
setMouseTracking(true);
setAutoFillBackground(false);
setAttribute(Qt::WA_OpaquePaintEvent);
setMinimumSize(150, 50);
setMaximumSize(250, 250);
d->setCurveModified();
setFocusPolicy(Qt::StrongFocus);
}
KisCurveWidget::~KisCurveWidget()
{
delete d->m_pixmapCache;
delete d;
}
void KisCurveWidget::setupInOutControls(QSpinBox *in, QSpinBox *out, int inMin, int inMax, int outMin, int outMax)
{
d->m_intIn = in;
d->m_intOut = out;
if (!d->m_intIn || !d->m_intOut)
return;
d->m_inMin = inMin;
d->m_inMax = inMax;
d->m_outMin = outMin;
d->m_outMax = outMax;
d->m_intIn->setRange(d->m_inMin, d->m_inMax);
d->m_intOut->setRange(d->m_outMin, d->m_outMax);
- connect(d->m_intIn, SIGNAL(valueChanged(int)), this, SLOT(inOutChanged(int)));
- connect(d->m_intOut, SIGNAL(valueChanged(int)), this, SLOT(inOutChanged(int)));
+ connect(d->m_intIn, SIGNAL(valueChanged(int)), this, SLOT(inOutChanged(int)), Qt::UniqueConnection);
+ connect(d->m_intOut, SIGNAL(valueChanged(int)), this, SLOT(inOutChanged(int)), Qt::UniqueConnection);
d->syncIOControls();
}
void KisCurveWidget::dropInOutControls()
{
if (!d->m_intIn || !d->m_intOut)
return;
disconnect(d->m_intIn, SIGNAL(valueChanged(int)), this, SLOT(inOutChanged(int)));
disconnect(d->m_intOut, SIGNAL(valueChanged(int)), this, SLOT(inOutChanged(int)));
d->m_intIn = d->m_intOut = 0;
}
void KisCurveWidget::inOutChanged(int)
{
QPointF pt;
Q_ASSERT(d->m_grab_point_index >= 0);
pt.setX(d->io2sp(d->m_intIn->value(), d->m_inMin, d->m_inMax));
pt.setY(d->io2sp(d->m_intOut->value(), d->m_outMin, d->m_outMax));
if (d->jumpOverExistingPoints(pt, d->m_grab_point_index)) {
d->m_curve.setPoint(d->m_grab_point_index, pt);
d->m_grab_point_index = d->m_curve.points().indexOf(pt);
emit pointSelectedChanged();
} else
pt = d->m_curve.points()[d->m_grab_point_index];
d->m_intIn->blockSignals(true);
d->m_intOut->blockSignals(true);
d->m_intIn->setValue(d->sp2io(pt.x(), d->m_inMin, d->m_inMax));
d->m_intOut->setValue(d->sp2io(pt.y(), d->m_outMin, d->m_outMax));
d->m_intIn->blockSignals(false);
d->m_intOut->blockSignals(false);
d->setCurveModified();
}
void KisCurveWidget::reset(void)
{
d->m_grab_point_index = -1;
emit pointSelectedChanged();
d->m_guideVisible = false;
//remove total - 2 points.
while (d->m_curve.points().count() - 2 ) {
d->m_curve.removePoint(d->m_curve.points().count() - 2);
}
d->setCurveModified();
}
void KisCurveWidget::setCurveGuide(const QColor & color)
{
d->m_guideVisible = true;
d->m_colorGuide = color;
}
void KisCurveWidget::setPixmap(const QPixmap & pix)
{
d->m_pix = pix;
d->m_pixmapDirty = true;
d->setCurveRepaint();
}
QPixmap KisCurveWidget::getPixmap()
{
return d->m_pix;
}
void KisCurveWidget::setBasePixmap(const QPixmap &pix)
{
d->m_pixmapBase = pix;
}
QPixmap KisCurveWidget::getBasePixmap()
{
return d->m_pixmapBase;
}
bool KisCurveWidget::pointSelected() const
{
return d->m_grab_point_index > 0 && d->m_grab_point_index < d->m_curve.points().count() - 1;
}
void KisCurveWidget::keyPressEvent(QKeyEvent *e)
{
if (e->key() == Qt::Key_Delete || e->key() == Qt::Key_Backspace) {
if (d->m_grab_point_index > 0 && d->m_grab_point_index < d->m_curve.points().count() - 1) {
//x() find closest point to get focus afterwards
double grab_point_x = d->m_curve.points()[d->m_grab_point_index].x();
int left_of_grab_point_index = d->m_grab_point_index - 1;
int right_of_grab_point_index = d->m_grab_point_index + 1;
int new_grab_point_index;
if (fabs(d->m_curve.points()[left_of_grab_point_index].x() - grab_point_x) <
fabs(d->m_curve.points()[right_of_grab_point_index].x() - grab_point_x)) {
new_grab_point_index = left_of_grab_point_index;
} else {
new_grab_point_index = d->m_grab_point_index;
}
d->m_curve.removePoint(d->m_grab_point_index);
d->m_grab_point_index = new_grab_point_index;
emit pointSelectedChanged();
setCursor(Qt::ArrowCursor);
d->setState(ST_NORMAL);
}
e->accept();
d->setCurveModified();
} else if (e->key() == Qt::Key_Escape && d->state() != ST_NORMAL) {
d->m_curve.setPoint(d->m_grab_point_index, QPointF(d->m_grabOriginalX, d->m_grabOriginalY) );
setCursor(Qt::ArrowCursor);
d->setState(ST_NORMAL);
e->accept();
d->setCurveModified();
} else if ((e->key() == Qt::Key_A || e->key() == Qt::Key_Insert) && d->state() == ST_NORMAL) {
/* FIXME: Lets user choose the hotkeys */
addPointInTheMiddle();
e->accept();
} else
QWidget::keyPressEvent(e);
}
void KisCurveWidget::addPointInTheMiddle()
{
QPointF pt(0.5, d->m_curve.value(0.5));
if (!d->jumpOverExistingPoints(pt, -1))
return;
d->m_grab_point_index = d->m_curve.addPoint(pt);
emit pointSelectedChanged();
if (d->m_intIn)
d->m_intIn->setFocus(Qt::TabFocusReason);
d->setCurveModified();
}
void KisCurveWidget::resizeEvent(QResizeEvent *e)
{
d->m_pixmapDirty = true;
QWidget::resizeEvent(e);
}
void KisCurveWidget::paintEvent(QPaintEvent *)
{
int wWidth = width() - 1;
int wHeight = height() - 1;
QPainter p(this);
// Antialiasing is not a good idea here, because
// the grid will drift one pixel to any side due to rounding of int
// FIXME: let's user tell the last word (in config)
//p.setRenderHint(QPainter::Antialiasing);
QPalette appPalette = QApplication::palette();
p.fillRect(rect(), appPalette.color(QPalette::Base)); // clear out previous paint call results
// make the entire widget grayed out if it is disabled
if (!this->isEnabled()) {
p.setOpacity(0.2);
}
// draw background
if (!d->m_pix.isNull()) {
if (d->m_pixmapDirty || !d->m_pixmapCache) {
delete d->m_pixmapCache;
d->m_pixmapCache = new QPixmap(width(), height());
QPainter cachePainter(d->m_pixmapCache);
cachePainter.scale(1.0*width() / d->m_pix.width(), 1.0*height() / d->m_pix.height());
cachePainter.drawPixmap(0, 0, d->m_pix);
d->m_pixmapDirty = false;
}
p.drawPixmap(0, 0, *d->m_pixmapCache);
}
d->drawGrid(p, wWidth, wHeight);
KisConfig cfg(true);
if (cfg.antialiasCurves()) {
p.setRenderHint(QPainter::Antialiasing);
}
// Draw curve.
double curY;
double normalizedX;
int x;
QPolygonF poly;
p.setPen(QPen(appPalette.color(QPalette::Text), 2, Qt::SolidLine));
for (x = 0 ; x < wWidth ; x++) {
normalizedX = double(x) / wWidth;
curY = wHeight - d->m_curve.value(normalizedX) * wHeight;
/**
* Keep in mind that QLineF rounds doubles
* to ints mathematically, not just rounds down
* like in C
*/
poly.append(QPointF(x, curY));
}
poly.append(QPointF(x, wHeight - d->m_curve.value(1.0) * wHeight));
p.drawPolyline(poly);
QPainterPath fillCurvePath;
QPolygonF fillPoly = poly;
fillPoly.append(QPoint(rect().width(), rect().height()));
fillPoly.append(QPointF(0,rect().height()));
// add a couple points to the edges so it fills in below always
QColor fillColor = appPalette.color(QPalette::Text);
fillColor.setAlphaF(0.2);
fillCurvePath.addPolygon(fillPoly);
p.fillPath(fillCurvePath, fillColor);
// Drawing curve handles.
double curveX;
double curveY;
if (!d->m_readOnlyMode) {
for (int i = 0; i < d->m_curve.points().count(); ++i) {
curveX = d->m_curve.points().at(i).x();
curveY = d->m_curve.points().at(i).y();
if (i == d->m_grab_point_index) {
p.setPen(QPen(appPalette.color(QPalette::Text), 6, Qt::SolidLine));
p.drawEllipse(QRectF(curveX * wWidth - 2,
wHeight - 2 - curveY * wHeight, 4, 4));
} else {
p.setPen(QPen(appPalette.color(QPalette::Text), 2, Qt::SolidLine));
p.drawEllipse(QRectF(curveX * wWidth - 3,
wHeight - 3 - curveY * wHeight, 6, 6));
}
}
}
// add border around widget to help contain everything
QPainterPath widgetBoundsPath;
widgetBoundsPath.addRect(rect());
p.strokePath(widgetBoundsPath, appPalette.color(QPalette::Text));
p.setOpacity(1.0); // reset to 1.0 in case we were drawing a disabled widget before
}
void KisCurveWidget::mousePressEvent(QMouseEvent * e)
{
if (d->m_readOnlyMode) return;
if (e->button() != Qt::LeftButton)
return;
double x = e->pos().x() / (double)(width() - 1);
double y = 1.0 - e->pos().y() / (double)(height() - 1);
int closest_point_index = d->nearestPointInRange(QPointF(x, y), width(), height());
if (closest_point_index < 0) {
QPointF newPoint(x, y);
if (!d->jumpOverExistingPoints(newPoint, -1))
return;
d->m_grab_point_index = d->m_curve.addPoint(newPoint);
emit pointSelectedChanged();
} else {
d->m_grab_point_index = closest_point_index;
emit pointSelectedChanged();
}
d->m_grabOriginalX = d->m_curve.points()[d->m_grab_point_index].x();
d->m_grabOriginalY = d->m_curve.points()[d->m_grab_point_index].y();
d->m_grabOffsetX = d->m_curve.points()[d->m_grab_point_index].x() - x;
d->m_grabOffsetY = d->m_curve.points()[d->m_grab_point_index].y() - y;
d->m_curve.setPoint(d->m_grab_point_index, QPointF(x + d->m_grabOffsetX, y + d->m_grabOffsetY));
d->m_draggedAwayPointIndex = -1;
d->setState(ST_DRAG);
d->setCurveModified();
}
void KisCurveWidget::mouseReleaseEvent(QMouseEvent *e)
{
if (d->m_readOnlyMode) return;
if (e->button() != Qt::LeftButton)
return;
setCursor(Qt::ArrowCursor);
d->setState(ST_NORMAL);
d->setCurveModified();
}
void KisCurveWidget::mouseMoveEvent(QMouseEvent * e)
{
if (d->m_readOnlyMode) return;
double x = e->pos().x() / (double)(width() - 1);
double y = 1.0 - e->pos().y() / (double)(height() - 1);
if (d->state() == ST_NORMAL) { // If no point is selected set the cursor shape if on top
int nearestPointIndex = d->nearestPointInRange(QPointF(x, y), width(), height());
if (nearestPointIndex < 0)
setCursor(Qt::ArrowCursor);
else
setCursor(Qt::CrossCursor);
} else { // Else, drag the selected point
bool crossedHoriz = e->pos().x() - width() > MOUSE_AWAY_THRES ||
e->pos().x() < -MOUSE_AWAY_THRES;
bool crossedVert = e->pos().y() - height() > MOUSE_AWAY_THRES ||
e->pos().y() < -MOUSE_AWAY_THRES;
bool removePoint = (crossedHoriz || crossedVert);
if (!removePoint && d->m_draggedAwayPointIndex >= 0) {
// point is no longer dragged away so reinsert it
QPointF newPoint(d->m_draggedAwayPoint);
d->m_grab_point_index = d->m_curve.addPoint(newPoint);
d->m_draggedAwayPointIndex = -1;
}
if (removePoint &&
(d->m_draggedAwayPointIndex >= 0))
return;
setCursor(Qt::CrossCursor);
x += d->m_grabOffsetX;
y += d->m_grabOffsetY;
double leftX;
double rightX;
if (d->m_grab_point_index == 0) {
leftX = 0.0;
if (d->m_curve.points().count() > 1)
rightX = d->m_curve.points()[d->m_grab_point_index + 1].x() - POINT_AREA;
else
rightX = 1.0;
} else if (d->m_grab_point_index == d->m_curve.points().count() - 1) {
leftX = d->m_curve.points()[d->m_grab_point_index - 1].x() + POINT_AREA;
rightX = 1.0;
} else {
Q_ASSERT(d->m_grab_point_index > 0 && d->m_grab_point_index < d->m_curve.points().count() - 1);
// the 1E-4 addition so we can grab the dot later.
leftX = d->m_curve.points()[d->m_grab_point_index - 1].x() + POINT_AREA;
rightX = d->m_curve.points()[d->m_grab_point_index + 1].x() - POINT_AREA;
}
x = bounds(x, leftX, rightX);
y = bounds(y, 0., 1.);
d->m_curve.setPoint(d->m_grab_point_index, QPointF(x, y));
if (removePoint && d->m_curve.points().count() > 2) {
d->m_draggedAwayPoint = d->m_curve.points()[d->m_grab_point_index];
d->m_draggedAwayPointIndex = d->m_grab_point_index;
d->m_curve.removePoint(d->m_grab_point_index);
d->m_grab_point_index = bounds(d->m_grab_point_index, 0, d->m_curve.points().count() - 1);
emit pointSelectedChanged();
}
d->setCurveModified();
}
}
KisCubicCurve KisCurveWidget::curve()
{
return d->m_curve;
}
void KisCurveWidget::setCurve(KisCubicCurve inlist)
{
d->m_curve = inlist;
d->m_grab_point_index = qBound(0, d->m_grab_point_index, d->m_curve.points().count() - 1);
emit pointSelectedChanged();
d->setCurveModified();
}
void KisCurveWidget::leaveEvent(QEvent *)
{
}
diff --git a/libs/ui/widgets/kis_curve_widget.h b/libs/ui/widgets/kis_curve_widget.h
index a0301fbe78..719c29093a 100644
--- a/libs/ui/widgets/kis_curve_widget.h
+++ b/libs/ui/widgets/kis_curve_widget.h
@@ -1,161 +1,161 @@
/*
* Copyright (c) 2005 C. Boemann <cbo@boemann.dk>
* Copyright (c) 2009 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_CURVE_WIDGET_H
#define KIS_CURVE_WIDGET_H
// Qt includes.
#include <QWidget>
#include <QColor>
#include <QPointF>
#include <QPixmap>
#include <QMouseEvent>
#include <QKeyEvent>
#include <QEvent>
#include <QPaintEvent>
#include <QList>
#include <kritaui_export.h>
class QSpinBox;
class KisCubicCurve;
/**
* KisCurveWidget is a widget that shows a single curve that can be edited
* by the user. The user can grab the curve and move it; this creates
* a new control point. Control points can be deleted by selecting a point
* and pressing the delete key.
*
* (From: http://techbase.kde.org/Projects/Widgets_and_Classes#KisCurveWidget)
* KisCurveWidget allows editing of spline based y=f(x) curves. Handy for cases
* where you want the user to control such things as tablet pressure
* response, color transformations, acceleration by time, aeroplane lift
*by angle of attack.
*/
class KRITAUI_EXPORT KisCurveWidget : public QWidget
{
Q_OBJECT
Q_PROPERTY(bool pointSelected READ pointSelected NOTIFY pointSelectedChanged);
public:
friend class CurveEditorItem;
/**
* Create a new curve widget with a default curve, that is a straight
* line from bottom-left to top-right.
*/
KisCurveWidget(QWidget *parent = 0, Qt::WindowFlags f = 0);
~KisCurveWidget() override;
/**
* Reset the curve to the default shape
*/
void reset(void);
/**
* Enable the guide and set the guide color to the specified color.
*
* XXX: it seems that the guide feature isn't actually implemented yet?
*/
void setCurveGuide(const QColor & color);
/**
* Set a background pixmap. The background pixmap will be drawn under
* the grid and the curve.
*
* XXX: or is the pixmap what is drawn to the left and bottom of the curve
* itself?
*/
void setPixmap(const QPixmap & pix);
QPixmap getPixmap();
void setBasePixmap(const QPixmap & pix);
QPixmap getBasePixmap();
/**
* Whether or not there is a point selected
* This does NOT include the first and last points
*/
bool pointSelected() const;
Q_SIGNALS:
/**
* Emitted whenever a control point has changed position.
*/
void modified(void);
/**
* Emitted whenever the status of whether a control point is selected or not changes
*/
void pointSelectedChanged();
protected Q_SLOTS:
void inOutChanged(int);
protected:
void keyPressEvent(QKeyEvent *) override;
void paintEvent(QPaintEvent *) override;
void mousePressEvent(QMouseEvent * e) override;
void mouseReleaseEvent(QMouseEvent * e) override;
void mouseMoveEvent(QMouseEvent * e) override;
void leaveEvent(QEvent *) override;
void resizeEvent(QResizeEvent *e) override;
public:
/**
* @return get a list with all defined points. If you want to know what the
* y value for a given x is on the curve defined by these points, use getCurveValue().
* @see getCurveValue
*/
KisCubicCurve curve();
/**
* Replace the current curve with a curve specified by the curve defined by the control
- * points in @param inlist.
+ * points in @p inlist.
*/
void setCurve(KisCubicCurve inlist);
/**
* Connect/disconnect external spinboxes to the curve
- * @inMin/@inMax - is the range for input values
- * @outMin/@outMax - is the range for output values
+ * @p inMin / @p inMax - is the range for input values
+ * @p outMin / @p outMax - is the range for output values
*/
void setupInOutControls(QSpinBox *in, QSpinBox *out, int inMin, int inMax, int outMin, int outMax);
void dropInOutControls();
/**
* Handy function that creates new point in the middle
- * of the curve and sets focus on the m_intIn field,
+ * of the curve and sets focus on the @p m_intIn field,
* so the user can move this point anywhere in a moment
*/
void addPointInTheMiddle();
private:
class Private;
Private * const d;
};
#endif /* KIS_CURVE_WIDGET_H */
diff --git a/libs/ui/widgets/kis_curve_widget_p.h b/libs/ui/widgets/kis_curve_widget_p.h
index c31b452b7c..ba67893926 100644
--- a/libs/ui/widgets/kis_curve_widget_p.h
+++ b/libs/ui/widgets/kis_curve_widget_p.h
@@ -1,271 +1,271 @@
/*
* Copyright (c) 2005 C. Boemann <cbo@boemann.dk>
* Copyright (c) 2009 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_CURVE_WIDGET_P_H_
#define _KIS_CURVE_WIDGET_P_H_
#include <kis_cubic_curve.h>
#include <QApplication>
#include <QPalette>
enum enumState {
ST_NORMAL,
ST_DRAG
};
/**
* Private members for KisCurveWidget class
*/
class Q_DECL_HIDDEN KisCurveWidget::Private
{
KisCurveWidget *m_curveWidget;
public:
Private(KisCurveWidget *parent);
/* Dragging variables */
int m_grab_point_index;
double m_grabOffsetX;
double m_grabOffsetY;
double m_grabOriginalX;
double m_grabOriginalY;
QPointF m_draggedAwayPoint;
int m_draggedAwayPointIndex;
bool m_readOnlyMode;
bool m_guideVisible;
QColor m_colorGuide;
/* The curve itself */
bool m_splineDirty;
KisCubicCurve m_curve;
QPixmap m_pix;
QPixmap m_pixmapBase;
bool m_pixmapDirty;
QPixmap *m_pixmapCache;
/* In/Out controls */
QSpinBox *m_intIn;
QSpinBox *m_intOut;
/* Working range of them */
int m_inMin;
int m_inMax;
int m_outMin;
int m_outMax;
/**
* State functions.
* At the moment used only for dragging.
*/
enumState m_state;
inline void setState(enumState st);
inline enumState state() const;
/*** Internal routines ***/
/**
* Common update routines
*/
void setCurveModified();
void setCurveRepaint();
/**
* Convert working range of
* In/Out controls to normalized
* range of spline (and reverse)
*/
double io2sp(int x, int min, int max);
int sp2io(double x, int min, int max);
/**
- * Check whether newly created/moved point @pt doesn't overlap
- * with any of existing ones from @m_points and adjusts its coordinates.
- * @skipIndex is the index of the point, that shouldn't be taken
+ * Check whether newly created/moved point @p pt doesn't overlap
+ * with any of existing ones from @p m_points and adjusts its coordinates.
+ * @p skipIndex is the index of the point, that shouldn't be taken
* into account during the search
- * (e.g. because it's @pt itself)
+ * (e.g. because it's @p pt itself)
*
* Returns false in case the point can't be placed anywhere
* without overlapping
*/
bool jumpOverExistingPoints(QPointF &pt, int skipIndex);
/**
* Synchronize In/Out spinboxes with the curve
*/
void syncIOControls();
/**
- * Find the nearest point to @pt from m_points
+ * Find the nearest point to @p pt from m_points
*/
int nearestPointInRange(QPointF pt, int wWidth, int wHeight) const;
/**
* Nothing to be said! =)
*/
inline
void drawGrid(QPainter &p, int wWidth, int wHeight);
};
KisCurveWidget::Private::Private(KisCurveWidget *parent)
{
m_curveWidget = parent;
}
double KisCurveWidget::Private::io2sp(int x, int min, int max)
{
int rangeLen = max - min;
return double(x - min) / rangeLen;
}
int KisCurveWidget::Private::sp2io(double x, int min, int max)
{
int rangeLen = max - min;
return int(x*rangeLen + 0.5) + min;
}
bool KisCurveWidget::Private::jumpOverExistingPoints(QPointF &pt, int skipIndex)
{
Q_FOREACH (const QPointF &it, m_curve.points()) {
if (m_curve.points().indexOf(it) == skipIndex)
continue;
if (fabs(it.x() - pt.x()) < POINT_AREA)
pt.rx() = pt.x() >= it.x() ?
it.x() + POINT_AREA : it.x() - POINT_AREA;
}
return (pt.x() >= 0 && pt.x() <= 1.);
}
int KisCurveWidget::Private::nearestPointInRange(QPointF pt, int wWidth, int wHeight) const
{
double nearestDistanceSquared = 1000;
int nearestIndex = -1;
int i = 0;
Q_FOREACH (const QPointF & point, m_curve.points()) {
double distanceSquared = (pt.x() - point.x()) *
(pt.x() - point.x()) +
(pt.y() - point.y()) *
(pt.y() - point.y());
if (distanceSquared < nearestDistanceSquared) {
nearestIndex = i;
nearestDistanceSquared = distanceSquared;
}
++i;
}
if (nearestIndex >= 0) {
if (fabs(pt.x() - m_curve.points()[nearestIndex].x()) *(wWidth - 1) < 5 &&
fabs(pt.y() - m_curve.points()[nearestIndex].y()) *(wHeight - 1) < 5) {
return nearestIndex;
}
}
return -1;
}
#define div2_round(x) (((x)+1)>>1)
#define div4_round(x) (((x)+2)>>2)
void KisCurveWidget::Private::drawGrid(QPainter &p, int wWidth, int wHeight)
{
/**
* Hint: widget size should conform
* formula 4n+5 to draw grid correctly
* without curious shifts between
* spline and it caused by rounding
*
* That is not mandatory but desirable
*/
QPalette appPalette = QApplication::palette();
p.setPen(QPen(appPalette.color(QPalette::Background), 1, Qt::SolidLine));
p.drawLine(div4_round(wWidth), 0, div4_round(wWidth), wHeight);
p.drawLine(div2_round(wWidth), 0, div2_round(wWidth), wHeight);
p.drawLine(div4_round(3*wWidth), 0, div4_round(3*wWidth), wHeight);
p.drawLine(0, div4_round(wHeight), wWidth, div4_round(wHeight));
p.drawLine(0, div2_round(wHeight), wWidth, div2_round(wHeight));
p.drawLine(0, div4_round(3*wHeight), wWidth, div4_round(3*wHeight));
}
void KisCurveWidget::Private::syncIOControls()
{
if (!m_intIn || !m_intOut)
return;
bool somethingSelected = (m_grab_point_index >= 0);
m_intIn->setEnabled(somethingSelected);
m_intOut->setEnabled(somethingSelected);
if (m_grab_point_index >= 0) {
m_intIn->blockSignals(true);
m_intOut->blockSignals(true);
m_intIn->setValue(sp2io(m_curve.points()[m_grab_point_index].x(), m_inMin, m_inMax));
m_intOut->setValue(sp2io(m_curve.points()[m_grab_point_index].y(), m_outMin, m_outMax));
m_intIn->blockSignals(false);
m_intOut->blockSignals(false);
} else {
/*FIXME: Ideally, these controls should hide away now */
}
}
void KisCurveWidget::Private::setCurveModified()
{
syncIOControls();
m_splineDirty = true;
m_curveWidget->update();
m_curveWidget->emit modified();
}
void KisCurveWidget::Private::setCurveRepaint()
{
m_curveWidget->update();
}
void KisCurveWidget::Private::setState(enumState st)
{
m_state = st;
}
enumState KisCurveWidget::Private::state() const
{
return m_state;
}
#endif /* _KIS_CURVE_WIDGET_P_H_ */
diff --git a/libs/ui/widgets/kis_custom_image_widget.cc b/libs/ui/widgets/kis_custom_image_widget.cc
index 5ee1eaf17e..b8be9165ff 100644
--- a/libs/ui/widgets/kis_custom_image_widget.cc
+++ b/libs/ui/widgets/kis_custom_image_widget.cc
@@ -1,512 +1,518 @@
/* This file is part of the Calligra project
* Copyright (C) 2005 Thomas Zander <zander@kde.org>
* Copyright (C) 2005 C. Boemann <cbo@boemann.dk>
* Copyright (C) 2007 Boudewijn Rempt <boud@valdyas.org>
*
* 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 "widgets/kis_custom_image_widget.h"
#include <QMimeData>
#include <QPushButton>
#include <QSlider>
#include <QComboBox>
#include <QRect>
#include <QApplication>
#include <QClipboard>
#include <QDesktopWidget>
#include <QDialogButtonBox>
#include <QFile>
#include <QSpacerItem>
#include <QMessageBox>
#include <KoResourcePaths.h>
#include <KFormat>
#include <kis_debug.h>
#include <kis_icon.h>
#include <KoCompositeOp.h>
#include <KoColorProfile.h>
#include <KoColorSpace.h>
#include <KoID.h>
#include <KoColor.h>
#include <KoUnit.h>
#include <KoColorModelStandardIds.h>
#include <kis_fill_painter.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <kis_group_layer.h>
#include <kis_paint_layer.h>
#include <kis_paint_device.h>
#include <kis_painter.h>
#include "kis_config.h"
#include "KisPart.h"
#include "kis_clipboard.h"
#include "KisDocument.h"
#include "widgets/kis_cmb_idlist.h"
#include <squeezedcombobox.h>
KisCustomImageWidget::KisCustomImageWidget(QWidget* parent, qint32 defWidth, qint32 defHeight, double resolution, const QString& defColorModel, const QString& defColorDepth, const QString& defColorProfile, const QString& imageName)
: WdgNewImage(parent)
{
setObjectName("KisCustomImageWidget");
m_openPane = qobject_cast<KisOpenPane*>(parent);
Q_ASSERT(m_openPane);
txtName->setText(imageName);
m_widthUnit = KoUnit(KoUnit::Pixel, resolution);
doubleWidth->setValue(defWidth);
doubleWidth->setDecimals(0);
m_width = m_widthUnit.fromUserValue(defWidth);
cmbWidthUnit->addItems(KoUnit::listOfUnitNameForUi(KoUnit::ListAll));
cmbWidthUnit->setCurrentIndex(m_widthUnit.indexInListForUi(KoUnit::ListAll));
m_heightUnit = KoUnit(KoUnit::Pixel, resolution);
doubleHeight->setValue(defHeight);
doubleHeight->setDecimals(0);
m_height = m_heightUnit.fromUserValue(defHeight);
cmbHeightUnit->addItems(KoUnit::listOfUnitNameForUi(KoUnit::ListAll));
cmbHeightUnit->setCurrentIndex(m_heightUnit.indexInListForUi(KoUnit::ListAll));
doubleResolution->setValue(72.0 * resolution);
doubleResolution->setDecimals(0);
imageGroupSpacer->changeSize(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed);
grpClipboard->hide();
sliderOpacity->setRange(0, 100, 0);
sliderOpacity->setValue(100);
sliderOpacity->setSuffix("%");
connect(cmbPredefined, SIGNAL(activated(int)), SLOT(predefinedClicked(int)));
connect(doubleResolution, SIGNAL(valueChanged(double)),
this, SLOT(resolutionChanged(double)));
connect(cmbWidthUnit, SIGNAL(activated(int)),
this, SLOT(widthUnitChanged(int)));
connect(doubleWidth, SIGNAL(valueChanged(double)),
this, SLOT(widthChanged(double)));
connect(cmbHeightUnit, SIGNAL(activated(int)),
this, SLOT(heightUnitChanged(int)));
connect(doubleHeight, SIGNAL(valueChanged(double)),
this, SLOT(heightChanged(double)));
// Create image
newDialogConfirmationButtonBox->button(QDialogButtonBox::Ok)->setText(i18n("&Create"));
connect(newDialogConfirmationButtonBox, SIGNAL(accepted()), this, SLOT(createImage()));
// Cancel Create image button
connect(newDialogConfirmationButtonBox, SIGNAL(rejected()), this->parentWidget(), SLOT(close()));
connect(newDialogConfirmationButtonBox, SIGNAL(rejected()), this->parentWidget(), SLOT(deleteLater()));
bnPortrait->setIcon(KisIconUtils::loadIcon("portrait"));
connect(bnPortrait, SIGNAL(clicked()), SLOT(setPortrait()));
connect(bnLandscape, SIGNAL(clicked()), SLOT(setLandscape()));
bnLandscape->setIcon(KisIconUtils::loadIcon("landscape"));
connect(doubleWidth, SIGNAL(valueChanged(double)), this, SLOT(switchPortraitLandscape()));
connect(doubleHeight, SIGNAL(valueChanged(double)), this, SLOT(switchPortraitLandscape()));
connect(bnSaveAsPredefined, SIGNAL(clicked()), this, SLOT(saveAsPredefined()));
colorSpaceSelector->setCurrentColorModel(KoID(defColorModel));
colorSpaceSelector->setCurrentColorDepth(KoID(defColorDepth));
colorSpaceSelector->setCurrentProfile(defColorProfile);
connect(colorSpaceSelector, SIGNAL(colorSpaceChanged(const KoColorSpace*)), this, SLOT(changeDocumentInfoLabel()));
//connect(chkFromClipboard,SIGNAL(stateChanged(int)),this,SLOT(clipboardDataChanged()));
connect(QApplication::clipboard(), SIGNAL(dataChanged()), this, SLOT(clipboardDataChanged()));
connect(QApplication::clipboard(), SIGNAL(selectionChanged()), this, SLOT(clipboardDataChanged()));
connect(QApplication::clipboard(), SIGNAL(changed(QClipboard::Mode)), this, SLOT(clipboardDataChanged()));
connect(colorSpaceSelector, SIGNAL(selectionChanged(bool)), newDialogConfirmationButtonBox->button(QDialogButtonBox::Ok), SLOT(setEnabled(bool)));
KisConfig cfg(true);
intNumLayers->setValue(cfg.numDefaultLayers());
KoColor bcol(KoColorSpaceRegistry::instance()->rgb8());
bcol.fromQColor(cfg.defaultBackgroundColor());
cmbColor->setColor(bcol);
setBackgroundOpacity(cfg.defaultBackgroundOpacity());
KisConfig::BackgroundStyle bgStyle = cfg.defaultBackgroundStyle();
- if (bgStyle == KisConfig::LAYER) {
- radioBackgroundAsLayer->setChecked(true);
+ if (bgStyle == KisConfig::RASTER_LAYER) {
+ radioBackgroundAsRaster->setChecked(true);
+ } else if (bgStyle == KisConfig::FILL_LAYER) {
+ radioBackgroundAsFill->setChecked(true);
} else {
radioBackgroundAsProjection->setChecked(true);
}
fillPredefined();
switchPortraitLandscape();
// this makes the portrait and landscape buttons more
// obvious what is selected by changing the highlight color
QPalette p = QApplication::palette();
QPalette palette_highlight(p );
QColor c = p.color(QPalette::Highlight);
palette_highlight.setColor(QPalette::Button, c);
bnLandscape->setPalette(palette_highlight);
bnPortrait->setPalette(palette_highlight);
changeDocumentInfoLabel();
}
void KisCustomImageWidget::showEvent(QShowEvent *)
{
fillPredefined();
newDialogConfirmationButtonBox->button(QDialogButtonBox::Ok)->setFocus();
newDialogConfirmationButtonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
}
KisCustomImageWidget::~KisCustomImageWidget()
{
m_predefined.clear();
}
void KisCustomImageWidget::resolutionChanged(double res)
{
if (m_widthUnit.type() == KoUnit::Pixel) {
m_widthUnit.setFactor(res / 72.0);
m_width = m_widthUnit.fromUserValue(doubleWidth->value());
}
if (m_heightUnit.type() == KoUnit::Pixel) {
m_heightUnit.setFactor(res / 72.0);
m_height = m_heightUnit.fromUserValue(doubleHeight->value());
}
changeDocumentInfoLabel();
}
void KisCustomImageWidget::widthUnitChanged(int index)
{
doubleWidth->blockSignals(true);
m_widthUnit = KoUnit::fromListForUi(index, KoUnit::ListAll);
if (m_widthUnit.type() == KoUnit::Pixel) {
doubleWidth->setDecimals(0);
m_widthUnit.setFactor(doubleResolution->value() / 72.0);
} else {
doubleWidth->setDecimals(2);
}
doubleWidth->setValue(KoUnit::ptToUnit(m_width, m_widthUnit));
doubleWidth->blockSignals(false);
changeDocumentInfoLabel();
}
void KisCustomImageWidget::widthChanged(double value)
{
m_width = m_widthUnit.fromUserValue(value);
changeDocumentInfoLabel();
}
void KisCustomImageWidget::heightUnitChanged(int index)
{
doubleHeight->blockSignals(true);
m_heightUnit = KoUnit::fromListForUi(index, KoUnit::ListAll);
if (m_heightUnit.type() == KoUnit::Pixel) {
doubleHeight->setDecimals(0);
m_heightUnit.setFactor(doubleResolution->value() / 72.0);
} else {
doubleHeight->setDecimals(2);
}
doubleHeight->setValue(KoUnit::ptToUnit(m_height, m_heightUnit));
doubleHeight->blockSignals(false);
changeDocumentInfoLabel();
}
void KisCustomImageWidget::heightChanged(double value)
{
m_height = m_heightUnit.fromUserValue(value);
changeDocumentInfoLabel();
}
void KisCustomImageWidget::createImage()
{
newDialogConfirmationButtonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
KisDocument *doc = createNewImage();
if (doc) {
doc->setModified(false);
emit m_openPane->documentSelected(doc);
}
}
KisDocument* KisCustomImageWidget::createNewImage()
{
-
const KoColorSpace * cs = colorSpaceSelector->currentColorSpace();
if (cs->colorModelId() == RGBAColorModelID &&
cs->colorDepthId() == Integer8BitsColorDepthID) {
const KoColorProfile *profile = cs->profile();
if (profile->name().contains("linear") ||
profile->name().contains("scRGB") ||
profile->info().contains("linear") ||
profile->info().contains("scRGB")) {
int result =
QMessageBox::warning(this,
i18nc("@title:window", "Krita"),
i18n("Linear gamma RGB color spaces are not supposed to be used "
"in 8-bit integer modes. It is suggested to use 16-bit integer "
"or any floating point colorspace for linear profiles.\n\n"
"Press \"Ok\" to create a 8-bit integer linear RGB color space "
"or \"Cancel\" to return to the settings dialog."),
QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel);
if (result == QMessageBox::Cancel) {
dbgKrita << "Model RGB8" << "NOT SUPPORTED";
dbgKrita << ppVar(cs->name());
dbgKrita << ppVar(cs->profile()->name());
dbgKrita << ppVar(cs->profile()->info());
return 0;
}
}
}
KisDocument *doc = static_cast<KisDocument*>(KisPart::instance()->createDocument());
qint32 width, height;
double resolution;
resolution = doubleResolution->value() / 72.0; // internal resolution is in pixels per pt
width = static_cast<qint32>(0.5 + KoUnit::ptToUnit(m_width, KoUnit(KoUnit::Pixel, resolution)));
height = static_cast<qint32>(0.5 + KoUnit::ptToUnit(m_height, KoUnit(KoUnit::Pixel, resolution)));
QColor qc = cmbColor->color().toQColor();
qc.setAlpha(backgroundOpacity());
KoColor bgColor(qc, cs);
- bool backgroundAsLayer = radioBackgroundAsLayer->isChecked();
+ KisConfig::BackgroundStyle bgStyle = KisConfig::CANVAS_COLOR;
+ if( radioBackgroundAsRaster->isChecked() ){
+ bgStyle = KisConfig::RASTER_LAYER;
+ } else if( radioBackgroundAsFill->isChecked() ){
+ bgStyle = KisConfig::FILL_LAYER;
+ }
- doc->newImage(txtName->text(), width, height, cs, bgColor, backgroundAsLayer, intNumLayers->value(), txtDescription->toPlainText(), resolution);
+ doc->newImage(txtName->text(), width, height, cs, bgColor, bgStyle, intNumLayers->value(), txtDescription->toPlainText(), resolution);
KisConfig cfg(true);
cfg.setNumDefaultLayers(intNumLayers->value());
cfg.setDefaultBackgroundOpacity(backgroundOpacity());
cfg.setDefaultBackgroundColor(cmbColor->color().toQColor());
- cfg.setDefaultBackgroundStyle(backgroundAsLayer ? KisConfig::LAYER : KisConfig::PROJECTION);
+ cfg.setDefaultBackgroundStyle(bgStyle);
return doc;
}
void KisCustomImageWidget::setNumberOfLayers(int layers)
{
intNumLayers->setValue(layers);
}
quint8 KisCustomImageWidget::backgroundOpacity() const
{
qint32 opacity = sliderOpacity->value();
if (!opacity)
return 0;
return (opacity * 255) / 100;
}
void KisCustomImageWidget::setBackgroundOpacity(quint8 value) {
sliderOpacity->setValue((value * 100) / 255);
}
void KisCustomImageWidget::clipboardDataChanged()
{
}
void KisCustomImageWidget::fillPredefined()
{
cmbPredefined->clear();
m_predefined.clear();
cmbPredefined->addItem("");
QStringList definitions = KoResourcePaths::findAllResources("data", "predefined_image_sizes/*.predefinedimage", KoResourcePaths::Recursive);
definitions.sort();
if (!definitions.empty()) {
Q_FOREACH (const QString &definition, definitions) {
QFile f(definition);
f.open(QIODevice::ReadOnly);
if (f.exists()) {
QString xml = QString::fromUtf8(f.readAll());
KisPropertiesConfigurationSP predefined = new KisPropertiesConfiguration;
predefined->fromXML(xml);
if (predefined->hasProperty("name")
&& predefined->hasProperty("width")
&& predefined->hasProperty("height")
&& predefined->hasProperty("resolution")
&& predefined->hasProperty("x-unit")
&& predefined->hasProperty("y-unit")) {
m_predefined << predefined;
cmbPredefined->addItem(predefined->getString("name"));
}
}
}
}
cmbPredefined->setCurrentIndex(0);
}
void KisCustomImageWidget::predefinedClicked(int index)
{
if (index < 1 || index > m_predefined.size()) return;
KisPropertiesConfigurationSP predefined = m_predefined[index - 1];
txtPredefinedName->setText(predefined->getString("name"));
doubleResolution->setValue(predefined->getDouble("resolution"));
cmbWidthUnit->setCurrentIndex(predefined->getInt("x-unit"));
cmbHeightUnit->setCurrentIndex(predefined->getInt("y-unit"));
widthUnitChanged(cmbWidthUnit->currentIndex());
heightUnitChanged(cmbHeightUnit->currentIndex());
doubleWidth->setValue(predefined->getDouble("width"));
doubleHeight->setValue(predefined->getDouble("height"));
changeDocumentInfoLabel();
}
void KisCustomImageWidget::saveAsPredefined()
{
QString fileName = txtPredefinedName->text();
if (fileName.isEmpty()) {
return;
}
QString saveLocation = KoResourcePaths::saveLocation("data", "predefined_image_sizes/", true);
QFile f(saveLocation + '/' + fileName.replace(' ', '_').replace('(', '_').replace(')', '_').replace(':', '_') + ".predefinedimage");
f.open(QIODevice::WriteOnly | QIODevice::Truncate);
KisPropertiesConfigurationSP predefined = new KisPropertiesConfiguration();
predefined->setProperty("name", txtPredefinedName->text());
predefined->setProperty("width", doubleWidth->value());
predefined->setProperty("height", doubleHeight->value());
predefined->setProperty("resolution", doubleResolution->value());
predefined->setProperty("x-unit", cmbWidthUnit->currentIndex());
predefined->setProperty("y-unit", cmbHeightUnit->currentIndex());
QString xml = predefined->toXML();
f.write(xml.toUtf8());
f.flush();
f.close();
int i = 0;
bool found = false;
Q_FOREACH (KisPropertiesConfigurationSP pr, m_predefined) {
if (pr->getString("name") == txtPredefinedName->text()) {
found = true;
break;
}
++i;
}
if (found) {
m_predefined[i] = predefined;
}
else {
m_predefined.append(predefined);
cmbPredefined->addItem(txtPredefinedName->text());
}
}
void KisCustomImageWidget::setLandscape()
{
if (doubleWidth->value() < doubleHeight->value()) {
switchWidthHeight();
}
}
void KisCustomImageWidget::setPortrait()
{
if (doubleWidth->value() > doubleHeight->value()) {
switchWidthHeight();
}
}
void KisCustomImageWidget::switchWidthHeight()
{
double width = doubleWidth->value();
double height = doubleHeight->value();
doubleHeight->blockSignals(true);
doubleWidth->blockSignals(true);
cmbWidthUnit->blockSignals(true);
cmbHeightUnit->blockSignals(true);
doubleWidth->setValue(height);
doubleHeight->setValue(width);
cmbWidthUnit->setCurrentIndex(m_heightUnit.indexInListForUi(KoUnit::ListAll));
cmbHeightUnit->setCurrentIndex(m_widthUnit.indexInListForUi(KoUnit::ListAll));
doubleHeight->blockSignals(false);
doubleWidth->blockSignals(false);
cmbWidthUnit->blockSignals(false);
cmbHeightUnit->blockSignals(false);
switchPortraitLandscape();
widthChanged(doubleWidth->value());
heightChanged(doubleHeight->value());
changeDocumentInfoLabel();
}
void KisCustomImageWidget::switchPortraitLandscape()
{
if(doubleWidth->value() > doubleHeight->value())
bnLandscape->setChecked(true);
else
bnPortrait->setChecked(true);
}
void KisCustomImageWidget::changeDocumentInfoLabel()
{
qint64 width, height;
double resolution;
resolution = doubleResolution->value() / 72.0; // internal resolution is in pixels per pt
width = static_cast<qint64>(0.5 + KoUnit::ptToUnit(m_width, KoUnit(KoUnit::Pixel, resolution)));
height = static_cast<qint64>(0.5 + KoUnit::ptToUnit(m_height, KoUnit(KoUnit::Pixel, resolution)));
qint64 layerSize = width * height;
const KoColorSpace *cs = colorSpaceSelector->currentColorSpace();
int bitSize = 8 * cs->pixelSize(); //pixelsize is in bytes.
layerSize = layerSize * cs->pixelSize();
QString text = i18nc("arg1: width. arg2: height. arg3: colorspace name. arg4: size of a channel in bits. arg5: image size",
"This document will be %1 pixels by %2 pixels in %3, which means the pixel size is %4 bit. A single paint layer will thus take up %5 of RAM.",
width,
height,
cs->name(),
bitSize,
KFormat().formatByteSize(layerSize));
lblDocumentInfo->setText(text);
}
diff --git a/libs/ui/widgets/kis_custom_image_widget.h b/libs/ui/widgets/kis_custom_image_widget.h
index e57f9ff2c6..0457d1d4a0 100644
--- a/libs/ui/widgets/kis_custom_image_widget.h
+++ b/libs/ui/widgets/kis_custom_image_widget.h
@@ -1,101 +1,107 @@
/* This file is part of the Calligra project
* Copyright (C) 2005 Thomas Zander <zander@kde.org>
* Copyright (C) 2005 C. Boemann <cbo@boemann.dk>
*
* 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_CUSTOM_IMAGE_WIDGET_H
#define KIS_CUSTOM_IMAGE_WIDGET_H
#include "kis_global.h"
#include "KoUnit.h"
#include "kis_properties_configuration.h"
#include "KisOpenPane.h"
#include <ui_wdgnewimage.h>
class KisDocument;
class KisDocument;
enum CustomImageWidgetType { CUSTOM_DOCUMENT, NEW_IMG_FROM_CB };
class WdgNewImage : public QWidget, public Ui::WdgNewImage
{
Q_OBJECT
public:
WdgNewImage(QWidget *parent)
: QWidget(parent)
{
setupUi(this);
}
};
/**
* The 'Custom Document' widget in the Krita startup widget.
* This class embeds the image size and colorspace to allow the user to select the image properties
* for a new empty image document.
*/
class KisCustomImageWidget : public WdgNewImage
{
Q_OBJECT
public:
/**
* Constructor. Please note that this class is being used/created by KisDoc.
* @param parent the parent widget
- * @param doc the document that wants to be altered
+ * @param defWidth The defined width
+ * @param defHeight The defined height
+ * @param resolution The image resolution
+ * @param defColorModel The defined color model
+ * @param defColorDepth The defined color depth
+ * @param defColorProfile The defined color profile
+ * @param imageName the document that wants to be altered
*/
KisCustomImageWidget(QWidget *parent, qint32 defWidth, qint32 defHeight, double resolution, const QString & defColorModel, const QString & defColorDepth, const QString & defColorProfile, const QString & imageName);
~KisCustomImageWidget() override;
private Q_SLOTS:
void widthUnitChanged(int index);
void widthChanged(double value);
void heightUnitChanged(int index);
void heightChanged(double value);
void resolutionChanged(double value);
void clipboardDataChanged();
void predefinedClicked(int index);
void saveAsPredefined();
void setLandscape();
void setPortrait();
void switchWidthHeight();
void createImage();
void switchPortraitLandscape();
void changeDocumentInfoLabel();
protected:
KisDocument *createNewImage();
/// Set the number of layers that will be created
void setNumberOfLayers(int layers);
KisOpenPane *m_openPane;
private:
double m_width, m_height;
quint8 backgroundOpacity() const;
void setBackgroundOpacity(quint8 value);
void fillPredefined();
void showEvent(QShowEvent *) override;
KoUnit m_widthUnit, m_heightUnit;
QList<KisPropertiesConfigurationSP> m_predefined;
};
#endif
diff --git a/libs/ui/widgets/kis_image_from_clipboard_widget.h b/libs/ui/widgets/kis_image_from_clipboard_widget.h
index c0fa76975f..553f887d10 100644
--- a/libs/ui/widgets/kis_image_from_clipboard_widget.h
+++ b/libs/ui/widgets/kis_image_from_clipboard_widget.h
@@ -1,53 +1,59 @@
/* This file is part of the Calligra project
* Copyright (C) 2005 Thomas Zander <zander@kde.org>
* Copyright (C) 2005 C. Boemann <cbo@boemann.dk>
*
* 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_IMAGE_FROM_CLIPBOARD_WIDGET_H
#define KIS_IMAGE_FROM_CLIPBOARD_WIDGET_H
#include "kis_global.h"
#include "kis_properties_configuration.h"
#include "kis_custom_image_widget.h"
#include <QPointer>
/**
* The 'New image from clipboard' widget in the Krita startup widget.
- * This class is an exstension of the KisCustomImageWidget("Custom document" widget"
+ * This class is an extension of the KisCustomImageWidget("Custom document" widget"
*/
class KisImageFromClipboard : public KisCustomImageWidget
{
Q_OBJECT
public:
/**
* Constructor. Please note that this class is being used/created by KisDoc.
* @param parent the parent widget
- * @param doc the document that wants to be altered
+ * @param defWidth The defined width
+ * @param defHeight The defined height
+ * @param resolution The image resolution
+ * @param defColorModel The defined color model
+ * @param defColorDepth The defined color depth
+ * @param defColorProfile The defined color profile
+ * @param imageName the document that wants to be altered
*/
KisImageFromClipboard(QWidget *parent, qint32 defWidth, qint32 defHeight, double resolution, const QString & defColorModel, const QString & defColorDepth, const QString & defColorProfile, const QString & imageName);
~KisImageFromClipboard() override;
private Q_SLOTS:
void createImage();
void clipboardDataChanged();
private:
void createClipboardPreview();
};
#endif
diff --git a/libs/ui/widgets/kis_preset_live_preview_view.h b/libs/ui/widgets/kis_preset_live_preview_view.h
index 4135851cfc..cb697b1f48 100644
--- a/libs/ui/widgets/kis_preset_live_preview_view.h
+++ b/libs/ui/widgets/kis_preset_live_preview_view.h
@@ -1,150 +1,150 @@
/*
* Copyright (c) 2017 Scott Petrovic <scottpetrovic@gmail.com>
*
* 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_PRESET_LIVE_PREVIEW_
#define _KIS_PRESET_LIVE_PREVIEW_
#include <QImage>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QPainterPath>
#include <QGraphicsPixmapItem>
#include "kis_paintop_preset.h"
#include "KoColorSpaceRegistry.h"
#include "kis_paint_layer.h"
#include "kis_painter.h"
#include "kis_distance_information.h"
#include "kis_painting_information_builder.h"
#include <kis_image.h>
#include <kis_types.h>
#include <KoColor.h>
/**
* Widget for displaying a live brush preview of your
* selected brush. It listens for signalsetting changes
* that the brush preset outputs and updates the preview
* accordingly. This class can be added to a UI file
* similar to how a QGraphicsView is added
*/
class KisPresetLivePreviewView : public QGraphicsView
{
Q_OBJECT
public:
KisPresetLivePreviewView(QWidget *parent);
~KisPresetLivePreviewView();
/**
* @brief one time setup for initialization of many variables.
* This live preview might be in a UI file, so make sure to
* call this before starting to use it
*/
void setup();
/**
* @brief set the current preset from resource manager for the live preview to use.
* Good to call this every stroke update in case the preset has changed
- * @param the current preset from the resource manager
+ * @param preset the current preset from the resource manager
*/
void setCurrentPreset(KisPaintOpPresetSP preset);
void updateStroke();
private:
/// internally sets the image area for brush preview
KisImageSP m_image;
/// internally sets the layer area for brush preview
KisLayerSP m_layer;
/// internally sets the color space for brush preview
const KoColorSpace *m_colorSpace;
/// the color which is used for rendering the stroke
KoColor m_paintColor;
/// the scene that can add items like text and the brush stroke image
QGraphicsScene *m_brushPreviewScene;
/// holds the preview brush stroke data
QGraphicsPixmapItem *m_sceneImageItem;
/// holds the 'no preview available' text object
QGraphicsTextItem *m_noPreviewText;
/// holds the width and height of the image of the brush preview
/// Probably can later add set functions to make this customizable
/// It is hard-coded to 1200 x 400 for right now for image size
QRect m_canvasSize;
/// convenience variable used internally when positioning the objects
/// and points in the scene
QPointF m_canvasCenterPoint;
/// internal variables for constructing the stroke start and end shape
/// there are two points that construct the "S" curve with this
KisDistanceInformation m_currentDistance;
QPainterPath m_curvedLine;
KisPaintInformation m_curvePointPI1;
KisPaintInformation m_curvePointPI2;
/// internally stores the current preset.
/// See setCurrentPreset(KisPaintOpPresetSP preset)
/// for setting this externally
KisPaintOpPresetSP m_currentPreset;
/// holds the current zoom(scale) level of scene
float m_scaleFactor;
/// internal reference for internal brush size
/// used to check if our brush size has changed
/// do zooming and other things internally if it has changed
float m_currentBrushSize = 1.0;
/// the range of brush sizes that will control zooming in/out
const float m_minBrushVal = 10.0;
const float m_maxBrushVal = 100.0;
/// range of scale values. 1.0 == 100%
const qreal m_minScale = 1.0;
const qreal m_maxScale = 0.3;
/// multiplier that is used for lengthening the brush stroke points
const float m_minStrokeScale = 0.4; // for smaller brush stroke
const float m_maxStrokeScale = 1.0; // for larger brush stroke
/**
* @brief works as both clearing the previous stroke, providing
* striped backgrounds for smudging brushes, and text if there is no preview
*/
void paintBackground();
/**
* @brief creates and performs the actual stroke that goes on top of the background
* this is internally and should always be called after the paintBackground()
*/
void setupAndPaintStroke();
};
#endif
diff --git a/libs/ui/widgets/kis_wdg_generator.cpp b/libs/ui/widgets/kis_wdg_generator.cpp
index ea0b8a033e..39f38d2309 100644
--- a/libs/ui/widgets/kis_wdg_generator.cpp
+++ b/libs/ui/widgets/kis_wdg_generator.cpp
@@ -1,169 +1,174 @@
/* This file is part of the KDE project
* Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2008
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "widgets/kis_wdg_generator.h"
#include <QListWidget>
#include <QListWidgetItem>
#include <QLabel>
#include <QString>
#include <QGridLayout>
#include <QStringList>
#include <kis_image.h>
#include <kis_paint_device.h>
#include <generator/kis_generator.h>
#include <generator/kis_generator_registry.h>
#include <kis_config_widget.h>
#include <filter/kis_filter_configuration.h>
#include <KoColorSpaceRegistry.h>
#include "ui_wdggenerators.h"
class KisGeneratorItem : public QListWidgetItem
{
public:
KisGeneratorItem(const QString & text, QListWidget * parent = 0, int type = Type)
: QListWidgetItem(text, parent, type) {
}
KisGeneratorSP generator;
};
struct KisWdgGenerator::Private
{
public:
Private()
: centralWidget(0), view(0) {
}
QWidget * centralWidget; // Active generator settings widget
KisGeneratorSP currentGenerator;
Ui_WdgGenerators uiWdgGenerators;
KisPaintDeviceSP dev;
QGridLayout *widgetLayout;
KisViewManager *view;
};
KisWdgGenerator::KisWdgGenerator(QWidget * parent)
: QWidget(parent)
, d(new Private())
{
KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8(0));
}
KisWdgGenerator::~KisWdgGenerator()
{
delete d;
}
void KisWdgGenerator::initialize(KisViewManager *view)
{
d->view = view;
d->uiWdgGenerators.setupUi(this);
d->widgetLayout = new QGridLayout(d->uiWdgGenerators.centralWidgetHolder);
QStringList generatorNames = KisGeneratorRegistry::instance()->keys();
generatorNames.sort();
Q_FOREACH (const QString &generatorName, generatorNames) {
KisGeneratorSP generator = KisGeneratorRegistry::instance()->get(generatorName);
Q_ASSERT(generator);
KisGeneratorItem * item = new KisGeneratorItem(generator->name(),
d->uiWdgGenerators.lstGenerators,
QListWidgetItem::UserType + 1);
+
item->generator = generator;
}
connect(d->uiWdgGenerators.lstGenerators, SIGNAL(currentRowChanged(int)),
this, SLOT(slotGeneratorActivated(int)));
if (d->uiWdgGenerators.lstGenerators->count() > 0) {
d->uiWdgGenerators.lstGenerators->setCurrentRow(0);
}
}
void KisWdgGenerator::setConfiguration(const KisFilterConfigurationSP config)
{
for (int i = 0; i < d->uiWdgGenerators.lstGenerators->count(); ++i) {
KisGeneratorItem * item = static_cast<KisGeneratorItem*>(d->uiWdgGenerators.lstGenerators->item(i));
if (item->generator->id() == config->name()) {
// match!
slotGeneratorActivated(i);
d->uiWdgGenerators.lstGenerators->setCurrentRow(i);
KisConfigWidget * wdg = dynamic_cast<KisConfigWidget*>(d->centralWidget);
if (wdg) {
wdg->setConfiguration(config);
}
+
return;
}
}
}
KisFilterConfigurationSP KisWdgGenerator::configuration()
{
KisConfigWidget * wdg = dynamic_cast<KisConfigWidget*>(d->centralWidget);
if (wdg) {
KisFilterConfigurationSP config = dynamic_cast<KisFilterConfiguration*>(wdg->configuration().data());
if (config) {
return config;
}
} else {
return d->currentGenerator->defaultConfiguration();
}
return 0;
}
void KisWdgGenerator::slotGeneratorActivated(int row)
{
KisGeneratorItem *item = dynamic_cast<KisGeneratorItem*>(d->uiWdgGenerators.lstGenerators->item(row));
if (!item) {
d->centralWidget = new QLabel(i18n("No configuration options."), d->uiWdgGenerators.centralWidgetHolder);
}
else {
d->currentGenerator = item->generator;
delete d->centralWidget;
KisConfigWidget* widget =
d->currentGenerator->createConfigurationWidget(d->uiWdgGenerators.centralWidgetHolder, d->dev);
if (!widget) { // No widget, so display a label instead
d->centralWidget = new QLabel(i18n("No configuration options."),
d->uiWdgGenerators.centralWidgetHolder);
} else {
d->centralWidget = widget;
+
+ connect( widget, SIGNAL(sigConfigurationUpdated()), this, SIGNAL(previewConfiguration()));
+
widget->setView(d->view);
widget->setConfiguration(d->currentGenerator->defaultConfiguration());
}
}
d->widgetLayout->addWidget(d->centralWidget, 0 , 0);
d->uiWdgGenerators.centralWidgetHolder->setMinimumSize(d->centralWidget->minimumSize());
}
diff --git a/libs/ui/widgets/kis_wdg_generator.h b/libs/ui/widgets/kis_wdg_generator.h
index 06c83517b1..e62872cea4 100644
--- a/libs/ui/widgets/kis_wdg_generator.h
+++ b/libs/ui/widgets/kis_wdg_generator.h
@@ -1,65 +1,67 @@
/* This file is part of the KDE project
* Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2008
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KIS_WDG_GENERATOR_H
#define KIS_WDG_GENERATOR_H
#include <QWidget>
#include <kis_types.h>
class KisFilterConfiguration;
class KisViewManager;
+class KoColor;
/**
* A widget that allows users to select a generator and
* create a config object for it.
*
* XXX: make use of bookmarked configuration things, like
* in the filter widget.
*/
class KisWdgGenerator : public QWidget
{
Q_OBJECT
public:
KisWdgGenerator(QWidget * parent);
KisWdgGenerator(QWidget * parent, KisPaintDeviceSP dev);
~KisWdgGenerator() override;
void initialize(KisViewManager *view);
void setConfiguration(const KisFilterConfigurationSP config);
KisFilterConfigurationSP configuration();
+Q_SIGNALS:
+ void previewConfiguration();
private Q_SLOTS:
-
void slotGeneratorActivated(int);
private:
struct Private;
Private * const d;
};
#endif
diff --git a/libs/ui/widgets/kis_workspace_chooser.cpp b/libs/ui/widgets/kis_workspace_chooser.cpp
index a55f204ec0..0f33fe248d 100644
--- a/libs/ui/widgets/kis_workspace_chooser.cpp
+++ b/libs/ui/widgets/kis_workspace_chooser.cpp
@@ -1,236 +1,238 @@
/* This file is part of the KDE project
* Copyright (C) 2011 Sven Langkamp <sven.langkamp@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_workspace_chooser.h"
#include <QVBoxLayout>
#include <QAbstractItemDelegate>
#include <QPainter>
#include <QPushButton>
#include <QAction>
#include <QGridLayout>
#include <QLineEdit>
#include <QLabel>
#include <KisKineticScroller.h>
#include <klocalizedstring.h>
#include <KoResourceItemChooser.h>
#include <KoResourceItemView.h>
#include <KoResourceServerAdapter.h>
#include <resources/KoResource.h>
#include "KisResourceServerProvider.h"
#include "kis_workspace_resource.h"
#include "KisViewManager.h"
#include <kis_canvas_resource_provider.h>
#include <KisMainWindow.h>
#include <KisPart.h>
#include <KisWindowLayoutManager.h>
#include <dialogs/KisNewWindowLayoutDialog.h>
#include <kis_config.h>
class KisWorkspaceDelegate : public QAbstractItemDelegate
{
public:
KisWorkspaceDelegate(QObject * parent = 0) : QAbstractItemDelegate(parent) {}
~KisWorkspaceDelegate() override {}
/// reimplemented
void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override;
/// reimplemented
QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex &) const override {
return option.decorationSize;
}
};
void KisWorkspaceDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
if (!index.isValid())
return;
KoResource* workspace = static_cast<KoResource*>(index.internalPointer());
QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) ? QPalette::Active : QPalette::Disabled;
QPalette::ColorRole cr = (option.state & QStyle::State_Selected) ? QPalette::HighlightedText : QPalette::Text;
painter->setPen(option.palette.color(cg, cr));
if (option.state & QStyle::State_Selected) {
painter->fillRect(option.rect, option.palette.highlight());
}
else {
painter->fillRect(option.rect, option.palette.base());
}
painter->drawText(option.rect.x() + 5, option.rect.y() + painter->fontMetrics().ascent() + 5, workspace->name());
}
KisWorkspaceChooser::KisWorkspaceChooser(KisViewManager * view, QWidget* parent): QWidget(parent), m_view(view)
{
KoResourceServer<KisWorkspaceResource> * workspaceServer = KisResourceServerProvider::instance()->workspaceServer();
QSharedPointer<KoAbstractResourceServerAdapter> workspaceAdapter(new KoResourceServerAdapter<KisWorkspaceResource>(workspaceServer));
KoResourceServer<KisWindowLayoutResource> * windowLayoutServer = KisResourceServerProvider::instance()->windowLayoutServer();
QSharedPointer<KoAbstractResourceServerAdapter> windowLayoutAdapter(new KoResourceServerAdapter<KisWindowLayoutResource>(windowLayoutServer));
m_layout = new QGridLayout(this);
m_workspaceWidgets = createChooserWidgets(workspaceAdapter, i18n("Workspaces"));
m_windowLayoutWidgets = createChooserWidgets(windowLayoutAdapter, i18n("Window layouts"));
connect(m_workspaceWidgets.itemChooser, SIGNAL(resourceSelected(KoResource*)),
this, SLOT(workspaceSelected(KoResource*)));
connect(m_workspaceWidgets.saveButton, SIGNAL(clicked(bool)), this, SLOT(slotSaveWorkspace()));
connect(m_windowLayoutWidgets.itemChooser, SIGNAL(resourceSelected(KoResource*)),
this, SLOT(windowLayoutSelected(KoResource*)));
connect(m_windowLayoutWidgets.saveButton, SIGNAL(clicked(bool)), this, SLOT(slotSaveWindowLayout()));
}
KisWorkspaceChooser::ChooserWidgets KisWorkspaceChooser::createChooserWidgets(QSharedPointer<KoAbstractResourceServerAdapter> adapter, const QString &title)
{
ChooserWidgets widgets;
QLabel *titleLabel = new QLabel(this);
QFont titleFont;
titleFont.setBold(true);
titleLabel->setFont(titleFont);
titleLabel->setText(title);
widgets.itemChooser = new KoResourceItemChooser(adapter, this);
widgets.itemChooser->setItemDelegate(new KisWorkspaceDelegate(this));
widgets.itemChooser->setFixedSize(250, 250);
widgets.itemChooser->setRowHeight(30);
widgets.itemChooser->setColumnCount(1);
widgets.itemChooser->showTaggingBar(false);
widgets.saveButton = new QPushButton(i18n("Save"));
widgets.nameEdit = new QLineEdit(this);
widgets.nameEdit->setPlaceholderText(i18n("Insert name"));
widgets.nameEdit->setClearButtonEnabled(true);
int firstRow = m_layout->rowCount();
m_layout->addWidget(titleLabel, firstRow, 0, 1, 2);
m_layout->addWidget(widgets.itemChooser, firstRow + 1, 0, 1, 2);
m_layout->addWidget(widgets.nameEdit, firstRow + 2, 0, 1, 1);
m_layout->addWidget(widgets.saveButton, firstRow + 2, 1, 1, 1);
return widgets;
}
KisWorkspaceChooser::~KisWorkspaceChooser()
{
}
void KisWorkspaceChooser::slotSaveWorkspace()
{
if (!m_view->qtMainWindow()) {
return;
}
KoResourceServer<KisWorkspaceResource> * rserver = KisResourceServerProvider::instance()->workspaceServer();
KisWorkspaceResource* workspace = new KisWorkspaceResource(QString());
workspace->setDockerState(m_view->qtMainWindow()->saveState());
m_view->resourceProvider()->notifySavingWorkspace(workspace);
workspace->setValid(true);
QString saveLocation = rserver->saveLocation();
QString name = m_workspaceWidgets.nameEdit->text();
bool newName = false;
if(name.isEmpty()) {
newName = true;
name = i18n("Workspace");
}
QFileInfo fileInfo(saveLocation + name + workspace->defaultFileExtension());
int i = 1;
while (fileInfo.exists()) {
fileInfo.setFile(saveLocation + name + QString("%1").arg(i) + workspace->defaultFileExtension());
i++;
}
workspace->setFilename(fileInfo.filePath());
if(newName) {
name = i18n("Workspace %1", i);
}
workspace->setName(name);
rserver->addResource(workspace);
}
void KisWorkspaceChooser::workspaceSelected(KoResource *resource)
{
if (!m_view->qtMainWindow()) {
return;
}
+ KisConfig cfg(false);
+ cfg.writeEntry("CurrentWorkspace", resource->name());
KisWorkspaceResource* workspace = static_cast<KisWorkspaceResource*>(resource);
KisMainWindow *mainWindow = qobject_cast<KisMainWindow*>(m_view->qtMainWindow());
mainWindow->restoreWorkspace(workspace);
}
void KisWorkspaceChooser::slotSaveWindowLayout()
{
KisMainWindow *thisWindow = qobject_cast<KisMainWindow*>(m_view->qtMainWindow());
if (!thisWindow) return;
KisNewWindowLayoutDialog dlg;
dlg.setName(m_windowLayoutWidgets.nameEdit->text());
dlg.exec();
if (dlg.result() != QDialog::Accepted) return;
QString name = dlg.name();
bool showImageInAllWindows = dlg.showImageInAllWindows();
bool primaryWorkspaceFollowsFocus = dlg.primaryWorkspaceFollowsFocus();
auto *layout = KisWindowLayoutResource::fromCurrentWindows(name, KisPart::instance()->mainWindows(), showImageInAllWindows, primaryWorkspaceFollowsFocus, thisWindow);
layout->setValid(true);
KisWindowLayoutManager::instance()->setShowImageInAllWindowsEnabled(showImageInAllWindows);
KisWindowLayoutManager::instance()->setPrimaryWorkspaceFollowsFocus(primaryWorkspaceFollowsFocus, thisWindow->id());
KoResourceServer<KisWindowLayoutResource> * rserver = KisResourceServerProvider::instance()->windowLayoutServer();
QString saveLocation = rserver->saveLocation();
bool newName = false;
if (name.isEmpty()) {
newName = true;
name = i18n("Window Layout");
}
QFileInfo fileInfo(saveLocation + name + layout->defaultFileExtension());
int i = 1;
while (fileInfo.exists()) {
fileInfo.setFile(saveLocation + name + QString("%1").arg(i) + layout->defaultFileExtension());
i++;
}
layout->setFilename(fileInfo.filePath());
if (newName) {
name = i18n("Window Layout %1", i);
}
layout->setName(name);
rserver->addResource(layout);
}
void KisWorkspaceChooser::windowLayoutSelected(KoResource * resource)
{
auto *layout = static_cast<KisWindowLayoutResource*>(resource);
layout->applyLayout();
}
diff --git a/libs/vectorimage/libemf/EmfOutput.h b/libs/vectorimage/libemf/EmfOutput.h
index 33c63982e8..101d5dd1e5 100644
--- a/libs/vectorimage/libemf/EmfOutput.h
+++ b/libs/vectorimage/libemf/EmfOutput.h
@@ -1,554 +1,554 @@
/*
Copyright 2008 Brad Hards <bradh@frogmouth.net>
Copyright 2009 Inge Wallin <inge@lysator.liu.se>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef EMFOUTPUT_H
#define EMFOUTPUT_H
#include "kritavectorimage_export.h"
#include <QList>
#include <QPainter>
#include <QRect> // also provides QSize, QPoint
#include <QString>
#include <QVariant>
#include "EmfEnums.h"
#include "EmfHeader.h"
#include "EmfRecords.h"
/**
\file
Primary definitions for EMF output strategies
*/
/**
Namespace for Enhanced Metafile (EMF) classes
*/
namespace Libemf
{
/**
Abstract output strategy for EMF Parser
*/
class KRITAVECTORIMAGE_EXPORT AbstractOutput
{
public:
AbstractOutput() {};
virtual ~AbstractOutput() {};
/**
Initialisation routine
\param header the EMF Header record
*/
virtual void init( const Header *header ) = 0;
/**
Cleanup routine
This function is called when the painting is done. Any
initializations that are done in init() can be undone here if
necessary.
\param header the EMF Header record
*/
virtual void cleanup( const Header *header ) = 0;
/**
Close-out routine
*/
virtual void eof() = 0;
/**
Handler for the EMR_SETPIXELV record type
This fills a specified pixel with a particular color
\param point the point to fill
\param red the red component of the color
\param green the green component of the color
\param blue the blue component of the color
\param reserved reserved component of the color
*/
virtual void setPixelV( QPoint &point, quint8 red, quint8 green, quint8 blue, quint8 reserved ) = 0;
/**
Handler for the EMR_CREATEPEN record type
This creates a pen object (at position ihPen) using the specified parameters.
\param ihPen the internal handle for the pen to be created
\param penStyle the pen configuration (style, type) - see the PenStyle enumeration
\param x the width of the pen
\param y reserved value - ignore this
\param red the red component of the pen color
\param green the green component of the pen color
\param blue the blue component of the pen color
\param reserved reserved value - ignore this
*/
virtual void createPen( quint32 ihPen, quint32 penStyle, quint32 x, quint32 y,
quint8 red, quint8 green, quint8 blue, quint8 reserved ) = 0;
/**
Handler for the EMR_CREATEBRUSHINDIRECT record type
*/
virtual void createBrushIndirect( quint32 ihBrush, quint32 BrushStyle, quint8 red,
quint8 green, quint8 blue, quint8 reserved,
quint32 BrushHatch ) = 0;
virtual void createMonoBrush( quint32 ihBrush, Bitmap *bitmap ) = 0;
/**
Handler for the EMR_SETMAPMODE record type.
From [MS-EMF]:\n
<em>The EMR_SETMAPMODE record specifies the mapping mode of the
playback device context. The mapping mode specifies the unit of
measure used to transform page-space units into device-space
units, and also specifies the orientation of the device's x and
y axes.</em>
The valid mapping modes are:
- 0x01 (MM_TEXT): Each logical unit is mapped to one device pixel.
- 0x02 (MM_LOMETRIC): Each logical unit is mapped to 0.1 millimeter
- 0x03 (MM_HIMETRIC): Each logical unit is mapped to 0.01 millimeter.
- 0x04 (MM_LOENGLISH): Each logical unit is mapped to 0.01 inch.
- 0x05 (MM_HIENGLISH): Each logical unit is mapped to 0.001 inch.
- 0x06 (MM_TWIPS): Each logical unit is mapped to one twentieth of a printer's point (1/1440 inch).
- 0x07 (MM_ISOTROPIC): Logical units are mapped to arbitrary units with equally-scaled axes; that is, one unit along the x-axis is equal to one unit along the y-axis.
- 0x08 (MM_ANISOTROPIC): Logical units are mapped to arbitrary units with arbitrarily scaled axes.
Note that increasing x is to the right, and increasing y is up,
except for MM_TEXT, where it is down.
You can expect to get several calls to this function
(e.g. MM_ANISOTROPIC, followed by MM_HIMETRIC). If you maintain
state based on this call, you probably need to maintain the
dimensions / direction separate from the isotropic /
anisotropic state.
\param mapMode the mapping mode value
*/
virtual void setMapMode( const quint32 mapMode ) = 0;
/**
Handler for the EMR_SETMETARGN record type
*/
virtual void setMetaRgn() = 0;
/**
Handler for the EMR_SETWINDOWORGEX record type
\param origin the origin of the window in logical coordinates
*/
virtual void setWindowOrgEx( const QPoint &origin ) = 0;
/**
Handler for the EMR_SETWINDOWEXTEX record type
\param size the size of the window in logical coordinates
*/
virtual void setWindowExtEx( const QSize &size ) = 0;
/**
Handler for the EMR_SETVIEWPORTORGEX record type
\param origin the origin of the viewport in logical coordinates
*/
virtual void setViewportOrgEx( const QPoint &origin ) = 0;
/**
Handler for the EMR_SETVIEWPORTEXTEX record type
\param size the size of the viewport in logical coordinates
*/
virtual void setViewportExtEx( const QSize &size ) = 0;
/**
Handler for the EMR_SETBKMODE record type
\param backgroundMode the background fill mode
*/
virtual void setBkMode( const quint32 backgroundMode ) = 0;
/**
Handler for the EMR_SETPOLYFILLMODE record type
\param polyFillMode the fill mode
*/
virtual void setPolyFillMode( const quint32 polyFillMode ) = 0;
/**
Handler for the EMR_SETLAYOUT record type
\param layoutMode the layout mode
*/
virtual void setLayout( const quint32 layoutMode ) = 0;
/**
Handler for the EMR_MODIFYWORLDTRANSFORM record type
There are a range of modes:
- 0x01 (MWT_IDENTIFY): Reset current world transform to identity matrix
- 0x02 (MWT_LEFTMULTIPLY): Left multiply this matrix with current matrix.
- 0x03 (MWT_RIGHTMULTIPLY): Right multiply current matrix with this matrix.
- 0x04 (MWT_SET): Set the world transform.
\param mode the mode to use.
\param M11
\param M12
\param M21
\param M22
\param Dx
\param Dy
*/
virtual void modifyWorldTransform(quint32 mode, float M11, float M12,
float M21, float M22, float Dx, float Dy ) = 0;
/**
Handler for the EMR_SETWORLDTRANSFORM record type
\param M11
\param M12
\param M21
\param M22
\param Dx
\param Dy
*/
virtual void setWorldTransform( float M11, float M12, float M21,
float M22, float Dx, float Dy ) = 0;
/**
Select a previously created (or stock) object
\param ihObject the reference number for the object to select
*/
virtual void selectObject( const quint32 ihObject ) = 0;
/**
Delete a previously created (or stock) object
\param ihObject the reference number for the object to delete
*/
virtual void deleteObject( const quint32 ihObject ) = 0;
/**
Handler for the EMR_ARC record type
\param box the bounding box
\param start the coordinates of the point that defines the first radial end point
\param end the coordinates of the point that defines the second radial end point
*/
virtual void arc( const QRect &box, const QPoint &start, const QPoint &end ) = 0;
/**
Handler for the EMR_CHORD record type
\param box the bounding box
\param start the coordinates of the point that defines the first radial end point
\param end the coordinates of the point that defines the second radial end point
*/
virtual void chord( const QRect &box, const QPoint &start, const QPoint &end ) = 0;
/**
Handler for the EMR_PIE record type
\param box the bounding box
\param start the coordinates of the point that defines the first radial end point
\param end the coordinates of the point that defines the second radial end point
*/
virtual void pie( const QRect &box, const QPoint &start, const QPoint &end ) = 0;
/**
Handler for the EMR_ELLIPSE record type
\param box the bounding box for the ellipse
*/
virtual void ellipse( const QRect &box ) = 0;
/**
Handler for the EMR_RECTANGLE record type
\param box the bounding box for the rectangle
*/
virtual void rectangle( const QRect &box ) = 0;
/**
Handler for the EMR_SETTEXTALIGN record type
The textAlignMode is a bit mask, see [MS-WMF] Section 2.1.2.3 for
values if the text has a horizontal baseline, [MS-WMF] Section
2.1.2.4 if the text has a vertical baseline.
\param textAlignMode the text alignment mode
*/
virtual void setTextAlign( const quint32 textAlignMode ) = 0;
/**
Handler for the EMR_SETTEXTCOLOR record type
\param red the red component of the text color
\param green the blue component of the text color
\param blue the blue component of the text color
\param reserved an unused value - ignore this
*/
virtual void setTextColor( const quint8 red, const quint8 green, const quint8 blue,
const quint8 reserved ) = 0;
/**
Handler for the EMR_SETBKCOLOR record type
\param red the red component of the background color
\param green the blue component of the background color
\param blue the blue component of the background color
\param reserved an unused value - ignore this
*/
virtual void setBkColor( const quint8 red, const quint8 green, const quint8 blue,
const quint8 reserved ) = 0;
/**
Handler for the EMR_EXTCREATEFONTINDIRECTW record type
- \param extCreateFontIndirectWRecord the contents of the
+ \param extCreateFontIndirectW the contents of the
EMR_EXTCREATEFONTINDIRECTW record
*/
virtual void extCreateFontIndirectW( const ExtCreateFontIndirectWRecord &extCreateFontIndirectW ) = 0;
/**
Handler for text rendering, as described in the
EMR_EXTTEXTOUTW and EMR_EXTTEXTOUTA record types.
\param bounds the bounds used for e.g. clipping
- \param texObject The object describing the text.
+ \param textObject The object describing the text.
*/
virtual void extTextOut( const QRect &bounds, const EmrTextObject &textObject ) = 0;
/**
Handler for the EMR_BEGINPATH record type
*/
virtual void beginPath() = 0;
/**
Handler for the EMR_CLOSEFIGURE record type
*/
virtual void closeFigure() = 0;
/**
Handler for the EMR_ENDPATH record type
*/
virtual void endPath() = 0;
/**
Handler for the EMR_MOVETOEX record type
\param x the X coordinate of the point to move to
\param y the Y coordinate of the point to move to
*/
virtual void moveToEx( const qint32 x, const qint32 y ) = 0;
/**
Handler for the EMR_SAVEDC record type
*/
virtual void saveDC() = 0;
/**
Handler for the EMR_RESTOREDC record type
\param savedDC the device context to restore to (always negative)
*/
virtual void restoreDC( const qint32 savedDC ) = 0;
/**
Handler for the EMR_LINETO record type
\param finishPoint the point to draw to
*/
virtual void lineTo( const QPoint &finishPoint ) = 0;
/**
Handler for the EMR_ARCTO record type
\param box the bounding box
\param start the coordinates of the point that defines the first radial end point
\param end the coordinates of the point that defines the second radial end point
*/
virtual void arcTo( const QRect &box, const QPoint &start, const QPoint &end ) = 0;
/**
Handler for the EMR_POLYGON16 record type.
This record type specifies how to output a multi-segment filled
polygon.
\param bounds the bounding rectangle for the line segment
\param points the sequence of points that describe the polygon
*/
virtual void polygon16( const QRect &bounds, const QList<QPoint> points ) = 0;
/**
Handler for the EMR_POLYLINE record type.
This record type specifies how to output a multi-segment line
(unfilled polyline).
\param bounds the bounding rectangle for the line segments
\param points the sequence of points that describe the line
\note the line is not meant to be closed (i.e. do not connect
the last point to the first point) or filled.
*/
virtual void polyLine( const QRect &bounds, const QList<QPoint> points ) = 0;
/**
Handler for the EMR_POLYLINE16 record type.
This record type specifies how to output a multi-segment line
(unfilled polyline).
\param bounds the bounding rectangle for the line segment
\param points the sequence of points that describe the line
\note the line is not meant to be closed (i.e. do not connect
the last point to the first point) or filled.
*/
virtual void polyLine16( const QRect &bounds, const QList<QPoint> points ) = 0;
/**
Handler for the EMR_POLYPOLYLINE16 record type.
This record type specifies how to output a set of multi-segment line
(unfilled polylines). Each vector in the list is a separate polyline
\param bounds the bounding rectangle for the line segments
\param points the sequence of points that describe the line
\note the lines are not meant to be closed (i.e. do not connect
the last point to the first point) or filled.
*/
virtual void polyPolyLine16( const QRect &bounds, const QList< QVector< QPoint > > &points ) = 0;
/**
Handler for the EMR_POLYPOLYGON16 record type.
This record type specifies how to output a set of multi-segment polygons.
Each vector in the list is a separate filled polygon.
\param bounds the bounding rectangle for the polygons
\param points the sequence of points that describe the polygons
*/
virtual void polyPolygon16( const QRect &bounds, const QList< QVector< QPoint > > &points ) = 0;
/**
Handler for the EMR_POLYLINETO16 record type.
This record type specifies how to output a multi-segment set of
lines (unfilled).
\param bounds the bounding rectangle for the bezier curves
\param points the sequence of points that describe the curves
\note the line is not meant to be closed (i.e. do not connect
the last point to the first point) or filled.
*/
virtual void polyLineTo16( const QRect &bounds, const QList<QPoint> points ) = 0;
/**
Handler for the EMR_POLYBEZIERO16 record type.
This record type specifies how to output a multi-segment set of
bezier curves (unfilled).
\param bounds the bounding rectangle for the bezier curves
\param points the sequence of points that describe the curves
\note the line is not meant to be closed (i.e. do not connect
the last point to the first point) or filled.
*/
virtual void polyBezier16( const QRect &bounds, const QList<QPoint> points ) = 0;
/**
Handler for the EMR_POLYLINETO16 record type.
This record type specifies how to output a multi-segment set of
bezier curves (unfilled), starting at the current point.
\param bounds the bounding rectangle for the bezier curves
\param points the sequence of points that describe the curves
\note the line is not meant to be closed (i.e. do not connect
the last point to the first point) or filled.
*/
virtual void polyBezierTo16( const QRect &bounds, const QList<QPoint> points ) = 0;
/**
Handler for the EMR_FILLPATH record type.
\param bounds the bounding rectangle for the region to be filled.
*/
virtual void fillPath( const QRect &bounds ) = 0;
/**
Handler for the EMR_STROKEANDFILLPATH record type.
\param bounds the bounding rectangle for the region to be stroked / filled
*/
virtual void strokeAndFillPath( const QRect &bounds ) = 0;
/**
Handler for the EMR_STROKEPATH record type.
\param bounds the bounding rectangle for the region to be stroked
*/
virtual void strokePath( const QRect &bounds ) = 0;
/**
Handler for the EMR_SETCLIPPATH record type.
See [MS-EMF] Section 2.1.29 for valid ways to set the path.
\param regionMode how to set the clipping path.
*/
virtual void setClipPath( const quint32 regionMode ) = 0;
/**
Handler for the EMR_BITBLT record type
\param bitBltRecord contents of the record type
*/
virtual void bitBlt( BitBltRecord &bitBltRecord ) = 0;
/**
Handler for the EMR_STRETCHBLTMODE record type
\param stretchMode the stretch mode
*/
virtual void setStretchBltMode( const quint32 stretchMode ) = 0;
/**
Handler for the EMR_STRETCHDIBITS record type
\param stretchDiBitsRecord contents of the record type
*/
virtual void stretchDiBits( StretchDiBitsRecord &stretchDiBitsRecord ) = 0;
};
}
#endif
diff --git a/libs/vectorimage/libemf/EmfRecords.h b/libs/vectorimage/libemf/EmfRecords.h
index 03235a4488..e2f0bef9a0 100644
--- a/libs/vectorimage/libemf/EmfRecords.h
+++ b/libs/vectorimage/libemf/EmfRecords.h
@@ -1,331 +1,333 @@
/*
Copyright 2008 Brad Hards <bradh@frogmouth.net>
Copyright 2009 Inge Wallin <inge@lysator.liu.se>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef EMFRECORDS_H
#define EMFRECORDS_H
#include <QDataStream>
#include <QColor>
#include <QImage>
#include <QRect> // also provides QSize
#include <QString>
#include "Bitmap.h"
/**
\file
Primary definitions for EMF Records
*/
/**
Namespace for Enhanced Metafile (EMF) classes
*/
namespace Libemf
{
class EmrTextObject;
/*****************************************************************************/
/**
Simple representation of an EMR_BITBLT record
See MS-EMF Section 2.3.1.2 for details
*/
class BitBltRecord
{
public:
/**
Constructor for record type
\param stream the stream to read the record structure from
+ \param recordSize the size of one record
*/
BitBltRecord( QDataStream &stream, quint32 recordSize );
~BitBltRecord();
/**
The X origin of the destination rectangle
*/
qint32 xDest() const { return m_xDest; };
/**
The Y origin of the destination rectangle
*/
qint32 yDest() const { return m_yDest; };
/**
The width of the destination rectangle
*/
qint32 cxDest() const { return m_cxDest; };
/**
The height of the destination rectangle
*/
qint32 cyDest() const { return m_cyDest; };
quint32 rasterOperation() const { return m_BitBltRasterOperation; }
QColor bkColorSrc() const { return QColor(m_red, m_green, m_blue, m_reserved); }
/**
The destination rectangle
*/
QRect destinationRectangle() const { return QRect( xDest(), yDest(), cxDest(), cyDest() ); };
/**
The image to display
*/
QImage image();
/**
Whether there is a valid image in this BitBlt record
*/
bool hasImage() const;
private:
// No copying for now, because we will get into trouble with the pointers.
// The remedy is to write a real operator=() and BitBltRecord(BitBltRecord&).
explicit BitBltRecord(BitBltRecord&);
BitBltRecord &operator=(BitBltRecord&);
private:
QRect m_bounds;
qint32 m_xDest;
qint32 m_yDest;
qint32 m_cxDest;
qint32 m_cyDest;
quint32 m_BitBltRasterOperation;
qint32 m_xSrc;
qint32 m_ySrc;
QTransform m_XFormSrc;
// Background color - elements below
quint8 m_red;
quint8 m_green;
quint8 m_blue;
quint8 m_reserved;
// Color table interpretation
quint32 m_UsageSrc;
// The source bitmap meta data
quint32 m_offBmiSrc;
quint32 m_cbBmiSrc;
quint32 m_offBitsSrc;
quint32 m_cbBitsSrc;
Bitmap *m_bitmap; // The source bitmap
//QByteArray m_imageData;
//QImage *m_image;
};
/*****************************************************************************/
/**
Simple representation of an EMR_STRETCHDIBITS record
See MS-EMF Section 2.3.1.7 for details
*/
class StretchDiBitsRecord
{
public:
/**
Constructor for record type
\param stream the stream to read the record structure from
+ \param recordSize the size of one record
*/
StretchDiBitsRecord( QDataStream &stream, quint32 recordSize );
~StretchDiBitsRecord();
/**
The bounds of the affected area, in device units
*/
QRect bounds() const;
/**
The X origin of the destination rectangle
*/
qint32 xDest() const { return m_xDest; };
/**
The Y origin of the destination rectangle
*/
qint32 yDest() const { return m_yDest; };
/**
The width of the destination rectangle
*/
qint32 cxDest() const { return m_cxDest; };
/**
The height of the destination rectangle
*/
qint32 cyDest() const { return m_cyDest; };
/**
The destination rectangle
*/
QRect destinationRectangle() const { return QRect( xDest(), yDest(), cxDest(), cyDest() ); };
/**
The X origin of the source rectangle
*/
qint32 xSrc() const { return m_xSrc; };
/**
The Y origin of the source rectangle
*/
qint32 ySrc() const { return m_ySrc; };
/**
The width of the source rectangle
*/
qint32 cxSrc() const { return m_cxSrc; };
/**
The height of the source rectangle
*/
qint32 cySrc() const { return m_cySrc; };
/**
The source rectangle
*/
QRect sourceRectangle() const { return QRect( xSrc(), ySrc(), cxSrc(), cySrc() ); };
/**
The raster operation
*/
qint32 rasterOperation() const { return m_BitBltRasterOperation; };
quint32 usageSrc() const { return m_UsageSrc; };
/**
The image to display
*/
QImage image();
/**
Whether there is a valid image in this StretchDiBitsRecord record
*/
bool hasImage() const;
private:
// No copying for now, because we will get into trouble with the pointers.
// The remedy is to write a real operator=() and StretchDiBitsRecord(StretchDiBitsRecord&).
explicit StretchDiBitsRecord(StretchDiBitsRecord&);
StretchDiBitsRecord &operator=(StretchDiBitsRecord&);
private:
QRect m_Bounds;
qint32 m_xDest;
qint32 m_yDest;
qint32 m_xSrc;
qint32 m_ySrc;
qint32 m_cxSrc;
qint32 m_cySrc;
quint32 m_offBmiSrc;
quint32 m_cbBmiSrc;
quint32 m_offBitsSrc;
quint32 m_cbBitsSrc;
quint32 m_UsageSrc;
quint32 m_BitBltRasterOperation;
qint32 m_cxDest;
qint32 m_cyDest;
Bitmap *m_bitmap; // The source bitmap
};
/*****************************************************************************/
/**
Simple representation of an EMR_EXTCREATEFONTINDIRECTW record
See MS-EMF Section 2.3.7.8 for details
*/
class ExtCreateFontIndirectWRecord
{
public:
/**
Constructor for record type
\param stream the stream to read the record structure from
\param size the number of bytes in this record
*/
ExtCreateFontIndirectWRecord( QDataStream &stream, quint32 size );
~ExtCreateFontIndirectWRecord();
/**
The font handle index
*/
quint32 ihFonts() const { return m_ihFonts; };
/**
The height of the font
*/
qint32 height() const { return m_height; };
/**
Whether this is a italic font
*/
quint8 italic() const { return m_italic; };
/**
Whether this is a underlined font
*/
quint8 underline() const { return m_underline; };
/**
The weight of this font
*/
quint32 weight() const { return m_weight; };
/**
The name of the font face
*/
QString fontFace() const { return m_facename; };
private:
quint32 m_ihFonts;
qint32 m_height;
qint32 m_width;
qint32 m_escapement;
qint32 m_orientation;
qint32 m_weight;
quint8 m_italic;
quint8 m_underline;
quint8 m_strikeout;
quint8 m_charSet;
quint8 m_outPrecision;
quint8 m_clipPrecision;
quint8 m_quality;
quint8 m_pitchAndFamily;
QString m_facename;
QString m_fullName;
QString m_style;
QString m_script;
// Routine to throw away a specific number of bytes
void soakBytes( QDataStream &stream, int numBytes );
};
}
#endif
diff --git a/libs/vectorimage/libsvm/SvmAbstractBackend.h b/libs/vectorimage/libsvm/SvmAbstractBackend.h
index 6e6de55a51..d053b0c1de 100644
--- a/libs/vectorimage/libsvm/SvmAbstractBackend.h
+++ b/libs/vectorimage/libsvm/SvmAbstractBackend.h
@@ -1,105 +1,105 @@
/*
Copyright 2009 Inge Wallin <inge@lysator.liu.se>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SVMABSTRACTBACKEND_H
#define SVMABSTRACTBACKEND_H
#include "kritavectorimage_export.h"
#include "SvmEnums.h"
#include "SvmStructs.h"
#include "SvmGraphicsContext.h"
class QPoint;
class QRect;
class QPolygon;
class QString;
/**
\file
Primary definitions for SVM output backend
*/
/**
Namespace for StarView Metafile (SVM) classes
*/
namespace Libsvm
{
/**
Abstract output strategy for SVM Parser
*/
class KRITAVECTORIMAGE_EXPORT SvmAbstractBackend
{
public:
SvmAbstractBackend() {};
virtual ~SvmAbstractBackend() {};
/**
Initialisation routine
\param header the SVM Header record
*/
virtual void init(const SvmHeader &header) = 0;
/**
Cleanup routine
This function is called when the parsing is done. Any
initializations that are done in init() can be undone here if
necessary.
*/
virtual void cleanup() = 0;
/**
Close-out routine
*/
virtual void eof() = 0;
virtual void rect(SvmGraphicsContext &context, const QRect &rect) = 0;
/**
Handler META_POLYLINE_ACTION
This action type specifies how to output a multi-segment line
(unfilled polyline).
\param context the graphics context to be used when drawing the polyline
- \param polygon the sequence of points that describe the line
+ \param polyline the sequence of points that describe the line
\note the line is not meant to be closed nor filled, i.e. do
not connect the last point to the first point.
*/
virtual void polyLine(SvmGraphicsContext &context, const QPolygon &polyline) = 0;
virtual void polygon(SvmGraphicsContext &context, const QPolygon &polygon) = 0;
virtual void polyPolygon(SvmGraphicsContext &context, const QList<QPolygon> &polyPolygon) = 0;
virtual void textArray(SvmGraphicsContext &context,
const QPoint &point, const QString &string,
quint16 startIndex, quint16 len,
quint32 dxArrayLen, qint32 *dxArray) = 0;
};
}
#endif
diff --git a/libs/vectorimage/libsvm/SvmPainterBackend.h b/libs/vectorimage/libsvm/SvmPainterBackend.h
index fc89b0b7d7..2d2e285113 100644
--- a/libs/vectorimage/libsvm/SvmPainterBackend.h
+++ b/libs/vectorimage/libsvm/SvmPainterBackend.h
@@ -1,116 +1,116 @@
/*
Copyright 2009 Inge Wallin <inge@lysator.liu.se>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SVMPAINTERBACKEND_H
#define SVMPAINTERBACKEND_H
#include "SvmAbstractBackend.h"
#include "kritavectorimage_export.h"
#include <QSize>
#include <QTransform>
#include "SvmEnums.h"
#include "SvmStructs.h"
#include "SvmGraphicsContext.h"
class QRect;
class QPolygon;
class QPainter;
/**
\file
Primary definitions for SVM output backend
*/
/**
Namespace for StarView Metafile (SVM) classes
*/
namespace Libsvm
{
/**
Painter output strategy for SVM Parser
*/
class KRITAVECTORIMAGE_EXPORT SvmPainterBackend : public SvmAbstractBackend
{
public:
SvmPainterBackend(QPainter *painter, const QSize &outputSize);
~SvmPainterBackend() override;
/**
Initialisation routine
\param header the SVM Header record
*/
void init(const SvmHeader &header) override;
/**
Cleanup routine
This function is called when the painting is done. Any
initializations that are done in init() can be undone here if
necessary.
*/
void cleanup() override;
/**
Close-out routine
*/
void eof() override;
void rect( SvmGraphicsContext &context, const QRect &rect ) override;
/**
Handler META_POLYLINE_ACTION
This action type specifies how to output a multi-segment line
(unfilled polyline).
\param context the graphics context to be used when drawing the polyline
- \param polygon the sequence of points that describe the line
+ \param polyline the sequence of points that describe the line
\note the line is not meant to be closed (i.e. do not connect
the last point to the first point) or filled.
*/
void polyLine(SvmGraphicsContext &context, const QPolygon &polyline) override;
void polygon(SvmGraphicsContext &context, const QPolygon &polygon) override;
void polyPolygon(SvmGraphicsContext &context, const QList<QPolygon> &polyPolygon) override;
void textArray(SvmGraphicsContext &context,
const QPoint &point, const QString &string,
quint16 startIndex, quint16 len,
quint32 dxArrayLen, qint32 *dxArray) override;
private:
void updateFromGraphicscontext(SvmGraphicsContext &context);
private:
QPainter *m_painter;
QSize m_outputSize;
QTransform m_outputTransform;
};
}
#endif
diff --git a/libs/widgets/KisDlgInternalColorSelector.cpp b/libs/widgets/KisDlgInternalColorSelector.cpp
index d7a5480311..e702ad0dd1 100644
--- a/libs/widgets/KisDlgInternalColorSelector.cpp
+++ b/libs/widgets/KisDlgInternalColorSelector.cpp
@@ -1,344 +1,345 @@
/*
* Copyright (C) Wolthera van Hovell tot Westerflier <griffinvalley@gmail.com>, (C) 2016
*
* 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 <QList>
#include <QAbstractSpinBox>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QPointer>
#include <QCompleter>
#include <functional>
#include <KConfigGroup>
#include "KoColorSpaceRegistry.h"
#include <KoColorSet.h>
#include <KisPaletteModel.h>
#include <KisPaletteListWidget.h>
#include <kis_palette_view.h>
#include <KoResourceServerProvider.h>
#include <KoResourceServer.h>
#include "kis_signal_compressor.h"
#include "KoColorDisplayRendererInterface.h"
#include "kis_spinbox_color_selector.h"
#include "KisDlgInternalColorSelector.h"
#include "ui_WdgDlgInternalColorSelector.h"
#include "kis_config_notifier.h"
#include "kis_color_input.h"
#include "kis_icon_utils.h"
#include "squeezedcombobox.h"
std::function<KisScreenColorPickerBase *(QWidget *)> KisDlgInternalColorSelector::s_screenColorPickerFactory = 0;
struct KisDlgInternalColorSelector::Private
{
bool allowUpdates = true;
KoColor currentColor;
KoColor previousColor;
KoColor sRGB = KoColor(KoColorSpaceRegistry::instance()->rgb8());
const KoColorSpace *currentColorSpace;
bool lockUsedCS = false;
bool chooseAlpha = false;
KisSignalCompressor *compressColorChanges;
const KoColorDisplayRendererInterface *displayRenderer;
KisHexColorInput *hexColorInput = 0;
KisPaletteModel *paletteModel = 0;
KisPaletteListWidget *paletteChooser = 0;
KisScreenColorPickerBase *screenColorPicker = 0;
};
KisDlgInternalColorSelector::KisDlgInternalColorSelector(QWidget *parent, KoColor color, Config config, const QString &caption, const KoColorDisplayRendererInterface *displayRenderer)
: QDialog(parent)
, m_d(new Private)
{
setModal(config.modal);
setFocusPolicy(Qt::ClickFocus);
m_ui = new Ui_WdgDlgInternalColorSelector();
m_ui->setupUi(this);
setWindowTitle(caption);
m_d->currentColor = color;
m_d->currentColorSpace = m_d->currentColor.colorSpace();
m_d->displayRenderer = displayRenderer;
m_ui->spinboxselector->slotSetColor(color);
connect(m_ui->spinboxselector, SIGNAL(sigNewColor(KoColor)), this, SLOT(slotColorUpdated(KoColor)));
m_ui->visualSelector->slotSetColor(color);
m_ui->visualSelector->setDisplayRenderer(displayRenderer);
m_ui->visualSelector->setConfig(false, config.modal);
if (config.visualColorSelector) {
connect(m_ui->visualSelector, SIGNAL(sigNewColor(KoColor)), this, SLOT(slotColorUpdated(KoColor)));
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), m_ui->visualSelector, SLOT(configurationChanged()));
} else {
m_ui->visualSelector->hide();
}
m_d->paletteChooser = new KisPaletteListWidget(this);
m_d->paletteModel = new KisPaletteModel(this);
m_ui->bnPaletteChooser->setIcon(KisIconUtils::loadIcon("hi16-palette_library"));
m_ui->paletteBox->setPaletteModel(m_d->paletteModel);
m_ui->paletteBox->setDisplayRenderer(displayRenderer);
m_ui->cmbNameList->setCompanionView(m_ui->paletteBox);
connect(m_d->paletteChooser, SIGNAL(sigPaletteSelected(KoColorSet*)), this, SLOT(slotChangePalette(KoColorSet*)));
connect(m_ui->cmbNameList, SIGNAL(sigColorSelected(KoColor)), SLOT(slotColorUpdated(KoColor)));
// For some bizare reason, the modal dialog doesn't like having the colorset set, so let's not.
if (config.paletteBox) {
//TODO: Add disable signal as well. Might be not necessary...?
KConfigGroup cfg(KSharedConfig::openConfig()->group(""));
QString paletteName = cfg.readEntry("internal_selector_active_color_set", QString());
KoResourceServer<KoColorSet>* rServer = KoResourceServerProvider::instance()->paletteServer();
KoColorSet *savedPal = rServer->resourceByName(paletteName);
if (savedPal) {
this->slotChangePalette(savedPal);
} else {
if (rServer->resources().count()) {
savedPal = rServer->resources().first();
if (savedPal) {
this->slotChangePalette(savedPal);
}
}
}
connect(m_ui->paletteBox, SIGNAL(sigColorSelected(KoColor)), this,
SLOT(slotColorUpdated(KoColor)));
m_ui->bnPaletteChooser->setPopupWidget(m_d->paletteChooser);
} else {
m_ui->paletteBox->setEnabled(false);
m_ui->cmbNameList->setEnabled(false);
m_ui->bnPaletteChooser->setEnabled(false);
}
if (config.prevNextButtons) {
m_ui->currentColor->setColor(m_d->currentColor);
m_ui->currentColor->setDisplayRenderer(displayRenderer);
m_ui->previousColor->setColor(m_d->currentColor);
m_ui->previousColor->setDisplayRenderer(displayRenderer);
connect(m_ui->previousColor, SIGNAL(triggered(KoColorPatch*)), SLOT(slotSetColorFromPatch(KoColorPatch*)));
} else {
m_ui->currentColor->hide();
m_ui->previousColor->hide();
}
if (config.hexInput) {
m_d->sRGB.fromKoColor(m_d->currentColor);
m_d->hexColorInput = new KisHexColorInput(this, &m_d->sRGB);
m_d->hexColorInput->update();
connect(m_d->hexColorInput, SIGNAL(updated()), SLOT(slotSetColorFromHex()));
m_ui->rightPane->addWidget(m_d->hexColorInput);
m_d->hexColorInput->setToolTip(i18n("This is a hexcode input, for webcolors. It can only get colors in the sRGB space."));
}
// KisScreenColorPicker is in the kritaui module, so dependency inversion is used to access it.
m_ui->screenColorPickerWidget->setLayout(new QHBoxLayout(m_ui->screenColorPickerWidget));
if (s_screenColorPickerFactory) {
m_d->screenColorPicker = s_screenColorPickerFactory(m_ui->screenColorPickerWidget);
m_ui->screenColorPickerWidget->layout()->addWidget(m_d->screenColorPicker);
if (config.screenColorPicker) {
connect(m_d->screenColorPicker, SIGNAL(sigNewColorPicked(KoColor)),this, SLOT(slotColorUpdated(KoColor)));
} else {
m_d->screenColorPicker->hide();
}
}
connect(this, SIGNAL(signalForegroundColorChosen(KoColor)), this, SLOT(slotLockSelector()));
m_d->compressColorChanges = new KisSignalCompressor(100 /* ms */, KisSignalCompressor::POSTPONE, this);
connect(m_d->compressColorChanges, SIGNAL(timeout()), this, SLOT(endUpdateWithNewColor()));
- connect(m_ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
- connect(m_ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
+ connect(m_ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()), Qt::UniqueConnection);
+ connect(m_ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject()), Qt::UniqueConnection);
connect(this, SIGNAL(finished(int)), SLOT(slotFinishUp()));
}
KisDlgInternalColorSelector::~KisDlgInternalColorSelector()
{
delete m_ui;
}
void KisDlgInternalColorSelector::slotColorUpdated(KoColor newColor)
{
//if the update did not come from this selector...
if (m_d->allowUpdates || QObject::sender() == this->parent()) {
if (m_d->lockUsedCS){
newColor.convertTo(m_d->currentColorSpace);
m_d->currentColor = newColor;
} else {
m_d->currentColor = newColor;
}
updateAllElements(QObject::sender());
}
}
void KisDlgInternalColorSelector::slotSetColorFromPatch(KoColorPatch *patch)
{
slotColorUpdated(patch->color());
}
void KisDlgInternalColorSelector::colorSpaceChanged(const KoColorSpace *cs)
{
if (cs == m_d->currentColorSpace) {
return;
}
m_d->currentColorSpace = KoColorSpaceRegistry::instance()->colorSpace(cs->colorModelId().id(), cs->colorDepthId().id(), cs->profile());
m_ui->spinboxselector->slotSetColorSpace(m_d->currentColorSpace);
m_ui->visualSelector->slotsetColorSpace(m_d->currentColorSpace);
}
void KisDlgInternalColorSelector::lockUsedColorSpace(const KoColorSpace *cs)
{
colorSpaceChanged(cs);
m_d->lockUsedCS = true;
}
void KisDlgInternalColorSelector::setDisplayRenderer(const KoColorDisplayRendererInterface *displayRenderer)
{
if (displayRenderer) {
m_d->displayRenderer = displayRenderer;
m_ui->visualSelector->setDisplayRenderer(displayRenderer);
m_ui->currentColor->setDisplayRenderer(displayRenderer);
m_ui->previousColor->setDisplayRenderer(displayRenderer);
m_ui->paletteBox->setDisplayRenderer(displayRenderer);
} else {
m_d->displayRenderer = KoDumbColorDisplayRenderer::instance();
}
}
KoColor KisDlgInternalColorSelector::getModalColorDialog(const KoColor color, QWidget* parent, QString caption)
{
Config config = Config();
KisDlgInternalColorSelector dialog(parent, color, config, caption);
dialog.setPreviousColor(color);
dialog.exec();
return dialog.getCurrentColor();
}
KoColor KisDlgInternalColorSelector::getCurrentColor()
{
return m_d->currentColor;
}
void KisDlgInternalColorSelector::chooseAlpha(bool chooseAlpha)
{
m_d->chooseAlpha = chooseAlpha;
}
void KisDlgInternalColorSelector::slotConfigurationChanged()
{
//m_d->canvas->displayColorConverter()->
//slotColorSpaceChanged(m_d->canvas->image()->colorSpace());
}
void KisDlgInternalColorSelector::slotLockSelector()
{
m_d->allowUpdates = false;
}
void KisDlgInternalColorSelector::setPreviousColor(KoColor c)
{
m_d->previousColor = c;
}
void KisDlgInternalColorSelector::reject()
{
slotColorUpdated(m_d->previousColor);
QDialog::reject();
}
void KisDlgInternalColorSelector::updateAllElements(QObject *source)
{
//update everything!!!
if (source != m_ui->spinboxselector) {
m_ui->spinboxselector->slotSetColor(m_d->currentColor);
}
if (source != m_ui->visualSelector) {
m_ui->visualSelector->slotSetColor(m_d->currentColor);
}
if (source != m_d->hexColorInput) {
m_d->sRGB.fromKoColor(m_d->currentColor);
m_d->hexColorInput->update();
}
- if (source != m_ui->paletteBox) {
+ KConfigGroup group(KSharedConfig::openConfig(), "");
+ if (source != m_ui->paletteBox && group.readEntry("colorsettings/forcepalettecolors", false)) {
m_ui->paletteBox->selectClosestColor(m_d->currentColor);
}
m_ui->previousColor->setColor(m_d->previousColor);
m_ui->currentColor->setColor(m_d->currentColor);
if (source != this->parent()) {
emit(signalForegroundColorChosen(m_d->currentColor));
m_d->compressColorChanges->start();
}
if (m_d->screenColorPicker) {
m_d->screenColorPicker->updateIcons();
}
}
void KisDlgInternalColorSelector::endUpdateWithNewColor()
{
m_d->allowUpdates = true;
}
void KisDlgInternalColorSelector::focusInEvent(QFocusEvent *)
{
//setPreviousColor();
}
void KisDlgInternalColorSelector::slotFinishUp()
{
setPreviousColor(m_d->currentColor);
KConfigGroup cfg(KSharedConfig::openConfig()->group(""));
if (m_d->paletteModel) {
if (m_d->paletteModel->colorSet()) {
cfg.writeEntry("internal_selector_active_color_set", m_d->paletteModel->colorSet()->name());
}
}
}
void KisDlgInternalColorSelector::slotSetColorFromHex()
{
slotColorUpdated(m_d->sRGB);
}
void KisDlgInternalColorSelector::slotChangePalette(KoColorSet *set)
{
if (!set) {
return;
}
m_d->paletteModel->setPalette(set);
}
void KisDlgInternalColorSelector::showEvent(QShowEvent *event)
{
updateAllElements(0);
QDialog::showEvent(event);
}
diff --git a/libs/widgets/KisDlgInternalColorSelector.h b/libs/widgets/KisDlgInternalColorSelector.h
index f703182557..558095bff5 100644
--- a/libs/widgets/KisDlgInternalColorSelector.h
+++ b/libs/widgets/KisDlgInternalColorSelector.h
@@ -1,194 +1,195 @@
/*
* Copyright (C) Wolthera van Hovell tot Westerflier <griffinvalley@gmail.com>, (C) 2016
*
* 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 KISDLGINTERNALCOLORSELECTOR_H
#define KISDLGINTERNALCOLORSELECTOR_H
#include "kritawidgets_export.h"
#include "KoColor.h"
#include "KoColorSpace.h"
#include "KoColorDisplayRendererInterface.h"
#include "KoColorSet.h"
#include <QScopedPointer>
#include "KisScreenColorPickerBase.h"
#include "ui_WdgDlgInternalColorSelector.h"
/**
* @brief The KisInternalColorSelector class
*
* A non-modal color selector dialog that is not a plugin and can thus be used for filters.
*/
class KRITAWIDGETS_EXPORT KisDlgInternalColorSelector : public QDialog
{
Q_OBJECT
static std::function<KisScreenColorPickerBase *(QWidget *)> s_screenColorPickerFactory;
public:
static void setScreenColorPickerFactory(std::function<KisScreenColorPickerBase *(QWidget *)> f) {
s_screenColorPickerFactory = f;
}
struct Config
{
Config() :
modal(true),
visualColorSelector(true),
paletteBox(true),
screenColorPicker(true),
prevNextButtons(true),
hexInput(true),
useAlpha(false){}
bool modal;
bool visualColorSelector;
bool paletteBox;
bool screenColorPicker;
bool prevNextButtons;
bool hexInput;
bool useAlpha;
};
KisDlgInternalColorSelector(QWidget* parent, KoColor color, Config config, const QString &caption, const KoColorDisplayRendererInterface *displayRenderer = KoDumbColorDisplayRenderer::instance());
~KisDlgInternalColorSelector() override;
/**
* @brief slotColorSpaceChanged
* Color space has changed, use this dialog to change the colorspace.
*/
void colorSpaceChanged(const KoColorSpace *cs);
/**
* @brief lockUsedColorSpace
* Lock the used colorspace of this selector.
* @param cs
*/
void lockUsedColorSpace(const KoColorSpace *cs);
/**
* @brief setDisplayRenderer
* Set the display renderer. This is necessary for HDR color manage support.
* @param displayRenderer
*/
void setDisplayRenderer(const KoColorDisplayRendererInterface *displayRenderer);
/**
* @brief getModalColorDialog
* Execute this dialog modally. The function returns
* the KoColor you want.
* @param color - The current color. Make sure this is in the color space you want your
* end color to be in.
- * @param chooseAlpha - Whether or not the alpha-choosing functionality should be used.
+ * @param parent parent widget.
+ * @param caption the dialog caption.
*/
static KoColor getModalColorDialog(const KoColor color, QWidget* parent = Q_NULLPTR, QString caption = QString());
/**
* @brief getCurrentColor
* @return gives currently active color;
*/
KoColor getCurrentColor();
void chooseAlpha(bool chooseAlpha);
Q_SIGNALS:
/**
* @brief signalForegroundColorChosen
* The most important signal. This will sent out when a color has been picked from the selector.
* There will be a small delay to make sure that the selector causes too many updates.
*
* Do not connect this to slotColorUpdated.
* @param color The new color chosen
*/
void signalForegroundColorChosen(KoColor color);
public Q_SLOTS:
/**
* @brief slotColorUpdated
* Very important slot. Is connected to krita's resources to make sure it has
* the currently active color. It's very important that this function is able to understand
* when the signal came from itself.
* @param newColor This is the new color.
*/
void slotColorUpdated(KoColor newColor);
/**
* @brief slotSetColorFromPatch
* update current color from kocolorpatch.
* @param patch
*/
void slotSetColorFromPatch(KoColorPatch* patch);
/**
* @brief setPreviousColor
* set the previous color.
*/
void setPreviousColor(KoColor c);
void reject() override;
private Q_SLOTS:
/**
* @brief slotLockSelector
* This slot will prevent the color from being updated.
*/
void slotLockSelector();
/**
* @brief slotConfigurationChanged
* Wrapper slot for changes to the colorspace.
*/
void slotConfigurationChanged();
void endUpdateWithNewColor();
/**
* @brief slotFinishUp
* This is called when the selector is closed, for saving the current palette.
*/
void slotFinishUp();
/**
* @brief slotSetColorFromHex
* Update from the hex color input.
*/
void slotSetColorFromHex();
void slotChangePalette(KoColorSet *set);
protected:
void showEvent(QShowEvent *event) override;
private:
void focusInEvent(QFocusEvent *) override;
/**
* @brief updateAllElements
* Updates each widget with the new element, and if it's responsible for the update sents
* a signal out that there's a new color.
*/
void updateAllElements(QObject *source);
private:
Ui_WdgDlgInternalColorSelector *m_ui;
struct Private; //The private struct
const QScopedPointer<Private> m_d; //the private pointer
};
#endif // KISDLGINTERNALCOLORSELECTOR_H
diff --git a/libs/widgets/KisPaletteComboBox.cpp b/libs/widgets/KisPaletteComboBox.cpp
index 494dad732e..f0ea347926 100644
--- a/libs/widgets/KisPaletteComboBox.cpp
+++ b/libs/widgets/KisPaletteComboBox.cpp
@@ -1,162 +1,162 @@
/*
* Copyright (c) 2018 Michael Zhou <simeirxh@gmail.com>
*
* 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.
*/
// Qt
#include <QPainter>
#include <QPen>
#include <QVector>
#include <QCompleter>
// STL
#include <algorithm>
#include "kis_palette_view.h"
#include "KisPaletteComboBox.h"
KisPaletteComboBox::KisPaletteComboBox(QWidget *parent)
: SqueezedComboBox(parent)
, m_model(Q_NULLPTR)
{
setEditable(true);
setInsertPolicy(NoInsert);
completer()->setCompletionMode(QCompleter::PopupCompletion);
completer()->setCaseSensitivity(Qt::CaseInsensitive);
completer()->setFilterMode(Qt::MatchContains);
connect(this, SIGNAL(currentIndexChanged(int)), SLOT(slotIndexUpdated(int)));
}
KisPaletteComboBox::~KisPaletteComboBox()
{ }
void KisPaletteComboBox::setPaletteModel(const KisPaletteModel *paletteModel)
{
if (!m_model.isNull()) {
m_model->disconnect(this);
}
m_model = paletteModel;
if (m_model.isNull()) { return; }
slotPaletteChanged();
connect(m_model, SIGNAL(sigPaletteChanged()),
SLOT(slotPaletteChanged()));
connect(m_model, SIGNAL(sigPaletteModified()),
SLOT(slotPaletteChanged()));
}
void KisPaletteComboBox::setCompanionView(KisPaletteView *view)
{
if (!m_view.isNull()) {
m_view->disconnect(this);
disconnect(m_view.data());
}
m_view = view;
setPaletteModel(view->paletteModel());
connect(view, SIGNAL(sigIndexSelected(QModelIndex)),
SLOT(slotSwatchSelected(QModelIndex)));
connect(this, SIGNAL(sigColorSelected(KoColor)),
- view, SLOT(slotFGColorChanged(KoColor)));
+ view, SLOT(slotSelectColor(KoColor)));
}
void KisPaletteComboBox::slotPaletteChanged()
{
clear();
m_groupMapMap.clear();
m_idxSwatchMap.clear();
if (QPointer<KoColorSet>(m_model->colorSet()).isNull()) { return; }
for (const QString &groupName : m_model->colorSet()->getGroupNames()) {
QVector<SwatchInfoType> infoList;
PosIdxMapType posIdxMap;
const KisSwatchGroup *group = m_model->colorSet()->getGroup(groupName);
for (const SwatchInfoType &info : group->infoList()) {
infoList.append(info);
}
std::sort(infoList.begin(), infoList.end(), swatchInfoLess);
for (const SwatchInfoType &info : infoList) {
const KisSwatch &swatch = info.swatch;
QString name = swatch.name();
if (!swatch.id().isEmpty()){
name = swatch.id() + " - " + swatch.name();
}
addSqueezedItem(QIcon(createColorSquare(swatch)), name);
posIdxMap[SwatchPosType(info.column, info.row)] = count() - 1;
m_idxSwatchMap.push_back(swatch);
}
m_groupMapMap[group->name()] = posIdxMap;
}
if (m_view.isNull()) {
setCurrentIndex(0);
}
QModelIndex idx = m_view->currentIndex();
if (!idx.isValid()) { return; }
if (qvariant_cast<bool>(idx.data(KisPaletteModel::IsGroupNameRole))) { return; }
if (!qvariant_cast<bool>(idx.data(KisPaletteModel::CheckSlotRole))) { return; }
blockSignals(true); // this is a passive selection; this shouldn't make others change
slotSwatchSelected(idx);
blockSignals(false);
}
bool KisPaletteComboBox::swatchInfoLess(const SwatchInfoType &first, const SwatchInfoType &second)
{
return first.swatch.name() < second.swatch.name();
}
QPixmap KisPaletteComboBox::createColorSquare(const KisSwatch &swatch) const
{
QPixmap colorSquare(32, 32);
if (swatch.spotColor()) {
QImage img = QImage(32, 32, QImage::Format_ARGB32);
QPainter circlePainter;
img.fill(Qt::transparent);
circlePainter.begin(&img);
QBrush brush = QBrush(Qt::SolidPattern);
brush.setColor(swatch.color().toQColor());
circlePainter.setBrush(brush);
QPen pen = circlePainter.pen();
pen.setColor(Qt::transparent);
pen.setWidth(0);
circlePainter.setPen(pen);
circlePainter.drawEllipse(0, 0, 32, 32);
circlePainter.end();
colorSquare = QPixmap::fromImage(img);
} else {
colorSquare.fill(swatch.color().toQColor());
}
return colorSquare;
}
void KisPaletteComboBox::slotSwatchSelected(const QModelIndex &index)
{
if (!qvariant_cast<bool>(index.data(KisPaletteModel::CheckSlotRole))) {
return;
}
if (qvariant_cast<bool>(index.data(KisPaletteModel::IsGroupNameRole))) {
return;
}
QString gName = qvariant_cast<QString>(index.data(KisPaletteModel::GroupNameRole));
int rowInGroup = qvariant_cast<int>(index.data(KisPaletteModel::RowInGroupRole));
setCurrentIndex(m_groupMapMap[gName][SwatchPosType(index.column(), rowInGroup)]);
}
void KisPaletteComboBox::slotIndexUpdated(int idx)
{
if (idx >= 0 && idx < m_idxSwatchMap.size()) {
emit sigColorSelected(m_idxSwatchMap[idx].color());
}
}
diff --git a/libs/widgets/KisPaletteModel.cpp b/libs/widgets/KisPaletteModel.cpp
index f106d10d39..0174774932 100644
--- a/libs/widgets/KisPaletteModel.cpp
+++ b/libs/widgets/KisPaletteModel.cpp
@@ -1,508 +1,508 @@
/*
* Copyright (c) 2013 Sven Langkamp <sven.langkamp@gmail.com>
* Copyright (c) 2018 Michael Zhou <simeirxh@gmail.com>
*
* 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 "KisPaletteModel.h"
#include <QBrush>
#include <QDomDocument>
#include <QDomElement>
#include <QMimeData>
#include <KoColor.h>
#include <KoColorSpace.h>
#include <KoColorModelStandardIds.h>
#include <resources/KoColorSet.h>
#include <KoColorDisplayRendererInterface.h>
KisPaletteModel::KisPaletteModel(QObject* parent)
: QAbstractTableModel(parent)
, m_colorSet(Q_NULLPTR)
, m_displayRenderer(KoDumbColorDisplayRenderer::instance())
{
connect(this, SIGNAL(sigPaletteModified()), SLOT(slotPaletteModified()));
}
KisPaletteModel::~KisPaletteModel()
{
}
QVariant KisPaletteModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid()) { return QVariant(); }
bool groupNameRow = m_rowGroupNameMap.contains(index.row());
if (role == IsGroupNameRole) {
return groupNameRow;
}
if (groupNameRow) {
return dataForGroupNameRow(index, role);
} else {
return dataForSwatch(index, role);
}
}
int KisPaletteModel::rowCount(const QModelIndex& /*parent*/) const
{
if (!m_colorSet)
return 0;
return m_colorSet->rowCount() // count of color rows
+ m_rowGroupNameMap.size() // rows for names
- 1; // global doesn't have a name
}
int KisPaletteModel::columnCount(const QModelIndex& /*parent*/) const
{
if (m_colorSet && m_colorSet->columnCount() > 0) {
return m_colorSet->columnCount();
}
if (!m_colorSet) {
return 0;
}
return 16;
}
Qt::ItemFlags KisPaletteModel::flags(const QModelIndex& index) const
{
if (index.isValid()) {
return Qt::ItemIsSelectable |
Qt::ItemIsEnabled |
Qt::ItemIsUserCheckable |
Qt::ItemIsDragEnabled |
Qt::ItemIsDropEnabled;
}
return Qt::ItemIsDropEnabled;
}
QModelIndex KisPaletteModel::index(int row, int column, const QModelIndex& parent) const
{
Q_UNUSED(parent);
Q_ASSERT(m_colorSet);
int groupNameRow = groupNameRowForRow(row);
KisSwatchGroup *group = m_colorSet->getGroup(m_rowGroupNameMap[groupNameRow]);
Q_ASSERT(group);
return createIndex(row, column, group);
}
void KisPaletteModel::resetGroupNameRows()
{
m_rowGroupNameMap.clear();
int row = -1;
for (const QString &groupName : m_colorSet->getGroupNames()) {
m_rowGroupNameMap[row] = groupName;
row += m_colorSet->getGroup(groupName)->rowCount();
row += 1; // row for group name
}
}
void KisPaletteModel::setPalette(KoColorSet* palette)
{
beginResetModel();
m_colorSet = palette;
if (palette) {
resetGroupNameRows();
}
endResetModel();
emit sigPaletteChanged();
}
KoColorSet* KisPaletteModel::colorSet() const
{
return m_colorSet;
}
int KisPaletteModel::rowNumberInGroup(int rowInModel) const
{
if (m_rowGroupNameMap.contains(rowInModel)) {
return -1;
}
QList<int> rowNumberList = m_rowGroupNameMap.keys();
for (auto it = rowNumberList.rbegin(); it != rowNumberList.rend(); it++) {
if (*it < rowInModel) {
return rowInModel - *it - 1;
}
}
return rowInModel;
}
int KisPaletteModel::groupNameRowForName(const QString &groupName)
{
for (auto it = m_rowGroupNameMap.begin(); it != m_rowGroupNameMap.end(); it++) {
if (it.value() == groupName) {
return it.key();
}
}
return -1;
}
bool KisPaletteModel::addEntry(const KisSwatch &entry, const QString &groupName)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount() + 1);
m_colorSet->add(entry, groupName);
endInsertRows();
if (m_colorSet->isGlobal()) {
m_colorSet->save();
}
emit sigPaletteModified();
return true;
}
bool KisPaletteModel::removeEntry(const QModelIndex &index, bool keepColors)
{
if (!qvariant_cast<bool>(data(index, IsGroupNameRole))) {
static_cast<KisSwatchGroup*>(index.internalPointer())->removeEntry(index.column(),
rowNumberInGroup(index.row()));
emit dataChanged(index, index);
} else {
int groupNameRow = groupNameRowForRow(index.row());
QString groupName = m_rowGroupNameMap[groupNameRow];
removeGroup(groupName, keepColors);
}
emit sigPaletteModified();
return true;
}
void KisPaletteModel::removeGroup(const QString &groupName, bool keepColors)
{
int removeStart = groupNameRowForName(groupName);
int removedRowCount = m_colorSet->getGroup(groupName)->rowCount();
int insertStart = m_colorSet->getGlobalGroup()->rowCount();
beginRemoveRows(QModelIndex(),
removeStart,
removeStart + removedRowCount);
m_colorSet->removeGroup(groupName, keepColors);
resetGroupNameRows();
endRemoveRows();
beginInsertRows(QModelIndex(),
insertStart, m_colorSet->getGlobalGroup()->rowCount());
endInsertRows();
emit sigPaletteModified();
}
bool KisPaletteModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent)
{
Q_UNUSED(row);
Q_UNUSED(column);
if (!data->hasFormat("krita/x-colorsetentry") && !data->hasFormat("krita/x-colorsetgroup")) {
return false;
}
if (action == Qt::IgnoreAction) {
return false;
}
QModelIndex finalIndex = parent;
if (!finalIndex.isValid()) { return false; }
if (data->hasFormat("krita/x-colorsetgroup")) {
// dragging group not supported for now
QByteArray encodedData = data->data("krita/x-colorsetgroup");
QDataStream stream(&encodedData, QIODevice::ReadOnly);
while (!stream.atEnd()) {
QString groupNameDroppedOn = qvariant_cast<QString>(finalIndex.data(GroupNameRole));
if (groupNameDroppedOn == KoColorSet::GLOBAL_GROUP_NAME) {
return false;
}
QString groupNameDragged;
stream >> groupNameDragged;
KisSwatchGroup *groupDragged = m_colorSet->getGroup(groupNameDragged);
int start = groupNameRowForName(groupNameDragged);
int end = start + groupDragged->rowCount();
if (!beginMoveRows(QModelIndex(), start, end, QModelIndex(), groupNameRowForName(groupNameDroppedOn))) {
return false;
}
m_colorSet->moveGroup(groupNameDragged, groupNameDroppedOn);
resetGroupNameRows();
endMoveRows();
emit sigPaletteModified();
if (m_colorSet->isGlobal()) {
m_colorSet->save();
}
}
return true;
}
if (qvariant_cast<bool>(finalIndex.data(KisPaletteModel::IsGroupNameRole))) {
return true;
}
QByteArray encodedData = data->data("krita/x-colorsetentry");
QDataStream stream(&encodedData, QIODevice::ReadOnly);
while (!stream.atEnd()) {
KisSwatch entry;
QString name, id;
bool spotColor;
QString oldGroupName;
int oriRow;
int oriColumn;
QString colorXml;
stream >> name >> id >> spotColor
>> oriRow >> oriColumn
>> oldGroupName
>> colorXml;
entry.setName(name);
entry.setId(id);
entry.setSpotColor(spotColor);
QDomDocument doc;
doc.setContent(colorXml);
QDomElement e = doc.documentElement();
QDomElement c = e.firstChildElement();
if (!c.isNull()) {
QString colorDepthId = c.attribute("bitdepth", Integer8BitsColorDepthID.id());
entry.setColor(KoColor::fromXML(c, colorDepthId));
}
if (action == Qt::MoveAction){
KisSwatchGroup *g = m_colorSet->getGroup(oldGroupName);
if (g) {
if (qvariant_cast<bool>(finalIndex.data(KisPaletteModel::CheckSlotRole))) {
g->setEntry(getEntry(finalIndex), oriColumn, oriRow);
} else {
g->removeEntry(oriColumn, oriRow);
}
}
setEntry(entry, finalIndex);
emit sigPaletteModified();
if (m_colorSet->isGlobal()) {
m_colorSet->save();
}
}
}
return true;
}
QMimeData *KisPaletteModel::mimeData(const QModelIndexList &indexes) const
{
QMimeData *mimeData = new QMimeData();
QByteArray encodedData;
QDataStream stream(&encodedData, QIODevice::WriteOnly);
QModelIndex index = indexes.last();
if (index.isValid() && qvariant_cast<bool>(index.data(CheckSlotRole))) {
QString mimeTypeName = "krita/x-colorsetentry";
if (qvariant_cast<bool>(index.data(IsGroupNameRole))==false) {
KisSwatch entry = getEntry(index);
QDomDocument doc;
QDomElement root = doc.createElement("Color");
root.setAttribute("bitdepth", entry.color().colorSpace()->colorDepthId().id());
doc.appendChild(root);
entry.color().toXML(doc, root);
stream << entry.name() << entry.id() << entry.spotColor()
<< rowNumberInGroup(index.row()) << index.column()
<< qvariant_cast<QString>(index.data(GroupNameRole))
<< doc.toString();
} else {
mimeTypeName = "krita/x-colorsetgroup";
QString groupName = qvariant_cast<QString>(index.data(GroupNameRole));
stream << groupName;
}
mimeData->setData(mimeTypeName, encodedData);
}
return mimeData;
}
QStringList KisPaletteModel::mimeTypes() const
{
return QStringList() << "krita/x-colorsetentry" << "krita/x-colorsetgroup";
}
Qt::DropActions KisPaletteModel::supportedDropActions() const
{
return Qt::MoveAction;
}
void KisPaletteModel::setEntry(const KisSwatch &entry,
const QModelIndex &index)
{
KisSwatchGroup *group = static_cast<KisSwatchGroup*>(index.internalPointer());
Q_ASSERT(group);
group->setEntry(entry, index.column(), rowNumberInGroup(index.row()));
emit sigPaletteModified();
emit dataChanged(index, index);
if (m_colorSet->isGlobal()) {
m_colorSet->save();
}
}
bool KisPaletteModel::renameGroup(const QString &groupName, const QString &newName)
{
beginResetModel();
bool success = m_colorSet->changeGroupName(groupName, newName);
for (auto it = m_rowGroupNameMap.begin(); it != m_rowGroupNameMap.end(); it++) {
if (it.value() == groupName) {
m_rowGroupNameMap[it.key()] = newName;
break;
}
}
endResetModel();
emit sigPaletteModified();
return success;
}
void KisPaletteModel::addGroup(const KisSwatchGroup &group)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount() + group.rowCount());
m_colorSet->addGroup(group.name());
*m_colorSet->getGroup(group.name()) = group;
endInsertColumns();
emit sigPaletteModified();
}
void KisPaletteModel::setRowNumber(const QString &groupName, int rowCount)
{
beginResetModel();
KisSwatchGroup *g = m_colorSet->getGroup(groupName);
if (g) {
g->setRowCount(rowCount);
}
endResetModel();
}
void KisPaletteModel::clear()
{
beginResetModel();
m_colorSet->clear();
endResetModel();
}
QVariant KisPaletteModel::dataForGroupNameRow(const QModelIndex &idx, int role) const
{
KisSwatchGroup *group = static_cast<KisSwatchGroup*>(idx.internalPointer());
Q_ASSERT(group);
QString groupName = group->name();
switch (role) {
case Qt::ToolTipRole:
case Qt::DisplayRole: {
return groupName;
}
case GroupNameRole: {
return groupName;
}
case CheckSlotRole: {
return true;
}
case RowInGroupRole: {
return -1;
}
default: {
return QVariant();
}
}
}
QVariant KisPaletteModel::dataForSwatch(const QModelIndex &idx, int role) const
{
KisSwatchGroup *group = static_cast<KisSwatchGroup*>(idx.internalPointer());
Q_ASSERT(group);
int rowInGroup = rowNumberInGroup(idx.row());
bool entryPresent = group->checkEntry(idx.column(), rowInGroup);
KisSwatch entry;
if (entryPresent) {
entry = group->getEntry(idx.column(), rowInGroup);
}
switch (role) {
case Qt::ToolTipRole:
case Qt::DisplayRole: {
return entryPresent ? entry.name() : i18n("Empty slot");
}
case Qt::BackgroundRole: {
QColor color(0, 0, 0, 0);
if (entryPresent) {
color = m_displayRenderer->toQColor(entry.color());
}
return QBrush(color);
}
case GroupNameRole: {
return group->name();
}
case CheckSlotRole: {
return entryPresent;
}
case RowInGroupRole: {
return rowInGroup;
}
default: {
return QVariant();
}
}
}
void KisPaletteModel::setDisplayRenderer(const KoColorDisplayRendererInterface *displayRenderer)
{
if (displayRenderer) {
if (m_displayRenderer) {
disconnect(m_displayRenderer, 0, this, 0);
}
m_displayRenderer = displayRenderer;
connect(m_displayRenderer, SIGNAL(displayConfigurationChanged()),
- SLOT(slotDisplayConfigurationChanged()));
+ SLOT(slotDisplayConfigurationChanged()), Qt::UniqueConnection);
} else {
m_displayRenderer = KoDumbColorDisplayRenderer::instance();
}
}
void KisPaletteModel::slotDisplayConfigurationChanged()
{
beginResetModel();
endResetModel();
}
void KisPaletteModel::slotPaletteModified() {
m_colorSet->setPaletteType(KoColorSet::KPL);
}
QModelIndex KisPaletteModel::indexForClosest(const KoColor &compare)
{
KisSwatchGroup::SwatchInfo info = colorSet()->getClosestColorInfo(compare);
return createIndex(indexRowForInfo(info), info.column, colorSet()->getGroup(info.group));
}
int KisPaletteModel::indexRowForInfo(const KisSwatchGroup::SwatchInfo &info)
{
for (auto it = m_rowGroupNameMap.begin(); it != m_rowGroupNameMap.end(); it++) {
if (it.value() == info.group) {
return it.key() + info.row + 1;
}
}
return info.row;
}
KisSwatch KisPaletteModel::getEntry(const QModelIndex &index) const
{
KisSwatchGroup *group = static_cast<KisSwatchGroup*>(index.internalPointer());
if (!group || !group->checkEntry(index.column(), rowNumberInGroup(index.row()))) {
return KisSwatch();
}
return group->getEntry(index.column(), rowNumberInGroup(index.row()));
}
int KisPaletteModel::groupNameRowForRow(int rowInModel) const
{
return rowInModel - rowNumberInGroup(rowInModel) - 1;
}
diff --git a/libs/widgets/KisPaletteModel.h b/libs/widgets/KisPaletteModel.h
index 45f465a405..5f5c113dc9 100644
--- a/libs/widgets/KisPaletteModel.h
+++ b/libs/widgets/KisPaletteModel.h
@@ -1,179 +1,180 @@
/*
* Copyright (c) 2013 Sven Langkamp <sven.langkamp@gmail.com>
*
* 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_PALETTEMODEL_H
#define KIS_PALETTEMODEL_H
#include <QPointer>
#include <QModelIndex>
#include <QMap>
#include <KoColorDisplayRendererInterface.h>
#include "kritawidgets_export.h"
#include <KoColorSet.h>
#include <QScopedPointer>
class KoColorSet;
class KisPaletteView;
/**
* @brief The KisPaletteModel class
* This, together with KisPaletteView and KisPaletteDelegate forms a mvc way to access kocolorsets.
* A display renderer is given to this model to convert KoColor to QColor when
* colors are requested
*/
class KRITAWIDGETS_EXPORT KisPaletteModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit KisPaletteModel(QObject* parent = Q_NULLPTR);
~KisPaletteModel() override;
enum AdditionalRoles {
IsGroupNameRole = Qt::UserRole + 1,
CheckSlotRole,
GroupNameRole,
RowInGroupRole
};
public /* overridden methods */: // QAbstractTableModel
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
/**
* @brief index
* @param row
* @param column
* @param parent
* @return the index of for the data at row, column
* if the data is a color entry, the internal pointer points to the group
* the entry belongs to, and the row and column are row number and column
* number inside the group.
* if the data is a group, the row number and group number is Q_INFINIFY,
* and the internal pointer also points to the group
*/
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
/**
* @brief dropMimeData
* This is an overridden function that handles dropped mimedata.
* right now only colorsetentries and colorsetgroups are handled.
* @return
*/
bool dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent) override;
/**
* @brief mimeData
* gives the mimedata for a kocolorsetentry or a kocolorsetgroup.
* @param indexes
* @return the mimedata for the given indices
*/
QMimeData *mimeData(const QModelIndexList &indexes) const override;
QStringList mimeTypes() const override;
Qt::DropActions supportedDropActions() const override;
/**
* @brief setData
* setData is not used as KoColor is not a QVariant
* use setEntry, addEntry and removeEntry instead
*/
// TODO Used QVariant::setValue and QVariant.value<KoColor> to implement this
// bool setData(const QModelIndex &index, const QVariant &value, int role) override;
Q_SIGNALS:
/**
* @brief sigPaletteModified
* emitted when palette associated with the model is modified
*/
void sigPaletteModified();
/**
* @brief sigPaletteChanged
* emitted when the palette associated with the model is made another one
*/
void sigPaletteChanged();
public /* methods */:
/**
* @brief addEntry
* proper function to handle adding entries.
* @return whether successful.
*/
bool addEntry(const KisSwatch &entry,
const QString &groupName = KoColorSet::GLOBAL_GROUP_NAME);
void setEntry(const KisSwatch &entry, const QModelIndex &index);
/**
* @brief removeEntry
* proper function to remove the colorsetentry at the given index.
* The consolidates both removeentry and removegroup.
- * @param keepColors: This bool determines whether, when deleting a group,
+ * @param index the given index
+ * @param keepColors This bool determines whether, when deleting a group,
* the colors should be added to the default group. This is usually desirable,
* so hence the default is true.
* @return if successful
*/
bool removeEntry(const QModelIndex &index, bool keepColors=true);
void removeGroup(const QString &groupName, bool keepColors);
bool renameGroup(const QString &groupName, const QString &newName);
void addGroup(const KisSwatchGroup &group);
void setRowNumber(const QString &groupName, int rowCount);
void clear();
KisSwatch getEntry(const QModelIndex &index) const;
void setPalette(KoColorSet* colorSet);
KoColorSet* colorSet() const;
QModelIndex indexForClosest(const KoColor &compare);
int indexRowForInfo(const KisSwatchGroup::SwatchInfo &info);
public Q_SLOTS:
private Q_SLOTS:
void slotDisplayConfigurationChanged();
void slotPaletteModified();
private /* methods */:
QVariant dataForGroupNameRow(const QModelIndex &idx, int role) const;
QVariant dataForSwatch(const QModelIndex &idx, int role) const;
int rowNumberInGroup(int rowInModel) const;
int groupNameRowForRow(int rowInModel) const;
int groupNameRowForName(const QString &groupName);
void resetGroupNameRows();
/**
* Installs a display renderer object for a palette that will
* convert the KoColor to the displayable QColor. Default is the
* dumb renderer.
*/
void setDisplayRenderer(const KoColorDisplayRendererInterface *displayRenderer);
private /* member variables */:
QPointer<KoColorSet> m_colorSet;
QPointer<const KoColorDisplayRendererInterface> m_displayRenderer;
QMap<int, QString> m_rowGroupNameMap;
friend class KisPaletteView;
};
#endif
diff --git a/libs/widgets/KisVisualColorSelectorShape.h b/libs/widgets/KisVisualColorSelectorShape.h
index ba76d7280b..a24219a26c 100644
--- a/libs/widgets/KisVisualColorSelectorShape.h
+++ b/libs/widgets/KisVisualColorSelectorShape.h
@@ -1,234 +1,236 @@
/*
* Copyright (C) Wolthera van Hovell tot Westerflier <griffinvalley@gmail.com>, (C) 2016
*
* 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_VISUAL_COLOR_SELECTOR_SHAPE_H
#define KIS_VISUAL_COLOR_SELECTOR_SHAPE_H
#include <QWidget>
#include <QScopedPointer>
#include <QPixmap>
#include <QRegion>
#include <QMouseEvent>
#include <KoColor.h>
#include <KoColorSpace.h>
#include "KoColorDisplayRendererInterface.h"
#include "KisVisualColorSelector.h"
#include "KisColorSelectorConfiguration.h"
/**
* @brief The KisVisualColorSelectorShape class
* A 2d widget can represent at maximum 2 coordinates.
* So first decide howmany coordinates you need. (onedimensional, or twodimensional)
* Then the model, (Channel, HSV, HSL, HSI, YUV). Channel is the raw color channels.
* When it finds a non-implemented feature it'll return to Channel.
* Then, select the channels you wish to be affected. This uses the model, so for cmyk
* the channel is c=0, m=1, y=2, k=3, but for hsv, hue=0, sat=1, and val=2
* These can also be set with 'slotsetactive channels'.
* Then finally, connect the displayrenderer, you can also do this with 'setdisplayrenderer'
*
* Either way, this class is made to be subclassed, with a few virtuals so that the geometry
* can be calculated properly.
*/
class KisVisualColorSelectorShape : public QWidget
{
Q_OBJECT
public:
/**
* @brief The Dimensions enum
* Whether or not the shape is single or two dimensional.
**/
enum Dimensions{onedimensional, twodimensional};
enum ColorModel{Channel, HSV, HSL, HSI, HSY, YUV};
explicit KisVisualColorSelectorShape(QWidget *parent,
KisVisualColorSelectorShape::Dimensions dimension,
KisVisualColorSelectorShape::ColorModel model,
const KoColorSpace *cs,
int channel1, int channel2,
const KoColorDisplayRendererInterface *displayRenderer = KoDumbColorDisplayRenderer::instance());
~KisVisualColorSelectorShape() override;
/**
* @brief getCursorPosition
* @return current cursor position in shape-coordinates.
*/
QPointF getCursorPosition();
/**
* @brief getDimensions
* @return whether this is a single or twodimensional widget.
*/
Dimensions getDimensions();
/**
* @brief getColorModel
* @return the model of this widget.
*/
ColorModel getColorModel();
/**
* @brief getPixmap
* @return the pixmap of the gradient, for drawing on with a subclass.
* the pixmap will not change unless 'm_d->setPixmap=true' which is toggled by
* refresh and update functions.
*/
bool imagesNeedUpdate() const;
QImage getImageMap();
/**
* @brief setFullImage
* Set the full widget image to be painted.
* @param full this should be the full image.
*/
void setFullImage(QImage full);
/**
* @brief getCurrentColor
* @return the current kocolor
*/
KoColor getCurrentColor();
/**
* @brief setDisplayRenderer
* disconnect the old display renderer if needed and connect the new one.
* @param displayRenderer
*/
void setDisplayRenderer (const KoColorDisplayRendererInterface *displayRenderer);
/**
* @brief getColorFromConverter
* @param c a koColor.
* @return get the qcolor from the given kocolorusing this widget's display renderer.
*/
QColor getColorFromConverter(KoColor c);
/**
* @brief getSpaceForSquare
* @param geom the full widget rectangle
* @return rectangle with enough space for second widget
*/
virtual QRect getSpaceForSquare(QRect geom) = 0;
virtual QRect getSpaceForCircle(QRect geom) = 0;
virtual QRect getSpaceForTriangle(QRect geom) = 0;
/**
* @brief forceImageUpdate
* force the image to recache.
*/
void forceImageUpdate();
/**
* @brief setBorderWidth
* set the border of the single dimensional selector.
* @param width
*/
virtual void setBorderWidth(int width) = 0;
/**
* @brief getChannels
* get used channels
* @return
*/
QVector <int> getChannels();
/**
* @brief setHSX
* This is for the cursor not to change when selecting
* black, white, and desaturated values. Will not change the non-native values.
* @param hsx the hsx value.
+ * @param wrangler defines whether this docker will update luminosity if there's not at the least 3\% more variation
*/
void setHSX(QVector <qreal> hsx, bool wrangler=false);
/**
* @brief getHSX sets the sat and hue so they won't
* switch around much.
* @param hsx the hsx values.
+ * @param wrangler defines whether this docker will update luminosity if there's not at the least 3\% more variation
* @return returns hsx, corrected.
*/
QVector <qreal> getHSX(QVector <qreal> hsx, bool wrangler= false);
Q_SIGNALS:
void sigNewColor(KoColor col);
void sigHSXchange();
public Q_SLOTS:
/**
* @brief setColor
* Set this widget's current color and change the cursor position.
* @param c
*/
void setColor(KoColor c);
/**
* @brief setColorFromSibling
* set this widget's current color, but don't change the cursor position,
* instead sent out a signal of the new color.
* @param c
*/
void setColorFromSibling(KoColor c);
/**
* @brief slotSetActiveChannels
* Change the active channels if necessary.
* @param channel1 used by single and twodimensional widgets.
* @param channel2 only used by twodimensional widgets.
*/
void slotSetActiveChannels(int channel1, int channel2);
/**
* @brief updateFromChangedDisplayRenderer
* for updating from the display renderer... not sure why this one is public.
*/
void updateFromChangedDisplayRenderer();
protected:
void mousePressEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void paintEvent(QPaintEvent*) override;
private:
struct Private;
const QScopedPointer<Private> m_d;
/**
* @brief convertShapeCoordinateToWidgetCoordinate
* @return take the position in the shape and convert it to screen coordinates.
*/
virtual QPointF convertShapeCoordinateToWidgetCoordinate(QPointF) = 0;
/**
* @brief convertWidgetCoordinateToShapeCoordinate
* Convert a coordinate in the widget's height/width to a shape coordinate.
* @param coordinate the position your wish to have the shape coordinates of.
*/
virtual QPointF convertWidgetCoordinateToShapeCoordinate(QPoint coordinate) = 0;
/**
* @brief updateCursor
* Update the cursor position.
*/
void updateCursor();
QPointF convertKoColorToShapeCoordinate(KoColor c);
KoColor convertShapeCoordinateToKoColor(QPointF coordinates, bool cursor = false);
/**
* @brief getPixmap
* @return the pixmap of this shape.
*/
virtual QRegion getMaskMap() = 0;
virtual void drawCursor() = 0;
QVector <float> convertvectorqrealTofloat(QVector<qreal> real);
QVector <qreal> convertvectorfloatToqreal(QVector <float> vloat);
};
#endif
diff --git a/libs/widgets/KoColorSlider.cpp b/libs/widgets/KoColorSlider.cpp
index 3a125f37d2..109805318f 100644
--- a/libs/widgets/KoColorSlider.cpp
+++ b/libs/widgets/KoColorSlider.cpp
@@ -1,199 +1,199 @@
/* This file is part of the KDE project
Copyright (C) 2006 Sven Langkamp <sven.langkamp@gmail.com>
Copyright (c) 2009 Cyrille Berger <cberger@cberger.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoColorSlider.h"
#include "KoColorSpace.h"
#include <KoColor.h>
#include <KoMixColorsOp.h>
#include <QPainter>
#include <QTimer>
#include <QStyleOption>
#include <QPointer>
#define ARROWSIZE 8
struct Q_DECL_HIDDEN KoColorSlider::Private
{
Private() : upToDate(false), displayRenderer(0) {}
KoColor minColor;
KoColor maxColor;
QPixmap pixmap;
bool upToDate;
QPointer<KoColorDisplayRendererInterface> displayRenderer;
};
KoColorSlider::KoColorSlider(QWidget* parent, KoColorDisplayRendererInterface *displayRenderer)
: KSelector(parent)
, d(new Private)
{
setMaximum(255);
d->displayRenderer = displayRenderer;
- connect(d->displayRenderer, SIGNAL(displayConfigurationChanged()), SLOT(update()));
+ connect(d->displayRenderer, SIGNAL(displayConfigurationChanged()), SLOT(update()), Qt::UniqueConnection);
}
KoColorSlider::KoColorSlider(Qt::Orientation o, QWidget *parent, KoColorDisplayRendererInterface *displayRenderer)
: KSelector(o, parent), d(new Private)
{
setMaximum(255);
d->displayRenderer = displayRenderer;
- connect(d->displayRenderer, SIGNAL(displayConfigurationChanged()), SLOT(update()));
+ connect(d->displayRenderer, SIGNAL(displayConfigurationChanged()), SLOT(update()), Qt::UniqueConnection);
}
KoColorSlider::~KoColorSlider()
{
delete d;
}
void KoColorSlider::setColors(const KoColor& mincolor, const KoColor& maxcolor)
{
d->minColor = mincolor;
d->maxColor = maxcolor;
d->upToDate = false;
QTimer::singleShot(1, this, SLOT(update()));
}
void KoColorSlider::drawContents( QPainter *painter )
{
QPixmap checker(8, 8);
QPainter p(&checker);
p.fillRect(0, 0, 4, 4, Qt::lightGray);
p.fillRect(4, 0, 4, 4, Qt::darkGray);
p.fillRect(0, 4, 4, 4, Qt::darkGray);
p.fillRect(4, 4, 4, 4, Qt::lightGray);
p.end();
QRect contentsRect_(contentsRect());
painter->fillRect(contentsRect_, QBrush(checker));
if( !d->upToDate || d->pixmap.isNull() || d->pixmap.width() != contentsRect_.width()
|| d->pixmap.height() != contentsRect_.height() )
{
KoColor c = d->minColor; // smart way to fetch colorspace
QColor color;
const quint8 *colors[2];
colors[0] = d->minColor.data();
colors[1] = d->maxColor.data();
KoMixColorsOp * mixOp = c.colorSpace()->mixColorsOp();
Q_ASSERT(mixOp);
QImage image(contentsRect_.width(), contentsRect_.height(), QImage::Format_ARGB32 );
if( orientation() == Qt::Horizontal ) {
for (int x = 0; x < contentsRect_.width(); x++) {
qreal t = static_cast<qreal>(x) / (contentsRect_.width() - 1);
qint16 colorWeights[2];
colorWeights[0] = static_cast<quint8>((1.0 - t) * 255 + 0.5);
colorWeights[1] = 255 - colorWeights[0];
mixOp->mixColors(colors, colorWeights, 2, c.data());
if (d->displayRenderer) {
color = d->displayRenderer->toQColor(c);
}
else {
color = c.toQColor();
}
for (int y = 0; y < contentsRect_.height(); y++)
image.setPixel(x, y, color.rgba());
}
}
else {
for (int y = 0; y < contentsRect_.height(); y++) {
qreal t = static_cast<qreal>(y) / (contentsRect_.height() - 1);
qint16 colorWeights[2];
colorWeights[0] = static_cast<quint8>((t) * 255 + 0.5);
colorWeights[1] = 255 - colorWeights[0];
mixOp->mixColors(colors, colorWeights, 2, c.data());
if (d->displayRenderer) {
color = d->displayRenderer->toQColor(c);
}
else {
color = c.toQColor();
}
for (int x = 0; x < contentsRect_.width(); x++)
image.setPixel(x, y, color.rgba());
}
}
d->pixmap = QPixmap::fromImage(image);
d->upToDate = true;
}
painter->drawPixmap( contentsRect_, d->pixmap, QRect( 0, 0, d->pixmap.width(), d->pixmap.height()) );
}
KoColor KoColorSlider::currentColor() const
{
const quint8 *colors[2];
colors[0] = d->minColor.data();
colors[1] = d->maxColor.data();
KoMixColorsOp * mixOp = d->minColor.colorSpace()->mixColorsOp();
KoColor c(d->minColor.colorSpace());
qint16 weights[2];
weights[1] = (value() - minimum()) / qreal(maximum() - minimum()) * 255;
weights[0] = 255 - weights[1];
mixOp->mixColors(colors, weights, 2, c.data());
return c;
}
void KoColorSlider::drawArrow(QPainter *painter, const QPoint &pos)
{
painter->setPen(QPen(palette().text().color(), 0));
painter->setBrush(palette().text());
QStyleOption o;
o.initFrom(this);
o.state &= ~QStyle::State_MouseOver;
if ( orientation() == Qt::Vertical ) {
o.rect = QRect( pos.x(), pos.y() - ARROWSIZE / 2,
ARROWSIZE, ARROWSIZE );
} else {
o.rect = QRect( pos.x() - ARROWSIZE / 2, pos.y(),
ARROWSIZE, ARROWSIZE );
}
QStyle::PrimitiveElement arrowPE;
switch (arrowDirection()) {
case Qt::UpArrow:
arrowPE = QStyle::PE_IndicatorArrowUp;
break;
case Qt::DownArrow:
arrowPE = QStyle::PE_IndicatorArrowDown;
break;
case Qt::RightArrow:
arrowPE = QStyle::PE_IndicatorArrowRight;
break;
case Qt::LeftArrow:
default:
arrowPE = QStyle::PE_IndicatorArrowLeft;
break;
}
style()->drawPrimitive(arrowPE, &o, painter, this);
}
diff --git a/libs/widgets/KoDialog.h b/libs/widgets/KoDialog.h
index 0a454e4fa2..db2fe87ef6 100644
--- a/libs/widgets/KoDialog.h
+++ b/libs/widgets/KoDialog.h
@@ -1,835 +1,836 @@
/* This file is part of the KDE Libraries
* Copyright (C) 1998 Thomas Tanghus (tanghus@earthling.net)
* Additions 1999-2000 by Espen Sand (espen@kde.org)
* and Holger Freyther <freyther@kde.org>
* 2005-2009 Olivier Goffart <ogoffart @ kde.org>
* 2006 Tobias Koenig <tokoe@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KODIALOG_H
#define KODIALOG_H
class QPushButton;
class QMenu;
class KoDialogPrivate;
#include <kritawidgets_export.h>
#include <kconfiggroup.h>
#include <kguiitem.h>
#include <QDialog>
/**
* @short A dialog base class with standard buttons and predefined layouts.
*
* Provides basic functionality needed by nearly all dialogs.
*
* It offers the standard action buttons you'd expect to find in a
* dialog as well as the capability to define at most three configurable
* buttons. You can define a main widget that contains your specific
* dialog layout
*
* The class takes care of the geometry management. You only need to define
* a minimum size for the widget you want to use as the main widget.
*
* By default, the dialog is non-modal.
*
* <b>Standard buttons (action buttons):</b>\n
*
* You select which buttons should be displayed, but you do not choose the
* order in which they are displayed. This ensures a standard interface in
* KDE. The button order can be changed, but this ability is only available
* for a central KDE control tool. The following buttons are available:
* OK, Cancel/Close, Apply/Try, Default, Help and three user definable
* buttons: User1, User2 and User3. You must specify the text of the UserN
* buttons. Each button emit a signal, so you can choose to connect that signal.
*
* The default action of the Help button will open the help system if you have
* provided a path to the help text.
* The default action of Ok and Cancel will run QDialog::accept() and QDialog::reject(),
* which you can override by reimplementing slotButtonClicked(). The default
* action of the Close button will close the dialog.
*
* Note that the KoDialog will animate a button press
* when the user presses Escape. The button that is enabled is either Cancel,
* Close or the button that is defined by setEscapeButton().
* Your custom dialog code should reimplement the keyPressEvent and
* animate the cancel button so that the dialog behaves like regular
* dialogs.
*
* <b>Layout:</b>\n
*
* The dialog consists of a help area on top (becomes visible if you define
* a help path and use enableLinkedHelp()), the main area which is
* the built-in dialog face or your own widget in the middle and by default
* a button box at the bottom. The button box can also be placed at the
* right edge (to the right of the main widget). Use
* setButtonsOrientation() to control this behavior. A separator
* can be placed above the button box (or to the left when the button box
* is at the right edge).
*
* <b>Standard compliance:</b>\n
*
* The marginHint() and spacingHint() sizes shall be used
* whenever you lay out the interior of a dialog. One special note. If
* you make your own action buttons (OK, Cancel etc), the space
* between the buttons shall be spacingHint(), whereas the space
* above, below, to the right and to the left shall be marginHint().
* If you add a separator line above the buttons, there shall be a
* marginHint() between the buttons and the separator and a
* marginHint() above the separator as well.
*
* <b>Example:</b>\n
*
* \code
* KoDialog *dialog = new KoDialog( this );
* dialog->setCaption( "My title" );
* dialog->setButtons( KoDialog::Ok | KoDialog::Cancel | KoDialog::Apply );
*
* FooWidget *widget = new FooWidget( dialog );
* dialog->setMainWidget( widget );
* connect( dialog, SIGNAL( applyClicked() ), widget, SLOT( save() ) );
* connect( dialog, SIGNAL( okClicked() ), widget, SLOT( save() ) );
* connect( widget, SIGNAL( changed( bool ) ), dialog, SLOT( enableButtonApply( bool ) ) );
*
* dialog->enableButtonApply( false );
* dialog->show();
* \endcode
*
* \image html kdialog.png "KDE Dialog example"
*
* This class can be used in many ways. Note that most KDE ui widgets
* and many of KDE core applications use the KoDialog so for more
* inspiration you should study the code for these.
*
*
* @see KPageDialog
* @author Thomas Tanghus <tanghus@earthling.net>
* @author Espen Sand <espensa@online.no>
* @author Mirko Boehm <mirko@kde.org>
* @author Olivier Goffart <ogoffart at kde.org>
* @author Tobias Koenig <tokoe@kde.org>
*/
class KRITAWIDGETS_EXPORT KoDialog : public QDialog //krazy:exclude=qclasses
{
Q_OBJECT
Q_ENUMS(ButtonCode)
Q_DECLARE_PRIVATE(KoDialog)
public:
enum ButtonCode {
None = 0x00000000,
Help = 0x00000001, ///< Show Help button. (this button will run the help set with setHelp)
Default = 0x00000002, ///< Show Default button.
Ok = 0x00000004, ///< Show Ok button. (this button accept()s the dialog; result set to QDialog::Accepted)
Apply = 0x00000008, ///< Show Apply button.
Try = 0x00000010, ///< Show Try button.
Cancel = 0x00000020, ///< Show Cancel-button. (this button reject()s the dialog; result set to QDialog::Rejected)
Close = 0x00000040, ///< Show Close-button. (this button closes the dialog)
No = 0x00000080, ///< Show No button. (this button closes the dialog and sets the result to KoDialog::No)
Yes = 0x00000100, ///< Show Yes button. (this button closes the dialog and sets the result to KoDialog::Yes)
Reset = 0x00000200, ///< Show Reset button
Details = 0x00000400, ///< Show Details button. (this button will show the detail widget set with setDetailsWidget)
User1 = 0x00001000, ///< Show User defined button 1.
User2 = 0x00002000, ///< Show User defined button 2.
User3 = 0x00004000, ///< Show User defined button 3.
NoDefault = 0x00008000 ///< Used when specifying a default button; indicates that no button should be marked by default.
};
// TODO KDE5: remove NoDefault and use the value None instead
Q_DECLARE_FLAGS(ButtonCodes, ButtonCode)
enum ButtonPopupMode {
InstantPopup = 0,
DelayedPopup = 1
};
Q_DECLARE_FLAGS(ButtonPopupModes, ButtonPopupMode)
public:
/**
* Creates a dialog.
*
* @param parent The parent of the dialog.
* @param flags The widget flags passed to the QDialog constructor
*/
explicit KoDialog(QWidget *parent = 0, Qt::WindowFlags flags = 0);
/**
* Destroys the dialog.
*/
~KoDialog() override;
/**
* Creates (or recreates) the button box and all the buttons in it.
*
* Note that some combinations are not possible. That means, you can't
* have the following pairs of buttons in a dialog:
* - Default and Details
* - Cancel and Close
* - Ok and Try
*
* This will reset all default KGuiItem of all button.
*
* @param buttonMask Specifies what buttons will be made.
*
* @deprecated Since 5.0 use QDialogButtonBox
*/
void setButtons(ButtonCodes buttonMask);
/**
* Sets the orientation of the button box.
*
* It can be @p Vertical or @p Horizontal. If @p Horizontal
* (default), the button box is positioned at the bottom of the
* dialog. If @p Vertical it will be placed at the right edge of the
* dialog.
*
* @param orientation The button box orientation.
*/
void setButtonsOrientation(Qt::Orientation orientation);
/**
* Sets the button that will be activated when the Escape key
* is pressed.
*
* By default, the Escape key is mapped to either the Cancel or the Close button
* if one of these buttons are defined. The user expects that Escape will
* cancel an operation so use this function with caution.
*
* @param id The button code.
*/
void setEscapeButton(ButtonCode id);
/**
* Sets the button that will be activated when the Enter key
* is pressed.
*
* By default, this is the Ok button if it is present
*
* @param id The button code.
*/
void setDefaultButton(ButtonCode id);
/**
* Returns the button code of the default button,
* or NoDefault if there is no default button.
*/
ButtonCode defaultButton() const;
/**
* Hide or display the a separator line drawn between the action
* buttons an the main widget.
*/
void showButtonSeparator(bool state);
/**
* Hide or display a general action button.
*
* Only buttons that have
* been created in the constructor can be displayed. This method will
* not create a new button.
*
* @param id Button identifier.
* @param state true display the button(s).
*/
void showButton(ButtonCode id, bool state);
/**
* Sets the text of any button.
*
* @param id The button identifier.
* @param text Button text.
*/
void setButtonText(ButtonCode id, const QString &text);
/**
* Returns the text of any button.
*/
QString buttonText(ButtonCode id) const;
/**
* Sets the icon of any button.
*
* @param id The button identifier.
* @param icon Button icon.
*/
void setButtonIcon(ButtonCode id, const QIcon &icon);
/**
* Returns the icon of any button.
*/
QIcon buttonIcon(ButtonCode id) const;
/**
* Sets the tooltip text of any button.
*
* @param id The button identifier.
* @param text Button text.
*/
void setButtonToolTip(ButtonCode id, const QString &text);
/**
* Returns the tooltip of any button.
*/
QString buttonToolTip(ButtonCode id) const;
/**
* Sets the "What's this?" text of any button.
*
* @param id The button identifier.
* @param text Button text.
*/
void setButtonWhatsThis(ButtonCode id, const QString &text);
/**
* Returns the "What's this?" text of any button.
*/
QString buttonWhatsThis(ButtonCode id) const;
/**
* Sets the KGuiItem directly for the button instead of using 3 methods to
* set the text, tooltip and whatsthis strings. This also allows to set an
* icon for the button which is otherwise not possible for the extra
* buttons beside Ok, Cancel and Apply.
*
* @param id The button identifier.
* @param item The KGuiItem for the button.
*/
void setButtonGuiItem(ButtonCode id, const KGuiItem &item);
/**
* Sets the focus to the button of the passed @p id.
*/
void setButtonFocus(ButtonCode id);
/**
* Convenience method. Sets the initial dialog size.
*
* This method should only be called right before show() or exec().
* The initial size will be ignored if smaller than
* the dialog's minimum size.
*
* @param size Startup size.
*/
void setInitialSize(const QSize &size);
/**
* Convenience method. Add a size to the default minimum size of a
* dialog.
*
* This method should only be called right before show() or exec().
*
* @param size Size added to minimum size.
*/
void incrementInitialSize(const QSize &size);
/**
* Returns the help link text.
*
* If no text has been defined,
* "Get help..." (internationalized) is returned.
*
* @return The help link text.
*
* @see enableLinkedHelp()
* @see setHelpLinkText()
* @see setHelp()
*/
QString helpLinkText() const;
/**
* Returns whether any button is enabled.
*/
bool isButtonEnabled(ButtonCode id) const;
/**
* Returns the button that corresponds to the @p id.
*
* Normally you should not use this function.
* @em Never delete the object returned by this function.
* See also enableButton(), showButton(), setButtonGuiItem().
*
* @param id Identifier of the button.
* @return The button or 0 if the button does not exist.
*/
QPushButton *button(ButtonCode id) const;
/**
* Returns the number of pixels that should be used between a
* dialog edge and the outermost widget(s) according to the KDE standard.
*
* @deprecated Use the style's pixelMetric() function to query individual margins.
* Different platforms may use different values for the four margins.
*/
static int marginHint();
/**
* Returns the number of pixels that should be used between
* widgets inside a dialog according to the KDE standard.
*
* @deprecated Use the style's layoutSpacing() function to query individual spacings.
* Different platforms may use different values depending on widget types and pairs.
*/
static int spacingHint();
/**
* Returns the number of pixels that should be used to visually
* separate groups of related options in a dialog according to
* the KDE standard.
* @since 4.2
*/
static int groupSpacingHint();
/**
- * @enum StandardCaptionFlag
+ * @enum CaptionFlag
* Used to specify how to construct a window caption
*
- * @value AppName Indicates that the method shall include
+ * @var NoCaptionFlags Indicates that the method has no caption flags.
+ * @var AppNameCaption Indicates that the method shall include
* the application name when making the caption string.
- * @value Modified Causes a 'modified' sign will be included in the
+ * @var ModifiedCaption Causes a 'modified' sign will be included in the
* returned string. This is useful when indicating that a file is
* modified, i.e., it contains data that has not been saved.
- * @value HIGCompliant The base minimum flags required to align a
+ * @var HIGCompliantCaption The base minimum flags required to align a
* caption with the KDE Human Interface Guidelines
*/
enum CaptionFlag {
NoCaptionFlags = 0,
AppNameCaption = 1,
ModifiedCaption = 2,
HIGCompliantCaption = AppNameCaption
};
Q_DECLARE_FLAGS(CaptionFlags, CaptionFlag)
/**
* Builds a caption that contains the application name along with the
* userCaption using a standard layout.
*
* To make a compliant caption for your window, simply do:
* @p setWindowTitle(KoDialog::makeStandardCaption(yourCaption));
*
* To ensure that the caption is appropriate to the desktop in which the
* application is running, pass in a pointer to the window the caption will
* be applied to.
*
* If using a KoDialog or KMainWindow subclass, call setCaption instead and
* an appropriate standard caption will be created for you
*
* @param userCaption The caption string you want to display in the
* window caption area. Do not include the application name!
* @param window a pointer to the window this application will apply to
* @param flags
* @return the created caption
*/
static QString makeStandardCaption(const QString &userCaption,
QWidget *window = 0,
CaptionFlags flags = HIGCompliantCaption);
/**
* Resize every layout manager used in @p widget and its nested children.
*
* @param widget The widget used.
* @param margin The new layout margin.
* @param spacing The new layout spacing.
*
* @deprecated Use QLayout functions where necessary. Setting margin and spacing
* values recursively for all children prevents QLayout from creating platform native
* layouts.
*/
static void resizeLayout(QWidget *widget, int margin, int spacing);
/**
* Resize every layout associated with @p lay and its children.
*
* @param lay layout to be resized
* @param margin The new layout margin
* @param spacing The new layout spacing
*
* @deprecated Use QLayout functions where necessary. Setting margin and spacing
* values recursively for all children prevents QLayout from creating platform native
* layouts.
*/
static void resizeLayout(QLayout *lay, int margin, int spacing);
/**
* Centers @p widget on the desktop, taking multi-head setups into
* account. If @p screen is -1, @p widget will be centered on its
* current screen (if it was shown already) or on the primary screen.
* If @p screen is -3, @p widget will be centered on the screen that
* currently contains the mouse pointer.
* @p screen will be ignored if a merged display (like Xinerama) is not
* in use, or merged display placement is not enabled in kdeglobals.
*/
static void centerOnScreen(QWidget *widget, int screen = -1);
/**
* Places @p widget so that it doesn't cover a certain @p area of the screen.
* This is typically used by the "find dialog" so that the match it finds can
* be read.
* For @p screen, see centerOnScreen
* @return true on success (widget doesn't cover area anymore, or never did),
* false on failure (not enough space found)
*/
static bool avoidArea(QWidget *widget, const QRect &area, int screen = -1);
/**
* Sets the main widget of the dialog.
*/
void setMainWidget(QWidget *widget);
/**
* @return The current main widget. Will create a QWidget as the mainWidget
* if none was set before. This way you can write
* \code
* ui.setupUi(mainWidget());
* \endcode
* when using designer.
*/
QWidget *mainWidget();
/**
* Reimplemented from QDialog.
*/
QSize sizeHint() const override;
/**
* Reimplemented from QDialog.
*/
QSize minimumSizeHint() const override;
public Q_SLOTS:
/**
* Make a KDE compliant caption.
*
* @param caption Your caption. Do @p not include the application name
* in this string. It will be added automatically according to the KDE
* standard.
*
* @deprecated Since 5.0 use QWidget::setWindowTitle
*/
virtual void setCaption(const QString &caption);
/**
* Makes a KDE compliant caption.
*
* @param caption Your caption. @em Do @em not include the application name
* in this string. It will be added automatically according to the KDE
* standard.
* @param modified Specify whether the document is modified. This displays
* an additional sign in the title bar, usually "**".
*
* @deprecated Since 5.0 use QWidget::setWindowTitle and QWidget::setWindowModified.
*/
virtual void setCaption(const QString &caption, bool modified);
/**
* Make a plain caption without any modifications.
*
* @param caption Your caption. This is the string that will be
* displayed in the window title.
*/
virtual void setPlainCaption(const QString &caption);
/**
* Enable or disable (gray out) a general action button.
*
* @param id Button identifier.
* @param state @p true enables the button(s).
*/
void enableButton(ButtonCode id, bool state);
/**
* Enable or disable (gray out) the OK button.
*
* @param state @p true enables the button.
*/
void enableButtonOk(bool state);
/**
* Enable or disable (gray out) the Apply button.
*
* @param state true enables the button.
*/
void enableButtonApply(bool state);
/**
* Enable or disable (gray out) the Cancel button.
*
* @param state true enables the button.
*/
void enableButtonCancel(bool state);
/**
* Display or hide the help link area on the top of the dialog.
*
* @param state @p true will display the area.
*
* @see helpLinkText()
* @see setHelpLinkText()
* @see setHelp()
*/
void enableLinkedHelp(bool state);
/**
* Sets the text that is shown as the linked text.
*
* If text is empty,
* the text "Get help..." (internationalized) is used instead.
*
* @param text The link text.
*
* @see helpLinkText()
* @see enableLinkedHelp()
* @see setHelp()
*/
void setHelpLinkText(const QString &text);
/**
* Sets the help path and topic.
*
* @param anchor Defined anchor in your docbook sources
* @param appname Defines the appname the help belongs to
* If empty it's the current one
*
* @note The help button works differently for the class
* KCMultiDialog, so it does not make sense to call this
* function for Dialogs of that type. See
* KCMultiDialog::slotHelp() for more information.
*/
void setHelp(const QString &anchor, const QString &appname = QString());
/**
* Returns the status of the Details button.
*/
bool isDetailsWidgetVisible() const;
/**
* Sets the status of the Details button.
*/
void setDetailsWidgetVisible(bool visible);
/**
* Sets the widget that gets shown when "Details" is enabled.
*
* The dialog takes over ownership of the widget.
* Any previously set widget gets deleted.
*/
void setDetailsWidget(QWidget *detailsWidget);
/**
* Destruct the dialog delayed.
*
* You can call this function from slots like closeClicked() and hidden().
* You should not use the dialog any more after calling this function.
* @deprecated use hide()+deleteLater()
*/
void delayedDestruct();
Q_SIGNALS:
/**
* Emitted when the margin size and/or spacing size
* have changed.
*
* Use marginHint() and spacingHint() in your slot
* to get the new values.
*
* @deprecated This signal is not emitted. Listen to QEvent::StyleChange events instead.
*/
void layoutHintChanged();
/**
* The Help button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void helpClicked();
/**
* The Default button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void defaultClicked();
/**
* The Reset button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void resetClicked();
/**
* The User3 button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void user3Clicked();
/**
* The User2 button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void user2Clicked();
/**
* The User1 button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void user1Clicked();
/**
* The Apply button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void applyClicked();
/**
* The Try button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void tryClicked();
/**
* The OK button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void okClicked();
/**
* The Yes button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void yesClicked();
/**
* The No button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void noClicked();
/**
* The Cancel button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void cancelClicked();
/**
* The Close button was pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
*/
void closeClicked();
/**
* A button has been pressed. This signal is only emitted if
* slotButtonClicked() is not replaced
* @param button is the code of the pressed button.
*/
void buttonClicked(KoDialog::ButtonCode button);
/**
* The dialog is about to be hidden.
*
* A dialog is hidden after a user clicks a button that ends
* the dialog or when the user switches to another desktop or
* minimizes the dialog.
*/
void hidden();
/**
* The dialog has finished.
*
* A dialog emits finished after a user clicks a button that ends
* the dialog.
*
* This signal is also emitted when you call hide()
*
* If you have stored a pointer to the
* dialog do @em not try to delete the pointer in the slot that is
* connected to this signal.
*
* You should use deleteLater() instead.
*/
void finished();
/**
* The detailsWidget is about to get shown. This is your last chance
* to call setDetailsWidget if you haven't done so yet.
*/
void aboutToShowDetails();
protected:
/**
* Emits the #hidden signal. You can connect to that signal to
* detect when a dialog has been closed.
*/
void hideEvent(QHideEvent *) override;
/**
* Detects when a dialog is being closed from the window manager
* controls. If the Cancel or Close button is present then the button
* is activated. Otherwise standard QDialog behavior
* will take place.
*/
void closeEvent(QCloseEvent *e) override;
/**
* @internal
*/
void keyPressEvent(QKeyEvent *) override;
protected Q_SLOTS:
/**
* Activated when the button @p button is clicked
*
* Sample that shows how to catch and handle button clicks within
* an own dialog;
* @code
* class MyDialog : public KoDialog {
* protected Q_SLOTS:
* virtual void slotButtonClicked(int button) {
* if (button == KoDialog::Ok)
* accept();
* else
* KoDialog::slotButtonClicked(button);
* }
* }
* @endcode
*
* @param button is the type @a KoDialog::ButtonCode
*
* @deprecated since 5.0 use QDialogButtonBox and connect to the clicked signal
*/
virtual void slotButtonClicked(int button);
/**
* Updates the margins and spacings.
*
* @deprecated KoDialog respects the style's margins and spacings automatically. Calling
* this function has no effect.
*/
void updateGeometry();
private:
KoDialog(KoDialogPrivate &dd, QWidget *parent, Qt::WindowFlags flags = 0);
KoDialogPrivate *const d_ptr;
private:
Q_DISABLE_COPY(KoDialog)
Q_PRIVATE_SLOT(d_ptr, void queuedLayoutUpdate())
Q_PRIVATE_SLOT(d_ptr, void helpLinkClicked())
};
Q_DECLARE_OPERATORS_FOR_FLAGS(KoDialog::ButtonCodes)
Q_DECLARE_OPERATORS_FOR_FLAGS(KoDialog::CaptionFlags)
#endif // KODIALOG_H
diff --git a/libs/widgets/KoResourcePopupAction.h b/libs/widgets/KoResourcePopupAction.h
index baa1054bee..ce64f1ad17 100644
--- a/libs/widgets/KoResourcePopupAction.h
+++ b/libs/widgets/KoResourcePopupAction.h
@@ -1,73 +1,74 @@
/* This file is part of the KDE project
* Made by Tomislav Lukman (tomislav.lukman@ck.tel.hr)
* Copyright (C) 2012 Jean-Nicolas Artaud <jeannicolasartaud@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KORESOURCEPOPUPACTION_H
#define KORESOURCEPOPUPACTION_H
#include <QAction>
#include <QSharedPointer>
#include "kritawidgets_export.h"
class KoShapeBackground;
class KoAbstractResourceServerAdapter;
class QModelIndex;
class KoResource;
class KRITAWIDGETS_EXPORT KoResourcePopupAction : public QAction
{
Q_OBJECT
public:
/**
* Constructs a KoResourcePopupAction (gradient or pattern) with the specified parent.
*
+ * @param gradientResourceAdapter pointer to the gradient or pattern
* @param parent The parent for this action.
*/
explicit KoResourcePopupAction(QSharedPointer<KoAbstractResourceServerAdapter>gradientResourceAdapter, QObject *parent = 0);
/**
* Destructor
*/
~KoResourcePopupAction() override;
QSharedPointer<KoShapeBackground> currentBackground() const;
void setCurrentBackground(QSharedPointer<KoShapeBackground> background);
void setCurrentResource(KoResource *resource);
KoResource *currentResource() const;
Q_SIGNALS:
/// Emitted when a resource was selected
void resourceSelected(QSharedPointer<KoShapeBackground> background);
public Q_SLOTS:
void updateIcon();
private Q_SLOTS:
void indexChanged(const QModelIndex &modelIndex);
private:
class Private;
Private * const d;
};
#endif /* KORESOURCEPOPUPACTION_H */
diff --git a/libs/widgets/KoResourceServer.h b/libs/widgets/KoResourceServer.h
index c79958712e..a2081680ca 100644
--- a/libs/widgets/KoResourceServer.h
+++ b/libs/widgets/KoResourceServer.h
@@ -1,760 +1,760 @@
/* This file is part of the KDE project
Copyright (c) 1999 Matthias Elter <elter@kde.org>
Copyright (c) 2003 Patrick Julien <freak@codepimps.org>
Copyright (c) 2005 Sven Langkamp <sven.langkamp@gmail.com>
Copyright (c) 2007 Jan Hambrecht <jaham@gmx.net>
Copyright (C) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
Copyright (c) 2013 Sascha Suelzer <s.suelzer@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef KORESOURCESERVER_H
#define KORESOURCESERVER_H
#include <QMutex>
#include <QString>
#include <QStringList>
#include <QList>
#include <QFileInfo>
#include <QDir>
#include <QTemporaryFile>
#include <QDomDocument>
#include "resources/KoResource.h"
#include "KoResourceServerPolicies.h"
#include "KoResourceServerObserver.h"
#include "KoResourceTagStore.h"
#include "KoResourcePaths.h"
#include <kconfiggroup.h>
#include <ksharedconfig.h>
#include "kritawidgets_export.h"
#include "WidgetsDebug.h"
class KoResource;
/**
* KoResourceServerBase is the base class of all resource servers
*/
class KRITAWIDGETS_EXPORT KoResourceServerBase {
public:
/**
* Constructs a KoResourceServerBase
- * @param resource type, has to be the same as used by KoResourcePaths
- * @param extensions the file extensions separate by ':', e.g. "*.kgr:*.svg:*.ggr"
+ * @param type type, has to be the same as used by KoResourcePaths
+ * @param extensions the file extensions separate by ':', e.g. *.svg:*.ggr"
*/
KoResourceServerBase(const QString& type, const QString& extensions)
: m_type(type)
, m_extensions(extensions)
{
}
virtual ~KoResourceServerBase() {}
virtual int resourceCount() const = 0;
virtual void loadResources(QStringList filenames) = 0;
virtual QStringList blackListedFiles() = 0;
virtual QStringList queryResources(const QString &query) const = 0;
QString type() const { return m_type; }
/**
* File extensions for resources of the server
- * @returns the file extensions separated by ':', e.g. "*.kgr:*.svg:*.ggr"
+ * @returns the file extensions separated by ':', e.g. "*.svg:*.ggr"
*/
QString extensions() const { return m_extensions; }
QStringList fileNames()
{
QStringList extensionList = m_extensions.split(':');
QStringList fileNames;
foreach (const QString &extension, extensionList) {
fileNames += KoResourcePaths::findAllResources(type().toLatin1(), extension, KoResourcePaths::Recursive);
}
return fileNames;
}
protected:
QStringList m_blackListFileNames;
friend class KoResourceTagStore;
virtual KoResource *byMd5(const QByteArray &md5) const = 0;
virtual KoResource *byFileName(const QString &fileName) const = 0;
private:
QString m_type;
QString m_extensions;
protected:
QMutex m_loadLock;
};
/**
* KoResourceServer manages the resources of one type. It stores,
* loads and saves the resources. To keep track of changes the server
* can be observed with a KoResourceServerObserver
*
* The \p Policy template parameter defines the way how the lifetime
* of a resource is handled. There are to predefined policies:
*
* o PointerStoragePolicy --- usual pointers with ownership over
* the resource.
* o SharedPointerStoragePolicy --- shared pointers. The server does no
* extra handling for the lifetime of
* the resource.
*
* Use the former for usual resources and the latter for shared pointer based
* ones.
*/
template <class T, class Policy = PointerStoragePolicy<T> >
class KoResourceServer : public KoResourceServerBase
{
public:
typedef typename Policy::PointerType PointerType;
typedef KoResourceServerObserver<T, Policy> ObserverType;
KoResourceServer(const QString& type, const QString& extensions)
: KoResourceServerBase(type, extensions)
{
m_blackListFile = KoResourcePaths::locateLocal("data", type + ".blacklist");
m_blackListFileNames = readBlackListFile();
m_tagStore = new KoResourceTagStore(this);
}
~KoResourceServer() override
{
if (m_tagStore) {
delete m_tagStore;
}
Q_FOREACH (ObserverType* observer, m_observers) {
observer->unsetResourceServer();
}
Q_FOREACH (PointerType res, m_resources) {
Policy::deleteResource(res);
}
m_resources.clear();
}
int resourceCount() const override {
return m_resources.size();
}
/**
* Loads a set of resources and adds them to the resource server.
* If a filename appears twice the resource will only be added once. Resources that can't
* be loaded or and invalid aren't added to the server.
* @param filenames list of filenames to be loaded
*/
void loadResources(QStringList filenames) override {
QStringList uniqueFiles;
while (!filenames.empty()) {
QString front = filenames.first();
filenames.pop_front();
// In the save location, people can use sub-folders... And then they probably want
// to load both versions! See https://bugs.kde.org/show_bug.cgi?id=321361.
QString fname;
if (front.contains(saveLocation())) {
fname = front.split(saveLocation())[1];
}
else {
fname = QFileInfo(front).fileName();
}
// XXX: Don't load resources with the same filename. Actually, we should look inside
// the resource to find out whether they are really the same, but for now this
// will prevent the same brush etc. showing up twice.
if (!uniqueFiles.contains(fname)) {
m_loadLock.lock();
uniqueFiles.append(fname);
QList<PointerType> resources = createResources(front);
Q_FOREACH (PointerType resource, resources) {
Q_CHECK_PTR(resource);
if (resource->load() && resource->valid() && !resource->md5().isEmpty()) {
addResourceToMd5Registry(resource);
m_resourcesByFilename[resource->shortFilename()] = resource;
if (resource->name().isEmpty()) {
resource->setName(fname);
}
if (m_resourcesByName.contains(resource->name())) {
resource->setName(resource->name() + "(" + resource->shortFilename() + ")");
}
m_resourcesByName[resource->name()] = resource;
notifyResourceAdded(resource);
}
else {
warnWidgets << "Loading resource " << front << "failed." << type();
Policy::deleteResource(resource);
}
}
m_loadLock.unlock();
}
}
m_resources = sortedResources();
Q_FOREACH (ObserverType* observer, m_observers) {
observer->syncTaggedResourceView();
}
// qDebug() << "done loading resources for type " << type();
}
void loadTags() {
m_tagStore->loadTags();
}
void clearOldSystemTags() {
m_tagStore->clearOldSystemTags();
}
/// Adds an already loaded resource to the server
bool addResource(PointerType resource, bool save = true, bool infront = false) {
if (!resource->valid()) {
warnWidgets << "Tried to add an invalid resource!";
return false;
}
if (save) {
QFileInfo fileInfo(resource->filename());
QDir d(fileInfo.path());
if (!d.exists()) {
d.mkdir(fileInfo.path());
}
if (fileInfo.exists()) {
QString filename = fileInfo.path() + "/" + fileInfo.baseName() + "XXXXXX" + "." + fileInfo.suffix();
debugWidgets << "fileName is " << filename;
QTemporaryFile file(filename);
if (file.open()) {
debugWidgets << "now " << file.fileName();
resource->setFilename(file.fileName());
}
}
if (!resource->save()) {
warnWidgets << "Could not save resource!";
return false;
}
}
Q_ASSERT(!resource->filename().isEmpty() || !resource->name().isEmpty());
if (resource->filename().isEmpty()) {
resource->setFilename(resource->name());
}
else if (resource->name().isEmpty()) {
resource->setName(resource->filename());
}
m_resourcesByFilename[resource->shortFilename()] = resource;
addResourceToMd5Registry(resource);
m_resourcesByName[resource->name()] = resource;
if (infront) {
m_resources.insert(0, resource);
}
else {
m_resources.append(resource);
}
notifyResourceAdded(resource);
return true;
}
/**
* Removes a given resource from the blacklist.
*/
bool removeFromBlacklist(PointerType resource) {
if (m_blackListFileNames.contains(resource->filename())) {
m_blackListFileNames.removeAll(resource->filename());
writeBlackListFile();
return true;
}
return false;
}
/// Remove a resource from Resource Server but not from a file
bool removeResourceFromServer(PointerType resource){
if ( !m_resourcesByFilename.contains( resource->shortFilename() ) ) {
return false;
}
removeResourceFromMd5Registry(resource);
m_resourcesByName.remove(resource->name());
m_resourcesByFilename.remove(resource->shortFilename());
m_resources.removeAt(m_resources.indexOf(resource));
m_tagStore->removeResource(resource);
notifyRemovingResource(resource);
Policy::deleteResource(resource);
return true;
}
/// Remove a resource from the resourceserver and blacklist it
bool removeResourceAndBlacklist(PointerType resource) {
if ( !m_resourcesByFilename.contains( resource->shortFilename() ) ) {
return false;
}
removeResourceFromMd5Registry(resource);
m_resourcesByName.remove(resource->name());
m_resourcesByFilename.remove(resource->shortFilename());
m_resources.removeAt(m_resources.indexOf(resource));
m_tagStore->removeResource(resource);
notifyRemovingResource(resource);
m_blackListFileNames.append(resource->filename());
writeBlackListFile();
Policy::deleteResource(resource);
return true;
}
QList<PointerType> resources() {
m_loadLock.lock();
QList<PointerType> resourceList = m_resources;
Q_FOREACH (PointerType r, m_resourceBlackList) {
resourceList.removeOne(r);
}
m_loadLock.unlock();
return resourceList;
}
/// Returns path where to save user defined and imported resources to
virtual QString saveLocation() {
return KoResourcePaths::saveLocation(type().toLatin1());
}
/**
* Creates a new resource from a given file and adds them to the resource server
* The base implementation does only load one resource per file, override to implement collections
* @param filename file name of the resource file to be imported
* @param fileCreation decides whether to create the file in the saveLocation() directory
*/
virtual bool importResourceFile(const QString & filename , bool fileCreation=true) {
QFileInfo fi(filename);
if (!fi.exists())
return false;
if ( fi.size() == 0)
return false;
PointerType resource = createResource( filename );
resource->load();
if (!resource->valid()) {
warnWidgets << "Import failed! Resource is not valid";
Policy::deleteResource(resource);
return false;
}
if (fileCreation) {
Q_ASSERT(!resource->defaultFileExtension().isEmpty());
Q_ASSERT(!saveLocation().isEmpty());
QString newFilename = saveLocation() + fi.baseName() + resource->defaultFileExtension();
QFileInfo fileInfo(newFilename);
int i = 1;
while (fileInfo.exists()) {
fileInfo.setFile(saveLocation() + fi.baseName() + QString("%1").arg(i) + resource->defaultFileExtension());
i++;
}
resource->setFilename(fileInfo.filePath());
}
if(!addResource(resource)) {
Policy::deleteResource(resource);
}
return true;
}
/// Removes the resource file from the resource server
void removeResourceFile(const QString & filename)
{
QFileInfo fi(filename);
PointerType resource = resourceByFilename(fi.fileName());
if (!resource) {
warnWidgets << "Resource file do not exist ";
return;
}
removeResourceFromServer(resource);
}
/**
* Addes an observer to the server
* @param observer the observer to be added
* @param notifyLoadedResources determines if the observer should be notified about the already loaded resources
*/
void addObserver(ObserverType* observer, bool notifyLoadedResources = true)
{
m_loadLock.lock();
if(observer && !m_observers.contains(observer)) {
m_observers.append(observer);
if(notifyLoadedResources) {
Q_FOREACH (PointerType resource, m_resourcesByFilename) {
observer->resourceAdded(resource);
}
}
}
m_loadLock.unlock();
}
/**
* Removes an observer from the server
* @param observer the observer to be removed
*/
void removeObserver(ObserverType* observer)
{
int index = m_observers.indexOf( observer );
if( index < 0 )
return;
m_observers.removeAt( index );
}
PointerType resourceByFilename(const QString& filename) const
{
if (m_resourcesByFilename.contains(filename)) {
return m_resourcesByFilename[filename];
}
return 0;
}
PointerType resourceByName( const QString& name ) const
{
if (m_resourcesByName.contains(name)) {
return m_resourcesByName[name];
}
return 0;
}
PointerType resourceByMD5(const QByteArray& md5) const
{
return m_resourcesByMd5.value(md5);
}
/**
* Call after changing the content of a resource;
* Notifies the connected views.
*/
void updateResource( PointerType resource )
{
notifyResourceChanged(resource);
}
QStringList blackListedFiles() override
{
if (type() == "kis_resourcebundles") {
KConfigGroup group = KSharedConfig::openConfig()->group("BundleHack");
if (group.readEntry("HideKrita3Bundle", true)) {
Q_FOREACH(const QString &filename, fileNames()) {
if (filename.endsWith("Krita_3_Default_Resources.bundle")) {
if (!m_blackListFileNames.contains(filename)) {
m_blackListFileNames.append(filename);
}
}
}
}
// qDebug() << "blacklisted filenames" << m_blackListFileNames;
}
return m_blackListFileNames;
}
void removeBlackListedFiles() {
QStringList remainingFiles; // Files that can't be removed e.g. no rights will stay blacklisted
Q_FOREACH (const QString &filename, m_blackListFileNames) {
QFile file( filename );
if( ! file.remove() ) {
remainingFiles.append(filename);
}
}
m_blackListFileNames = remainingFiles;
writeBlackListFile();
}
QStringList tagNamesList() const
{
return m_tagStore->tagNamesList();
}
// don't use these method directly since it doesn't update views!
void addTag( KoResource* resource,const QString& tag)
{
m_tagStore->addTag(resource,tag);
}
// don't use these method directly since it doesn't update views!
void delTag( KoResource* resource,const QString& tag)
{
m_tagStore->delTag(resource, tag);
}
QStringList searchTag(const QString& lineEditText)
{
return m_tagStore->searchTag(lineEditText);
}
void tagCategoryAdded(const QString& tag)
{
m_tagStore->serializeTags();
Q_FOREACH (ObserverType* observer, m_observers) {
observer->syncTagAddition(tag);
}
}
void tagCategoryRemoved(const QString& tag)
{
m_tagStore->delTag(tag);
m_tagStore->serializeTags();
Q_FOREACH (ObserverType* observer, m_observers) {
observer->syncTagRemoval(tag);
}
}
void tagCategoryMembersChanged()
{
m_tagStore->serializeTags();
Q_FOREACH (ObserverType* observer, m_observers) {
observer->syncTaggedResourceView();
}
}
QStringList queryResources(const QString &query) const override
{
return m_tagStore->searchTag(query);
}
QStringList assignedTagsList(KoResource* resource) const
{
return m_tagStore->assignedTagsList(resource);
}
/**
* Create one or more resources from a single file. By default one resource is created.
* Override to create more resources from the file.
* @param filename the filename of the resource or resource collection
*/
virtual QList<PointerType> createResources( const QString & filename )
{
QList<PointerType> createdResources;
createdResources.append(createResource(filename));
return createdResources;
}
virtual PointerType createResource( const QString & filename ) = 0;
/// Return the currently stored resources in alphabetical order, overwrite for customized sorting
virtual QList<PointerType> sortedResources()
{
QMap<QString, PointerType> sortedNames;
Q_FOREACH (const QString &name, m_resourcesByName.keys()) {
sortedNames.insert(name.toLower(), m_resourcesByName[name]);
}
return sortedNames.values();
}
protected:
void notifyResourceAdded(PointerType resource)
{
Q_FOREACH (ObserverType* observer, m_observers) {
observer->resourceAdded(resource);
}
}
void notifyRemovingResource(PointerType resource)
{
Q_FOREACH (ObserverType* observer, m_observers) {
observer->removingResource(resource);
}
}
void notifyResourceChanged(PointerType resource)
{
Q_FOREACH (ObserverType* observer, m_observers) {
observer->resourceChanged(resource);
}
}
/// Reads the xml file and returns the filenames as a list
QStringList readBlackListFile()
{
QStringList filenameList;
QFile f(m_blackListFile);
if (!f.open(QIODevice::ReadOnly)) {
return filenameList;
}
QDomDocument doc;
if (!doc.setContent(&f)) {
warnWidgets << "The file could not be parsed.";
return filenameList;
}
QDomElement root = doc.documentElement();
if (root.tagName() != "resourceFilesList") {
warnWidgets << "The file doesn't seem to be of interest.";
return filenameList;
}
QDomElement file = root.firstChildElement("file");
while (!file.isNull()) {
QDomNode n = file.firstChild();
QDomElement e = n.toElement();
if (e.tagName() == "name") {
// If the krita bundle has landed in the blacklist, skip it.
if (type() == "kis_resourcebundles") {
// qDebug() << "Checking for not reading bundle" << e.text();
if (e.text().endsWith("Krita_3_Default_Resources.bundle")) {
file = file.nextSiblingElement("file");
}
}
filenameList.append(e.text().replace(QString("~"), QDir::homePath()));
}
file = file.nextSiblingElement("file");
}
// if (type() == "kis_resourcebundles") {
// qDebug() << "Read bundle blacklist" << filenameList;
// }
return filenameList;
}
/// write the blacklist file entries to an xml file
void writeBlackListFile()
{
QFile f(m_blackListFile);
if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
warnWidgets << "Cannot write meta information to '" << m_blackListFile << "'." << endl;
return;
}
QDomDocument doc;
QDomElement root;
QDomDocument docTemp("m_blackListFile");
doc = docTemp;
doc.appendChild(doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""));
root = doc.createElement("resourceFilesList");
doc.appendChild(root);
Q_FOREACH (QString filename, m_blackListFileNames) {
// Don't write the krita3 bundle to the blacklist, since its location will change
// when using the appimate.
if (type() == "kis_resourcebundles") {
// qDebug() << "Checking for Not writing krita 3 bundle" << filename;
if (filename.endsWith("Krita_3_Default_Resources.bundle")) continue;
}
QDomElement fileEl = doc.createElement("file");
QDomElement nameEl = doc.createElement("name");
QDomText nameText = doc.createTextNode(filename.replace(QDir::homePath(), QString("~")));
nameEl.appendChild(nameText);
fileEl.appendChild(nameEl);
root.appendChild(fileEl);
}
QTextStream metastream(&f);
metastream << doc.toString();
f.close();
}
protected:
KoResource* byMd5(const QByteArray &md5) const override
{
return Policy::toResourcePointer(resourceByMD5(md5));
}
KoResource* byFileName(const QString &fileName) const override
{
return Policy::toResourcePointer(resourceByFilename(fileName));
}
private:
void addResourceToMd5Registry(PointerType resource) {
const QByteArray md5 = resource->md5();
if (!md5.isEmpty()) {
m_resourcesByMd5.insert(md5, resource);
}
}
void removeResourceFromMd5Registry(PointerType resource) {
const QByteArray md5 = resource->md5();
if (!md5.isEmpty()) {
m_resourcesByMd5.remove(md5);
}
}
private:
QHash<QString, PointerType> m_resourcesByName;
QHash<QString, PointerType> m_resourcesByFilename;
QHash<QByteArray, PointerType> m_resourcesByMd5;
QList<PointerType> m_resourceBlackList;
QList<PointerType> m_resources; ///< list of resources in order of addition
QList<ObserverType*> m_observers;
QString m_blackListFile;
KoResourceTagStore* m_tagStore;
};
template <class T, class Policy = PointerStoragePolicy<T> >
class KoResourceServerSimpleConstruction : public KoResourceServer<T, Policy>
{
public:
KoResourceServerSimpleConstruction(const QString& type, const QString& extensions)
: KoResourceServer<T, Policy>(type, extensions)
{
}
typename KoResourceServer<T, Policy>::PointerType createResource( const QString & filename ) override {
return new T(filename);
}
};
#endif // KORESOURCESERVER_H
diff --git a/libs/widgets/KoResourceServerProvider.cpp b/libs/widgets/KoResourceServerProvider.cpp
index 3d5a7db45f..0a89dc691b 100644
--- a/libs/widgets/KoResourceServerProvider.cpp
+++ b/libs/widgets/KoResourceServerProvider.cpp
@@ -1,198 +1,198 @@
/* This file is part of the KDE project
Copyright (c) 1999 Matthias Elter <elter@kde.org>
Copyright (c) 2003 Patrick Julien <freak@codepimps.org>
Copyright (c) 2005 Sven Langkamp <sven.langkamp@gmail.com>
Copyright (C) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "KoResourceServerProvider.h"
#include <QApplication>
#include <QFileInfo>
#include <QStringList>
#include <QDir>
#include <QStandardPaths>
#include <QGlobalStatic>
#include <resources/KoSegmentGradient.h>
#include <resources/KoStopGradient.h>
#include "KoColorSpaceRegistry.h"
#include "KoResourcePaths.h"
#include <iostream>
using namespace std;
class GradientResourceServer : public KoResourceServer<KoAbstractGradient> {
public:
GradientResourceServer(const QString& type, const QString& extensions) :
KoResourceServer<KoAbstractGradient>(type, extensions) , m_foregroundToTransparent(0) , m_foregroundToBackground(0)
{
insertSpecialGradients();
}
void insertSpecialGradients()
{
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
QList<KoGradientStop> stops;
KoStopGradient* gradient = new KoStopGradient();
gradient->setType(QGradient::LinearGradient);
gradient->setName("Foreground to Transparent");
stops << KoGradientStop(0.0, KoColor(Qt::black, cs)) << KoGradientStop(1.0, KoColor(QColor(0, 0, 0, 0), cs));
gradient->setStops(stops);
gradient->setValid(true);
gradient->setPermanent(true);
addResource(gradient, false, true);
m_foregroundToTransparent = gradient;
gradient = new KoStopGradient();
gradient->setType(QGradient::LinearGradient);
gradient->setName("Foreground to Background");
stops.clear();
stops << KoGradientStop(0.0, KoColor(Qt::black, cs)) << KoGradientStop(1.0, KoColor(Qt::white, cs));
gradient->setStops(stops);
gradient->setValid(true);
gradient->setPermanent(true);
addResource(gradient, false, true);
m_foregroundToBackground = gradient;
}
private:
friend class KoResourceBundle;
KoAbstractGradient* createResource( const QString & filename ) override {
QString fileExtension;
int index = filename.lastIndexOf('.');
if (index != -1)
fileExtension = filename.mid(index).toLower();
KoAbstractGradient* grad = 0;
if(fileExtension == ".svg" || fileExtension == ".kgr")
grad = new KoStopGradient(filename);
else if(fileExtension == ".ggr" )
grad = new KoSegmentGradient(filename);
return grad;
}
QList< KoAbstractGradient* > sortedResources() override {
QList< KoAbstractGradient* > resources = KoResourceServer<KoAbstractGradient>::sortedResources();
QList< KoAbstractGradient* > sorted;
if (m_foregroundToTransparent && resources.contains(m_foregroundToTransparent)) {
sorted.append(resources.takeAt(resources.indexOf(m_foregroundToTransparent)));
}
if (m_foregroundToBackground && resources.contains(m_foregroundToBackground)) {
sorted.append(resources.takeAt(resources.indexOf(m_foregroundToBackground)));
}
return sorted + resources;
}
KoAbstractGradient* m_foregroundToTransparent;
KoAbstractGradient* m_foregroundToBackground;
};
struct Q_DECL_HIDDEN KoResourceServerProvider::Private
{
KoResourceServer<KoPattern>* patternServer;
KoResourceServer<KoAbstractGradient>* gradientServer;
KoResourceServer<KoColorSet>* paletteServer;
KoResourceServer<KoSvgSymbolCollectionResource> *svgSymbolCollectionServer;
KoResourceServer<KoGamutMask>* gamutMaskServer;
};
KoResourceServerProvider::KoResourceServerProvider() : d(new Private)
{
d->patternServer = new KoResourceServerSimpleConstruction<KoPattern>("ko_patterns", "*.pat:*.jpg:*.gif:*.png:*.tif:*.xpm:*.bmp" );
d->patternServer->loadResources(blacklistFileNames(d->patternServer->fileNames(), d->patternServer->blackListedFiles()));
- d->gradientServer = new GradientResourceServer("ko_gradients", "*.kgr:*.svg:*.ggr");
+ d->gradientServer = new GradientResourceServer("ko_gradients", "*.svg:*.ggr");
d->gradientServer->loadResources(blacklistFileNames(d->gradientServer->fileNames(), d->gradientServer->blackListedFiles()));
d->paletteServer = new KoResourceServerSimpleConstruction<KoColorSet>("ko_palettes", "*.kpl:*.gpl:*.pal:*.act:*.aco:*.css:*.colors:*.xml:*.sbz");
d->paletteServer->loadResources(blacklistFileNames(d->paletteServer->fileNames(), d->paletteServer->blackListedFiles()));
d->svgSymbolCollectionServer = new KoResourceServerSimpleConstruction<KoSvgSymbolCollectionResource>("symbols", "*.svg");
d->svgSymbolCollectionServer->loadResources(blacklistFileNames(d->svgSymbolCollectionServer->fileNames(), d->svgSymbolCollectionServer->blackListedFiles()));
d->gamutMaskServer = new KoResourceServerSimpleConstruction<KoGamutMask>("ko_gamutmasks", "*.kgm");
d->gamutMaskServer->loadResources(blacklistFileNames(d->gamutMaskServer->fileNames(), d->gamutMaskServer->blackListedFiles()));
}
KoResourceServerProvider::~KoResourceServerProvider()
{
delete d->patternServer;
delete d->gradientServer;
delete d->paletteServer;
delete d->svgSymbolCollectionServer;
delete d->gamutMaskServer;
delete d;
}
Q_GLOBAL_STATIC(KoResourceServerProvider, s_instance);
KoResourceServerProvider* KoResourceServerProvider::instance()
{
return s_instance;
}
QStringList KoResourceServerProvider::blacklistFileNames(QStringList fileNames, const QStringList &blacklistedFileNames)
{
if (!blacklistedFileNames.isEmpty()) {
foreach (const QString &s, blacklistedFileNames) {
fileNames.removeAll(s);
}
}
return fileNames;
}
KoResourceServer<KoPattern>* KoResourceServerProvider::patternServer()
{
return d->patternServer;
}
KoResourceServer<KoAbstractGradient>* KoResourceServerProvider::gradientServer()
{
return d->gradientServer;
}
KoResourceServer<KoColorSet>* KoResourceServerProvider::paletteServer()
{
return d->paletteServer;
}
KoResourceServer<KoSvgSymbolCollectionResource> *KoResourceServerProvider::svgSymbolCollectionServer()
{
return d->svgSymbolCollectionServer;
}
KoResourceServer<KoGamutMask>* KoResourceServerProvider::gamutMaskServer()
{
return d->gamutMaskServer;
}
diff --git a/libs/widgets/KoTagChooserWidget.cpp b/libs/widgets/KoTagChooserWidget.cpp
index 93487dbb30..d5c56bce81 100644
--- a/libs/widgets/KoTagChooserWidget.cpp
+++ b/libs/widgets/KoTagChooserWidget.cpp
@@ -1,199 +1,199 @@
/*
* This file is part of the KDE project
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2007 Jan Hambrecht <jaham@gmx.net>
* Copyright (c) 2007 Sven Langkamp <sven.langkamp@gmail.com>
* Copyright (C) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
* Copyright (c) 2011 José Luis Vergara <pentalis@gmail.com>
* Copyright (c) 2013 Sascha Suelzer <s.suelzer@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoTagChooserWidget.h"
#include <QDebug>
#include <QToolButton>
#include <QGridLayout>
#include <klocalizedstring.h>
#include <squeezedcombobox.h>
#include <KoIcon.h>
#include "KoResourceItemChooserContextMenu.h"
#include "KoTagToolButton.h"
class Q_DECL_HIDDEN KoTagChooserWidget::Private
{
public:
SqueezedComboBox* comboBox;
KoTagToolButton* tagToolButton;
QStringList readOnlyTags;
QStringList tags;
};
KoTagChooserWidget::KoTagChooserWidget(QWidget* parent): QWidget(parent)
, d(new Private())
{
d->comboBox = new SqueezedComboBox(this);
d->comboBox->setToolTip(i18n("Tag"));
d->comboBox->setSizePolicy(QSizePolicy::MinimumExpanding , QSizePolicy::Fixed );
QGridLayout* comboLayout = new QGridLayout(this);
comboLayout->addWidget(d->comboBox, 0, 0);
d->tagToolButton = new KoTagToolButton(this);
comboLayout->addWidget(d->tagToolButton, 0, 1);
comboLayout->setSpacing(0);
comboLayout->setMargin(0);
comboLayout->setColumnStretch(0, 3);
this->setEnabled(true);
clear();
- connect(d->comboBox, SIGNAL(currentIndexChanged(QString)),
+ connect(d->comboBox, SIGNAL(currentTextChanged(QString)),
this, SIGNAL(tagChosen(QString)));
connect(d->tagToolButton, SIGNAL(popupMenuAboutToShow()),
this, SLOT (tagOptionsContextMenuAboutToShow()));
connect(d->tagToolButton, SIGNAL(newTagRequested(QString)),
this, SIGNAL(newTagRequested(QString)));
connect(d->tagToolButton, SIGNAL(deletionOfCurrentTagRequested()),
this, SLOT(contextDeleteCurrentTag()));
connect(d->tagToolButton, SIGNAL(renamingOfCurrentTagRequested(QString)),
this, SLOT(tagRenamingRequested(QString)));
connect(d->tagToolButton, SIGNAL(undeletionOfTagRequested(QString)),
this, SIGNAL(tagUndeletionRequested(QString)));
connect(d->tagToolButton, SIGNAL(purgingOfTagUndeleteListRequested()),
this, SIGNAL(tagUndeletionListPurgeRequested()));
}
KoTagChooserWidget::~KoTagChooserWidget()
{
delete d;
}
void KoTagChooserWidget::contextDeleteCurrentTag()
{
if (selectedTagIsReadOnly()) {
return;
}
emit tagDeletionRequested(currentlySelectedTag());
}
void KoTagChooserWidget::tagRenamingRequested(const QString& newName)
{
if (newName.isEmpty() || selectedTagIsReadOnly()) {
return;
}
emit tagRenamingRequested(currentlySelectedTag(), newName);
}
void KoTagChooserWidget::setUndeletionCandidate(const QString& tag)
{
d->tagToolButton->setUndeletionCandidate(tag);
}
void KoTagChooserWidget::setCurrentIndex(int index)
{
d->comboBox->setCurrentIndex(index);
}
int KoTagChooserWidget::findIndexOf(QString tagName)
{
return d->comboBox->findOriginalText(tagName);
}
void KoTagChooserWidget::addReadOnlyItem(QString tagName)
{
d->readOnlyTags.append(tagName);
}
void KoTagChooserWidget::insertItem(QString tagName)
{
QStringList tags = allTags();
tags.append(tagName);
tags.sort();
foreach (QString readOnlyTag, d->readOnlyTags) {
tags.prepend(readOnlyTag);
}
int index = tags.indexOf(tagName);
if (d->comboBox->findOriginalText(tagName) == -1) {
d->comboBox->insertSqueezedItem(tagName, index);
d->tags.append(tagName);
}
}
QString KoTagChooserWidget::currentlySelectedTag()
{
return d->comboBox->itemHighlighted();
}
QStringList KoTagChooserWidget::allTags()
{
return d->tags;
}
bool KoTagChooserWidget::selectedTagIsReadOnly()
{
return d->readOnlyTags.contains(d->comboBox->itemHighlighted()) ;
}
void KoTagChooserWidget::addItems(QStringList tagNames)
{
d->tags.append(tagNames);
d->tags.removeDuplicates();
d->tags.sort();
d->readOnlyTags.sort();
QStringList items = d->readOnlyTags + d->tags;
items.removeDuplicates();
d->comboBox->resetOriginalTexts(items);
}
void KoTagChooserWidget::clear()
{
d->comboBox->resetOriginalTexts(QStringList());
}
void KoTagChooserWidget::removeItem(QString item)
{
int pos = findIndexOf(item);
if (pos >= 0) {
d->comboBox->removeSqueezedItem(pos);
d->tags.removeOne(item);
}
}
void KoTagChooserWidget::tagOptionsContextMenuAboutToShow()
{
/* only enable the save button if the selected tag set is editable */
d->tagToolButton->readOnlyMode(selectedTagIsReadOnly());
emit popupMenuAboutToShow();
}
void KoTagChooserWidget::showTagToolButton(bool show)
{
d->tagToolButton->setVisible(show);
}
diff --git a/libs/widgets/KoToolBox_p.h b/libs/widgets/KoToolBox_p.h
index 7f0527ff3e..3835f0842b 100644
--- a/libs/widgets/KoToolBox_p.h
+++ b/libs/widgets/KoToolBox_p.h
@@ -1,113 +1,112 @@
/*
* Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2005-2008 Thomas Zander <zander@kde.org>
* Copyright (c) 2009 Peter Simonsson <peter.simonsson@gmail.com>
* Copyright (c) 2010 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _KO_TOOLBOX_H_
#define _KO_TOOLBOX_H_
#include <KoCanvasObserverBase.h>
#include <QWidget>
#include <QList>
#include <KoToolManager.h>
class KoCanvasController;
class KoShapeLayer;
class KoToolBoxLayout;
/**
* KoToolBox is a dock widget that can order tools according to type and
* priority.
*
* The ToolBox is a container for tool buttons which are themselves
* divided into sections.
*
* Adding buttons using addButton() will allow you to show those buttons. You should connect
* the button to your handling method yourself.
*
* The unique property of this toolbox is that it can be shown horizontal as well as vertical,
* rotating in a smart way to show the buttons optimally.
* @see KoToolManager
*/
class KoToolBox : public QWidget {
Q_OBJECT
public:
/// constructor
explicit KoToolBox();
~KoToolBox() override;
public Q_SLOTS:
/**
* Using the buttongroup id passed in addButton() you can set the new active button.
* If the id does not resolve to a visible button, this call is ignored.
* @param canvas the currently active canvas.
* @param id an id to identify the button to activate.
*/
void setActiveTool(KoCanvasController *canvas, int id);
/**
* Show only the dynamic buttons that have a code from parameter codes.
* The toolbox allows buttons to be optionally registered with a visibilityCode. This code
* can be passed here and all buttons that have that code are shown. All buttons that
* have another visibility code registered are hidden.
- * @param canvas the currently active canvas.
* @param codes a list of all the codes to show.
*/
void setButtonsVisible(const QList<QString> &codes);
/// Set the orientation of the layout to @p orientation
void setOrientation(Qt::Orientation orientation);
void setFloating(bool v);
KoToolBoxLayout *toolBoxLayout() const;
private:
/**
* Add a button to the toolbox.
* The buttons should all be added before the first showing since adding will not really add
* them to the UI until setup() is called.
*
* @param toolAction the action of the tool
* @see setup()
*/
void addButton(KoToolAction *toolAction);
private Q_SLOTS:
void setCurrentLayer(const KoCanvasController *canvas, const KoShapeLayer* newLayer);
/// add a tool post-initialization. The tool will also be activated.
void toolAdded(KoToolAction *toolAction, KoCanvasController *canvas);
/// set the icon size for all the buttons
void slotContextIconSize();
protected:
void paintEvent(QPaintEvent *event) override;
void contextMenuEvent(QContextMenuEvent *event) override;
private:
class Private;
Private * const d;
};
#endif // _KO_TOOLBOX_H_
diff --git a/libs/widgets/KoTriangleColorSelector.cpp b/libs/widgets/KoTriangleColorSelector.cpp
index 48ba589489..994f41e0d0 100644
--- a/libs/widgets/KoTriangleColorSelector.cpp
+++ b/libs/widgets/KoTriangleColorSelector.cpp
@@ -1,439 +1,439 @@
/*
* Copyright (c) 2008 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "KoTriangleColorSelector.h"
#include <math.h>
#include <QMouseEvent>
#include <QPainter>
#include <QPixmap>
#include <QTimer>
#include <KoColorSpaceRegistry.h>
#include <KoColorConversions.h>
#include <KoColorDisplayRendererInterface.h>
enum CurrentHandle {
NoHandle,
HueHandle,
ValueSaturationHandle };
struct Q_DECL_HIDDEN KoTriangleColorSelector::Private {
Private(KoTriangleColorSelector *_q, const KoColorDisplayRendererInterface *_displayRenderer)
: q(_q),
displayRenderer(_displayRenderer),
hue(0),
saturation(0),
value(0),
updateAllowed(true),
invalidTriangle(true),
lastX(-1),
lastY(-1)
{
}
KoTriangleColorSelector *q;
const KoColorDisplayRendererInterface *displayRenderer;
QPixmap wheelPixmap;
QPixmap trianglePixmap;
int hue;
int saturation;
int value;
int sizeColorSelector;
qreal centerColorSelector;
qreal wheelWidthProportion;
qreal wheelWidth;
qreal wheelNormExt;
qreal wheelNormInt;
qreal wheelInnerRadius;
qreal triangleRadius;
qreal triangleLength;
qreal triangleHeight;
qreal triangleBottom;
qreal triangleTop;
qreal normExt;
qreal normInt;
bool updateAllowed;
CurrentHandle handle;
qreal triangleHandleSize;
bool invalidTriangle;
int lastX, lastY;
QTimer updateTimer;
void init();
};
void KoTriangleColorSelector::Private::init()
{
q->setMinimumHeight( 100 );
q->setMinimumWidth( 100 );
q->setMouseTracking( true );
q->updateTriangleCircleParameters();
updateTimer.setInterval(1);
updateTimer.setSingleShot(true);
q->connect(&updateTimer, SIGNAL(timeout()), q, SLOT(update()));
}
KoTriangleColorSelector::KoTriangleColorSelector(QWidget* parent)
: KisColorSelectorInterface(parent),
d(new Private(this, KoDumbColorDisplayRenderer::instance()))
{
d->init();
}
KoTriangleColorSelector::KoTriangleColorSelector(const KoColorDisplayRendererInterface *displayRenderer, QWidget *parent)
: KisColorSelectorInterface(parent),
d(new Private(this, displayRenderer))
{
d->init();
- connect(displayRenderer, SIGNAL(displayConfigurationChanged()), this, SLOT(configurationChanged()));
+ connect(displayRenderer, SIGNAL(displayConfigurationChanged()), this, SLOT(configurationChanged()), Qt::UniqueConnection);
}
KoTriangleColorSelector::~KoTriangleColorSelector()
{
delete d;
}
void KoTriangleColorSelector::updateTriangleCircleParameters()
{
d->sizeColorSelector = qMin(width(), height());
d->centerColorSelector = 0.5 * d->sizeColorSelector;
d->wheelWidthProportion = 0.25;
d->wheelWidth = d->centerColorSelector * d->wheelWidthProportion;
d->wheelNormExt = qAbs( d->centerColorSelector );
d->wheelNormInt = qAbs( d->centerColorSelector * (1.0 - d->wheelWidthProportion));
d->wheelInnerRadius = d->centerColorSelector * (1.0 - d->wheelWidthProportion);
d->triangleRadius = d->wheelInnerRadius * 0.9;
d->triangleLength = 3.0 / sqrt(3.0) * d->triangleRadius;
d->triangleHeight = d->triangleLength * sqrt(3.0) * 0.5;
d->triangleTop = 0.5 * d->sizeColorSelector - d->triangleRadius;
d->triangleBottom = d->triangleHeight + d->triangleTop;
d->triangleHandleSize = 10.0;
}
void KoTriangleColorSelector::paintEvent( QPaintEvent * event )
{
if( d->invalidTriangle )
{
generateTriangle();
}
Q_UNUSED(event);
QPainter p(this);
p.setRenderHint(QPainter::SmoothPixmapTransform);
p.setRenderHint(QPainter::Antialiasing);
QPointF pos(d->centerColorSelector, d->centerColorSelector);
p.translate(QPointF( 0.5*width(), 0.5*height() ) );
// Draw the wheel
p.drawPixmap( -pos, d->wheelPixmap );
// Draw the triangle
p.save();
p.rotate( hue() + 150 );
p.drawPixmap( -pos , d->trianglePixmap );
// Draw selectors
p.restore();
// Draw value,saturation selector
// Compute coordinates
{
qreal vs_selector_ypos_ = value() / 255.0;
qreal ls_ = (vs_selector_ypos_) * d->triangleLength; // length of the saturation on the triangle
qreal vs_selector_xpos_ = ls_ * (saturation() / 255.0 - 0.5);
// Draw it
p.save();
p.setPen( QPen( Qt::white, 1.0) );
QColor currentColor = d->displayRenderer->toQColor(getCurrentColor());
p.setBrush(currentColor);
p.rotate( hue() + 150 );
p.drawEllipse( QRectF( -d->triangleHandleSize*0.5 + vs_selector_xpos_,
-d->triangleHandleSize*0.5 - (d->centerColorSelector - d->triangleTop) + vs_selector_ypos_ * d->triangleHeight,
d->triangleHandleSize , d->triangleHandleSize ));
}
p.restore();
// Draw Hue selector
p.save();
p.setPen( QPen( Qt::white, 1.0) );
p.rotate( hue() - 90 );
qreal hueSelectorWidth_ = 0.8;
qreal hueSelectorOffset_ = 0.5 *( 1.0 - hueSelectorWidth_) * d->wheelWidth;
qreal hueSelectorSize_ = 0.8 * d->wheelWidth;
p.drawRect( QRectF( -1.5, -d->centerColorSelector + hueSelectorOffset_, 3.0, hueSelectorSize_ ));
p.restore();
p.end();
}
// make sure to always use get/set functions when managing HSV properties( don't call directly like d->hue)
// these settings get updated A LOT when the color picker is being used. You might get unexpected results
int KoTriangleColorSelector::hue() const
{
return d->hue;
}
void KoTriangleColorSelector::setHue(int h)
{
// setRealColor() will give you -1 when saturation is 0
// ignore setting hue in this instance. otherwise it will mess up the hue ring
if (h == -1)
return;
h = qBound(0, h, 359);
d->hue = h;
tellColorChanged();
d->invalidTriangle = true;
d->updateTimer.start();
}
int KoTriangleColorSelector::value() const
{
return d->value;
}
void KoTriangleColorSelector::setValue(int v)
{
v = qBound(0, v, 255);
d->value = v;
tellColorChanged();
d->invalidTriangle = true;
d->updateTimer.start();
}
int KoTriangleColorSelector::saturation() const
{
return d->saturation;
}
void KoTriangleColorSelector::setSaturation(int s)
{
s = qBound(0, s, 255);
d->saturation = s;
tellColorChanged();
d->invalidTriangle = true;
d->updateTimer.start();
}
void KoTriangleColorSelector::setHSV(int h, int s, int v)
{
d->invalidTriangle = (hue() != h);
setHue(h);
setValue(v);
setSaturation(s);
}
KoColor KoTriangleColorSelector::getCurrentColor() const
{
return d->displayRenderer->fromHsv(hue(), saturation(), value());
}
void KoTriangleColorSelector::slotSetColor(const KoColor & color)
{
if ( getCurrentColor() == color)
return;
//displayrenderer->getHsv is what sets the foreground color in the application
if(d->updateAllowed) {
int hueRef = hue();
int saturationRef = saturation();
int valueRef = value();
d->displayRenderer->getHsv(color, &hueRef, &saturationRef, &valueRef);
setHSV(hueRef, saturationRef, valueRef);
d->invalidTriangle = true;
d->updateTimer.start();
}
}
void KoTriangleColorSelector::resizeEvent( QResizeEvent * event )
{
QWidget::resizeEvent( event );
updateTriangleCircleParameters();
generateWheel();
d->invalidTriangle = true;
}
inline qreal pow2(qreal v)
{
return v*v;
}
void KoTriangleColorSelector::tellColorChanged()
{
d->updateAllowed = false;
emit(sigNewColor(getCurrentColor()));
emit(colorChanged(getCurrentColor().toQColor()));
d->updateAllowed = true;
}
void KoTriangleColorSelector::generateTriangle()
{
QImage image(d->sizeColorSelector, d->sizeColorSelector, QImage::Format_ARGB32);
// Length of triangle
int hue_ = hue();
for(int y = 0; y < d->sizeColorSelector; ++y)
{
qreal ynormalize = ( d->triangleTop - y ) / ( d->triangleTop - d->triangleBottom );
qreal v = 255 * ynormalize;
qreal ls_ = (ynormalize) * d->triangleLength;
qreal startx_ = d->centerColorSelector - 0.5 * ls_;
uint* data = reinterpret_cast<uint*>(image.scanLine(y));
for(int x = 0; x < d->sizeColorSelector; ++x, ++data)
{
qreal s = 255 * (x - startx_) / ls_;
if(v < -1.0 || v > 256.0 || s < -1.0 || s > 256.0 )
{
*data = qRgba(0,0,0,0);
} else {
qreal va = 1.0, sa = 1.0;
if( v < 0.0) { va = 1.0 + v; v = 0; }
else if( v > 255.0 ) { va = 256.0 - v; v = 255; }
if( s < 0.0) { sa = 1.0 + s; s = 0; }
else if( s > 255.0 ) { sa = 256.0 - s; s = 255; }
qreal coeff = va * sa;
KoColor color = d->displayRenderer->fromHsv(hue_, s, v, int(coeff * 255.0));
QColor qcolor = d->displayRenderer->toQColor(color);
*data = qcolor.rgba();
}
}
}
d->trianglePixmap = QPixmap::fromImage(image);
d->invalidTriangle = false;
}
void KoTriangleColorSelector::generateWheel()
{
QImage image(d->sizeColorSelector, d->sizeColorSelector, QImage::Format_ARGB32);
for(int y = 0; y < d->sizeColorSelector; y++)
{
qreal yc = y - d->centerColorSelector;
qreal y2 = pow2( yc );
for(int x = 0; x < d->sizeColorSelector; x++)
{
qreal xc = x - d->centerColorSelector;
qreal norm = sqrt(pow2( xc ) + y2);
if( norm <= d->wheelNormExt + 1.0 && norm >= d->wheelNormInt - 1.0 )
{
qreal acoef = 1.0;
if(norm > d->wheelNormExt ) acoef = (1.0 + d->wheelNormExt - norm);
else if(norm < d->wheelNormInt ) acoef = (1.0 - d->wheelNormInt + norm);
qreal angle = atan2(yc, xc);
int h = (int)((180 * angle / M_PI) + 180) % 360;
KoColor color = d->displayRenderer->fromHsv(h, 255, 255, int(acoef * 255.0));
QColor qcolor = d->displayRenderer->toQColor(color);
image.setPixel(x,y, qcolor.rgba());
} else {
image.setPixel(x,y, qRgba(0,0,0,0));
}
}
}
d->wheelPixmap = QPixmap::fromImage(image);
}
void KoTriangleColorSelector::mouseReleaseEvent( QMouseEvent * event )
{
if(event->button() == Qt::LeftButton)
{
selectColorAt( event->x(), event->y());
d->handle = NoHandle;
} else {
QWidget::mouseReleaseEvent( event );
}
}
void KoTriangleColorSelector::mousePressEvent( QMouseEvent * event )
{
if(event->button() == Qt::LeftButton)
{
d->handle = NoHandle;
selectColorAt( event->x(), event->y());
} else {
QWidget::mousePressEvent( event );
}
}
void KoTriangleColorSelector::mouseMoveEvent( QMouseEvent * event )
{
if(event->buttons() & Qt::LeftButton)
{
selectColorAt( event->x(), event->y(), false );
} else {
QWidget::mouseMoveEvent( event);
}
}
void KoTriangleColorSelector::selectColorAt(int _x, int _y, bool checkInWheel)
{
Q_UNUSED( checkInWheel );
if (d->lastX == _x && d->lastY == _y)
{
return;
}
d->lastX = _x;
d->lastY = _y;
qreal x = _x - 0.5*width();
qreal y = _y - 0.5*height();
// Check if the click is inside the wheel
qreal norm = sqrt( x * x + y * y);
if ( ( (norm < d->wheelNormExt) && (norm > d->wheelNormInt) && d->handle == NoHandle )
|| d->handle == HueHandle ) {
d->handle = HueHandle;
setHue( (int)(atan2(y, x) * 180 / M_PI ) + 180);
d->updateTimer.start();
}
else {
// Compute the s and v value, if they are in range, use them
qreal rotation = -(hue() + 150) * M_PI / 180;
qreal cr = cos(rotation);
qreal sr = sin(rotation);
qreal x1 = x * cr - y * sr; // <- now x1 gives the saturation
qreal y1 = x * sr + y * cr; // <- now y1 gives the value
y1 += d->wheelNormExt;
qreal ynormalize = (d->triangleTop - y1 ) / ( d->triangleTop - d->triangleBottom );
if( (ynormalize >= 0.0 && ynormalize <= 1.0 ) || d->handle == ValueSaturationHandle)
{
d->handle = ValueSaturationHandle;
qreal ls_ = (ynormalize) * d->triangleLength; // length of the saturation on the triangle
qreal sat = ( x1 / ls_ + 0.5) ;
if((sat >= 0.0 && sat <= 1.0) || d->handle == ValueSaturationHandle)
{
setHSV( hue(), sat * 255, ynormalize * 255);
}
}
d->updateTimer.start();
}
}
void KoTriangleColorSelector::configurationChanged()
{
generateWheel();
d->invalidTriangle = true;
update();
}
diff --git a/libs/widgets/KoZoomController.h b/libs/widgets/KoZoomController.h
index fb38af78b3..f1b7b652e8 100644
--- a/libs/widgets/KoZoomController.h
+++ b/libs/widgets/KoZoomController.h
@@ -1,202 +1,203 @@
/* This file is part of the KDE project
* Copyright (C) 2007 Thomas Zander <zander@kde.org>
* Copyright (C) 2007,2012 C. Boemann <cbo@boemann.dk>
* Copyright (C) 2007 Jan Hambrecht <jaham@gmx.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOZOOMCONTROLLER_H
#define KOZOOMCONTROLLER_H
#include "KoZoomAction.h"
#include "kritawidgets_export.h"
#include <KoZoomMode.h>
#include <QObject>
#include <QSizeF>
class KoCanvasController;
class KoZoomAction;
class KoZoomHandler;
class KActionCollection;
class QSize;
/**
* This controller class handles zoom levels for any canvas.
*
* For each KoCanvasController you should have one instance of this
* class to go with it. This class then creates a KoZoomAction and
* basically handles all zooming for you.
*
* All you need to do is connect to the setDocumentSize() slot and
* keep the controller up-to-date if your on-screen document ever
* changes (note that this is in document units, so this is a zoom
* independent size).
*
* If you choose to have zoom modes of 'page' and 'width' you are
* required to set the page size using the setPageSize() method.
*
* Additionally you can connect to the zoomChanged() signal if you
* want to store the latest zoom level and mode, for example to
* restore the last used one at next restart.
*
* The specialAspectMode toggle is only a UI element. It does nothing
* except emit the aspectModeChanged signal.
*
*/
class KRITAWIDGETS_EXPORT KoZoomController : public QObject {
Q_OBJECT
public:
/**
* Constructor. Create one per canvasController. The zoomAction is created in the constructor and will
* be available to the passed actionCollection for usage by XMLGui.
* @param controller the canvasController
* @param zoomHandler the zoom handler (viewconverter with setter methods)
* @param actionCollection the action collection where the KoZoomAction is added to
+ * @param parent the parent QObject
*/
KoZoomController(KoCanvasController *controller,
KoZoomHandler *zoomHandler,
KActionCollection *actionCollection,
QObject *parent = 0);
/// destructor
~KoZoomController() override;
/// returns the zoomAction that is maintained by this controller
KoZoomAction *zoomAction() const;
/**
* Alter the current zoom mode which updates the Gui.
* @param mode the new mode that will be used to auto-calculate a new zoom-level if needed.
*/
void setZoomMode(KoZoomMode::Mode mode);
/**
* @return the current zoom mode.
*/
KoZoomMode::Mode zoomMode() const;
/**
* Set the resolution, zoom, the zoom mode for this zoom Controller.
* Typically for use just after construction to restore the
* persistent data.
*
* @param mode new zoom mode for the canvas
* @param zoom (for ZOOM_CONSTANT zoom mode only) new zoom value for
* the canvas
* @param resolutionX new X resolution for the document
* @param resolutionY new Y resolution for the document
* @param stillPoint (for ZOOM_CONSTANT zoom mode only) the point
* which will not change its position in widget
* during the zooming. It is measured in view
* coordinate system *before* zoom.
*/
void setZoom(KoZoomMode::Mode mode, qreal zoom, qreal resolutionX, qreal resolutionY, const QPointF &stillPoint);
/**
* Convenience function that changes resolution with
* keeping the centering unchanged
*/
void setZoom(KoZoomMode::Mode mode, qreal zoom, qreal resolutionX, qreal resolutionY);
/**
* Convenience function that does not touch the resolution of the
* document
*/
void setZoom(KoZoomMode::Mode mode, qreal zoom, const QPointF &stillPoint);
/**
* Convenience function with @p center always set to the current
* center point of the canvas
*/
void setZoom(KoZoomMode::Mode mode, qreal zoom);
/**
* Set Aspect Mode button status and begin a chain of signals
*/
void setAspectMode(bool status);
public Q_SLOTS:
/**
* Set the size of the current page in document coordinates which allows zoom modes that use the pageSize
* to update.
* @param pageSize the new page size in points
*/
void setPageSize(const QSizeF &pageSize);
/**
* Returns the size of the current page in document coordinates
* @returns the page size in points
*/
QSizeF pageSize() const;
/**
* Set the size of the whole document currently being shown on the canvas.
* The document size will be used together with the current zoom level to calculate the size of the
* canvas in the canvasController.
* @param documentSize the new document size in points
* @param recalculateCenter tells canvas controller not to touch
* preferredCenterFraction
*/
void setDocumentSize(const QSizeF &documentSize, bool recalculateCenter = false);
/**
* Returns the size of the whole document currently being shown on the canvas.
* @returns the document size in points
*/
QSizeF documentSize() const;
Q_SIGNALS:
/**
* This signal is emitted whenever either the zoommode or the zoom level is changed by the user.
* the application can use the emitted data for persistency purposes.
*/
void zoomChanged (KoZoomMode::Mode mode, qreal zoom);
/**
* emitted when the special aspect mode toggle changes.
* @see KoZoomAction::aspectModeChanged()
*/
void aspectModeChanged (bool aspectModeActivated);
/**
* Signal is triggered when the user clicks the zoom to selection button.
* Nothing else happens except that this signal is emitted.
*/
void zoomedToSelection();
/**
* Signal is triggered when the user clicks the zoom to all button.
* Nothing else happens except that this signal is emitted.
*/
void zoomedToAll();
protected:
virtual QSize documentToViewport(const QSizeF &size);
private:
Q_PRIVATE_SLOT(d, void setAvailableSize())
Q_PRIVATE_SLOT(d, void requestZoomRelative(const qreal, const QPointF&))
Q_PRIVATE_SLOT(d, void setZoom(KoZoomMode::Mode, qreal))
Q_DISABLE_COPY( KoZoomController )
class Private;
Private * const d;
};
#endif
diff --git a/libs/widgets/KoZoomWidget.h b/libs/widgets/KoZoomWidget.h
index ee676fdedd..8b88e915fd 100644
--- a/libs/widgets/KoZoomWidget.h
+++ b/libs/widgets/KoZoomWidget.h
@@ -1,83 +1,83 @@
/*
Copyright (C) 2004 Ariya Hidayat <ariya@kde.org>
Copyright (C) 2006 Peter Simonsson <peter.simonsson@gmail.com>
Copyright (C) 2006-2007 C. Boemann <cbo@boemann.dk>
Copyright (C) 2014 Sven Langkamp <sven.langkamp@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KOZOOMWIDGET_H
#define KOZOOMWIDGET_H
#include <QWidget>
#include "KoZoomAction.h"
#include <QScopedPointer>
class KoZoomWidget : public QWidget
{
Q_OBJECT
public:
KoZoomWidget(QWidget* parent, int maxZoom);
~KoZoomWidget() override;
Q_SIGNALS:
/**
* Signal sliderValueChanged is triggered when the user moves the slider
* @param value value of the slider
*/
void sliderValueChanged(int value);
/**
* Signal zoomLevelChanged is triggered when the user changes the KoZoomInput combobox widget
- * @param value value of the slider
+ * @param level value of the slider
*/
void zoomLevelChanged(const QString& level);
/**
* Signal aspectModeChanged is triggered when the user toggles the widget.
* Nothing else happens except that this signal is emitted.
* @param status Whether the special aspect mode is on
*/
void aspectModeChanged( bool status );
/**
* Signal is triggered when the user clicks the zoom to selection button.
* Nothing else happens except that this signal is emitted.
*/
void zoomedToSelection();
/**
* Signal is triggered when the user clicks the zoom to all button.
* Nothing else happens except that this signal is emitted.
*/
void zoomedToAll();
public Q_SLOTS:
void setZoomLevels(const QStringList &values);
void setCurrentZoomLevel(const QString &valueString);
void setSliderValue(int value);
/**
* Change status of "Use same aspect as pixels" button
*/
void setAspectMode(bool status);
private:
class Private;
QScopedPointer<Private> const d;
};
#endif // KOZOOMWIDGET_H
diff --git a/libs/widgets/kis_palette_view.cpp b/libs/widgets/kis_palette_view.cpp
index 3c197a54ee..587eaf839e 100644
--- a/libs/widgets/kis_palette_view.cpp
+++ b/libs/widgets/kis_palette_view.cpp
@@ -1,294 +1,310 @@
/*
* Copyright (c) 2016 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_palette_view.h"
#include <QWheelEvent>
#include <QHeaderView>
#include <QFormLayout>
#include <QLabel>
#include <QLineEdit>
#include <QCheckBox>
#include <QComboBox>
#include <QMenu>
#include <KConfigGroup>
#include <KSharedConfig>
#include <KLocalizedString>
#include <kis_icon_utils.h>
#include <KisKineticScroller.h>
#include <KoDialog.h>
#include <KoColorDisplayRendererInterface.h>
#include "KisPaletteDelegate.h"
#include "KisPaletteModel.h"
#include "kis_color_button.h"
#include <KisSwatch.h>
int KisPaletteView::MININUM_ROW_HEIGHT = 10;
struct KisPaletteView::Private
{
QPointer<KisPaletteModel> model;
bool allowPaletteModification {false}; // if modification is allowed from this widget
};
KisPaletteView::KisPaletteView(QWidget *parent)
: QTableView(parent)
, m_d(new Private)
{
setItemDelegate(new KisPaletteDelegate(this));
setShowGrid(true);
setDropIndicatorShown(true);
setDragDropMode(QAbstractItemView::InternalMove);
setSelectionMode(QAbstractItemView::SingleSelection);
setDragEnabled(false);
setAcceptDrops(false);
/*
* without this, a cycle might be created:
* the view stretches to right border, and this make it need a scroll bar;
* after the bar is added, the view shrinks to the bar, and this makes it
* no longer need the bar any more, and the bar is removed again
*/
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
// set the size of swatches
horizontalHeader()->setVisible(false);
verticalHeader()->setVisible(false);
horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
horizontalHeader()->setMinimumSectionSize(MININUM_ROW_HEIGHT);
verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
verticalHeader()->setMinimumSectionSize(MININUM_ROW_HEIGHT);
connect(horizontalHeader(), SIGNAL(sectionResized(int,int,int)),
SLOT(slotHorizontalHeaderResized(int,int,int)));
setAutoFillBackground(true);
QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(this);
if (scroller) {
connect(scroller, SIGNAL(stateChanged(QScroller::State)),
this, SLOT(slotScrollerStateChanged(QScroller::State)));
}
}
KisPaletteView::~KisPaletteView()
{
}
void KisPaletteView::setCrossedKeyword(const QString &value)
{
KisPaletteDelegate *delegate =
dynamic_cast<KisPaletteDelegate*>(itemDelegate());
KIS_ASSERT_RECOVER_RETURN(delegate);
delegate->setCrossedKeyword(value);
}
bool KisPaletteView::addEntryWithDialog(KoColor color)
{
QScopedPointer<KoDialog> window(new KoDialog(this));
window->setWindowTitle(i18nc("@title:window", "Add a new Colorset Entry"));
QFormLayout *editableItems = new QFormLayout(window.data());
window->mainWidget()->setLayout(editableItems);
QComboBox *cmbGroups = new QComboBox(window.data());
QString defaultGroupName = i18nc("Name for default group", "Default");
cmbGroups->addItem(defaultGroupName);
cmbGroups->addItems(m_d->model->colorSet()->getGroupNames());
QLineEdit *lnIDName = new QLineEdit(window.data());
QLineEdit *lnName = new QLineEdit(window.data());
KisColorButton *bnColor = new KisColorButton(window.data());
QCheckBox *chkSpot = new QCheckBox(window.data());
chkSpot->setToolTip(i18nc("@info:tooltip", "A spot color is a color that the printer is able to print without mixing the paints it has available to it. The opposite is called a process color."));
editableItems->addRow(i18n("Group"), cmbGroups);
editableItems->addRow(i18n("ID"), lnIDName);
editableItems->addRow(i18n("Name"), lnName);
editableItems->addRow(i18n("Color"), bnColor);
editableItems->addRow(i18nc("Spot color", "Spot"), chkSpot);
cmbGroups->setCurrentIndex(0);
lnName->setText(i18nc("Part of a default name for a color","Color")+" " + QString::number(m_d->model->colorSet()->colorCount()+1));
lnIDName->setText(QString::number(m_d->model->colorSet()->colorCount() + 1));
bnColor->setColor(color);
chkSpot->setChecked(false);
if (window->exec() == KoDialog::Accepted) {
QString groupName = cmbGroups->currentText();
if (groupName == defaultGroupName) {
groupName = QString();
}
KisSwatch newEntry;
newEntry.setColor(bnColor->color());
newEntry.setName(lnName->text());
newEntry.setId(lnIDName->text());
newEntry.setSpotColor(chkSpot->isChecked());
m_d->model->addEntry(newEntry, groupName);
return true;
}
return false;
}
bool KisPaletteView::addGroupWithDialog()
{
KoDialog *window = new KoDialog();
window->setWindowTitle(i18nc("@title:window","Add a new group"));
QFormLayout *editableItems = new QFormLayout();
window->mainWidget()->setLayout(editableItems);
QLineEdit *lnName = new QLineEdit();
editableItems->addRow(i18nc("Name for a group", "Name"), lnName);
lnName->setText(i18nc("Part of default name for a new group", "Color Group")+""+QString::number(m_d->model->colorSet()->getGroupNames().size()+1));
if (window->exec() == KoDialog::Accepted) {
KisSwatchGroup group;
group.setName(lnName->text());
m_d->model->addGroup(group);
m_d->model->colorSet()->save();
return true;
}
return false;
}
bool KisPaletteView::removeEntryWithDialog(QModelIndex index)
{
bool keepColors = false;
if (qvariant_cast<bool>(index.data(KisPaletteModel::IsGroupNameRole))) {
QScopedPointer<KoDialog> window(new KoDialog(this));
window->setWindowTitle(i18nc("@title:window","Removing Group"));
QFormLayout *editableItems = new QFormLayout(window.data());
QCheckBox *chkKeep = new QCheckBox(window.data());
window->mainWidget()->setLayout(editableItems);
editableItems->addRow(i18nc("Shows up when deleting a swatch group", "Keep the Colors"), chkKeep);
if (window->exec() != KoDialog::Accepted) { return false; }
keepColors = chkKeep->isChecked();
}
m_d->model->removeEntry(index, keepColors);
if (m_d->model->colorSet()->isGlobal()) {
m_d->model->colorSet()->save();
}
return true;
}
void KisPaletteView::selectClosestColor(const KoColor &color)
{
KoColorSet* color_set = m_d->model->colorSet();
if (!color_set) {
return;
}
//also don't select if the color is the same as the current selection
if (m_d->model->getEntry(currentIndex()).color() == color) {
return;
}
selectionModel()->clearSelection();
QModelIndex index = m_d->model->indexForClosest(color);
selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select);
}
void KisPaletteView::slotFGColorChanged(const KoColor &color)
{
KConfigGroup group(KSharedConfig::openConfig(), "");
if (group.readEntry("colorsettings/forcepalettecolors", false)) {
selectClosestColor(color);
}
}
+void KisPaletteView::slotFGColorResourceChanged(const KoColor& color)
+{
+ // This slot is called, because fg color was changed in the resource manager.
+ // To enable re-picking the swatch color again, we reset currentIndex
+ // of the selectionModel. We are not clearing the selection itself,
+ // so the user can see the swatch selected previously.
+ // See bug 402072
+ selectionModel()->clearCurrentIndex();
+ slotFGColorChanged(color);
+}
+
+void KisPaletteView::slotSelectColor(const KoColor &color)
+{
+ selectClosestColor(color);
+}
+
void KisPaletteView::setPaletteModel(KisPaletteModel *model)
{
if (m_d->model) {
disconnect(m_d->model, Q_NULLPTR, this, Q_NULLPTR);
}
m_d->model = model;
setModel(model);
slotAdditionalGuiUpdate();
connect(model, SIGNAL(sigPaletteModified()), SLOT(slotAdditionalGuiUpdate()));
connect(model, SIGNAL(sigPaletteChanged()), SLOT(slotAdditionalGuiUpdate()));
connect(selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), SLOT(slotCurrentSelectionChanged(QModelIndex)));
}
KisPaletteModel* KisPaletteView::paletteModel() const
{
return m_d->model;
}
void KisPaletteView::setAllowModification(bool allow)
{
m_d->allowPaletteModification = allow;
setDragEnabled(allow);
setAcceptDrops(allow);
}
void KisPaletteView::slotHorizontalHeaderResized(int, int, int newSize)
{
resizeRows(newSize);
slotAdditionalGuiUpdate();
}
void KisPaletteView::resizeRows(int newSize)
{
verticalHeader()->setDefaultSectionSize(newSize);
verticalHeader()->resizeSections(QHeaderView::Fixed);
}
void KisPaletteView::removeSelectedEntry()
{
if (selectedIndexes().size() <= 0) {
return;
}
m_d->model->removeEntry(currentIndex());
}
void KisPaletteView::slotAdditionalGuiUpdate()
{
clearSpans();
resizeRows(verticalHeader()->defaultSectionSize());
for (int groupNameRowNumber : m_d->model->m_rowGroupNameMap.keys()) {
if (groupNameRowNumber == -1) { continue; }
setSpan(groupNameRowNumber, 0, 1, m_d->model->columnCount());
setRowHeight(groupNameRowNumber, fontMetrics().lineSpacing() + 6);
verticalHeader()->resizeSection(groupNameRowNumber, fontMetrics().lineSpacing() + 6);
}
}
void KisPaletteView::slotCurrentSelectionChanged(const QModelIndex &newCurrent)
{
if (!newCurrent.isValid()) { return; }
const bool isGroupName = newCurrent.data(KisPaletteModel::IsGroupNameRole).toBool();
const bool isCheckSlot = newCurrent.data(KisPaletteModel::CheckSlotRole).toBool();
const KisSwatch newEntry = m_d->model->getEntry(newCurrent);
emit sigIndexSelected(newCurrent);
if (isGroupName) {
return;
}
if (isCheckSlot) {
emit sigColorSelected(newEntry.color());
}
}
void KisPaletteView::setDisplayRenderer(const KoColorDisplayRendererInterface *displayRenderer)
{
Q_ASSERT(m_d->model);
m_d->model->setDisplayRenderer(displayRenderer);
}
diff --git a/libs/widgets/kis_palette_view.h b/libs/widgets/kis_palette_view.h
index 3799fda598..6b18ea436d 100644
--- a/libs/widgets/kis_palette_view.h
+++ b/libs/widgets/kis_palette_view.h
@@ -1,122 +1,134 @@
/*
* Copyright (c) 2016 Dmitry Kazakov <dimula73@gmail.com>
* Copyright (c) 2017 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
*
* 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_PALETTE_VIEW_H
#define __KIS_PALETTE_VIEW_H
#include <QScopedPointer>
#include <QTableView>
#include <QColorDialog>
#include <QPushButton>
#include <QPixmap>
#include <QIcon>
#include <KoColorSet.h>
#include "kritawidgets_export.h"
#include <KisKineticScroller.h>
class KisPaletteModel;
class QWheelEvent;
class KoColorDisplayRendererInterface;
class KRITAWIDGETS_EXPORT KisPaletteView : public QTableView
{
Q_OBJECT
private:
static int MININUM_ROW_HEIGHT;
public:
explicit KisPaletteView(QWidget *parent = Q_NULLPTR);
~KisPaletteView() override;
void setPaletteModel(KisPaletteModel *model);
KisPaletteModel* paletteModel() const;
public:
/**
* @brief setAllowModification
* Set whether doubleclick calls up a modification window. This is to prevent users from editing
* the palette when the palette is intended to be a list of items.
*/
void setAllowModification(bool allow);
void setDisplayRenderer(const KoColorDisplayRendererInterface *displayRenderer);
/**
* @brief setCrossedKeyword
* this apparently allows you to set keywords that can cross out colors.
* This is implemented to mark the lazybrush "transparent" color.
* @param value
*/
void setCrossedKeyword(const QString &value);
void removeSelectedEntry();
/**
* @brief selectClosestColor
* select a color that's closest to parameter color
* @param color
*/
void selectClosestColor(const KoColor &color);
/**
* add an entry with a dialog window.
* @warning deprecated.
- * kept for compatibility with @ref PaletteView in @ref libkis
+ * kept for compatibility with PaletteView in libkis
*/
bool addEntryWithDialog(KoColor color);
/**
* remove entry with a dialog window.(Necessary for groups.
* @warning deprecated.
- * kept for compatibility with @ref PaletteView in @ref libkis
+ * kept for compatibility with PaletteView in libkis
*/
bool removeEntryWithDialog(QModelIndex index);
/**
* add entry with a dialog window.
* @warning deprecated.
- * kept for compatibility with @ref PaletteView in @ref libkis
+ * kept for compatibility with PaletteView in libkis
*/
bool addGroupWithDialog();
Q_SIGNALS:
void sigIndexSelected(const QModelIndex &index);
void sigColorSelected(const KoColor &);
public Q_SLOTS:
/**
* This tries to select the closest color in the palette.
* This doesn't update the foreground color, just the visual selection.
*/
void slotFGColorChanged(const KoColor &);
+ /**
+ * @brief slot that reacts to color changes in resource manager
+ * @param color
+ */
+ void slotFGColorResourceChanged(const KoColor& color);
+
+ /**
+ * Slot that selects the right index for provided color.
+ * Called from KisPaletteComboBox when user selects color in the dropdown.
+ */
+ void slotSelectColor(const KoColor& color);
+
void slotScrollerStateChanged(QScroller::State state){KisKineticScroller::updateCursor(this, state);}
private Q_SLOTS:
void slotHorizontalHeaderResized(int, int, int newSize);
void slotAdditionalGuiUpdate();
void slotCurrentSelectionChanged(const QModelIndex &newCurrent);
private:
void resizeRows(int newSize);
private:
struct Private;
const QScopedPointer<Private> m_d;
};
#endif /* __KIS_PALETTE_VIEW_H */
diff --git a/libs/widgets/squeezedcombobox.h b/libs/widgets/squeezedcombobox.h
index beaa3934f4..d7b6c4c947 100644
--- a/libs/widgets/squeezedcombobox.h
+++ b/libs/widgets/squeezedcombobox.h
@@ -1,158 +1,162 @@
/* ============================================================
* Author: Tom Albers <tomalbers@kde.nl>
* Date : 2005-01-01
* Description :
*
* Copyright 2005 by Tom Albers <tomalbers@kde.nl>
*
* 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, 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.
*
* ============================================================ */
/** @file squeezedcombobox.h */
#ifndef SQUEEZEDCOMBOBOX_H
#define SQUEEZEDCOMBOBOX_H
class QTimer;
class QResizeEvent;
class QWidget;
// Qt includes.
#include <QComboBox>
#include <QWidget>
#include <QIcon>
#include "kritawidgets_export.h"
/** @class SqueezedComboBox
*
* This widget is a QComboBox, but then a little bit
* different. It only shows the right part of the items
* depending on de size of the widget. When it is not
* possible to show the complete item, it will be shortened
* and "..." will be prepended.
*
* @image html squeezedcombobox.png "This is how it looks"
* @author Tom Albers
*/
class KRITAWIDGETS_EXPORT SqueezedComboBox : public QComboBox
{
Q_OBJECT
public:
/**
* Constructor
* @param parent parent widget
* @param name name to give to the widget
*/
SqueezedComboBox(QWidget *parent = 0, const char *name = 0);
/**
* destructor
*/
~SqueezedComboBox() override;
/**
*
* Returns true if the combobox contains the original (not-squeezed)
* version of text.
* @param text the original (not-squeezed) text to check for
*/
bool contains(const QString & text) const;
/**
- * Returns index of a orinal text, -1 if the text isn't found
+ * Returns index of a original text, -1 if the text isn't found
* @param text the original (not-squeezed) text to search for
*/
qint32 findOriginalText(const QString & text) const;
/**
* Return the list of original text items
*/
QStringList originalTexts() const;
/**
* Reset the combo box and initialize it with the list of (original) text items
*/
void resetOriginalTexts(const QStringList &texts);
/**
* This inserts a item to the list. See QComboBox::insertItem()
- * for detaills. Please do not use QComboBox::insertItem() to this
+ * for details. Please do not use QComboBox::insertItem() to this
* widget, as that will fail.
* @param newItem the original (long version) of the item which needs
* to be added to the combobox
* @param index the position in the widget.
+ * @param userData the user data.
*/
void insertSqueezedItem(const QString& newItem, int index, QVariant userData = QVariant());
void insertSqueezedItem(const QIcon &icon, const QString& newItem, int index, QVariant userData = QVariant());
/**
* Append an item.
* @param newItem the original (long version) of the item which needs
* to be added to the combobox
+ * @param userData the user data.
*/
void addSqueezedItem(const QString& newItem, QVariant userData = QVariant());
/**
* Append an item.
+ * @param icon the item icon
* @param newItem the original (long version) of the item which needs
* to be added to the combobox
+ * @param userData the user data
*/
void addSqueezedItem(const QIcon &icon, const QString& newItem, QVariant userData = QVariant());
/**
* Set the current item to the one matching the given text.
*
* @param itemText the original (long version) of the item text
*/
void setCurrent(const QString& itemText);
/**
* This method returns the full text (not squeezed) of the currently
* highlighted item.
* @return full text of the highlighted item
*/
QString itemHighlighted();
/**
* remove the squeezed item at index
*/
void removeSqueezedItem(int index);
/**
* Sets the sizeHint() of this widget.
*/
QSize sizeHint() const override;
static QString squeezeText(const QString& original, const QWidget *widget);
private Q_SLOTS:
void slotTimeOut();
private:
void resizeEvent(QResizeEvent *) override;
// Prevent these from being used.
void setCurrentText(const QString& itemText);
void insertItem(const QString &text);
void insertItem(qint32 index, const QString &text);
void addItem(const QString &text);
QMap<int, QString> m_originalItems;
QTimer *m_timer;
};
#endif // SQUEEZEDCOMBOBOX_H
diff --git a/libs/widgetutils/CMakeLists.txt b/libs/widgetutils/CMakeLists.txt
index 28632a616d..4716a38004 100644
--- a/libs/widgetutils/CMakeLists.txt
+++ b/libs/widgetutils/CMakeLists.txt
@@ -1,133 +1,132 @@
add_subdirectory(tests)
configure_file(xmlgui/config-xmlgui.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-xmlgui.h )
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/config)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/xmlgui)
set(kritawidgetutils_LIB_SRCS
WidgetUtilsDebug.cpp
kis_icon_utils.cpp
kis_action_registry.cpp
KisActionsSnapshot.cpp
KoGroupButton.cpp
KoProgressProxy.cpp
KoFakeProgressProxy.cpp
KoProgressBar.cpp
KoProgressUpdater.cpp
KoUpdater.cpp
KoUpdaterPrivate_p.cpp
KoProperties.cpp
KoFileDialog.cpp
KoResourcePaths.cpp
KisKineticScroller.cpp
kis_num_parser.cpp
kis_spin_box_unit_manager.cpp
config/kcolorscheme.cpp
config/kcolorschememanager.cpp
config/khelpclient.cpp
config/klanguagebutton.cpp
config/krecentfilesaction.cpp
config/kstandardaction.cpp
xmlgui/KisShortcutsEditorItem.cpp
xmlgui/KisShortcutEditWidget.cpp
xmlgui/KisShortcutsEditorDelegate.cpp
xmlgui/KisShortcutsDialog.cpp
xmlgui/KisShortcutsDialog_p.cpp
xmlgui/KisShortcutsEditor.cpp
xmlgui/KisShortcutsEditor_p.cpp
xmlgui/kshortcutschemeseditor.cpp
xmlgui/kshortcutschemeshelper.cpp
xmlgui/kaboutkdedialog_p.cpp
xmlgui/kactioncategory.cpp
xmlgui/kactioncollection.cpp
- xmlgui/kactionconflictdetector.cpp
xmlgui/kbugreport.cpp
xmlgui/kcheckaccelerators.cpp
xmlgui/kedittoolbar.cpp
xmlgui/kgesture.cpp
xmlgui/kgesturemap.cpp
xmlgui/khelpmenu.cpp
xmlgui/kkeysequencewidget.cpp
xmlgui/kmainwindow.cpp
xmlgui/kmenumenuhandler_p.cpp
xmlgui/kshortcutwidget.cpp
xmlgui/kswitchlanguagedialog_p.cpp
xmlgui/ktoggletoolbaraction.cpp
xmlgui/ktoolbar.cpp
xmlgui/ktoolbarhandler.cpp
xmlgui/kundoactions.cpp
xmlgui/kxmlguibuilder.cpp
xmlgui/kxmlguiclient.cpp
xmlgui/kxmlguifactory.cpp
xmlgui/kxmlguifactory_p.cpp
xmlgui/kxmlguiversionhandler.cpp
xmlgui/kxmlguiwindow.cpp
)
if (HAVE_DBUS)
set(kritawidgetutils_LIB_SRCS ${kritawidgetutils_LIB_SRCS}
xmlgui/kmainwindowiface.cpp
)
endif()
ki18n_wrap_ui(kritawidgetutils_LIB_SRCS
xmlgui/KisShortcutsDialog.ui
xmlgui/kshortcutwidget.ui
)
qt5_add_resources(kritawidgetutils_LIB_SRCS xmlgui/kxmlgui.qrc)
add_library(kritawidgetutils SHARED ${kritawidgetutils_LIB_SRCS})
target_include_directories(kritawidgetutils
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/config>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/xmlgui>
)
generate_export_header(kritawidgetutils BASE_NAME kritawidgetutils)
if (HAVE_DBUS)
set (KRITA_WIDGET_UTILS_EXTRA_LIBS ${KRITA_WIDGET_UTILS_EXTRA_LIBS} Qt5::DBus)
endif ()
if (APPLE)
find_library(FOUNDATION_LIBRARY Foundation)
set(KRITA_WIDGET_UTILS_EXTRA_LIBS ${KRITA_WIDGET_UTILS_EXTRA_LIBS} ${FOUNDATION_LIBRARY})
endif ()
target_link_libraries(kritawidgetutils
PUBLIC
Qt5::Widgets
Qt5::Gui
Qt5::Xml
Qt5::Core
KF5::ItemViews
kritaglobal
PRIVATE
Qt5::PrintSupport
KF5::I18n
KF5::ConfigCore
KF5::CoreAddons
KF5::ConfigGui
KF5::GuiAddons
KF5::WidgetsAddons
KF5::WindowSystem
kritaplugin
kritaodf
${KRITA_WIDGET_UTILS_EXTRA_LIBS}
)
set_target_properties(kritawidgetutils
PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION}
)
install(TARGETS kritawidgetutils ${INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/libs/widgetutils/config/kcolorscheme.h b/libs/widgetutils/config/kcolorscheme.h
index e877b58b6a..dd240b9e0c 100644
--- a/libs/widgetutils/config/kcolorscheme.h
+++ b/libs/widgetutils/config/kcolorscheme.h
@@ -1,566 +1,570 @@
/* This file is part of the KDE project
* Copyright (C) 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KCOLORSCHEME_H
#define KCOLORSCHEME_H
#include <kritawidgetutils_export.h>
#include <ksharedconfig.h>
#include <QExplicitlySharedDataPointer>
#include <QPalette>
class QColor;
class QBrush;
class KColorSchemePrivate;
/**
* A set of methods used to work with colors.
*
* KColorScheme currently provides access to the system color palette that the
* user has selected (in the future, it is expected to do more). It greatly
* expands on QPalette by providing five distinct "sets" with several color
* choices each, covering background, foreground, and decoration colors.
*
* A KColorScheme instance represents colors corresponding to a "set", where a
* set consists of those colors used to draw a particular type of element, such
* as a menu, button, view, selected text, or tooltip. Each set has a distinct
* set of colors, so you should always use the correct set for drawing and
* never assume that a particular foreground for one set is the same as the
* foreground for any other set. Individual colors may be quickly referenced by
* creating an anonymous instance and invoking a lookup member.
*
* @note
* The color palettes for the various states of a widget (active, inactive,
* disabled) may be wildly different. Therefore, it is important to take the
* state into account. This is why the KColorScheme constructor requires a
* QPalette::ColorGroup as an argument.
*
* To facilitate working with potentially-varying states, two convenience API's
* are provided. These are KColorScheme::adjustBackground and its sister
* KColorScheme::adjustForeground, and the helper class ::KStatefulBrush.
*
* @see KColorScheme::ColorSet, KColorScheme::ForegroundRole,
* KColorScheme::BackgroundRole, KColorScheme::DecorationRole,
* KColorScheme::ShadeRole
*/
class KRITAWIDGETUTILS_EXPORT KColorScheme
{
public:
/**
* This enumeration describes the color set for which a color is being
* selected.
*
* Color sets define a color "environment", suitable for drawing all parts
* of a given region. Colors from different sets should not be combined.
*/
enum ColorSet {
/**
* Views; for example, frames, input fields, etc.
*
* If it contains things that can be selected, it is probably a View.
*/
View,
/**
* Non-editable window elements; for example, menus.
*
* If it isn't a Button, View, or Tooltip, it is probably a Window.
*/
Window,
/**
* Buttons and button-like controls.
*
* In addition to buttons, "button-like" controls such as non-editable
* dropdowns, scrollbar sliders, slider handles, etc. should also use
* this role.
*/
Button,
/**
* Selected items in views.
*
* Note that unfocused or disabled selections should use the Window
* role. This makes it more obvious to the user that the view
* containing the selection does not have input focus.
*/
Selection,
/**
* Tooltips.
*
* The tooltip set can often be substituted for the view
* set when editing is not possible, but the Window set is deemed
* inappropriate. "What's This" help is an excellent example, another
* might be pop-up notifications (depending on taste).
*/
Tooltip
};
/**
* This enumeration describes the background color being selected from the
* given set.
*
* Background colors are suitable for drawing under text, and should never
* be used to draw text. In combination with one of the overloads of
* KColorScheme::shade, they may be used to generate colors for drawing
* frames, bevels, and similar decorations.
*/
enum BackgroundRole {
/**
* Normal background.
*/
NormalBackground = 0,
/**
* Alternate background; for example, for use in lists.
*
* This color may be the same as BackgroundNormal, especially in sets
* other than View and Window.
*/
AlternateBackground = 1,
/**
* Third color; for example, items which are new, active, requesting
* attention, etc.
*
* Alerting the user that a certain field must be filled out would be a
* good usage (although NegativeBackground could be used to the same
* effect, depending on what you are trying to achieve). Unlike
* ActiveText, this should not be used for mouseover effects.
*/
ActiveBackground = 2,
/**
* Fourth color; corresponds to (unvisited) links.
*
* Exactly what this might be used for is somewhat harder to qualify;
* it might be used for bookmarks, as a 'you can click here' indicator,
* or to highlight recent content (i.e. in a most-recently-accessed
* list).
*/
LinkBackground = 3,
/**
* Fifth color; corresponds to visited links.
*
* This can also be used to indicate "not recent" content, especially
* when a color is needed to denote content which is "old" or
* "archival".
*/
VisitedBackground = 4,
/**
* Sixth color; for example, errors, untrusted content, etc.
*/
NegativeBackground = 5,
/**
* Seventh color; for example, warnings, secure/encrypted content.
*/
NeutralBackground = 6,
/**
* Eighth color; for example, success messages, trusted content.
*/
PositiveBackground = 7
};
/**
* This enumeration describes the foreground color being selected from the
* given set.
*
* Foreground colors are suitable for drawing text or glyphs (such as the
* symbols on window decoration buttons, assuming a suitable background
* brush is used), and should never be used to draw backgrounds.
*
* For window decorations, the following is suggested, but not set in
* stone:
* @li Maximize - PositiveText
* @li Minimize - NeutralText
* @li Close - NegativeText
* @li WhatsThis - LinkText
* @li Sticky - ActiveText
*/
enum ForegroundRole {
/**
* Normal foreground.
*/
NormalText = 0,
/**
* Second color; for example, comments, items which are old, inactive
* or disabled. Generally used for things that are meant to be "less
* important". InactiveText is not the same role as NormalText in the
* inactive state.
*/
InactiveText = 1,
/**
* Third color; for example items which are new, active, requesting
* attention, etc. May be used as a hover color for clickable items.
*/
ActiveText = 2,
/**
* Fourth color; use for (unvisited) links. May also be used for other
* clickable items or content that indicates relationships, items that
* indicate somewhere the user can visit, etc.
*/
LinkText = 3,
/**
* Fifth color; used for (visited) links. As with LinkText, may be used
* for items that have already been "visited" or accessed. May also be
* used to indicate "historical" (i.e. "old") items or information,
* especially if InactiveText is being used in the same context to
* express something different.
*/
VisitedText = 4,
/**
* Sixth color; for example, errors, untrusted content, deletions,
* etc.
*/
NegativeText = 5,
/**
* Seventh color; for example, warnings, secure/encrypted content.
*/
NeutralText = 6,
/**
* Eighth color; for example, additions, success messages, trusted
* content.
*/
PositiveText = 7
};
/**
* This enumeration describes the decoration color being selected from the
* given set.
*
* Decoration colors are used to draw decorations (such as frames) for
* special purposes. Like color shades, they are neither foreground nor
* background colors. Text should not be painted over a decoration color,
* and decoration colors should not be used to draw text.
*/
enum DecorationRole {
/**
* Color used to draw decorations for items which have input focus.
*/
FocusColor,
/**
* Color used to draw decorations for items which will be activated by
* clicking.
*/
HoverColor
};
/**
* This enumeration describes the color shade being selected from the given
* set.
*
* Color shades are used to draw "3d" elements, such as frames and bevels.
* They are neither foreground nor background colors. Text should not be
* painted over a shade, and shades should not be used to draw text.
*/
enum ShadeRole {
/**
* The light color is lighter than dark() or shadow() and contrasts
* with the base color.
*/
LightShade,
/**
* The midlight color is in between base() and light().
*/
MidlightShade,
/**
* The mid color is in between base() and dark().
*/
MidShade,
/**
* The dark color is in between mid() and shadow().
*/
DarkShade,
/**
* The shadow color is darker than light() or midlight() and contrasts
* the base color.
*/
ShadowShade
};
/** Construct a copy of another KColorScheme. */
KColorScheme(const KColorScheme &);
/** Destructor */
virtual ~KColorScheme();
/** Standard assignment operator */
KColorScheme &operator=(const KColorScheme &);
/**
* Construct a palette from given color set and state, using the colors
* from the given KConfig (if null, the system colors are used).
*
* @note KColorScheme provides direct access to the color scheme for users
* that deal directly with widget states. Unless you are a low-level user
* or have a legitimate reason to only care about a fixed, limited number
* of states (e.g. windows that cannot be inactive), consider using a
* ::KStatefulBrush instead.
*/
explicit KColorScheme(QPalette::ColorGroup, ColorSet = View, KSharedConfigPtr = KSharedConfigPtr());
/**
* Retrieve the requested background brush.
*/
QBrush background(BackgroundRole = NormalBackground) const;
/**
* Retrieve the requested foreground brush.
*/
QBrush foreground(ForegroundRole = NormalText) const;
/**
* Retrieve the requested decoration brush.
*/
QBrush decoration(DecorationRole) const;
/**
* Retrieve the requested shade color, using
* KColorScheme::background(KColorScheme::NormalBackground)
* as the base color and the contrast setting from the KConfig used to
* create this KColorScheme instance (the system contrast setting, if no
* KConfig was specified).
*
* @note Shades are chosen such that all shades would contrast with the
* base color. This means that if base is very dark, the 'dark' shades will
* be lighter than the base color, with midlight() == shadow().
* Conversely, if the base color is very light, the 'light' shades will be
* darker than the base color, with light() == mid().
*/
QColor shade(ShadeRole) const;
/**
* Returns the contrast for borders.
* @return the contrast (between 0 for minimum and 10 for maximum
* contrast)
*/
static int contrast();
/**
* Returns the contrast for borders as a floating point value.
* @param config pointer to the config from which to read the contrast
* setting (the default is to use KSharedConfig::openConfig())
* @return the contrast (between 0.0 for minimum and 1.0 for maximum
* contrast)
*/
static qreal contrastF(const KSharedConfigPtr &config = KSharedConfigPtr());
/**
* Retrieve the requested shade color, using the specified color as the
* base color and the system contrast setting.
*
* @note Shades are chosen such that all shades would contrast with the
* base color. This means that if base is very dark, the 'dark' shades will
* be lighter than the base color, with midlight() == shadow().
* Conversely, if the base color is very light, the 'light' shades will be
* darker than the base color, with light() == mid().
*/
static QColor shade(const QColor &, ShadeRole);
/**
* Retrieve the requested shade color, using the specified color as the
* base color and the specified contrast.
*
+ * @param color the shade color
+ * @param role the shade role
* @param contrast Amount roughly specifying the contrast by which to
* adjust the base color, between -1.0 and 1.0 (values between 0.0 and 1.0
* correspond to the value from KColorScheme::contrastF)
* @param chromaAdjust (optional) Amount by which to adjust the chroma of
* the shade (1.0 means no adjustment)
*
* @note Shades are chosen such that all shades would contrast with the
* base color. This means that if base is very dark, the 'dark' shades will
* be lighter than the base color, with midlight() == shadow().
* Conversely, if the base color is very light, the 'light' shades will be
* darker than the base color, with light() == mid().
*
* @see KColorUtils::shade
*/
static QColor shade(const QColor &, ShadeRole,
qreal contrast, qreal chromaAdjust = 0.0);
/**
* Adjust a QPalette by replacing the specified QPalette::ColorRole with
* the requested background color for all states. Using this method is
* safer than replacing individual states, as it insulates you against
* changes in QPalette::ColorGroup.
*
* @note Although it is possible to replace a foreground color using this
* method, it's bad usability to do so. Just say "no".
*/
static void adjustBackground(QPalette &,
BackgroundRole newRole = NormalBackground,
QPalette::ColorRole color = QPalette::Base,
ColorSet set = View,
KSharedConfigPtr = KSharedConfigPtr());
/**
* Adjust a QPalette by replacing the specified QPalette::ColorRole with
* the requested foreground color for all states. Using this method is
* safer than replacing individual states, as it insulates you against
* changes in QPalette::ColorGroup.
*
* @note Although it is possible to replace a background color using this
* method, it's bad usability to do so. Just say "no".
*/
static void adjustForeground(QPalette &,
ForegroundRole newRole = NormalText,
QPalette::ColorRole color = QPalette::Text,
ColorSet set = View,
KSharedConfigPtr = KSharedConfigPtr());
/**
* Used to obtain the QPalette that will be used to set the application
* palette from KDE Platform theme.
*
* @param config KConfig from which to load the colors
*
* @returns the QPalette
*
* @since 5.0
*/
static QPalette createApplicationPalette(const KSharedConfigPtr &config);
private:
QExplicitlySharedDataPointer<KColorSchemePrivate> d;
};
/**
* A container for a "state-aware" brush.
*
* KStatefulBrush provides an easy and safe way to store a color for use in a
* user interface. It is "safe" both in that it will make it easy to deal with
* widget states in a correct manner, and that it insulates you against changes
* in QPalette::ColorGroup.
*
* Basically, a stateful brush is used to cache a particular "color" from the
* KDE system palette (usually, one which does not live in QPalette). When you
* are ready to draw using the brush, you use the current state to retrieve the
* appropriate brush.
*
* Stateful brushes can also be used to apply state effects to arbitrary
* brushes, for example when working with a application specific user-defined
* color palette.
*
* @note As of Qt 4.3, QPalette::ColorGroup is missing a state for disabled
* widgets in an inactive window. Hopefully Trolltech will fix this bug, at
* which point KColorScheme and KStatefulBrush will be updated to recognize the
* new state. Using KStatefulBrush will allow your application to inherit these
* changes "for free", without even recompiling.
*/
class KRITAWIDGETUTILS_EXPORT KStatefulBrush
{
public:
/**
* Construct a "default" stateful brush. For such an instance, all
* overloads of KStatefulBrush::brush will return a default brush (i.e.
* <tt>QBrush()</tt>).
*/
explicit KStatefulBrush();
/**
* Construct a stateful brush from given color set and foreground role,
* using the colors from the given KConfig (if null, the system colors are
* used).
*/
explicit KStatefulBrush(KColorScheme::ColorSet,
KColorScheme::ForegroundRole,
KSharedConfigPtr = KSharedConfigPtr());
/**
* Construct a stateful brush from given color set and background role,
* using the colors from the given KConfig (if null, the system colors are
* used).
*/
explicit KStatefulBrush(KColorScheme::ColorSet,
KColorScheme::BackgroundRole,
KSharedConfigPtr = KSharedConfigPtr());
/**
* Construct a stateful brush from given color set and decoration role,
* using the colors from the given KConfig (if null, the system colors are
* used).
*/
explicit KStatefulBrush(KColorScheme::ColorSet,
KColorScheme::DecorationRole,
KSharedConfigPtr = KSharedConfigPtr());
/**
* Construct a stateful background brush from a specified QBrush (or
* QColor, via QBrush's implicit constructor). The various states are
* determined from the base QBrush (which fills in the Active state)
* according to the same rules used to build stateful color schemes from
* the system color scheme. The state effects from the given KConfig are
* used (if null, the system state effects are used).
*/
explicit KStatefulBrush(const QBrush &, KSharedConfigPtr = KSharedConfigPtr());
/**
* Construct a stateful foreground/decoration brush from a specified
* QBrush (or QColor, via QBrush's implicit constructor). The various
* states are determined from the base QBrush (which fills in the Active
* state) according to the same rules used to build stateful color schemes
* from the system color scheme. The state effects from the given KConfig
* are used (if null, the system state effects are used).
*
+ * @param brush The foreground brush
* @param background The background brush (or color) corresponding to the
* KColorScheme::NormalBackground role and QPalette::Active state for this
* foreground/decoration color.
+ * @param config The configuration.
*/
explicit KStatefulBrush(const QBrush &, const QBrush &background,
KSharedConfigPtr = KSharedConfigPtr());
/** Construct a copy of another KStatefulBrush. */
KStatefulBrush(const KStatefulBrush &);
/** Destructor */
~KStatefulBrush();
/** Standard assignment operator */
KStatefulBrush &operator=(const KStatefulBrush &);
/**
* Retrieve the brush for the specified widget state. This is used when you
* know explicitly what state is wanted. Otherwise one of overloads is
* often more convenient.
*/
QBrush brush(QPalette::ColorGroup) const;
/**
* Retrieve the brush, using a QPalette reference to determine the correct
* state. Use when your painting code has easy access to the QPalette that
* it is supposed to be using. The state used in this instance is the
* currentColorGroup of the palette.
*/
QBrush brush(const QPalette &) const;
/**
* Retrieve the brush, using a QWidget pointer to determine the correct
* state. Use when you have a pointer to the widget that you are painting.
* The state used is the current state of the widget.
*
* @note If you pass an invalid widget, you will get a default brush (i.e.
* <tt>QBrush()</tt>).
*/
QBrush brush(const QWidget *) const;
private:
class KStatefulBrushPrivate *d;
};
Q_DECLARE_METATYPE(KStatefulBrush) /* so we can pass it in QVariant's */
#endif // KCOLORSCHEME_H
diff --git a/libs/widgetutils/config/kcolorschememanager.h b/libs/widgetutils/config/kcolorschememanager.h
index 71fde0ca6e..20c2bbd17e 100644
--- a/libs/widgetutils/config/kcolorschememanager.h
+++ b/libs/widgetutils/config/kcolorschememanager.h
@@ -1,121 +1,121 @@
/* This file is part of the KDE project
* Copyright (C) 2013 Martin Gräßlin <mgraesslin@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KCOLORSCHEMEMANAGER_H
#define KCOLORSCHEMEMANAGER_H
#include <kritawidgetutils_export.h>
#include <QObject>
class QAbstractItemModel;
class KActionMenu;
class KColorSchemeManagerPrivate;
/**
* A small helper to get access to all available color schemes and activating a scheme in the
* QApplication. This is useful for applications which want to provide a selection of custom color
* schemes to their user. For example it is very common for photo and painting applications to use
* a dark color scheme even if the default is a light scheme.
*
* The KColorSchemeManager provides access to a QAbstractItemModel which holds all the available
* schemes. A possible usage looks like the following:
*
* @code
* KColorSchemeManager *schemes = new KColorSchemeManager(this);
* QListView *view = new QListView(this);
* view->setModel(schemes->model());
* connect(view, &QListView::activated, schemes, &KColorSchemeManager::activateScheme);
* @endcode
*
* In addition the KColorSchemeManager also provides the possibility to create a KActionMenu populated
* with all the available color schemes in an action group. If one of the actions is selected the
* scheme is applied instantly:
*
* @code
* QToolButton *button = new QToolButton();
* KColorSchemeManager *schemes = new KColorSchemeManager(this);
* KActionMenu *menu = schemes->createSchemeSelectionMenu(QStringLiteral("Oxygen"), button);
* button->setMenu(menu->menu());
* @endcode
*
* @since 5.0
*/
class KRITAWIDGETUTILS_EXPORT KColorSchemeManager : public QObject
{
Q_OBJECT
public:
explicit KColorSchemeManager(QObject *parent = 0);
~KColorSchemeManager() override;
/**
* A QAbstractItemModel of all available color schemes.
*
* The model provides the name of the scheme in Qt::DisplayRole, a preview
* in Qt::DelegateRole and the full path to the scheme file in Qt::UserRole.
*
* @return Model of all available color schemes.
*/
QAbstractItemModel *model() const;
/**
* Returns the model index for the scheme with the given @p name. If no such
* scheme exists an invalid index is returned.
* @see model
*/
QModelIndex indexForScheme(const QString &name) const;
/**
* Creates a KActionMenu populated with all the available color schemes.
* All actions are in an action group and when one of the actions is triggered the scheme
* referenced by this action is activated.
*
* The color scheme with the same name as @p selectedSchemeName will be checked. If none
* of the available color schemes has the same name, no action will be checked.
*
* The KActionMenu will not be updated in case the installed color schemes change. It's the
* task of the user of the KActionMenu to monitor for changes if required.
*
* @param icon The icon to use for the KActionMenu
* @param text The text to use for the KActionMenu
* @param selectedSchemeName The name of the color scheme to select
* @param parent The parent of the KActionMenu
* @return KActionMenu populated with all available color schemes.
* @see activateScheme
*/
KActionMenu *createSchemeSelectionMenu(const QIcon &icon, const QString &text, const QString &selectedSchemeName, QObject *parent);
KActionMenu *createSchemeSelectionMenu(const QString &text, const QString &selectedSchemeName, QObject *parent);
KActionMenu *createSchemeSelectionMenu(const QString &selectedSchemeName, QObject *parent);
public Q_SLOTS:
/**
* @brief Activates the KColorScheme identified by the provided @p index.
*
* Installs the KColorScheme as the QApplication's QPalette.
*
* @param index The index for the KColorScheme to activate.
- * The index must reference the QAbstractItemModel provided by @link model
- * @see model()
+ * The index must reference the QAbstractItemModel provided by @c model
+ * @see model
*/
void activateScheme(const QModelIndex &index);
private:
QScopedPointer<KColorSchemeManagerPrivate> d;
};
#endif
diff --git a/libs/widgetutils/config/kstandardaction.h b/libs/widgetutils/config/kstandardaction.h
index e9c5609a10..9055e1614b 100644
--- a/libs/widgetutils/config/kstandardaction.h
+++ b/libs/widgetutils/config/kstandardaction.h
@@ -1,598 +1,598 @@
/* This file is part of the KDE libraries
Copyright (C) 1999,2000 Kurt Granroth <granroth@kde.org>
Copyright (C) 2001,2002 Ellis Whitehead <ellis@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KSTANDARDACTION_H
#define KSTANDARDACTION_H
#include <kritawidgetutils_export.h>
#include <kstandardshortcut.h>
#include <QList>
class QObject;
class QStringList;
class QWidget;
class QAction;
class KRecentFilesAction;
class KDualAction;
class KToggleAction;
class KToggleFullScreenAction;
/**
* Convenience methods to access all standard KDE actions.
*
* These actions should be used instead of hardcoding menubar and
* toolbar items. Using these actions helps your application easily
* conform to the KDE UI Style Guide
* @see http://developer.kde.org/documentation/standards/kde/style/basics/index.html .
*
* All of the documentation for QAction holds for KStandardAction
* also. When in doubt on how things work, check the QAction
* documentation first.
* Please note that calling any of these methods automatically adds the action
* to the actionCollection() of the QObject given by the 'parent' parameter.
*
* <b>Simple Example:</b>\n
*
* In general, using standard actions should be a drop in replacement
* for regular actions. For example, if you previously had:
*
* \code
* QAction *newAct = new QAction(i18n("&New"), KisIconUtils::loadIcon("document-new"),
* KStandardShortcut::shortcut(KStandardShortcut::New), this,
* SLOT(fileNew()), actionCollection());
* \endcode
*
* You could drop that and replace it with:
*
* \code
* QAction *newAct = KStandardAction::openNew(this, SLOT(fileNew()),
* actionCollection());
* \endcode
*
* <b>Non-standard Usages</b>\n
*
* It is possible to use the standard actions in various
* non-recommended ways. Say, for instance, you wanted to have a
* standard action (with the associated correct text and icon and
* accelerator, etc) but you didn't want it to go in the standard
* place (this is not recommended, by the way). One way to do this is
* to simply not use the XML UI framework and plug it into wherever
* you want. If you do want to use the XML UI framework (good!), then
* it is still possible.
*
* Basically, the XML building code matches names in the XML code with
* the internal names of the actions. You can find out the internal
* names of each of the standard actions by using the name
* method like so: KStandardAction::name(KStandardAction::Cut) would return
* 'edit_cut'. The XML building code will match 'edit_cut' to the
* attribute in the global XML file and place your action there.
*
* However, you can change the internal name. In this example, just
* do something like:
*
* \code
* (void)KStandardAction::cut(this, SLOT(editCut()), actionCollection(), "my_cut");
* \endcode
*
* Now, in your local XML resource file (e.g., yourappui.rc), simply
* put 'my_cut' where you want it to go.
*
* Another non-standard usage concerns getting a pointer to an
* existing action if, say, you want to enable or disable the action.
* You could do it the recommended way and just grab a pointer when
* you instantiate it as in the 'openNew' example above... or you
* could do it the hard way:
*
* \code
* QAction *cut = actionCollection()->action(KStandardAction::name(KStandardAction::Cut));
* \endcode
*
* Another non-standard usage concerns instantiating the action in the
* first place. Usually, you would use the member functions as
* shown above (e.g., KStandardAction::cut(this, SLOT, parent)). You
* may, however, do this using the enums provided. This author can't
* think of a reason why you would want to, but, hey, if you do,
* here's how:
*
* \code
* (void)KStandardAction::action(KStandardAction::New, this, SLOT(fileNew()), actionCollection());
* (void)KStandardAction::action(KStandardAction::Cut, this, SLOT(editCut()), actionCollection());
* \endcode
*
* @author Kurt Granroth <granroth@kde.org>
*/
namespace KStandardAction
{
/**
* The standard menubar and toolbar actions.
*/
enum StandardAction {
ActionNone,
// File Menu
New, Open, OpenRecent, Save, SaveAs, Revert, Close,
Print, PrintPreview, Mail, Quit,
// Edit Menu
Undo, Redo, Cut, Copy, Paste, SelectAll, Deselect, Find, FindNext, FindPrev,
Replace,
// View Menu
ActualSize, FitToPage, FitToWidth, FitToHeight, ZoomIn, ZoomOut,
Zoom, Redisplay,
// Go Menu
Up, Back, Forward, Home /*Home page*/, Prior, Next, Goto, GotoPage, GotoLine,
FirstPage, LastPage, DocumentBack, DocumentForward,
// Bookmarks Menu
AddBookmark, EditBookmarks,
// Tools Menu
Spelling,
// Settings Menu
ShowMenubar, ShowToolbar, ShowStatusbar,
SaveOptions, KeyBindings,
Preferences, ConfigureToolbars,
// Help Menu
Help, HelpContents, WhatsThis, ReportBug, AboutApp, AboutKDE,
TipofDay,
// Other standard actions
ConfigureNotifications,
FullScreen,
Clear,
PasteText,
SwitchApplicationLanguage
};
/**
* Creates an action corresponding to one of the
* KStandardAction::StandardAction actions, which is connected to the given
* object and @p slot, and is owned by @p parent.
*
* The signal that is connected to @p slot is triggered(bool), except for the case of
* OpenRecent standard action, which uses the urlSelected(const QUrl &) signal of
* KRecentFilesAction.
*
* @param id The StandardAction identifier to create a QAction for.
* @param recvr The QObject to receive the signal, or 0 if no notification
* is needed.
* @param slot The slot to connect the signal to (remember to use the SLOT() macro).
* @param parent The QObject that should own the created QAction, or 0 if no parent will
* own the QAction returned (ensure you delete it manually in this case).
*/
KRITAWIDGETUTILS_EXPORT QAction *create(StandardAction id, const QObject *recvr, const char *slot,
QObject *parent);
/**
* This will return the internal name of a given standard action.
*/
KRITAWIDGETUTILS_EXPORT const char *name(StandardAction id);
/// @deprecated use name()
#ifndef KDE_NO_DEPRECATED
inline KRITAWIDGETUTILS_DEPRECATED const char *stdName(StandardAction act_enum)
{
return name(act_enum);
}
#endif
/**
* Returns a list of all standard names. Used by KAccelManager
* to give those higher weight.
*/
KRITAWIDGETUTILS_EXPORT QStringList stdNames();
/**
* Returns a list of all actionIds.
*
* @since 4.2
*/
KRITAWIDGETUTILS_EXPORT QList<StandardAction> actionIds();
/**
* Returns the standardshortcut associated with @a actionId.
*
- * @param actionId The actionId whose associated shortcut is wanted.
+ * @param id The actionId whose associated shortcut is wanted.
*
* @since 4.2
*/
KRITAWIDGETUTILS_EXPORT KStandardShortcut::StandardShortcut shortcutForActionId(StandardAction id);
/**
* Create a new document or window.
*/
KRITAWIDGETUTILS_EXPORT QAction *openNew(const QObject *recvr, const char *slot, QObject *parent);
/**
* Open an existing file.
*/
KRITAWIDGETUTILS_EXPORT QAction *open(const QObject *recvr, const char *slot, QObject *parent);
/**
* Open a recently used document. The signature of the slot being called
* is of the form slotURLSelected( const QUrl & ).
* @param recvr object to receive slot
* @param slot The SLOT to invoke when a URL is selected. The slot's
* signature is slotURLSelected( const QUrl & ).
* @param parent parent widget
*/
KRITAWIDGETUTILS_EXPORT KRecentFilesAction *openRecent(const QObject *recvr, const char *slot, QObject *parent);
/**
* Save the current document.
*/
KRITAWIDGETUTILS_EXPORT QAction *save(const QObject *recvr, const char *slot, QObject *parent);
/**
* Save the current document under a different name.
*/
KRITAWIDGETUTILS_EXPORT QAction *saveAs(const QObject *recvr, const char *slot, QObject *parent);
/**
* Revert the current document to the last saved version
* (essentially will undo all changes).
*/
KRITAWIDGETUTILS_EXPORT QAction *revert(const QObject *recvr, const char *slot, QObject *parent);
/**
* Close the current document.
*/
KRITAWIDGETUTILS_EXPORT QAction *close(const QObject *recvr, const char *slot, QObject *parent);
/**
* Print the current document.
*/
KRITAWIDGETUTILS_EXPORT QAction *print(const QObject *recvr, const char *slot, QObject *parent);
/**
* Show a print preview of the current document.
*/
KRITAWIDGETUTILS_EXPORT QAction *printPreview(const QObject *recvr, const char *slot, QObject *parent);
/**
* Mail this document.
*/
KRITAWIDGETUTILS_EXPORT QAction *mail(const QObject *recvr, const char *slot, QObject *parent);
/**
* Quit the program.
*
* Note that you probably want to connect this action to either QWidget::close()
* or QApplication::closeAllWindows(), but not QApplication::quit(), so that
* KMainWindow::queryClose() is called on any open window (to warn the user
* about unsaved changes for example).
*/
KRITAWIDGETUTILS_EXPORT QAction *quit(const QObject *recvr, const char *slot, QObject *parent);
/**
* Undo the last operation.
*/
KRITAWIDGETUTILS_EXPORT QAction *undo(const QObject *recvr, const char *slot, QObject *parent);
/**
* Redo the last operation.
*/
KRITAWIDGETUTILS_EXPORT QAction *redo(const QObject *recvr, const char *slot, QObject *parent);
/**
* Cut selected area and store it in the clipboard.
* Calls cut() on the widget with the current focus.
*/
KRITAWIDGETUTILS_EXPORT QAction *cut(QObject *parent);
/**
* Copy selected area and store it in the clipboard.
* Calls copy() on the widget with the current focus.
*/
KRITAWIDGETUTILS_EXPORT QAction *copy(QObject *parent);
/**
* Paste the contents of clipboard at the current mouse or cursor
* Calls paste() on the widget with the current focus.
*/
KRITAWIDGETUTILS_EXPORT QAction *paste(QObject *parent);
/**
* Clear selected area. Calls clear() on the widget with the current focus.
* Note that for some widgets, this may not provide the intended behavior. For
* example if you make use of the code above and a K3ListView has the focus, clear()
* will clear all of the items in the list. If this is not the intened behavior
* and you want to make use of this slot, you can subclass K3ListView and reimplement
* this slot. For example the following code would implement a K3ListView without this
* behavior:
*
* \code
* class MyListView : public K3ListView {
* Q_OBJECT
* public:
* MyListView( QWidget * parent = 0, const char * name = 0, WFlags f = 0 ) : K3ListView( parent, name, f ) {}
* virtual ~MyListView() {}
* public Q_SLOTS:
* virtual void clear() {}
* };
* \endcode
*/
KRITAWIDGETUTILS_EXPORT QAction *clear(QObject *parent);
/**
* Calls selectAll() on the widget with the current focus.
*/
KRITAWIDGETUTILS_EXPORT QAction *selectAll(QObject *parent);
/**
* Cut selected area and store it in the clipboard.
*/
KRITAWIDGETUTILS_EXPORT QAction *cut(const QObject *recvr, const char *slot, QObject *parent);
/**
* Copy the selected area into the clipboard.
*/
KRITAWIDGETUTILS_EXPORT QAction *copy(const QObject *recvr, const char *slot, QObject *parent);
/**
* Paste the contents of clipboard at the current mouse or cursor
* position.
*/
KRITAWIDGETUTILS_EXPORT QAction *paste(const QObject *recvr, const char *slot, QObject *parent);
/**
* Paste the contents of clipboard at the current mouse or cursor
* position. Provide a button on the toolbar with the clipboard history
* menu if Klipper is running.
*/
KRITAWIDGETUTILS_EXPORT QAction *pasteText(const QObject *recvr, const char *slot, QObject *parent);
/**
* Clear the content of the focus widget
*/
KRITAWIDGETUTILS_EXPORT QAction *clear(const QObject *recvr, const char *slot, QObject *parent);
/**
* Select all elements in the current document.
*/
KRITAWIDGETUTILS_EXPORT QAction *selectAll(const QObject *recvr, const char *slot, QObject *parent);
/**
* Deselect any selected elements in the current document.
*/
KRITAWIDGETUTILS_EXPORT QAction *deselect(const QObject *recvr, const char *slot, QObject *parent);
/**
* Initiate a 'find' request in the current document.
*/
KRITAWIDGETUTILS_EXPORT QAction *find(const QObject *recvr, const char *slot, QObject *parent);
/**
* Find the next instance of a stored 'find'.
*/
KRITAWIDGETUTILS_EXPORT QAction *findNext(const QObject *recvr, const char *slot, QObject *parent);
/**
* Find a previous instance of a stored 'find'.
*/
KRITAWIDGETUTILS_EXPORT QAction *findPrev(const QObject *recvr, const char *slot, QObject *parent);
/**
* Find and replace matches.
*/
KRITAWIDGETUTILS_EXPORT QAction *replace(const QObject *recvr, const char *slot, QObject *parent);
/**
* View the document at its actual size.
*/
KRITAWIDGETUTILS_EXPORT QAction *actualSize(const QObject *recvr, const char *slot, QObject *parent);
/**
* Fit the document view to the size of the current window.
*/
KRITAWIDGETUTILS_EXPORT QAction *fitToPage(const QObject *recvr, const char *slot, QObject *parent);
/**
* Fit the document view to the width of the current window.
*/
KRITAWIDGETUTILS_EXPORT QAction *fitToWidth(const QObject *recvr, const char *slot, QObject *parent);
/**
* Fit the document view to the height of the current window.
*/
KRITAWIDGETUTILS_EXPORT QAction *fitToHeight(const QObject *recvr, const char *slot, QObject *parent);
/**
* Zoom in.
*/
KRITAWIDGETUTILS_EXPORT QAction *zoomIn(const QObject *recvr, const char *slot, QObject *parent);
/**
* Zoom out.
*/
KRITAWIDGETUTILS_EXPORT QAction *zoomOut(const QObject *recvr, const char *slot, QObject *parent);
/**
* Popup a zoom dialog.
*/
KRITAWIDGETUTILS_EXPORT QAction *zoom(const QObject *recvr, const char *slot, QObject *parent);
/**
* Redisplay or redraw the document.
*/
KRITAWIDGETUTILS_EXPORT QAction *redisplay(const QObject *recvr, const char *slot, QObject *parent);
/**
* Move up (web style menu).
*/
KRITAWIDGETUTILS_EXPORT QAction *up(const QObject *recvr, const char *slot, QObject *parent);
/**
* Move back (web style menu).
*/
KRITAWIDGETUTILS_EXPORT QAction *back(const QObject *recvr, const char *slot, QObject *parent);
/**
* Move forward (web style menu).
*/
KRITAWIDGETUTILS_EXPORT QAction *forward(const QObject *recvr, const char *slot, QObject *parent);
/**
* Go to the "Home" position or document.
*/
KRITAWIDGETUTILS_EXPORT QAction *home(const QObject *recvr, const char *slot, QObject *parent);
/**
* Scroll up one page.
*/
KRITAWIDGETUTILS_EXPORT QAction *prior(const QObject *recvr, const char *slot, QObject *parent);
/**
* Scroll down one page.
*/
KRITAWIDGETUTILS_EXPORT QAction *next(const QObject *recvr, const char *slot, QObject *parent);
/**
* Go to somewhere in general.
*/
KRITAWIDGETUTILS_EXPORT QAction *goTo(const QObject *recvr, const char *slot, QObject *parent);
/**
* Go to a specific page (dialog).
*/
KRITAWIDGETUTILS_EXPORT QAction *gotoPage(const QObject *recvr, const char *slot, QObject *parent);
/**
* Go to a specific line (dialog).
*/
KRITAWIDGETUTILS_EXPORT QAction *gotoLine(const QObject *recvr, const char *slot, QObject *parent);
/**
* Jump to the first page.
*/
KRITAWIDGETUTILS_EXPORT QAction *firstPage(const QObject *recvr, const char *slot, QObject *parent);
/**
* Jump to the last page.
*/
KRITAWIDGETUTILS_EXPORT QAction *lastPage(const QObject *recvr, const char *slot, QObject *parent);
/**
* Move back (document style menu).
*/
KRITAWIDGETUTILS_EXPORT QAction *documentBack(const QObject *recvr, const char *slot, QObject *parent);
/**
* Move forward (document style menu).
*/
KRITAWIDGETUTILS_EXPORT QAction *documentForward(const QObject *recvr, const char *slot, QObject *parent);
/**
* Add the current page to the bookmarks tree.
*/
KRITAWIDGETUTILS_EXPORT QAction *addBookmark(const QObject *recvr, const char *slot, QObject *parent);
/**
* Edit the application bookmarks.
*/
KRITAWIDGETUTILS_EXPORT QAction *editBookmarks(const QObject *recvr, const char *slot, QObject *parent);
/**
* Pop up the spell checker.
*/
KRITAWIDGETUTILS_EXPORT QAction *spelling(const QObject *recvr, const char *slot, QObject *parent);
/**
* Show/Hide the menubar.
*/
KRITAWIDGETUTILS_EXPORT KToggleAction *showMenubar(const QObject *recvr, const char *slot, QObject *parent);
/**
* Show/Hide the statusbar.
*/
KRITAWIDGETUTILS_EXPORT KToggleAction *showStatusbar(const QObject *recvr, const char *slot, QObject *parent);
/**
* Switch to/from full screen mode
*/
KRITAWIDGETUTILS_EXPORT KToggleFullScreenAction *fullScreen(const QObject *recvr, const char *slot, QWidget *window, QObject *parent);
/**
* Display the save options dialog.
*/
KRITAWIDGETUTILS_EXPORT QAction *saveOptions(const QObject *recvr, const char *slot, QObject *parent);
/**
* Display the configure key bindings dialog.
*
* Note that you might be able to use the pre-built KXMLGUIFactory's function:
* KStandardAction::keyBindings(guiFactory(), SLOT(configureShortcuts()), actionCollection());
*/
KRITAWIDGETUTILS_EXPORT QAction *keyBindings(const QObject *recvr, const char *slot, QObject *parent);
/**
* Display the preferences/options dialog.
*/
KRITAWIDGETUTILS_EXPORT QAction *preferences(const QObject *recvr, const char *slot, QObject *parent);
/**
* The Customize Toolbar dialog.
*/
KRITAWIDGETUTILS_EXPORT QAction *configureToolbars(const QObject *recvr, const char *slot, QObject *parent);
/**
* The Configure Notifications dialog.
*/
KRITAWIDGETUTILS_EXPORT QAction *configureNotifications(const QObject *recvr, const char *slot, QObject *parent);
/**
* Display the help.
*/
KRITAWIDGETUTILS_EXPORT QAction *help(const QObject *recvr, const char *slot, QObject *parent);
/**
* Display the help contents.
*/
KRITAWIDGETUTILS_EXPORT QAction *helpContents(const QObject *recvr, const char *slot, QObject *parent);
/**
* Trigger the What's This cursor.
*/
KRITAWIDGETUTILS_EXPORT QAction *whatsThis(const QObject *recvr, const char *slot, QObject *parent);
/**
* Display "Tip of the Day"
*/
KRITAWIDGETUTILS_EXPORT QAction *tipOfDay(const QObject *recvr, const char *slot, QObject *parent);
/**
* Open up the Report Bug dialog.
*/
KRITAWIDGETUTILS_EXPORT QAction *reportBug(const QObject *recvr, const char *slot, QObject *parent);
/**
* Display the application's About box.
*/
KRITAWIDGETUTILS_EXPORT QAction *aboutApp(const QObject *recvr, const char *slot, QObject *parent);
/**
* Display the About KDE dialog.
*/
KRITAWIDGETUTILS_EXPORT QAction *aboutKDE(const QObject *recvr, const char *slot, QObject *parent);
}
#endif // KSTDACTION_H
diff --git a/libs/widgetutils/kis_action_registry.cpp b/libs/widgetutils/kis_action_registry.cpp
index 242b3afa0c..a8a7f3def1 100644
--- a/libs/widgetutils/kis_action_registry.cpp
+++ b/libs/widgetutils/kis_action_registry.cpp
@@ -1,417 +1,417 @@
/*
* Copyright (c) 2015 Michael Abrahams <miabraha@gmail.com>
*
* 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 3 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 <QString>
#include <QHash>
#include <QGlobalStatic>
#include <QFile>
#include <QDomElement>
#include <KSharedConfig>
#include <klocalizedstring.h>
#include <KisShortcutsDialog.h>
#include <KConfigGroup>
#include "kis_debug.h"
#include "KoResourcePaths.h"
#include "kis_icon_utils.h"
#include "kis_action_registry.h"
#include "kshortcutschemeshelper_p.h"
namespace {
/**
* We associate several pieces of information with each shortcut. The first
* piece of information is a QDomElement, containing the raw data from the
* .action XML file. The second and third are QKeySequences, the first of
* which is the default shortcut, the last of which is any custom shortcut.
* The last two are the KActionCollection and KActionCategory used to
* organize the shortcut editor.
*/
struct ActionInfoItem {
QDomElement xmlData;
QString collectionName;
QString categoryName;
inline QList<QKeySequence> defaultShortcuts() const {
return m_defaultShortcuts;
}
inline void setDefaultShortcuts(const QList<QKeySequence> &value) {
m_defaultShortcuts = value;
}
inline QList<QKeySequence> customShortcuts() const {
return m_customShortcuts;
}
inline void setCustomShortcuts(const QList<QKeySequence> &value, bool explicitlyReset) {
m_customShortcuts = value;
m_explicitlyReset = explicitlyReset;
}
inline QList<QKeySequence> effectiveShortcuts() const {
return m_customShortcuts.isEmpty() && !m_explicitlyReset ?
m_defaultShortcuts : m_customShortcuts;
}
private:
QList<QKeySequence> m_defaultShortcuts;
QList<QKeySequence> m_customShortcuts;
bool m_explicitlyReset = false;
};
// Convenience macros to extract text of a child node.
QString getChildContent(QDomElement xml, QString node) {
return xml.firstChildElement(node).text();
}
// Use Krita debug logging categories instead of KDE's default qDebug() for
// harmless empty strings and translations
QString quietlyTranslate(const QString &s) {
if (s.isEmpty()) {
return s;
}
QString translatedString = i18nc("action", s.toUtf8());
if (translatedString == s) {
translatedString = i18n(s.toUtf8());
}
if (translatedString.isEmpty()) {
dbgAction << "No translation found for" << s;
return s;
}
return translatedString;
}
}
class Q_DECL_HIDDEN KisActionRegistry::Private
{
public:
Private(KisActionRegistry *_q) : q(_q) {}
// This is the main place containing ActionInfoItems.
QMap<QString, ActionInfoItem> actionInfoList;
void loadActionFiles();
void loadCustomShortcuts(QString filename = QStringLiteral("kritashortcutsrc"));
// XXX: this adds a default item for the given name to the list of actioninfo objects!
ActionInfoItem &actionInfo(const QString &name) {
if (!actionInfoList.contains(name)) {
dbgAction << "Tried to look up info for unknown action" << name;
}
return actionInfoList[name];
}
KisActionRegistry *q;
QSet<QString> sanityPropertizedShortcuts;
};
Q_GLOBAL_STATIC(KisActionRegistry, s_instance)
KisActionRegistry *KisActionRegistry::instance()
{
if (!s_instance.exists()) {
dbgRegistry << "initializing KoActionRegistry";
}
return s_instance;
}
bool KisActionRegistry::hasAction(const QString &name) const
{
return d->actionInfoList.contains(name);
}
KisActionRegistry::KisActionRegistry()
: d(new KisActionRegistry::Private(this))
{
KConfigGroup cg = KSharedConfig::openConfig()->group("Shortcut Schemes");
QString schemeName = cg.readEntry("Current Scheme", "Default");
loadShortcutScheme(schemeName);
loadCustomShortcuts();
}
KisActionRegistry::ActionCategory KisActionRegistry::fetchActionCategory(const QString &name) const
{
if (!d->actionInfoList.contains(name)) return ActionCategory();
const ActionInfoItem info = d->actionInfoList.value(name);
return ActionCategory(info.collectionName, info.categoryName);
}
void KisActionRegistry::notifySettingsUpdated()
{
d->loadCustomShortcuts();
}
void KisActionRegistry::loadCustomShortcuts()
{
d->loadCustomShortcuts();
}
void KisActionRegistry::loadShortcutScheme(const QString &schemeName)
{
// Load scheme file
if (schemeName != QStringLiteral("Default")) {
QString schemeFileName = KShortcutSchemesHelper::schemeFileLocations().value(schemeName);
if (schemeFileName.isEmpty()) {
return;
}
KConfig schemeConfig(schemeFileName, KConfig::SimpleConfig);
applyShortcutScheme(&schemeConfig);
} else {
// Apply default scheme, updating KisActionRegistry data
applyShortcutScheme();
}
}
QAction * KisActionRegistry::makeQAction(const QString &name, QObject *parent)
{
QAction * a = new QAction(parent);
if (!d->actionInfoList.contains(name)) {
qWarning() << "Warning: requested data for unknown action" << name;
a->setObjectName(name);
return a;
}
propertizeAction(name, a);
return a;
}
void KisActionRegistry::settingsPageSaved()
{
// For now, custom shortcuts are dealt with by writing to file and reloading.
loadCustomShortcuts();
// Announce UI should reload current shortcuts.
emit shortcutsUpdated();
}
void KisActionRegistry::applyShortcutScheme(const KConfigBase *config)
{
// First, update the things in KisActionRegistry
d->actionInfoList.clear();
d->loadActionFiles();
if (config == 0) {
// Use default shortcut scheme. Simplest just to reload everything.
loadCustomShortcuts();
} else {
const auto schemeEntries = config->group(QStringLiteral("Shortcuts")).entryMap();
// Load info item for each shortcut, reset custom shortcuts
auto it = schemeEntries.constBegin();
while (it != schemeEntries.end()) {
ActionInfoItem &info = d->actionInfo(it.key());
info.setDefaultShortcuts(QKeySequence::listFromString(it.value()));
it++;
}
}
}
void KisActionRegistry::updateShortcut(const QString &name, QAction *action)
{
const ActionInfoItem &info = d->actionInfo(name);
action->setShortcuts(info.effectiveShortcuts());
action->setProperty("defaultShortcuts", qVariantFromValue(info.defaultShortcuts()));
d->sanityPropertizedShortcuts.insert(name);
}
bool KisActionRegistry::sanityCheckPropertized(const QString &name)
{
return d->sanityPropertizedShortcuts.contains(name);
}
QList<QString> KisActionRegistry::registeredShortcutIds() const
{
return d->actionInfoList.keys();
}
bool KisActionRegistry::propertizeAction(const QString &name, QAction * a)
{
if (!d->actionInfoList.contains(name)) {
warnAction << "No XML data found for action" << name;
return false;
}
const ActionInfoItem info = d->actionInfo(name);
QDomElement actionXml = info.xmlData;
if (!actionXml.text().isEmpty()) {
// i18n requires converting format from QString.
auto getChildContent_i18n = [=](QString node){return quietlyTranslate(getChildContent(actionXml, node));};
// Note: the fields in the .action documents marked for translation are determined by extractrc.
QString icon = getChildContent(actionXml, "icon");
QString text = getChildContent_i18n("text");
QString whatsthis = getChildContent_i18n("whatsThis");
QString toolTip = getChildContent_i18n("toolTip");
QString statusTip = getChildContent_i18n("statusTip");
QString iconText = getChildContent_i18n("iconText");
bool isCheckable = getChildContent(actionXml, "isCheckable") == QString("true");
a->setObjectName(name); // This is helpful, should be added more places in Krita
a->setIcon(KisIconUtils::loadIcon(icon.toLatin1()));
a->setText(text);
a->setObjectName(name);
a->setWhatsThis(whatsthis);
a->setToolTip(toolTip);
a->setStatusTip(statusTip);
a->setIconText(iconText);
a->setCheckable(isCheckable);
}
updateShortcut(name, a);
return true;
}
QString KisActionRegistry::getActionProperty(const QString &name, const QString &property)
{
ActionInfoItem info = d->actionInfo(name);
QDomElement actionXml = info.xmlData;
if (actionXml.text().isEmpty()) {
dbgAction << "No XML data found for action" << name;
return QString();
}
return getChildContent(actionXml, property);
}
void KisActionRegistry::Private::loadActionFiles()
{
QStringList actionDefinitions =
KoResourcePaths::findAllResources("kis_actions", "*.action", KoResourcePaths::Recursive);
// Extract actions all XML .action files.
Q_FOREACH (const QString &actionDefinition, actionDefinitions) {
QDomDocument doc;
QFile f(actionDefinition);
f.open(QFile::ReadOnly);
doc.setContent(f.readAll());
QDomElement base = doc.documentElement(); // "ActionCollection" outer group
QString collectionName = base.attribute("name");
QString version = base.attribute("version");
if (version != "2") {
errAction << ".action XML file" << actionDefinition << "has incorrect version; skipping.";
continue;
}
// Loop over <Actions> nodes. Each of these corresponds to a
// KActionCategory, producing a group of actions in the shortcut dialog.
QDomElement actions = base.firstChild().toElement();
while (!actions.isNull()) {
// <text> field
QDomElement categoryTextNode = actions.firstChild().toElement();
QString categoryName = quietlyTranslate(categoryTextNode.text());
// <action></action> tags
QDomElement actionXml = categoryTextNode.nextSiblingElement();
// Loop over individual actions
while (!actionXml.isNull()) {
if (actionXml.tagName() == "Action") {
// Read name from format <Action name="save">
QString name = actionXml.attribute("name");
// Bad things
if (name.isEmpty()) {
errAction << "Unnamed action in definitions file " << actionDefinition;
}
else if (actionInfoList.contains(name)) {
- errAction << "NOT COOL: Duplicated action name from xml data: " << name;
+ qWarning() << "NOT COOL: Duplicated action name from xml data: " << name;
}
else {
ActionInfoItem info;
info.xmlData = actionXml;
// Use empty list to signify no shortcut
QString shortcutText = getChildContent(actionXml, "shortcut");
if (!shortcutText.isEmpty()) {
info.setDefaultShortcuts(QKeySequence::listFromString(shortcutText));
}
info.categoryName = categoryName;
info.collectionName = collectionName;
actionInfoList.insert(name,info);
}
}
actionXml = actionXml.nextSiblingElement();
}
actions = actions.nextSiblingElement();
}
}
}
void KisActionRegistry::Private::loadCustomShortcuts(QString filename)
{
const KConfigGroup localShortcuts(KSharedConfig::openConfig(filename),
QStringLiteral("Shortcuts"));
if (!localShortcuts.exists()) {
return;
}
// Distinguish between two "null" states for custom shortcuts.
for (auto i = actionInfoList.begin(); i != actionInfoList.end(); ++i) {
if (localShortcuts.hasKey(i.key())) {
QString entry = localShortcuts.readEntry(i.key(), QString());
if (entry == QStringLiteral("none")) {
i.value().setCustomShortcuts(QList<QKeySequence>(), true);
} else {
i.value().setCustomShortcuts(QKeySequence::listFromString(entry), false);
}
} else {
i.value().setCustomShortcuts(QList<QKeySequence>(), false);
}
}
}
KisActionRegistry::ActionCategory::ActionCategory()
{
}
KisActionRegistry::ActionCategory::ActionCategory(const QString &_componentName, const QString &_categoryName)
: componentName(_componentName),
categoryName(_categoryName),
m_isValid(true)
{
}
bool KisActionRegistry::ActionCategory::isValid() const
{
return m_isValid && !categoryName.isEmpty() && !componentName.isEmpty();
}
diff --git a/libs/widgetutils/xmlgui/KisShortcutsDialog.h b/libs/widgetutils/xmlgui/KisShortcutsDialog.h
index 8502616b11..391e0ac78b 100644
--- a/libs/widgetutils/xmlgui/KisShortcutsDialog.h
+++ b/libs/widgetutils/xmlgui/KisShortcutsDialog.h
@@ -1,178 +1,180 @@
/* This file is part of the KDE libraries
Copyright (C) 1997 Nicolas Hadacek <hadacek@kde.org>
Copyright (C) 2001 Ellis Whitehead <ellis@kde.org>
Copyright (C) 2006 Hamish Rodda <rodda@kde.org>
Copyright (C) 2007 Roberto Raggi <roberto@kdevelop.org>
Copyright (C) 2007 Andreas Hartmetz <ahartmetz@gmail.com>
Copyright (C) 2008 Michael Jansen <kde@michael-jansen.biz>
Copyright (C) 2015 Michael Abrahams <miabraha@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KISSHORTCUTSDIALOG_H
#define KISSHORTCUTSDIALOG_H
#include <QDialog>
#include <KisShortcutsEditor.h>
// Altering this class and some classes it uses was one major impetus behind
// forking XmlGui. The first major workaround was to allow
// KisPart::configureShortcuts() to pull up the dialog, and to remote the scheme
// editor support, since it's incompatible with Krita.
//
// The files were forked from KF5 XmlGui version 5.12.0
// dialogs/KisShortcutsEditorItem.cpp <- kshortcutseditoritem.cpp
// dialogs/KisShortcutEditWidget.cpp <- kshortcuteditwidget.cpp
// dialogs/KisShortcutsEditorDelegate.cpp <- kshortcutseditordelegate.cpp
// dialogs/KisShortcutsDialog.cpp <- kshortcutsdialog.cpp, , kshortcutsdialog_p.cpp
// dialogs/KisShortcutsDialog.h <- kshortcutsdialog.h
// dialogs/KisShortcutsDialog_p.h <- kshortcutsdialog_p.h, kshortcutseditor_p.h
// forms/KisShortcutsDialog.ui <- kshortcutsdialog.ui
//
//
// Changes that have been done to the files:
// * Adapt of includes
// * Removing unwanted parts related to schemes
// * Renamed KShortcutsDialog/Editor to KisShortcutsDialog/Editor
// * Add export macro
// * Split apart kshortcutseditor_p
// * Copied KShortcutsEditorPrivate::itemFromIndex() implementation from
// KF5 XmlGui's kshortcutseditor.cpp to begin of KisShortcutsEditorItem.cpp
/**
* @short Dialog for configuration of KActionCollection and KGlobalAccel.
*
* The KisShortcutsDialog class is used for configuring dictionaries of
* key/action associations for KActionCollection and KGlobalAccel. It uses the
* KShortcutsEditor widget and offers buttons to set all keys to defaults and
* invoke on-line help.
*
* Several static methods are supplied which provide the most convenient
* interface to the dialog. The most common and most encouraged use is with
* KActionCollection.
*
* \code
* KisShortcutsDialog::configure( actionCollection() );
* \endcode
*
* @since 4.3 By default this dialog is modal. If you don't want that,
* setModal(false) and then the non-static configure() will show the dialog. If
* you want to do anything extra when the dialog is done, connect to okClicked()
* and/or cancelClicked(). However, if your extra stuff depends on the changed
* settings already being saved, connect to saved() instead to be safe; if you
* connect to okClicked() your function might be called before the save happens.
*
* example:
* \code
* KisShortcutsDialog dlg;
* dlg.addCollection(myActions);
* dlg.setModal(false);
* connect(&dlg, SIGNAL(saved()), this, SLOT(doExtraStuff()));
* dlg.configure();
* \endcode
*
* \image html kshortcutsdialog.png "KDE Shortcuts Dialog"
*
* @author Nicolas Hadacek <hadacek@via.ecp.fr>
* @author Hamish Rodda <rodda@kde.org> (KDE 4 porting)
* @author Michael Jansen <kde@michael-jansen.biz>
*/
const auto defaultActionTypes = KisShortcutsEditor::WidgetAction \
| KisShortcutsEditor::WindowAction \
| KisShortcutsEditor::ApplicationAction;
class KRITAWIDGETUTILS_EXPORT KisShortcutsDialog : public QWidget
{
Q_OBJECT
public:
/**
* Constructs a KisShortcutsDialog. Mostly UI boilerplate.
*
+ * @param types the action types
* @param allowLetterShortcuts set to KisShortcutsEditor::LetterShortcutsDisallowed if unmodified alphanumeric
* keys ('A', '1', etc.) are not permissible shortcuts.
*
* @param parent the parent widget to attach to
*
* There is some legacy support for global (i.e. desktop-wide) shortucts
* that should probably be removed.
*/
explicit KisShortcutsDialog(KisShortcutsEditor::ActionTypes types = defaultActionTypes,
KisShortcutsEditor::LetterShortcuts allowLetterShortcuts \
= KisShortcutsEditor::LetterShortcutsAllowed,
QWidget *parent = 0);
~KisShortcutsDialog() override;
/**
* Add all actions of the collection to the ones displayed and configured
* by the dialog. This is where the business happens.
*
+ * @param collection the action collection.
* @param title the title associated with the collection.
*/
void addCollection(KActionCollection *, const QString &title = QString());
/**
* @return the list of action collections that are available for configuration in the dialog.
*/
QList<KActionCollection *> actionCollections() const;
/** @see QWidget::sizeHint() */
QSize sizeHint() const override;
/**
* Called when the "OK" button in the main configuration page is pressed.
*/
void save();
void allDefault();
void undo();
/**
* Import shortcut scheme file from @p path
*/
void importConfiguration(const QString &path);
/**
* Exports shortcut scheme file to @p path
*/
void exportConfiguration(const QString &path) const;
/**
* Import custom shortcuts from @p path
*/
void loadCustomShortcuts(const QString &path);
/**
* Exports custom shortcuts to @p path
*/
void saveCustomShortcuts(const QString &path) const;
private:
Q_PRIVATE_SLOT(d, void changeShortcutScheme(const QString &))
Q_PRIVATE_SLOT(d, void undo())
class KisShortcutsDialogPrivate;
class KisShortcutsDialogPrivate *const d;
Q_DISABLE_COPY(KisShortcutsDialog)
};
#endif // KISSHORTCUTSDIALOG_H
diff --git a/libs/widgetutils/xmlgui/KisShortcutsEditor.h b/libs/widgetutils/xmlgui/KisShortcutsEditor.h
index 1d96c0bae8..e7f11aa3c8 100644
--- a/libs/widgetutils/xmlgui/KisShortcutsEditor.h
+++ b/libs/widgetutils/xmlgui/KisShortcutsEditor.h
@@ -1,256 +1,257 @@
/* This file is part of the KDE libraries
Copyright (C) 1997 Nicolas Hadacek <hadacek@kde.org>
Copyright (C) 2001,2001 Ellis Whitehead <ellis@kde.org>
Copyright (C) 2006 Hamish Rodda <rodda@kde.org>
Copyright (C) 2007 Roberto Raggi <roberto@kdevelop.org>
Copyright (C) 2007 Andreas Hartmetz <ahartmetz@gmail.com>
Copyright (C) 2008 Michael Jansen <kde@michael-jansen.biz>
Copyright (c) 2015 Michael Abrahams <miabraha@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KISSHORTCUTSEDITOR_H
#define KISSHORTCUTSEDITOR_H
#include <kritawidgetutils_export.h>
#include <QWidget>
#include <KisKineticScroller.h>
class KActionCollection;
class KConfig;
class KConfigBase;
class KConfigGroup;
class KGlobalAccel;
class KisShortcutsEditorPrivate;
/**
* WARNING: KisShortcutsEditor expects that the list of existing shortcuts is
* already free of conflicts. If it is not, nothing will crash, but your users
* won't like the resulting behavior.
*
* TODO: Find the right place to check for conflicts.
*/
/**
* @short Widget for configuration of KAccel and KGlobalAccel.
*
* Configure dictionaries of key/action associations for KActions,
* including global shortcuts.
*
* The class takes care of all aspects of configuration, including
* handling key conflicts internally. Connect to the allDefault()
* slot if you want to set all configurable shortcuts to their
* default values.
*
* @see KShortcutsDialog
* @author Nicolas Hadacek <hadacek@via.ecp.fr>
* @author Hamish Rodda <rodda@kde.org> (KDE 4 porting)
* @author Michael Jansen <kde@michael-jansen.biz>
*/
class KRITAWIDGETUTILS_EXPORT KisShortcutsEditor : public QWidget
{
Q_OBJECT
Q_PROPERTY(ActionTypes actionTypes READ actionTypes WRITE setActionTypes)
public:
/*
* These attempt to build some sort of characterization of all actions. The
* idea is to determine which sorts of actions will be configured in the
* dialog.
*
* Enumerating all possible actions is a sorrowful, pitiable endeavor,
* useless for Krita. We should do something about this.
*/
enum ActionType {
/// Actions which are triggered by any keypress in a widget which has the action added to it
WidgetAction = Qt::WidgetShortcut /*0*/,
/// Actions which are triggered by any keypress in a window which has the action added to it or its child widget(s)
WindowAction = Qt::WindowShortcut /*1*/,
/// Actions which are triggered by any keypress in the application
ApplicationAction = Qt::ApplicationShortcut /*2*/,
/// Actions which are triggered by any keypress in the windowing system
GlobalAction = 4,
/// All actions
AllActions = 0xffffffff
};
Q_DECLARE_FLAGS(ActionTypes, ActionType)
enum LetterShortcuts {
/// Shortcuts without a modifier are not allowed, so 'A' would not be
/// valid, whereas 'Ctrl+A' would be. This only applies to printable
/// characters, however. 'F1', 'Insert' etc. could still be used.
LetterShortcutsDisallowed = 0,
/// Letter shortcuts are allowed
LetterShortcutsAllowed
};
/**
* \overload
*
* Creates a key chooser without a starting action collection.
*
* @param parent parent widget
* @param actionTypes types of actions to display in this widget.
* @param allowLetterShortcuts set to LetterShortcutsDisallowed if unmodified alphanumeric
* keys ('A', '1', etc.) are not permissible shortcuts.
*/
explicit KisShortcutsEditor(QWidget *parent,
ActionTypes actionTypes = AllActions, LetterShortcuts allowLetterShortcuts = LetterShortcutsAllowed);
/// Destructor
~KisShortcutsEditor() override;
/**
- * @ret true if there are unsaved changes.
+ * @return @c true if there are unsaved changes.
*/
bool isModified() const;
/**
* Removes all action collections from the editor
*/
void clearCollections();
/**
* Clears search area
*/
void clearSearch();
/**
* Note: the reason this is so damn complicated is because it's supposed to
* support having multiple applications running inside of each other through
* KisParts. That means we have to be able to separate sections within each
* configuration file.
*
* Insert an action collection, i.e. add all its actions to the ones
* already associated with the KisShortcutsEditor object.
*
+ * @param collection the action collection.
* @param title subtree title of this collection of shortcut.
*/
void addCollection(KActionCollection *, const QString &title = QString());
/**
* Undo all change made since the last commit().
*/
void undo();
/**
* Save the changes.
*
* Before saving the changes are committed. This saves the actions to disk.
* Any KActionCollection objects with the xmlFile() value set will be
* written to an XML file. All other will be written to the application's
* rc file.
*/
void save();
/**
* Update the dialog entries without saving.
*
* @since 4.2
*/
void commit();
/**
* Removes all configured shortcuts.
*/
void clearConfiguration();
/**
* Write the current custom shortcut settings to the \p config object.
*
* @param config Config object to save to. Default is kritashortcutsrc.
*
*/
void saveShortcuts(KConfigGroup *config = 0) const;
/**
* Write the current shortcuts to a new scheme to configuration file
*
* @param config Config object to save to.
*/
void exportConfiguration(KConfigBase *config) const;
/**
* Import a shortcut configuration file.
*
* @param config Config object to load from.
* @param isScheme true for shortcut scheme, false for custom shortcuts
*/
void importConfiguration(KConfigBase *config, bool isScheme);
/**
* Sets the types of actions to display in this widget.
*
* @param actionTypes New types of actions
* @since 5.0
*/
void setActionTypes(ActionTypes actionTypes);
/**
*
* @return The types of actions currently displayed in this widget.
* @since 5.0
*/
ActionTypes actionTypes() const;
Q_SIGNALS:
/**
* Emitted when an action's shortcut has been changed.
**/
void keyChange();
public Q_SLOTS:
/**
* Resize columns to width required
*/
void resizeColumns();
/**
* Set all shortcuts to their default values (bindings).
**/
void allDefault();
/**
* Opens a printing dialog to print all the shortcuts
*/
void printShortcuts() const;
/**
* Expand or collapse the tree view when the search text changes
*/
void searchUpdated(QString s);
void slotScrollerStateChanged(QScroller::State state){KisKineticScroller::updateCursor(this, state);}
private:
Q_PRIVATE_SLOT(d, void capturedShortcut(QVariant, const QModelIndex &))
private:
friend class KisShortcutsDialog;
friend class KisShortcutsEditorPrivate;
KisShortcutsEditorPrivate *const d;
Q_DISABLE_COPY(KisShortcutsEditor)
};
Q_DECLARE_OPERATORS_FOR_FLAGS(KisShortcutsEditor::ActionTypes)
#endif // KISSHORTCUTSEDITOR_H
diff --git a/libs/widgetutils/xmlgui/kactioncollection.h b/libs/widgetutils/xmlgui/kactioncollection.h
index 8985661090..962aab2531 100644
--- a/libs/widgetutils/xmlgui/kactioncollection.h
+++ b/libs/widgetutils/xmlgui/kactioncollection.h
@@ -1,514 +1,514 @@
/* This file is part of the KDE libraries
Copyright (C) 1999 Reginald Stadlbauer <reggie@kde.org>
(C) 1999 Simon Hausmann <hausmann@kde.org>
(C) 2000 Nicolas Hadacek <haadcek@kde.org>
(C) 2000 Kurt Granroth <granroth@kde.org>
(C) 2000 Michael Koch <koch@kde.org>
(C) 2001 Holger Freyther <freyther@kde.org>
(C) 2002 Ellis Whitehead <ellis@kde.org>
(C) 2005-2006 Hamish Rodda <rodda@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KACTIONCOLLECTION_H
#define KACTIONCOLLECTION_H
#include "config-xmlgui.h"
#include <kritawidgetutils_export.h>
#include <kstandardaction.h>
#include <QObject>
class QAction;
class KXMLGUIClient;
class KConfigGroup;
class QActionGroup;
class QString;
class KActionCategory;
/**
* \short A container for a set of QAction objects.
*
* KActionCollection manages a set of QAction objects. It
* allows them to be grouped for organized presentation of configuration to the user,
* saving + loading of configuration, and optionally for automatic plugging into
* specified widget(s).
*
* Additionally, KActionCollection provides several convenience functions for locating
* named actions, and actions grouped by QActionGroup.
*
* \note If you create your own action collection and need to assign shortcuts
* to the actions within, you have to call associateWidget() or
* addAssociatedWidget() to have them working.
*/
class KRITAWIDGETUTILS_EXPORT KActionCollection : public QObject
{
friend class KXMLGUIClient;
Q_OBJECT
Q_PROPERTY(QString configGroup READ configGroup WRITE setConfigGroup)
public:
/**
* Constructor. Allows specification of a component name other than the default
* application name, where needed (remember to call setComponentDisplayName() too).
*/
explicit KActionCollection(QObject *parent, const QString &cName = QString());
/**
* Destructor.
*/
~KActionCollection() override;
/**
* Access the list of all action collections in existence for this app
*/
static const QList<KActionCollection *> &allCollections();
/**
* Clears the entire action collection, deleting all actions.
*/
void clear();
/**
* Associate all actions in this collection to the given \a widget.
* Unlike addAssociatedWidget, this method only adds all current actions
* in the collection to the given widget. Any action added after this call
* will not be added to the given widget automatically.
* So this is just a shortcut for a foreach loop and a widget->addAction call.
*/
void associateWidget(QWidget *widget) const;
/**
* Associate all actions in this collection to the given \a widget, including any actions
* added after this association is made.
*
* This does not change the action's shortcut context, so if you need to have the actions only
* trigger when the widget has focus, you'll need to set the shortcut context on each action
* to Qt::WidgetShortcut (or better still, Qt::WidgetWithChildrenShortcut with Qt 4.4+)
*/
void addAssociatedWidget(QWidget *widget);
/**
* Remove an association between all actions in this collection and the given \a widget, i.e.
* remove those actions from the widget, and stop associating newly added actions as well.
*/
void removeAssociatedWidget(QWidget *widget);
/**
* Return a list of all associated widgets.
*/
QList<QWidget *> associatedWidgets() const;
/**
* Clear all associated widgets and remove the actions from those widgets.
*/
void clearAssociatedWidgets();
/**
* Returns the KConfig group with which settings will be loaded and saved.
*/
QString configGroup() const;
/**
* Sets \a group as the KConfig group with which settings will be loaded and saved.
*/
void setConfigGroup(const QString &group);
/**
* Read all key associations from @p config.
*
* If @p config is zero, read all key associations from the
* application's configuration file KSharedConfig::openConfig(),
* in the group set by setConfigGroup().
*/
void readSettings();
/**
* Update shortcuts from the KisActionRegistry.
*/
void updateShortcuts();
/**
* Write the current configurable key associations. If @a is nonzero, use
* that configuration group.
*
* Otherwise, the output file is determined as follows. If this action
* collection belongs to a KXMLGuiClient the setting are saved to the
* kxmlgui definition file. If not the settings are written to the
* applications config file.
*
* \note oneAction() and writeDefaults() have no meaning for the kxmlgui
* configuration file.
*
* \param config Config object to save to, or null (see above)
* \param writeScheme set to true if we export all settings as new shortcut scheme
* \param oneAction pass an action here if you just want to save the values for one action, eg.
* if you know that action is the only one which has changed.
*/
void writeSettings(KConfigGroup *config = 0, bool writeScheme = false, QAction *oneAction = 0) const;
/**
* Returns the number of actions in the collection.
*
* This is equivalent to actions().count().
*/
int count() const;
/**
* Returns whether the action collection is empty or not.
*/
bool isEmpty() const;
/**
* Return the QAction* at position "index" in the action collection.
*
* This is equivalent to actions().value(index);
*/
QAction *action(int index) const;
/**
* Get the action with the given \a name from the action collection.
*
* @param name Name of the QAction
* @return A pointer to the QAction in the collection which matches the parameters or
* null if nothing matches.
*/
QAction *action(const QString &name) const;
/**
* Returns the list of QActions which belong to this action collection.
*
* The list is guaranteed to be in the same order the action were put into
* the collection.
*/
QList<QAction *> actions() const;
/**
* Returns the list of QActions without an QAction::actionGroup() which belong to this action collection.
*/
const QList<QAction *> actionsWithoutGroup() const;
/**
* Returns the list of all QActionGroups associated with actions in this action collection.
*/
const QList<QActionGroup *> actionGroups() const;
/**
* Set the \a componentName associated with this action collection.
*
* \warning Don't call this method on a KActionCollection that contains
* actions. This is not supported.
*
- * \param componentData the name which is to be associated with this action collection,
+ * \param componentName the name which is to be associated with this action collection,
* or QString() to indicate the app name. This is used to load/save settings into XML files.
* KXmlGuiClient::setComponentName takes care of calling this.
*/
void setComponentName(const QString &componentName);
/** The component name with which this class is associated. */
QString componentName() const;
/**
* Set the component display name associated with this action collection.
* (e.g. for the toolbar editor)
* KXmlGuiClient::setComponentName takes care of calling this.
*/
void setComponentDisplayName(const QString &displayName);
/** The display name for the associated component. */
QString componentDisplayName() const;
/**
* The parent KXMLGUIClient, or null if not available.
*/
const KXMLGUIClient *parentGUIClient() const;
/**
* Returns the KActionCategories inside this collection
*/
QList<KActionCategory *> categories() const;
/**
* Gets a category with name @p name inside this collection.
*
* Creates a new category if one does not exist.
*/
KActionCategory *getCategory(const QString &categoryName);
Q_SIGNALS:
/**
* Indicates that \a action was inserted into this action collection.
*/
void inserted(QAction *action);
/**
* Indicates that \a action was removed from this action collection.
* @deprecated
*/
QT_MOC_COMPAT void removed(QAction *action);
/**
* Indicates that \a action was highlighted (hovered over).
* @deprecated Replaced by actionHovered(QAction* action);
*/
QT_MOC_COMPAT void actionHighlighted(QAction *action);
/**
* Indicates that \a action was hovered.
*/
void actionHovered(QAction *action);
/**
* Indicates that \a action was triggered
*/
void actionTriggered(QAction *action);
protected:
/// Overridden to perform connections when someone wants to know whether an action was highlighted or triggered
void connectNotify(const QMetaMethod &signal) override;
protected Q_SLOTS:
virtual void slotActionTriggered();
/**
* @internal
* @deprecated Replaced by slotActionHovered();
*/
QT_MOC_COMPAT virtual void slotActionHighlighted();
private Q_SLOTS:
void slotActionHovered();
public:
/**
* Add an action under the given name to the collection.
*
* Inserting an action that was previously inserted under a different name will replace the
* old entry, i.e. the action will not be available under the old name anymore but only under
* the new one.
*
* Inserting an action under a name that is already used for another action will replace
* the other action in the collection (but will not delete it).
*
* If KAuthorized::authorizeKAction() reports that the action is not
* authorized, it will be disabled and hidden.
*
* @param name The name by which the action be retrieved again from the collection.
* @param action The action to add.
* @return the same as the action given as parameter. This is just for convenience
* (chaining calls) and consistency with the other addAction methods, you can also
* simply ignore the return value.
*/
Q_INVOKABLE QAction *addAction(const QString &name, QAction *action);
/**
* Adds a new action to the collection in category @p category.
*
* The category will be created if it does not already exist.
*/
Q_INVOKABLE QAction *addCategorizedAction(const QString &name, QAction *action, const QString &categoryName);
/**
* Adds a list of actions to the collection.
*
* The objectName of the actions is used as their internal name in the collection.
*
* Uses addAction(QString, QAction*).
*
* @param actions the list of the actions to add.
*
* @see addAction()
* @since 5.0
*/
void addActions(const QList<QAction *> &actions);
/**
* Removes an action from the collection and deletes it.
* @param action The action to remove.
*/
void removeAction(QAction *action);
/**
* Removes an action from the collection.
* @param action the action to remove.
*/
QAction *takeAction(QAction *action);
/**
* Creates a new standard action, adds it to the collection and connects the
* action's triggered(bool) signal to the specified receiver/member. The
* newly created action is also returned.
*
* Note: Using KStandardAction::OpenRecent will cause a different signal than
* triggered(bool) to be used, see KStandardAction for more information.
*
* The action can be retrieved later from the collection by its standard name as per
* KStandardAction::stdName.
*
* @param actionType The standard action type of the action to create.
* @param receiver The QObject to connect the triggered(bool) signal to. Leave 0 if no
* connection is desired.
* @param member The SLOT to connect the triggered(bool) signal to. Leave 0 if no
* connection is desired.
* @return new action of the given type ActionType.
*/
QAction *addAction(KStandardAction::StandardAction actionType, const QObject *receiver = 0, const char *member = 0);
/**
* Creates a new standard action, adds to the collection under the given name
* and connects the action's triggered(bool) signal to the specified
* receiver/member. The newly created action is also returned.
*
* Note: Using KStandardAction::OpenRecent will cause a different signal than
* triggered(bool) to be used, see KStandardAction for more information.
*
* The action can be retrieved later from the collection by the specified name.
*
* @param actionType The standard action type of the action to create.
* @param name The name by which the action be retrieved again from the collection.
* @param receiver The QObject to connect the triggered(bool) signal to. Leave 0 if no
* connection is desired.
* @param member The SLOT to connect the triggered(bool) signal to. Leave 0 if no
* connection is desired.
* @return new action of the given type ActionType.
*/
QAction *addAction(KStandardAction::StandardAction actionType, const QString &name,
const QObject *receiver = 0, const char *member = 0);
/**
* Creates a new action under the given name to the collection and connects
* the action's triggered(bool) signal to the specified receiver/member. The
* newly created action is returned.
*
* NOTE: KDE prior to 4.2 used the triggered() signal instead of the triggered(bool)
* signal.
*
* Inserting an action that was previously inserted under a different name will replace the
* old entry, i.e. the action will not be available under the old name anymore but only under
* the new one.
*
* Inserting an action under a name that is already used for another action will replace
* the other action in the collection.
*
* @param name The name by which the action be retrieved again from the collection.
* @param receiver The QObject to connect the triggered(bool) signal to. Leave 0 if no
* connection is desired.
* @param member The SLOT to connect the triggered(bool) signal to. Leave 0 if no
* connection is desired.
* @return new action of the given type ActionType.
*/
QAction *addAction(const QString &name, const QObject *receiver = 0, const char *member = 0);
/**
* Creates a new action under the given name, adds it to the collection and connects the action's triggered(bool)
* signal to the specified receiver/member. The receiver slot may accept either a bool or no
* parameters at all (i.e. slotTriggered(bool) or slotTriggered() ).
* The type of the action is specified by the template parameter ActionType.
*
* NOTE: KDE prior to 4.2 connected the triggered() signal instead of the triggered(bool)
* signal.
*
* @param name The internal name of the action (e.g. "file-open").
* @param receiver The QObject to connect the triggered(bool) signal to. Leave 0 if no
* connection is desired.
* @param member The SLOT to connect the triggered(bool) signal to. Leave 0 if no
* connection is desired.
* @return new action of the given type ActionType.
*
* @see addAction()
*/
template<class ActionType>
ActionType *add(const QString &name, const QObject *receiver = 0, const char *member = 0)
{
ActionType *a = new ActionType(this);
if (receiver && member) {
connect(a, SIGNAL(triggered(bool)), receiver, member);
}
addAction(name, a);
return a;
}
/**
* Get the default primary shortcut for the given action.
*
* @param action the action for which the default primary shortcut should be returned.
* @return the default primary shortcut of the given action
* @since 5.0
*/
QKeySequence defaultShortcut(QAction *action) const;
/**
* Get the default shortcuts for the given action.
*
* @param action the action for which the default shortcuts should be returned.
* @return the default shortcuts of the given action
* @since 5.0
*/
QList<QKeySequence> defaultShortcuts(QAction *action) const;
//TODO KF6: Make setDefaultShortcut static
/**
* Set the default shortcut for the given action.
* Since 5.2, this also calls action->setShortcut(shortcut), i.e. the default shortcut is
* made active initially.
*
* @param action the action for which the default shortcut should be set.
* @param shortcut the shortcut to use for the given action in its specified shortcutContext()
* @since 5.0
*/
void setDefaultShortcut(QAction *action, const QKeySequence &shortcut);
/**
* Set the default shortcuts for the given action.
* Since 5.2, this also calls action->setShortcuts(shortcuts), i.e. the default shortcut is
* made active initially.
*
* @param action the action for which the default shortcut should be set.
* @param shortcuts the shortcuts to use for the given action in its specified shortcutContext()
* @since 5.0
*/
Q_INVOKABLE void setDefaultShortcuts(QAction *action, const QList<QKeySequence> &shortcuts);
/**
* Returns true if the given action's shortcuts may be configured by the user.
*
* @param action the action for the hint should be verified.
* @since 5.0
*/
bool isShortcutsConfigurable(QAction *action) const;
/**
* Indicate whether the user may configure the action's shortcuts.
*
* @param action the action for the hint should be verified.
* @param configurable set to true if the shortcuts of the given action may be configured by the user, otherwise false.
* @since 5.0
*/
void setShortcutsConfigurable(QAction *action, bool configurable);
private:
Q_PRIVATE_SLOT(d, void _k_actionDestroyed(QObject *))
Q_PRIVATE_SLOT(d, void _k_associatedWidgetDestroyed(QObject *))
KActionCollection(const KXMLGUIClient *parent); // used by KXMLGUIClient
friend class KActionCollectionPrivate;
class KActionCollectionPrivate *const d;
};
#endif
diff --git a/libs/widgetutils/xmlgui/kactionconflictdetector.cpp b/libs/widgetutils/xmlgui/kactionconflictdetector.cpp
deleted file mode 100644
index 60334c9db6..0000000000
--- a/libs/widgetutils/xmlgui/kactionconflictdetector.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/* This file is part of the KDE libraries
- Copyright (C) 1999 Reginald Stadlbauer <reggie@kde.org>
- (C) 1999 Simon Hausmann <hausmann@kde.org>
- (C) 2000 Nicolas Hadacek <haadcek@kde.org>
- (C) 2000 Kurt Granroth <granroth@kde.org>
- (C) 2000 Michael Koch <koch@kde.org>
- (C) 2001 Holger Freyther <freyther@kde.org>
- (C) 2002 Ellis Whitehead <ellis@kde.org>
- (C) 2002 Joseph Wenninger <jowenn@kde.org>
- (C) 2005-2006 Hamish Rodda <rodda@kde.org>
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License version 2 as published by the Free Software Foundation.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public License
- along with this library; see the file COPYING.LIB. If not, write to
- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA.
-*/
-
-#include <QAction>
-#include <QCoreApplication>
-#include <QShortcutEvent>
-
-#include <klocalizedstring.h>
-#include <kmessagebox.h>
-
-class KActionConflictDetector : public QObject
-{
-public:
- explicit KActionConflictDetector(QObject *parent = 0)
- : QObject(parent)
- {
- }
-
- bool eventFilter(QObject *watched, QEvent *event) override
- {
- if (qobject_cast<QAction *>(watched) && (event->type() == QEvent::Shortcut)) {
- QShortcutEvent *se = static_cast<QShortcutEvent *>(event);
- if (se->isAmbiguous()) {
- KMessageBox::information(
- 0, // No widget to be seen around here
- i18n("XXXX The key sequence '%1' is ambiguous. Use the 'Keyboard Shortcuts'\n"
- "tab in 'Settings->Configure Krita...' dialog to solve the ambiguity.\n"
- "No action will be triggered. %2",
- se->key().toString(QKeySequence::NativeText), se->shortcutId()),
- i18n("Ambiguous shortcut detected"));
- return true;
- }
- }
-
- return QObject::eventFilter(watched, event);
- }
-};
-
-void _k_installConflictDetector()
-{
- QCoreApplication *app = QCoreApplication::instance();
- app->installEventFilter(new KActionConflictDetector(app));
-}
-
-Q_COREAPP_STARTUP_FUNCTION(_k_installConflictDetector)
diff --git a/libs/widgetutils/xmlgui/kcheckaccelerators.cpp b/libs/widgetutils/xmlgui/kcheckaccelerators.cpp
index 596af2f4ba..49cbae1f1e 100644
--- a/libs/widgetutils/xmlgui/kcheckaccelerators.cpp
+++ b/libs/widgetutils/xmlgui/kcheckaccelerators.cpp
@@ -1,317 +1,317 @@
/* This file is part of the KDE libraries
Copyright (C) 1997 Matthias Kalle Dalheimer (kalle@kde.org)
Copyright (C) 1998, 1999, 2000 KDE Team
Copyright (C) 2008 Nick Shaforostoff <shaforostoff@kde.ru>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kcheckaccelerators.h"
#include <QApplication>
#include <QCheckBox>
#include <QDialog>
#include <QShortcutEvent>
#include <QMouseEvent>
#include <QLayout>
#include <QMenuBar>
#include <QPushButton>
#include <QTabBar>
#include <QTextBrowser>
#include <QChar>
#include <QLabel>
#include <QComboBox>
#include <QGroupBox>
#include <QClipboard>
#include <QProcess>
#include <QDialogButtonBox>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <ksharedconfig.h>
#include <klocalizedstring.h>
#include <kacceleratormanager.h>
class KCheckAcceleratorsInitializer : public QObject
{
Q_OBJECT
public:
explicit KCheckAcceleratorsInitializer(QObject *parent = Q_NULLPTR)
: QObject(parent)
{
}
public Q_SLOTS:
void initiateIfNeeded()
{
KConfigGroup cg(KSharedConfig::openConfig(), "Development");
QString sKey = cg.readEntry("CheckAccelerators").trimmed();
int key = 0;
if (!sKey.isEmpty()) {
QList<QKeySequence> cuts = QKeySequence::listFromString(sKey);
if (!cuts.isEmpty()) {
key = cuts.first()[0];
}
}
const bool autoCheck = cg.readEntry("AutoCheckAccelerators", true);
const bool copyWidgetText = cg.readEntry("CopyWidgetText", false);
if (!copyWidgetText && key == 0 && !autoCheck) {
deleteLater();
return;
}
new KCheckAccelerators(qApp, key, autoCheck, copyWidgetText);
deleteLater();
}
};
static void startupFunc()
{
// Call initiateIfNeeded once we're in the event loop
// This is to prevent using KSharedConfig before main() can set the app name
QCoreApplication *app = QCoreApplication::instance();
KCheckAcceleratorsInitializer *initializer = new KCheckAcceleratorsInitializer(app);
QMetaObject::invokeMethod(initializer, "initiateIfNeeded", Qt::QueuedConnection);
}
Q_COREAPP_STARTUP_FUNCTION(startupFunc)
KCheckAccelerators::KCheckAccelerators(QObject *parent, int key_, bool autoCheck_, bool copyWidgetText_)
: QObject(parent)
, key(key_)
, block(false)
, autoCheck(autoCheck_)
, copyWidgetText(copyWidgetText_)
, drklash(0)
{
setObjectName(QStringLiteral("kapp_accel_filter"));
KConfigGroup cg(KSharedConfig::openConfig(), "Development");
alwaysShow = cg.readEntry("AlwaysShowCheckAccelerators", false);
copyWidgetTextCommand = cg.readEntry("CopyWidgetTextCommand", QString());
parent->installEventFilter(this);
connect(&autoCheckTimer, SIGNAL(timeout()), SLOT(autoCheckSlot()));
}
bool KCheckAccelerators::eventFilter(QObject *obj, QEvent *e)
{
if (block) {
return false;
}
switch (e->type()) { // just simplify debuggin
case QEvent::ShortcutOverride:
if (key && (static_cast<QKeyEvent *>(e)->key() == key)) {
block = true;
checkAccelerators(false);
block = false;
e->accept();
return true;
}
break;
case QEvent::ChildAdded:
case QEvent::ChildRemoved:
// Only care about widgets; this also avoids starting the timer in other threads
if (!static_cast<QChildEvent *>(e)->child()->isWidgetType()) {
break;
}
- // fall-through
+ Q_FALLTHROUGH();
case QEvent::Resize:
case QEvent::LayoutRequest:
case QEvent::WindowActivate:
case QEvent::WindowDeactivate:
if (autoCheck) {
autoCheckTimer.setSingleShot(true);
autoCheckTimer.start(20); // 20 ms
}
break;
//case QEvent::MouseButtonDblClick:
case QEvent::MouseButtonPress:
if (copyWidgetText && static_cast<QMouseEvent *>(e)->button() == Qt::MidButton) {
//kWarning()<<"obj"<<obj;
QWidget *w = static_cast<QWidget *>(obj)->childAt(static_cast<QMouseEvent *>(e)->pos());
if (!w) {
w = static_cast<QWidget *>(obj);
}
if (!w) {
return false;
}
//kWarning()<<"MouseButtonDblClick"<<w;
QString text;
if (qobject_cast<QLabel *>(w)) {
text = static_cast<QLabel *>(w)->text();
} else if (qobject_cast<QAbstractButton *>(w)) {
text = static_cast<QAbstractButton *>(w)->text();
} else if (qobject_cast<QComboBox *>(w)) {
text = static_cast<QComboBox *>(w)->currentText();
} else if (qobject_cast<QTabBar *>(w)) {
text = static_cast<QTabBar *>(w)->tabText(static_cast<QTabBar *>(w)->tabAt(static_cast<QMouseEvent *>(e)->pos()));
} else if (qobject_cast<QGroupBox *>(w)) {
text = static_cast<QGroupBox *>(w)->title();
} else if (qobject_cast<QMenu *>(obj)) {
QAction *a = static_cast<QMenu *>(obj)->actionAt(static_cast<QMouseEvent *>(e)->pos());
if (!a) {
return false;
}
text = a->text();
if (text.isEmpty()) {
text = a->iconText();
}
}
if (text.isEmpty()) {
return false;
}
if (static_cast<QMouseEvent *>(e)->modifiers() == Qt::ControlModifier) {
text.remove(QChar::fromLatin1('&'));
}
//kWarning()<<KGlobal::activeComponent().catalogName()<<text;
if (copyWidgetTextCommand.isEmpty()) {
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(text);
} else {
QProcess *script = new QProcess(this);
script->start(copyWidgetTextCommand.arg(text).arg(QFile::decodeName(KLocalizedString::applicationDomain())));
connect(script, SIGNAL(finished(int,QProcess::ExitStatus)), script, SLOT(deleteLater()));
}
e->accept();
return true;
//kWarning()<<"MouseButtonDblClick"<<static_cast<QWidget*>(obj)->childAt(static_cast<QMouseEvent*>(e)->globalPos());
}
return false;
case QEvent::Timer:
case QEvent::MouseMove:
case QEvent::Paint:
return false;
default:
// qDebug() << "KCheckAccelerators::eventFilter " << e->type() << " " << autoCheck;
break;
}
return false;
}
void KCheckAccelerators::autoCheckSlot()
{
if (block) {
autoCheckTimer.setSingleShot(true);
autoCheckTimer.start(20);
return;
}
block = true;
checkAccelerators(!alwaysShow);
block = false;
}
void KCheckAccelerators::createDialog(QWidget *actWin, bool automatic)
{
if (drklash) {
return;
}
drklash = new QDialog(actWin);
drklash->setAttribute(Qt::WA_DeleteOnClose);
drklash->setObjectName(QStringLiteral("kapp_accel_check_dlg"));
drklash->setWindowTitle(i18nc("@title:window", "Dr. Klash' Accelerator Diagnosis"));
drklash->resize(500, 460);
QVBoxLayout *layout = new QVBoxLayout(drklash);
drklash_view = new QTextBrowser(drklash);
layout->addWidget(drklash_view);
QCheckBox *disableAutoCheck = 0;
if (automatic) {
disableAutoCheck = new QCheckBox(i18nc("@option:check", "Disable automatic checking"), drklash);
connect(disableAutoCheck, SIGNAL(toggled(bool)), SLOT(slotDisableCheck(bool)));
layout->addWidget(disableAutoCheck);
}
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, drklash);
layout->addWidget(buttonBox);
connect(buttonBox, SIGNAL(rejected()), drklash, SLOT(close()));
if (disableAutoCheck) {
disableAutoCheck->setFocus();
} else {
drklash_view->setFocus();
}
}
void KCheckAccelerators::slotDisableCheck(bool on)
{
autoCheck = !on;
if (!on) {
autoCheckSlot();
}
}
void KCheckAccelerators::checkAccelerators(bool automatic)
{
QWidget *actWin = qApp->activeWindow();
if (!actWin) {
return;
}
KAcceleratorManager::manage(actWin);
QString a, c, r;
KAcceleratorManager::last_manage(a, c, r);
if (automatic) { // for now we only show dialogs on F12 checks
return;
}
if (c.isEmpty() && r.isEmpty() && (automatic || a.isEmpty())) {
return;
}
QString s;
if (! c.isEmpty()) {
s += i18n("<h2>Accelerators changed</h2>");
s += QStringLiteral("<table border><tr><th><b>");
s += i18n("Old Text");
s += QStringLiteral("</b></th><th><b>");
s += i18n("New Text");
s += QStringLiteral("</b></th></tr>");
s += c;
s += QStringLiteral("</table>");
}
if (! r.isEmpty()) {
s += i18n("<h2>Accelerators removed</h2>");
s += QStringLiteral("<table border><tr><th><b>");
s += i18n("Old Text");
s += QStringLiteral("</b></th></tr>");
s += r;
s += QStringLiteral("</table>");
}
if (! a.isEmpty()) {
s += i18n("<h2>Accelerators added (just for your info)</h2>");
s += QStringLiteral("<table border><tr><th><b>");
s += i18n("New Text");
s += QStringLiteral("</b></th></tr>");
s += a;
s += QStringLiteral("</table>");
}
createDialog(actWin, automatic);
drklash_view->setHtml(s);
drklash->show();
drklash->raise();
// dlg will be destroyed before returning
}
#include "kcheckaccelerators.moc"
diff --git a/libs/widgetutils/xmlgui/kedittoolbar.cpp b/libs/widgetutils/xmlgui/kedittoolbar.cpp
index 5999b586f8..0d68ccf6b9 100644
--- a/libs/widgetutils/xmlgui/kedittoolbar.cpp
+++ b/libs/widgetutils/xmlgui/kedittoolbar.cpp
@@ -1,1624 +1,1623 @@
/* This file is part of the KDE libraries
Copyright (C) 2000 Kurt Granroth <granroth@kde.org>
Copyright (C) 2006 Hamish Rodda <rodda@kde.org>
Copyright 2007 David Faure <faure@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kedittoolbar.h"
#include "kedittoolbar_p.h"
#include "config-xmlgui.h"
#include <QShowEvent>
#include <QAction>
#include <QDialogButtonBox>
#include <QDomDocument>
#include <QLayout>
#include <QDir>
#include <QFile>
#include <QHeaderView>
#include <QToolButton>
#include <QLabel>
#include <QApplication>
#include <QGridLayout>
#include <QCheckBox>
#include <QMimeData>
#include <QPushButton>
#include <QStandardPaths>
#include <QComboBox>
#include <QLineEdit>
#include <QDebug>
#ifdef HAVE_ICONTHEMES
#include <kicondialog.h>
#endif
#include <klistwidgetsearchline.h>
#include <klocalizedstring.h>
#include <kmessagebox.h>
#include <kseparator.h>
#include <kconfig.h>
#include "kactioncollection.h"
#include "kxmlguifactory.h"
#include "ktoolbar.h"
#include <kis_icon_utils.h>
#include "kis_action_registry.h"
static const char separatorstring[] = I18N_NOOP("--- separator ---");
#define SEPARATORSTRING i18n(separatorstring)
//static const char *const s_XmlTypeToString[] = { "Shell", "Part", "Local", "Merged" };
typedef QList<QDomElement> ToolBarList;
namespace KDEPrivate
{
/**
* Return a list of toolbar elements given a toplevel element
*/
static ToolBarList findToolBars(const QDomElement &start)
{
ToolBarList list;
for (QDomElement elem = start; !elem.isNull(); elem = elem.nextSiblingElement()) {
if (elem.tagName() == QStringLiteral("ToolBar")) {
if (elem.attribute(QStringLiteral("noEdit")) != QLatin1String("true")) {
list.append(elem);
}
} else {
if (elem.tagName() != QStringLiteral("MenuBar")) { // there are no toolbars inside the menubar :)
list += findToolBars(elem.firstChildElement()); // recursive
}
}
}
return list;
}
class XmlData
{
public:
enum XmlType { Shell = 0, Part, Local, Merged };
explicit XmlData(XmlType xmlType, const QString &xmlFile, KActionCollection *collection)
: m_isModified(false)
, m_xmlFile(xmlFile)
, m_type(xmlType)
, m_actionCollection(collection)
{
}
~XmlData()
{
}
void dump() const
{
#if 0
qDebug() << "XmlData" << this << "xmlFile:" << m_xmlFile;
foreach (const QDomElement &element, m_barList) {
qDebug() << " ToolBar:" << toolBarText(element);
}
//KisActionRegistry::instance()->
if (m_actionCollection) {
qDebug() << " " << m_actionCollection->actions().count() << "actions in the collection.";
} else {
qDebug() << " no action collection.";
}
#endif
}
QString xmlFile() const
{
return m_xmlFile;
}
XmlType type() const
{
return m_type;
}
KActionCollection *actionCollection() const
{
return m_actionCollection;
}
void setDomDocument(const QDomDocument &domDoc)
{
m_document = domDoc.cloneNode().toDocument();
m_barList = findToolBars(m_document.documentElement());
}
// Return reference, for e.g. actionPropertiesElement() to modify the document
QDomDocument &domDocument()
{
return m_document;
}
const QDomDocument &domDocument() const
{
return m_document;
}
/**
* Return the text (user-visible name) of a given toolbar
*/
QString toolBarText(const QDomElement &it) const;
bool m_isModified;
ToolBarList &barList()
{
return m_barList;
}
const ToolBarList &barList() const
{
return m_barList;
}
private:
ToolBarList m_barList;
QString m_xmlFile;
QDomDocument m_document;
XmlType m_type;
KActionCollection *m_actionCollection {0};
};
QString XmlData::toolBarText(const QDomElement &it) const
{
const QLatin1String attrName("name");
QString name;
QByteArray txt(it.namedItem(QStringLiteral("text")).toElement().text().toUtf8());
if (txt.isEmpty()) {
txt = it.namedItem(QStringLiteral("text")).toElement().text().toUtf8();
}
if (txt.isEmpty()) {
name = it.attribute(attrName);
} else {
QByteArray domain = it.namedItem(QStringLiteral("text")).toElement().attribute(QStringLiteral("translationDomain")).toUtf8();
if (domain.isEmpty()) {
domain = it.ownerDocument().documentElement().attribute(QStringLiteral("translationDomain")).toUtf8();
if (domain.isEmpty()) {
domain = KLocalizedString::applicationDomain();
}
}
name = i18nd(domain.constData(), txt.constData());
}
// the name of the toolbar might depend on whether or not
// it is in kparts
if ((m_type == XmlData::Shell) ||
(m_type == XmlData::Part)) {
QString doc_name(m_document.documentElement().attribute(attrName));
name += QStringLiteral(" <") + doc_name + QLatin1Char('>');
}
return name;
}
class ToolBarItem : public QListWidgetItem
{
public:
ToolBarItem(QListWidget *parent, const QString &tag = QString(), const QString &name = QString(), const QString &statusText = QString())
: QListWidgetItem(parent),
m_internalTag(tag),
m_internalName(name),
m_statusText(statusText),
m_isSeparator(false),
m_isTextAlongsideIconHidden(false)
{
// Drop between items, not onto items
setFlags((flags() | Qt::ItemIsDragEnabled) & ~Qt::ItemIsDropEnabled);
}
void setInternalTag(const QString &tag)
{
m_internalTag = tag;
}
void setInternalName(const QString &name)
{
m_internalName = name;
}
void setStatusText(const QString &text)
{
m_statusText = text;
}
void setSeparator(bool sep)
{
m_isSeparator = sep;
}
void setTextAlongsideIconHidden(bool hidden)
{
m_isTextAlongsideIconHidden = hidden;
}
QString internalTag() const
{
return m_internalTag;
}
QString internalName() const
{
return m_internalName;
}
QString statusText() const
{
return m_statusText;
}
bool isSeparator() const
{
return m_isSeparator;
}
bool isTextAlongsideIconHidden() const
{
return m_isTextAlongsideIconHidden;
}
int index() const
{
return listWidget()->row(const_cast<ToolBarItem *>(this));
}
private:
QString m_internalTag;
QString m_internalName;
QString m_statusText;
bool m_isSeparator;
bool m_isTextAlongsideIconHidden;
};
static QDataStream &operator<< (QDataStream &s, const ToolBarItem &item)
{
s << item.internalTag();
s << item.internalName();
s << item.statusText();
s << item.isSeparator();
s << item.isTextAlongsideIconHidden();
return s;
}
static QDataStream &operator>> (QDataStream &s, ToolBarItem &item)
{
QString internalTag;
s >> internalTag;
item.setInternalTag(internalTag);
QString internalName;
s >> internalName;
item.setInternalName(internalName);
QString statusText;
s >> statusText;
item.setStatusText(statusText);
bool sep;
s >> sep;
item.setSeparator(sep);
bool hidden;
s >> hidden;
item.setTextAlongsideIconHidden(hidden);
return s;
}
////
ToolBarListWidget::ToolBarListWidget(QWidget *parent)
: QListWidget(parent),
m_activeList(true)
{
setDragDropMode(QAbstractItemView::DragDrop); // no internal moves
}
QMimeData *ToolBarListWidget::mimeData(const QList<QListWidgetItem *> items) const
{
if (items.isEmpty()) {
return 0;
}
QMimeData *mimedata = new QMimeData();
QByteArray data;
{
QDataStream stream(&data, QIODevice::WriteOnly);
// we only support single selection
ToolBarItem *item = static_cast<ToolBarItem *>(items.first());
stream << *item;
}
mimedata->setData(QStringLiteral("application/x-kde-action-list"), data);
mimedata->setData(QStringLiteral("application/x-kde-source-treewidget"), m_activeList ? "active" : "inactive");
return mimedata;
}
bool ToolBarListWidget::dropMimeData(int index, const QMimeData *mimeData, Qt::DropAction action)
{
Q_UNUSED(action)
const QByteArray data = mimeData->data(QStringLiteral("application/x-kde-action-list"));
if (data.isEmpty()) {
return false;
}
QDataStream stream(data);
const bool sourceIsActiveList = mimeData->data(QStringLiteral("application/x-kde-source-treewidget")) == "active";
ToolBarItem *item = new ToolBarItem(this); // needs parent, use this temporarily
stream >> *item;
emit dropped(this, index, item, sourceIsActiveList);
return true;
}
ToolBarItem *ToolBarListWidget::currentItem() const
{
return static_cast<ToolBarItem *>(QListWidget::currentItem());
}
IconTextEditDialog::IconTextEditDialog(QWidget *parent)
: QDialog(parent)
{
setWindowTitle(i18n("Change Text"));
setModal(true);
QVBoxLayout *layout = new QVBoxLayout;
setLayout(layout);
QGridLayout *grid = new QGridLayout;
grid->setMargin(0);
m_lineEdit = new QLineEdit(this);
m_lineEdit->setClearButtonEnabled(true);
QLabel *label = new QLabel(i18n("Icon te&xt:"), this);
label->setBuddy(m_lineEdit);
grid->addWidget(label, 0, 0);
grid->addWidget(m_lineEdit, 0, 1);
m_cbHidden = new QCheckBox(i18n("&Hide text when toolbar shows text alongside icons"), this);
grid->addWidget(m_cbHidden, 1, 1);
layout->addLayout(grid);
m_buttonBox = new QDialogButtonBox(this);
m_buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(m_buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(m_buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
layout->addWidget(m_buttonBox);
connect(m_lineEdit, SIGNAL(textChanged(QString)), SLOT(slotTextChanged(QString)));
m_lineEdit->setFocus();
setFixedHeight(sizeHint().height());
}
void IconTextEditDialog::setIconText(const QString &text)
{
m_lineEdit->setText(text);
}
QString IconTextEditDialog::iconText() const
{
return m_lineEdit->text().trimmed();
}
void IconTextEditDialog::setTextAlongsideIconHidden(bool hidden)
{
m_cbHidden->setChecked(hidden);
}
bool IconTextEditDialog::textAlongsideIconHidden() const
{
return m_cbHidden->isChecked();
}
void IconTextEditDialog::slotTextChanged(const QString &text)
{
// Do not allow empty icon text
m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!text.trimmed().isEmpty());
}
class KEditToolBarWidgetPrivate
{
public:
/**
*
* @param collection In the old-style constructor, this is the collection passed
* to the KEditToolBar constructor.
* In the xmlguifactory-based constructor, we let KXMLGUIClient create a dummy one,
* but it probably isn't used.
*/
KEditToolBarWidgetPrivate(KEditToolBarWidget *widget,
const QString &cName, KActionCollection *collection)
: m_collection(collection),
m_widget(widget),
m_factory(0),
m_loadedOnce(false)
{
m_componentName = cName;
m_isPart = false;
m_helpArea = 0L;
// We want items with an icon to align with items without icon
// So we use an empty QPixmap for that
const int iconSize = widget->style()->pixelMetric(QStyle::PM_SmallIconSize);
m_emptyIcon = QPixmap(iconSize, iconSize);
m_emptyIcon.fill(Qt::transparent);
}
~KEditToolBarWidgetPrivate()
{
}
// private slots
void slotToolBarSelected(int index);
void slotInactiveSelectionChanged();
void slotActiveSelectionChanged();
void slotInsertButton();
void slotRemoveButton();
void slotUpButton();
void slotDownButton();
void selectActiveItem(const QString &);
void slotDropped(ToolBarListWidget *list, int index, ToolBarItem *item, bool sourceIsActiveList);
void setupLayout();
void initOldStyle(const QString &file, bool global, const QString &defaultToolbar);
void initFromFactory(KXMLGUIFactory *factory, const QString &defaultToolbar);
void loadToolBarCombo(const QString &defaultToolbar);
void loadActions(const QDomElement &elem);
QString xmlFile(const QString &xml_file) const
{
return xml_file.isEmpty() ? m_componentName + QStringLiteral("ui.xmlgui") : xml_file;
}
/**
* Load in the specified XML file and dump the raw xml
*/
QString loadXMLFile(const QString &_xml_file)
{
QString raw_xml;
QString xml_file = xmlFile(_xml_file);
//qDebug() << "loadXMLFile xml_file=" << xml_file;
if (!QDir::isRelativePath(xml_file)) {
raw_xml = KXMLGUIFactory::readConfigFile(xml_file);
} else {
raw_xml = KXMLGUIFactory::readConfigFile(xml_file, m_componentName);
}
return raw_xml;
}
/**
* Look for a given item in the current toolbar
*/
QDomElement findElementForToolBarItem(const ToolBarItem *item) const
{
//qDebug(240) << "looking for name=" << item->internalName() << "and tag=" << item->internalTag();
for (QDomNode n = m_currentToolBarElem.firstChild(); !n.isNull(); n = n.nextSibling()) {
QDomElement elem = n.toElement();
if ((elem.attribute(QStringLiteral("name")) == item->internalName()) &&
(elem.tagName() == item->internalTag())) {
return elem;
}
}
//qDebug(240) << "no item found in the DOM with name=" << item->internalName() << "and tag=" << item->internalTag();
return QDomElement();
}
void insertActive(ToolBarItem *item, ToolBarItem *before, bool prepend = false);
void removeActive(ToolBarItem *item);
void moveActive(ToolBarItem *item, ToolBarItem *before);
void updateLocal(QDomElement &elem);
#ifndef NDEBUG
void dump() const
{
QList<XmlData>::const_iterator xit = m_xmlFiles.begin();
for (; xit != m_xmlFiles.end(); ++xit) {
(*xit).dump();
}
}
#endif
QComboBox *m_toolbarCombo;
QToolButton *m_upAction;
QToolButton *m_removeAction;
QToolButton *m_insertAction;
QToolButton *m_downAction;
//QValueList<QAction*> m_actionList;
KActionCollection *m_collection;
KEditToolBarWidget *m_widget;
KXMLGUIFactory *m_factory;
QString m_componentName;
QPixmap m_emptyIcon;
XmlData *m_currentXmlData;
QDomElement m_currentToolBarElem;
QString m_xmlFile;
QString m_globalFile;
QString m_rcFile;
QDomDocument m_localDoc;
ToolBarList m_barList;
ToolBarListWidget *m_inactiveList;
ToolBarListWidget *m_activeList;
QList<XmlData> m_xmlFiles;
QLabel *m_comboLabel;
KSeparator *m_comboSeparator;
QLabel *m_helpArea;
bool m_isPart : 1;
bool m_loadedOnce : 1;
};
}
using namespace KDEPrivate;
class KEditToolBarPrivate
{
public:
KEditToolBarPrivate(KEditToolBar *q): q(q),
m_accept(false), m_global(false),
m_collection(0), m_factory(0), m_widget(0) {}
void init();
void _k_slotButtonClicked(QAbstractButton *button);
void _k_acceptOK(bool);
void _k_enableApply(bool);
void okClicked();
void applyClicked();
void defaultClicked();
KEditToolBar *q;
bool m_accept;
// Save parameters for recreating widget after resetting toolbar
bool m_global;
KActionCollection *m_collection;
QString m_file;
QString m_defaultToolBar;
KXMLGUIFactory *m_factory;
KEditToolBarWidget *m_widget;
QVBoxLayout *m_layout;
QDialogButtonBox *m_buttonBox;
};
Q_GLOBAL_STATIC(QString, s_defaultToolBarName)
KEditToolBar::KEditToolBar(KXMLGUIFactory *factory,
QWidget *parent)
: QDialog(parent),
d(new KEditToolBarPrivate(this))
{
d->m_widget = new KEditToolBarWidget(this);
d->init();
d->m_factory = factory;
}
void KEditToolBarPrivate::init()
{
m_accept = false;
m_factory = 0;
q->setDefaultToolBar(QString());
q->setWindowTitle(i18n("Configure Toolbars"));
q->setModal(false);
m_layout = new QVBoxLayout;
q->setLayout(m_layout);
m_layout->addWidget(m_widget);
m_buttonBox = new QDialogButtonBox(q);
m_buttonBox->setStandardButtons(QDialogButtonBox::RestoreDefaults
| QDialogButtonBox::Ok
| QDialogButtonBox::Apply
| QDialogButtonBox::Cancel);
KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::Ok), KStandardGuiItem::ok());
KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::Apply), KStandardGuiItem::apply());
KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel());
KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::RestoreDefaults), KStandardGuiItem::defaults());
q->connect(m_buttonBox, SIGNAL(clicked(QAbstractButton*)), SLOT(_k_slotButtonClicked(QAbstractButton*)));
q->connect(m_buttonBox, SIGNAL(rejected()), SLOT(reject()));
m_layout->addWidget(m_buttonBox);
q->connect(m_widget, SIGNAL(enableOk(bool)), SLOT(_k_acceptOK(bool)));
q->connect(m_widget, SIGNAL(enableOk(bool)), SLOT(_k_enableApply(bool)));
_k_enableApply(false);
q->setMinimumSize(q->sizeHint());
}
void KEditToolBar::setResourceFile(const QString &file, bool global)
{
d->m_file = file;
d->m_global = global;
d->m_widget->load(d->m_file, d->m_global, d->m_defaultToolBar);
}
KEditToolBar::~KEditToolBar()
{
delete d;
s_defaultToolBarName()->clear();
}
void KEditToolBar::setDefaultToolBar(const QString &toolBarName)
{
if (toolBarName.isEmpty()) {
d->m_defaultToolBar = *s_defaultToolBarName();
} else {
d->m_defaultToolBar = toolBarName;
}
}
void KEditToolBarPrivate::_k_acceptOK(bool b)
{
m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(b);
m_accept = b;
}
void KEditToolBarPrivate::_k_enableApply(bool b)
{
m_buttonBox->button(QDialogButtonBox::Apply)->setEnabled(b);
}
void KEditToolBarPrivate::defaultClicked()
{
if (KMessageBox::warningContinueCancel(q, i18n("Do you really want to reset all toolbars of this application to their default? The changes will be applied immediately."), i18n("Reset Toolbars"), KGuiItem(i18n("Reset"))) != KMessageBox::Continue) {
return;
}
KEditToolBarWidget *oldWidget = m_widget;
m_widget = 0;
m_accept = false;
if (m_factory) {
foreach (KXMLGUIClient *client, m_factory->clients()) {
const QString file = client->localXMLFile();
if (file.isEmpty()) {
continue;
}
//qDebug(240) << "Deleting local xml file" << file;
// << "for client" << client << typeid(*client).name();
if (QFile::exists(file))
if (!QFile::remove(file)) {
qWarning() << "Could not delete" << file;
}
}
// Reload the xml files in all clients, now that the local files are gone
oldWidget->rebuildKXMLGUIClients();
m_widget = new KEditToolBarWidget(q);
m_widget->load(m_factory, m_defaultToolBar);
} else {
int slash = m_file.lastIndexOf(QLatin1Char('/')) + 1;
if (slash) {
m_file = m_file.mid(slash);
}
const QString xml_file = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) +
QStringLiteral("/kxmlgui5/") + QCoreApplication::instance()->applicationName() + QLatin1Char('/') + m_file;
if (QFile::exists(xml_file))
if (!QFile::remove(xml_file)) {
qWarning() << "Could not delete " << xml_file;
}
m_widget = new KEditToolBarWidget(m_collection, q);
q->setResourceFile(m_file, m_global);
}
// Copy the geometry to minimize UI flicker
m_widget->setGeometry(oldWidget->geometry());
delete oldWidget;
m_layout->insertWidget(0, m_widget);
q->connect(m_widget, SIGNAL(enableOk(bool)), SLOT(_k_acceptOK(bool)));
q->connect(m_widget, SIGNAL(enableOk(bool)), SLOT(_k_enableApply(bool)));
_k_enableApply(false);
emit q->newToolBarConfig();
emit q->newToolbarConfig(); // compat
}
void KEditToolBarPrivate::_k_slotButtonClicked(QAbstractButton *button)
{
QDialogButtonBox::StandardButton type = m_buttonBox->standardButton(button);
switch (type) {
case QDialogButtonBox::Ok:
okClicked();
break;
case QDialogButtonBox::Apply:
applyClicked();
break;
case QDialogButtonBox::RestoreDefaults:
defaultClicked();
break;
default:
break;
}
}
void KEditToolBarPrivate::okClicked()
{
if (!m_accept) {
q->reject();
return;
}
// Do not rebuild GUI and emit the "newToolBarConfig" signal again here if the "Apply"
// button was already pressed and no further changes were made.
if (m_buttonBox->button(QDialogButtonBox::Apply)->isEnabled()) {
m_widget->save();
emit q->newToolBarConfig();
emit q->newToolbarConfig(); // compat
}
q->accept();
}
void KEditToolBarPrivate::applyClicked()
{
(void)m_widget->save();
_k_enableApply(false);
emit q->newToolBarConfig();
emit q->newToolbarConfig(); // compat
}
void KEditToolBar::setGlobalDefaultToolBar(const char *toolbarName)
{
*s_defaultToolBarName() = QString::fromLatin1(toolbarName);
}
KEditToolBarWidget::KEditToolBarWidget(KActionCollection *collection,
QWidget *parent)
: QWidget(parent),
d(new KEditToolBarWidgetPrivate(this, componentName(), collection))
{
d->setupLayout();
}
KEditToolBarWidget::KEditToolBarWidget(QWidget *parent)
: QWidget(parent),
d(new KEditToolBarWidgetPrivate(this, componentName(), KXMLGUIClient::actionCollection() /*create new one*/))
{
d->setupLayout();
}
KEditToolBarWidget::~KEditToolBarWidget()
{
delete d;
}
void KEditToolBarWidget::load(const QString &file, bool global, const QString &defaultToolBar)
{
d->initOldStyle(file, global, defaultToolBar);
}
void KEditToolBarWidget::load(KXMLGUIFactory *factory, const QString &defaultToolBar)
{
d->initFromFactory(factory, defaultToolBar);
}
void KEditToolBarWidgetPrivate::initOldStyle(const QString &resourceFile,
bool global,
const QString &defaultToolBar)
{
qDebug() << "initOldStyle";
//TODO: make sure we can call this multiple times?
if (m_loadedOnce) {
return;
}
m_loadedOnce = true;
//d->m_actionList = collection->actions();
// handle the merging
if (global) {
m_widget->loadStandardsXmlFile(); // ui_standards.xmlgui
}
const QString localXML = loadXMLFile(resourceFile);
m_widget->setXML(localXML, global ? true /*merge*/ : false);
// first, get all of the necessary info for our local xml
XmlData local(XmlData::Local, xmlFile(resourceFile), m_collection);
QDomDocument domDoc;
domDoc.setContent(localXML);
local.setDomDocument(domDoc);
m_xmlFiles.append(local);
// then, the merged one (ui_standards + local xml)
XmlData merge(XmlData::Merged, QString(), m_collection);
merge.setDomDocument(m_widget->domDocument());
m_xmlFiles.append(merge);
#ifndef NDEBUG
dump();
#endif
// now load in our toolbar combo box
loadToolBarCombo(defaultToolBar);
m_widget->adjustSize();
m_widget->setMinimumSize(m_widget->sizeHint());
}
void KEditToolBarWidgetPrivate::initFromFactory(KXMLGUIFactory *factory,
const QString &defaultToolBar)
{
qDebug() << "initFromFactory";
//TODO: make sure we can call this multiple times?
if (m_loadedOnce) {
return;
}
m_loadedOnce = true;
m_factory = factory;
// add all of the client data
bool first = true;
foreach (KXMLGUIClient *client, factory->clients()) {
if (client->xmlFile().isEmpty()) {
continue;
}
XmlData::XmlType type = XmlData::Part;
if (first) {
type = XmlData::Shell;
first = false;
Q_ASSERT(!client->localXMLFile().isEmpty()); // where would we save changes??
}
XmlData data(type, client->localXMLFile(), client->actionCollection());
QDomDocument domDoc = client->domDocument();
data.setDomDocument(domDoc);
m_xmlFiles.append(data);
//d->m_actionList += client->actionCollection()->actions();
}
#ifndef NDEBUG
//d->dump();
#endif
// now load in our toolbar combo box
loadToolBarCombo(defaultToolBar);
m_widget->adjustSize();
m_widget->setMinimumSize(m_widget->sizeHint());
m_widget->actionCollection()->addAssociatedWidget(m_widget);
foreach (QAction *action, m_widget->actionCollection()->actions()) {
action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
}
}
void KEditToolBarWidget::save()
{
//qDebug(240) << "KEditToolBarWidget::save";
QList<XmlData>::Iterator it = d->m_xmlFiles.begin();
for (; it != d->m_xmlFiles.end(); ++it) {
// let's not save non-modified files
if (!((*it).m_isModified)) {
continue;
}
// let's also skip (non-existent) merged files
if ((*it).type() == XmlData::Merged) {
continue;
}
// Add noMerge="1" to all the menus since we are saving the merged data
QDomNodeList menuNodes = (*it).domDocument().elementsByTagName(QStringLiteral("Menu"));
for (int i = 0; i < menuNodes.length(); ++i) {
QDomNode menuNode = menuNodes.item(i);
QDomElement menuElement = menuNode.toElement();
if (menuElement.isNull()) {
continue;
}
menuElement.setAttribute(QStringLiteral("noMerge"), QLatin1String("1"));
}
//qDebug() << (*it).domDocument().toString();
//qDebug(240) << "Saving " << (*it).xmlFile();
// if we got this far, we might as well just save it
KXMLGUIFactory::saveConfigFile((*it).domDocument(), (*it).xmlFile());
}
if (!d->m_factory) {
return;
}
rebuildKXMLGUIClients();
}
void KEditToolBarWidget::rebuildKXMLGUIClients()
{
if (!d->m_factory) {
return;
}
const QList<KXMLGUIClient *> clients = d->m_factory->clients();
//qDebug(240) << "factory: " << clients.count() << " clients";
// remove the elements starting from the last going to the first
if (!clients.count()) {
return;
}
QListIterator<KXMLGUIClient *> clientIterator = clients;
clientIterator.toBack();
while (clientIterator.hasPrevious()) {
KXMLGUIClient *client = clientIterator.previous();
//qDebug(240) << "factory->removeClient " << client;
d->m_factory->removeClient(client);
}
KXMLGUIClient *firstClient = clients.first();
// now, rebuild the gui from the first to the last
//qDebug(240) << "rebuilding the gui";
foreach (KXMLGUIClient *client, clients) {
//qDebug(240) << "updating client " << client << " " << client->componentName() << " xmlFile=" << client->xmlFile();
QString file(client->xmlFile()); // before setting ui_standards!
if (!file.isEmpty()) {
// passing an empty stream forces the clients to reread the XML
client->setXMLGUIBuildDocument(QDomDocument());
// for the shell, merge in ui_standards.xmlgui
if (client == firstClient) { // same assumption as in the ctor: first==shell
client->loadStandardsXmlFile();
}
// and this forces it to use the *new* XML file
client->setXMLFile(file, client == firstClient /* merge if shell */);
// [we can't use reloadXML, it doesn't load ui_standards.xmlgui]
}
}
// Now we can add the clients to the factory
// We don't do it in the loop above because adding a part automatically
// adds its plugins, so we must make sure the plugins were updated first.
foreach (KXMLGUIClient *client, clients) {
d->m_factory->addClient(client);
}
}
void KEditToolBarWidgetPrivate::setupLayout()
{
// the toolbar name combo
m_comboLabel = new QLabel(i18n("&Toolbar:"), m_widget);
m_toolbarCombo = new QComboBox(m_widget);
m_comboLabel->setBuddy(m_toolbarCombo);
m_comboSeparator = new KSeparator(m_widget);
QObject::connect(m_toolbarCombo, SIGNAL(activated(int)),
m_widget, SLOT(slotToolBarSelected(int)));
// QPushButton *new_toolbar = new QPushButton(i18n("&New"), this);
// new_toolbar->setPixmap(BarIcon("document-new", KisIconUtils::SizeSmall));
// new_toolbar->setEnabled(false); // disabled until implemented
// QPushButton *del_toolbar = new QPushButton(i18n("&Delete"), this);
// del_toolbar->setPixmap(BarIcon("edit-delete", KisIconUtils::SizeSmall));
// del_toolbar->setEnabled(false); // disabled until implemented
// our list of inactive actions
QLabel *inactive_label = new QLabel(i18n("A&vailable actions:"), m_widget);
m_inactiveList = new ToolBarListWidget(m_widget);
m_inactiveList->setDragEnabled(true);
m_inactiveList->setActiveList(false);
m_inactiveList->setMinimumSize(180, 250);
m_inactiveList->setDropIndicatorShown(false); // #165663
inactive_label->setBuddy(m_inactiveList);
QObject::connect(m_inactiveList, SIGNAL(itemSelectionChanged()),
m_widget, SLOT(slotInactiveSelectionChanged()));
QObject::connect(m_inactiveList, SIGNAL(itemDoubleClicked(QListWidgetItem*)),
m_widget, SLOT(slotInsertButton()));
QObject::connect(m_inactiveList, SIGNAL(dropped(ToolBarListWidget*,int,ToolBarItem*,bool)),
m_widget, SLOT(slotDropped(ToolBarListWidget*,int,ToolBarItem*,bool)));
KListWidgetSearchLine *inactiveListSearchLine = new KListWidgetSearchLine(m_widget, m_inactiveList);
inactiveListSearchLine->setPlaceholderText(i18n("Filter"));
// our list of active actions
QLabel *active_label = new QLabel(i18n("Curr&ent actions:"), m_widget);
m_activeList = new ToolBarListWidget(m_widget);
m_activeList->setDragEnabled(true);
m_activeList->setActiveList(true);
// With Qt-4.1 only setting MiniumWidth results in a 0-width icon column ...
m_activeList->setMinimumSize(m_inactiveList->minimumWidth(), 100);
active_label->setBuddy(m_activeList);
QObject::connect(m_activeList, SIGNAL(itemSelectionChanged()),
m_widget, SLOT(slotActiveSelectionChanged()));
QObject::connect(m_activeList, SIGNAL(itemDoubleClicked(QListWidgetItem*)),
m_widget, SLOT(slotRemoveButton()));
QObject::connect(m_activeList, SIGNAL(dropped(ToolBarListWidget*,int,ToolBarItem*,bool)),
m_widget, SLOT(slotDropped(ToolBarListWidget*,int,ToolBarItem*,bool)));
KListWidgetSearchLine *activeListSearchLine = new KListWidgetSearchLine(m_widget, m_activeList);
activeListSearchLine->setPlaceholderText(i18n("Filter"));
// The buttons in the middle
m_upAction = new QToolButton(m_widget);
m_upAction->setIcon(KisIconUtils::loadIcon(QStringLiteral("arrow-up")));
m_upAction->setEnabled(false);
m_upAction->setAutoRepeat(true);
QObject::connect(m_upAction, SIGNAL(clicked()), m_widget, SLOT(slotUpButton()));
m_insertAction = new QToolButton(m_widget);
m_insertAction->setIcon(KisIconUtils::loadIcon(QApplication::isRightToLeft() ? QStringLiteral("arrow-left") : QLatin1String("arrow-right")));
m_insertAction->setEnabled(false);
QObject::connect(m_insertAction, SIGNAL(clicked()), m_widget, SLOT(slotInsertButton()));
m_removeAction = new QToolButton(m_widget);
m_removeAction->setIcon(KisIconUtils::loadIcon(QApplication::isRightToLeft() ? QStringLiteral("arrow-right") : QLatin1String("arrow-left")));
m_removeAction->setEnabled(false);
QObject::connect(m_removeAction, SIGNAL(clicked()), m_widget, SLOT(slotRemoveButton()));
m_downAction = new QToolButton(m_widget);
m_downAction->setIcon(KisIconUtils::loadIcon(QStringLiteral("arrow-down")));
m_downAction->setEnabled(false);
m_downAction->setAutoRepeat(true);
QObject::connect(m_downAction, SIGNAL(clicked()), m_widget, SLOT(slotDownButton()));
m_helpArea = new QLabel(m_widget);
m_helpArea->setWordWrap(true);
// now start with our layouts
QVBoxLayout *top_layout = new QVBoxLayout(m_widget);
top_layout->setMargin(0);
QVBoxLayout *name_layout = new QVBoxLayout();
QHBoxLayout *list_layout = new QHBoxLayout();
QVBoxLayout *inactive_layout = new QVBoxLayout();
QVBoxLayout *active_layout = new QVBoxLayout();
QGridLayout *button_layout = new QGridLayout();
name_layout->addWidget(m_comboLabel);
name_layout->addWidget(m_toolbarCombo);
// name_layout->addWidget(new_toolbar);
// name_layout->addWidget(del_toolbar);
button_layout->setSpacing(0);
button_layout->setRowStretch(0, 10);
button_layout->addWidget(m_upAction, 1, 1);
button_layout->addWidget(m_removeAction, 2, 0);
button_layout->addWidget(m_insertAction, 2, 2);
button_layout->addWidget(m_downAction, 3, 1);
button_layout->setRowStretch(4, 10);
inactive_layout->addWidget(inactive_label);
inactive_layout->addWidget(inactiveListSearchLine);
inactive_layout->addWidget(m_inactiveList, 1);
active_layout->addWidget(active_label);
active_layout->addWidget(activeListSearchLine);
active_layout->addWidget(m_activeList, 1);
list_layout->addLayout(inactive_layout);
list_layout->addLayout(button_layout);
list_layout->addLayout(active_layout);
top_layout->addLayout(name_layout);
top_layout->addWidget(m_comboSeparator);
top_layout->addLayout(list_layout, 10);
top_layout->addWidget(m_helpArea);
top_layout->addWidget(new KSeparator(m_widget));
}
void KEditToolBarWidgetPrivate::loadToolBarCombo(const QString &defaultToolBar)
{
const QLatin1String attrName("name");
// just in case, we clear our combo
m_toolbarCombo->clear();
int defaultToolBarId = -1;
int count = 0;
// load in all of the toolbar names into this combo box
QList<XmlData>::const_iterator xit = m_xmlFiles.constBegin();
for (; xit != m_xmlFiles.constEnd(); ++xit) {
// skip the merged one in favor of the local one,
// so that we can change icons
// This also makes the app-defined named for "mainToolBar" appear rather than the ui_standards-defined name.
if ((*xit).type() == XmlData::Merged) {
continue;
}
// each xml file may have any number of toolbars
ToolBarList::const_iterator it = (*xit).barList().begin();
for (; it != (*xit).barList().constEnd(); ++it) {
const QString text = (*xit).toolBarText(*it);
m_toolbarCombo->addItem(text);
const QString name = (*it).attribute(attrName);
if (defaultToolBarId == -1 && name == defaultToolBar) {
defaultToolBarId = count;
}
count++;
}
}
const bool showCombo = (count > 1);
m_comboLabel->setVisible(showCombo);
m_comboSeparator->setVisible(showCombo);
m_toolbarCombo->setVisible(showCombo);
if (defaultToolBarId == -1) {
defaultToolBarId = 0;
}
// we want to the specified item selected and its actions loaded
m_toolbarCombo->setCurrentIndex(defaultToolBarId);
slotToolBarSelected(m_toolbarCombo->currentIndex());
}
void KEditToolBarWidgetPrivate::loadActions(const QDomElement &elem)
{
const QLatin1String tagSeparator("Separator");
const QLatin1String tagMerge("Merge");
const QLatin1String tagActionList("ActionList");
const QLatin1String tagAction("Action");
const QLatin1String attrName("name");
int sep_num = 0;
QString sep_name(QStringLiteral("separator_%1"));
// clear our lists
m_inactiveList->clear();
m_activeList->clear();
m_insertAction->setEnabled(false);
m_removeAction->setEnabled(false);
m_upAction->setEnabled(false);
m_downAction->setEnabled(false);
// We'll use this action collection
KActionCollection *actionCollection = m_currentXmlData->actionCollection();
// store the names of our active actions
QSet<QString> active_list;
// Filtering message requested by translators (scripting).
KLocalizedString nameFilter = ki18nc("@item:intable Action name in toolbar editor", "%1");
// see if our current action is in this toolbar
QDomNode n = elem.firstChild();
for (; !n.isNull(); n = n.nextSibling()) {
QDomElement it = n.toElement();
if (it.isNull()) {
continue;
}
if (it.tagName() == tagSeparator) {
ToolBarItem *act = new ToolBarItem(m_activeList, tagSeparator, sep_name.arg(sep_num++), QString());
act->setSeparator(true);
act->setText(SEPARATORSTRING);
it.setAttribute(attrName, act->internalName());
continue;
}
if (it.tagName() == tagMerge) {
// Merge can be named or not - use the name if there is one
QString name = it.attribute(attrName);
ToolBarItem *act = new ToolBarItem(m_activeList, tagMerge, name, i18n("This element will be replaced with all the elements of an embedded component."));
if (name.isEmpty()) {
act->setText(i18n("<Merge>"));
} else {
act->setText(i18n("<Merge %1>", name));
}
continue;
}
if (it.tagName() == tagActionList) {
ToolBarItem *act = new ToolBarItem(m_activeList, tagActionList, it.attribute(attrName), i18n("This is a dynamic list of actions. You can move it, but if you remove it you will not be able to re-add it."));
act->setText(i18n("ActionList: %1", it.attribute(attrName)));
continue;
}
// iterate through this client's actions
// This used to iterate through _all_ actions, but we don't support
// putting any action into any client...
foreach (QAction *action, actionCollection->actions()) {
// do we have a match?
if (it.attribute(attrName) == action->objectName()) {
// we have a match!
ToolBarItem *act = new ToolBarItem(m_activeList, it.tagName(), action->objectName(), action->toolTip());
act->setText(nameFilter.subs(KLocalizedString::removeAcceleratorMarker(action->iconText())).toString());
act->setIcon(!action->icon().isNull() ? action->icon() : m_emptyIcon);
act->setTextAlongsideIconHidden(action->priority() < QAction::NormalPriority);
active_list.insert(action->objectName());
break;
}
}
}
// go through the rest of the collection
foreach (QAction *action, actionCollection->actions()) {
// skip our active ones
if (active_list.contains(action->objectName())) {
continue;
}
ToolBarItem *act = new ToolBarItem(m_inactiveList, tagAction, action->objectName(), action->toolTip());
act->setText(nameFilter.subs(KLocalizedString::removeAcceleratorMarker(action->text())).toString());
act->setIcon(!action->icon().isNull() ? action->icon() : m_emptyIcon);
}
m_inactiveList->sortItems(Qt::AscendingOrder);
// finally, add default separators to the inactive list
ToolBarItem *act = new ToolBarItem(0L, tagSeparator, sep_name.arg(sep_num++), QString());
act->setSeparator(true);
act->setText(SEPARATORSTRING);
m_inactiveList->insertItem(0, act);
}
KActionCollection *KEditToolBarWidget::actionCollection() const
{
return d->m_collection;
}
void KEditToolBarWidgetPrivate::slotToolBarSelected(int index)
{
- const QLatin1String attrName("name");
// We need to find the XmlData and toolbar element for this index
// To do that, we do the same iteration as the one which filled in the combobox.
int toolbarNumber = 0;
QList<XmlData>::iterator xit = m_xmlFiles.begin();
for (; xit != m_xmlFiles.end(); ++xit) {
// skip the merged one in favor of the local one,
// so that we can change icons
if ((*xit).type() == XmlData::Merged) {
continue;
}
// each xml file may have any number of toolbars
ToolBarList::Iterator it = (*xit).barList().begin();
for (; it != (*xit).barList().end(); ++it) {
// is this our toolbar?
if (toolbarNumber == index) {
// save our current settings
m_currentXmlData = & (*xit);
m_currentToolBarElem = *it;
//qDebug() << "found toolbar" << m_currentXmlData->toolBarText(*it) << "m_currentXmlData set to";
m_currentXmlData->dump();
// If this is a Merged xmldata, clicking the "change icon" button would assert...
Q_ASSERT(m_currentXmlData->type() != XmlData::Merged);
// load in our values
loadActions(m_currentToolBarElem);
if ((*xit).type() == XmlData::Part || (*xit).type() == XmlData::Shell) {
m_widget->setDOMDocument((*xit).domDocument());
}
return;
}
++toolbarNumber;
}
}
}
void KEditToolBarWidgetPrivate::slotInactiveSelectionChanged()
{
if (m_inactiveList->selectedItems().count()) {
m_insertAction->setEnabled(true);
QString statusText = static_cast<ToolBarItem *>(m_inactiveList->selectedItems().first())->statusText();
m_helpArea->setText(i18nc("@label Action tooltip in toolbar editor, below the action list", "%1", statusText));
} else {
m_insertAction->setEnabled(false);
m_helpArea->setText(QString());
}
}
void KEditToolBarWidgetPrivate::slotActiveSelectionChanged()
{
ToolBarItem *toolitem = 0;
if (!m_activeList->selectedItems().isEmpty()) {
toolitem = static_cast<ToolBarItem *>(m_activeList->selectedItems().first());
}
m_removeAction->setEnabled(toolitem);
if (toolitem) {
m_upAction->setEnabled(toolitem->index() != 0);
m_downAction->setEnabled(toolitem->index() != toolitem->listWidget()->count() - 1);
QString statusText = toolitem->statusText();
m_helpArea->setText(i18nc("@label Action tooltip in toolbar editor, below the action list", "%1", statusText));
} else {
m_upAction->setEnabled(false);
m_downAction->setEnabled(false);
m_helpArea->setText(QString());
}
}
void KEditToolBarWidgetPrivate::slotInsertButton()
{
QString internalName = static_cast<ToolBarItem *>(m_inactiveList->currentItem())->internalName();
insertActive(m_inactiveList->currentItem(), m_activeList->currentItem(), false);
// we're modified, so let this change
emit m_widget->enableOk(true);
slotToolBarSelected(m_toolbarCombo->currentIndex());
selectActiveItem(internalName);
}
void KEditToolBarWidgetPrivate::selectActiveItem(const QString &internalName)
{
int activeItemCount = m_activeList->count();
for (int i = 0; i < activeItemCount; i++) {
ToolBarItem *item = static_cast<ToolBarItem *>(m_activeList->item(i));
if (item->internalName() == internalName) {
m_activeList->setCurrentItem(item);
break;
}
}
}
void KEditToolBarWidgetPrivate::slotRemoveButton()
{
removeActive(m_activeList->currentItem());
slotToolBarSelected(m_toolbarCombo->currentIndex());
}
void KEditToolBarWidgetPrivate::insertActive(ToolBarItem *item, ToolBarItem *before, bool prepend)
{
if (!item) {
return;
}
QDomElement new_item;
// let's handle the separator specially
if (item->isSeparator()) {
new_item = m_widget->domDocument().createElement(QStringLiteral("Separator"));
} else {
new_item = m_widget->domDocument().createElement(QStringLiteral("Action"));
}
new_item.setAttribute(QStringLiteral("name"), item->internalName());
Q_ASSERT(!m_currentToolBarElem.isNull());
if (before) {
// we have the item in the active list which is before the new
// item.. so let's try our best to add our new item right after it
QDomElement elem = findElementForToolBarItem(before);
Q_ASSERT(!elem.isNull());
m_currentToolBarElem.insertAfter(new_item, elem);
} else {
// simply put it at the beginning or the end of the list.
if (prepend) {
m_currentToolBarElem.insertBefore(new_item, m_currentToolBarElem.firstChild());
} else {
m_currentToolBarElem.appendChild(new_item);
}
}
// and set this container as a noMerge
m_currentToolBarElem.setAttribute(QStringLiteral("noMerge"), QLatin1String("1"));
// update the local doc
updateLocal(m_currentToolBarElem);
}
void KEditToolBarWidgetPrivate::removeActive(ToolBarItem *item)
{
if (!item) {
return;
}
// we're modified, so let this change
emit m_widget->enableOk(true);
// now iterate through to find the child to nuke
QDomElement elem = findElementForToolBarItem(item);
if (!elem.isNull()) {
// nuke myself!
m_currentToolBarElem.removeChild(elem);
// and set this container as a noMerge
m_currentToolBarElem.setAttribute(QStringLiteral("noMerge"), QLatin1String("1"));
// update the local doc
updateLocal(m_currentToolBarElem);
}
}
void KEditToolBarWidgetPrivate::slotUpButton()
{
ToolBarItem *item = m_activeList->currentItem();
if (!item) {
Q_ASSERT(false);
return;
}
int row = item->listWidget()->row(item) - 1;
// make sure we're not the top item already
if (row < 0) {
Q_ASSERT(false);
return;
}
// we're modified, so let this change
emit m_widget->enableOk(true);
moveActive(item, static_cast<ToolBarItem *>(item->listWidget()->item(row - 1)));
}
void KEditToolBarWidgetPrivate::moveActive(ToolBarItem *item, ToolBarItem *before)
{
QDomElement e = findElementForToolBarItem(item);
if (e.isNull()) {
return;
}
// remove item
m_activeList->takeItem(m_activeList->row(item));
// put it where it's supposed to go
m_activeList->insertItem(m_activeList->row(before) + 1, item);
// make it selected again
m_activeList->setCurrentItem(item);
// and do the real move in the DOM
if (!before) {
m_currentToolBarElem.insertBefore(e, m_currentToolBarElem.firstChild());
} else {
m_currentToolBarElem.insertAfter(e, findElementForToolBarItem((ToolBarItem *)before));
}
// and set this container as a noMerge
m_currentToolBarElem.setAttribute(QStringLiteral("noMerge"), QLatin1String("1"));
// update the local doc
updateLocal(m_currentToolBarElem);
}
void KEditToolBarWidgetPrivate::slotDownButton()
{
ToolBarItem *item = m_activeList->currentItem();
if (!item) {
Q_ASSERT(false);
return;
}
// make sure we're not the bottom item already
int newRow = item->listWidget()->row(item) + 1;
if (newRow >= item->listWidget()->count()) {
Q_ASSERT(false);
return;
}
// we're modified, so let this change
emit m_widget->enableOk(true);
moveActive(item, static_cast<ToolBarItem *>(item->listWidget()->item(newRow)));
}
void KEditToolBarWidgetPrivate::updateLocal(QDomElement &elem)
{
QList<XmlData>::Iterator xit = m_xmlFiles.begin();
for (; xit != m_xmlFiles.end(); ++xit) {
if ((*xit).type() == XmlData::Merged) {
continue;
}
if ((*xit).type() == XmlData::Shell ||
(*xit).type() == XmlData::Part) {
if (m_currentXmlData->xmlFile() == (*xit).xmlFile()) {
(*xit).m_isModified = true;
return;
}
continue;
}
(*xit).m_isModified = true;
const QLatin1String attrName("name");
ToolBarList::Iterator it = (*xit).barList().begin();
for (; it != (*xit).barList().end(); ++it) {
QString name((*it).attribute(attrName));
QString tag((*it).tagName());
if ((tag != elem.tagName()) || (name != elem.attribute(attrName))) {
continue;
}
QDomElement toolbar = (*xit).domDocument().documentElement().toElement();
toolbar.replaceChild(elem, (*it));
return;
}
// just append it
QDomElement toolbar = (*xit).domDocument().documentElement().toElement();
Q_ASSERT(!toolbar.isNull());
toolbar.appendChild(elem);
}
}
void KEditToolBarWidgetPrivate::slotDropped(ToolBarListWidget *list, int index, ToolBarItem *item, bool sourceIsActiveList)
{
//qDebug() << "slotDropped list=" << (list==m_activeList?"activeList":"inactiveList")
// << "index=" << index << "sourceIsActiveList=" << sourceIsActiveList;
if (list == m_activeList) {
ToolBarItem *after = index > 0 ? static_cast<ToolBarItem *>(list->item(index - 1)) : 0;
//qDebug() << "after" << after->text() << after->internalTag();
if (sourceIsActiveList) {
// has been dragged within the active list (moved).
moveActive(item, after);
} else {
// dragged from the inactive list to the active list
insertActive(item, after, true);
}
} else if (list == m_inactiveList) {
// has been dragged to the inactive list -> remove from the active list.
removeActive(item);
}
delete item; // not needed anymore. must be deleted before slotToolBarSelected clears the lists
// we're modified, so let this change
emit m_widget->enableOk(true);
slotToolBarSelected(m_toolbarCombo->currentIndex());
}
void KEditToolBar::showEvent(QShowEvent *event)
{
if (!event->spontaneous()) {
// The dialog has been shown, enable toolbar editing
if (d->m_factory) {
// call the xmlgui-factory version
d->m_widget->load(d->m_factory, d->m_defaultToolBar);
} else {
// call the action collection version
d->m_widget->load(d->m_file, d->m_global, d->m_defaultToolBar);
}
KToolBar::setToolBarsEditable(true);
}
QDialog::showEvent(event);
}
void KEditToolBar::hideEvent(QHideEvent *event)
{
// The dialog has been hidden, disable toolbar editing
KToolBar::setToolBarsEditable(false);
QDialog::hideEvent(event);
}
#include "moc_kedittoolbar.cpp"
#include "moc_kedittoolbar_p.cpp"
diff --git a/libs/widgetutils/xmlgui/kedittoolbar.h b/libs/widgetutils/xmlgui/kedittoolbar.h
index 6ab950ed07..af06e473ae 100644
--- a/libs/widgetutils/xmlgui/kedittoolbar.h
+++ b/libs/widgetutils/xmlgui/kedittoolbar.h
@@ -1,158 +1,158 @@
/* This file is part of the KDE libraries
Copyright (C) 2000 Kurt Granroth <granroth@kde.org>
Copyright (C) 2006 Hamish Rodda <rodda@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KEDITTOOLBAR_H
#define KEDITTOOLBAR_H
#include <QDialog>
#include <kritawidgetutils_export.h>
class KActionCollection;
class KEditToolBarPrivate;
class KXMLGUIFactory;
/**
* @short A dialog used to customize or configure toolbars.
*
* This dialog only works if your application uses the XML UI
* framework for creating menus and toolbars. It depends on the XML
* files to describe the toolbar layouts and it requires the actions
* to determine which buttons are active.
*
* Typically you do not need to use it directly as KXmlGuiWindow::setupGUI
* takes care of it.
*
* If you use plugListAction you need to overload saveNewToolbarConfig()
* to plug actions again:
*
* \code
* void MyClass::saveNewToolbarConfig()
* {
* KXmlGuiWindow::saveNewToolbarConfig();
* plugActionList( "list1", list1Actions );
* plugActionList( "list2", list2Actions );
* }
* \endcode
*
* When created, KEditToolBar takes a KXMLGUIFactory object, and uses it to
* find all of the action collections and XML files (there is one of each for the
* mainwindow, but there could be more, when adding other XMLGUI clients like
* KParts or plugins). The editor aims to be semi-intelligent about where it
* assigns any modifications. In other words, it will not write out part specific
* changes to your application's main XML file.
*
* KXmlGuiWindow and KParts::MainWindow take care of creating KEditToolBar correctly
* and connecting to its newToolBarConfig slot, but if you really really want to do it
* yourself, see the KXmlGuiWindow::configureToolbars() and KXmlGuiWindow::saveNewToolbarConfig() code.
*
* \image html kedittoolbar.png "KDE Toolbar Editor (KWrite)"
*
* @author Kurt Granroth <granroth@kde.org>
* @maintainer David Faure <faure@kde.org>
*/
class KRITAWIDGETUTILS_EXPORT KEditToolBar : public QDialog
{
Q_OBJECT
public:
/**
* Main constructor.
*
* The main parameter, @p factory, is a pointer to the
* XML GUI factory object for your application. It contains a list
* of all of the GUI clients (along with the action collections and
* xml files) and the toolbar editor uses that.
*
* Use this like so:
* \code
* KEditToolBar edit(factory());
* if (edit.exec())
* ...
* \endcode
*
* @param factory Your application's factory object
* @param parent The usual parent for the dialog.
*/
explicit KEditToolBar(KXMLGUIFactory *factory,
QWidget *parent = 0);
/// destructor
~KEditToolBar() override;
/**
* Sets the default toolbar that will be selected when the dialog is shown.
* If not set, or QString() is passed in, the global default tool bar name
* will be used.
* @param toolBarName the name of the tool bar
* @see setGlobalDefaultToolBar
*/
void setDefaultToolBar(const QString &toolBarName);
/**
* The name (absolute or relative) of your application's UI resource file
* is assumed to be share/apps/appname/appnameui.xmlgui though this can be
* overridden by calling this method.
*
* The global parameter controls whether or not the
* global resource file is used. If this is @p true, then you may
* edit all of the actions in your toolbars -- global ones and
* local one. If it is @p false, then you may edit only your
* application's entries. The only time you should set this to
* false is if your application does not use the global resource
* file at all (very rare).
*
- * @param xmlfile The application's local resource file.
+ * @param file The application's local resource file.
* @param global If @p true, then the global resource file will also
* be parsed.
*/
void setResourceFile(const QString &file, bool global = true);
/**
* Sets the default toolbar which will be auto-selected for all
* KEditToolBar instances. Can be overridden on a per-dialog basis
* by calling setDefaultToolBar( const QString& ) on the dialog.
- * @param toolbarName the name of the tool bar
+ * @param toolBarName the name of the tool bar
*/
static void setGlobalDefaultToolBar(const char *toolBarName); // TODO should be const QString&
Q_SIGNALS:
/**
* Signal emitted when 'apply' or 'ok' is clicked or toolbars were reset.
* Connect to it, to plug action lists and to call applyMainWindowSettings
* (see sample code in this class's documentation)
*/
void newToolBarConfig();
QT_MOC_COMPAT void newToolbarConfig();
protected:
void showEvent(QShowEvent *event) override;
void hideEvent(QHideEvent *event) override;
private:
friend class KEditToolBarPrivate;
KEditToolBarPrivate *const d;
Q_PRIVATE_SLOT(d, void _k_slotButtonClicked(QAbstractButton *))
Q_PRIVATE_SLOT(d, void _k_acceptOK(bool))
Q_PRIVATE_SLOT(d, void _k_enableApply(bool))
Q_DISABLE_COPY(KEditToolBar)
};
#endif // _KEDITTOOLBAR_H
diff --git a/libs/widgetutils/xmlgui/kedittoolbar_p.h b/libs/widgetutils/xmlgui/kedittoolbar_p.h
index 52f31c2a5e..56f9df7233 100644
--- a/libs/widgetutils/xmlgui/kedittoolbar_p.h
+++ b/libs/widgetutils/xmlgui/kedittoolbar_p.h
@@ -1,256 +1,255 @@
/* This file is part of the KDE libraries
Copyright (C) 2000 Kurt Granroth <granroth@kde.org>
Copyright (C) 2006 Hamish Rodda <rodda@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KEDITTOOLBARP_H
#define KEDITTOOLBARP_H
#include "kxmlguiclient.h"
#include <QDialog>
#include <QListWidget>
class QDialogButtonBox;
class QLineEdit;
class QCheckBox;
namespace KDEPrivate
{
class ToolBarItem;
class KEditToolBarWidgetPrivate;
class ToolBarListWidget : public QListWidget
{
Q_OBJECT
public:
ToolBarListWidget(QWidget *parent = 0);
void makeVisible(QListWidgetItem *item)
{
scrollTo(indexFromItem(item));
}
ToolBarItem *currentItem() const;
void setActiveList(bool isActiveList)
{
m_activeList = isActiveList;
}
Q_SIGNALS:
void dropped(ToolBarListWidget *list, int index, ToolBarItem *item, bool sourceIsActiveList);
protected:
Qt::DropActions supportedDropActions() const override
{
return Qt::MoveAction;
}
QStringList mimeTypes() const override
{
return QStringList() << QStringLiteral("application/x-kde-action-list");
}
QMimeData *mimeData(const QList<QListWidgetItem *> items) const override;
bool dropMimeData(int index, const QMimeData *data, Qt::DropAction action) override;
// Skip internal dnd handling in QListWidget ---- how is one supposed to figure this out
// without reading the QListWidget code !?
void dropEvent(QDropEvent *ev) override
{
QAbstractItemView::dropEvent(ev);
}
private:
bool m_activeList;
};
class IconTextEditDialog : public QDialog
{
Q_OBJECT
public:
explicit IconTextEditDialog(QWidget *parent = 0);
public:
void setIconText(const QString &text);
QString iconText() const;
void setTextAlongsideIconHidden(bool hidden);
bool textAlongsideIconHidden() const;
private Q_SLOTS:
void slotTextChanged(const QString &text);
private:
QLineEdit *m_lineEdit;
QCheckBox *m_cbHidden;
QDialogButtonBox *m_buttonBox;
};
/**
* @short A widget used to customize or configure toolbars
*
* This is the widget that does all of the work for the
* KEditToolBar dialog. In most cases, you will want to use the
* dialog instead of this widget directly.
*
* Typically, you would use this widget only if you wanted to embed
* the toolbar editing directly into your existing configure or
* preferences dialog.
*
* This widget only works if your application uses the XML UI
* framework for creating menus and toolbars. It depends on the XML
* files to describe the toolbar layouts and it requires the actions
* to determine which buttons are active.
*
* @author Kurt Granroth <granroth@kde.org>
* @internal
*/
class KEditToolBarWidget : public QWidget, virtual public KXMLGUIClient
{
Q_OBJECT
public:
/**
* Old constructor for apps that do not use components.
* This constructor is somewhat deprecated, since it doesn't work
* with any KXMLGuiClient being added to the mainwindow.
* You really want to use the other constructor.
*
* You @em must pass along your collection of actions (some of which appear in your toolbars).
* Then call old-style load.
*
* @param collection The collection of actions to work on
* @param parent This widget's parent
*/
explicit KEditToolBarWidget(KActionCollection *collection,
QWidget *parent = 0L);
/**
* Main constructor.
*
* Use this like so:
* \code
* KEditToolBarWidget widget(this);
* widget.load(factory());
* ...
* \endcode
*
- * @param factory Your application's factory object
* @param parent This widget's parent
*/
explicit KEditToolBarWidget(QWidget *parent = 0L);
/**
* Destructor. Note that any changes done in this widget will
* @p NOT be saved in the destructor. You @p must call save()
* to do that.
*/
~KEditToolBarWidget() override;
/**
* Old-style load.
*
* Loads the toolbar configuration into the widget. Should be called before being shown.
*
* @param resourceFile the name (absolute or relative) of your application's UI
* resource file. If it is left blank, then the resource file: share/apps/appname/appnameui.xmlgui
* is used. This is the same resource file that is used by the
* default createGUI function in KMainWindow so you're usually
* pretty safe in leaving it blank.
*
* @param global controls whether or not the
* global resource file is used. If this is true, then you may
* edit all of the actions in your toolbars -- global ones and
* local one. If it is false, then you may edit only your
* application's entries. The only time you should set this to
* false is if your application does not use the global resource
* file at all (very rare)
*
* @param defaultToolBar the default toolbar that will be selected when the dialog is shown.
* If not set, or QString() is passed in, the global default tool bar name
* will be used.
*
* @see KEditToolBar
*/
void load(const QString &resourceFile,
bool global = true,
const QString &defaultToolBar = QString());
/**
* Loads the toolbar configuration into the widget. Should be called before being shown.
*
* @param factory pointer to the XML GUI factory object for your application.
* It contains a list of all of the GUI clients (along with the action
* collections and xml files) and the toolbar editor uses that.
*
* @param defaultToolBar the default toolbar that will be selected when the dialog is shown.
* If not set, or QString() is passed in, the global default tool bar name
* will be used.
*
* @see KEditToolBar
*/
void load(KXMLGUIFactory *factory,
const QString &defaultToolBar = QString());
/**
* @internal Reimplemented for internal purposes.
*/
KActionCollection *actionCollection() const override;
/**
* Save any changes the user made. The file will be in the user's
* local directory (usually $HOME/.kde/share/apps/\<appname\>). The
* filename will be the one specified in the constructor.. or the
* made up one if the filename was NULL.
*
*/
void save();
/**
* Remove and readd all KMXLGUIClients to update the GUI
*/
void rebuildKXMLGUIClients();
Q_SIGNALS:
/**
* Emitted whenever any modifications are made by the user.
*/
void enableOk(bool);
private:
Q_PRIVATE_SLOT(d, void slotToolBarSelected(int index))
Q_PRIVATE_SLOT(d, void slotInactiveSelectionChanged())
Q_PRIVATE_SLOT(d, void slotActiveSelectionChanged())
Q_PRIVATE_SLOT(d, void slotInsertButton())
Q_PRIVATE_SLOT(d, void slotRemoveButton())
Q_PRIVATE_SLOT(d, void slotUpButton())
Q_PRIVATE_SLOT(d, void slotDownButton())
Q_PRIVATE_SLOT(d, void slotDropped(ToolBarListWidget *, int, ToolBarItem *, bool))
private:
friend class KEditToolBarWidgetPrivate;
KEditToolBarWidgetPrivate *const d;
Q_DISABLE_COPY(KEditToolBarWidget)
};
}
#endif
diff --git a/libs/widgetutils/xmlgui/kgesture_p.h b/libs/widgetutils/xmlgui/kgesture_p.h
index 4f50c9e107..2889529315 100644
--- a/libs/widgetutils/xmlgui/kgesture_p.h
+++ b/libs/widgetutils/xmlgui/kgesture_p.h
@@ -1,246 +1,247 @@
/* This file is part of the KDE libraries
Copyright (C) 2006,2007 Andreas Hartmetz (ahartmetz@gmail.com)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KGESTURE_H
#define KGESTURE_H
#include <kritawidgetutils_export.h>
#include <QString>
#include <QHash>
#include <QPolygon>
/*
kinds of gestures:
-shapes like triangle, right angle, line
-"rocker" (i.e. two mouse button) gestures
*/
class KShapeGesturePrivate;
//TODO: implement operator== for special situations like in KKeyChooser.
class KRITAWIDGETUTILS_EXPORT KShapeGesture
{
public:
/**
* Create a new invalid shape gesture.
*/
KShapeGesture();
/**
* Creates a new gesture consisting of given shape.
* If the gesture belongs to a KAction, and the user draws approximately the same shape
* on the screen while holding down the right mouse button, the action will trigger.
* @p shape must be a "reasonable" polygon. It must contain at least two points
* and it should contain at most 50 for performance reasons. No two consecutive points
* are allowed to be at the same position.
* @param shape shape to draw to trigger this gesture
*/
KShapeGesture(const QPolygon &shape);
/**
* Creates a new gesture from a string description.
* @param description create gesture according to this
*/
KShapeGesture(const QString &description);
/**
* Copies the given gesture.
* @param other gesture to copy
*/
KShapeGesture(const KShapeGesture &other);
/**
* Destructor.
*/
~KShapeGesture();
/**
* Set the shape to draw to trigger this gesture.
*/
void setShape(const QPolygon &shape);
/**
* set a user-visible name for this gesture's shape, like "triangle" or "line".
*/
void setShapeName(const QString &friendlyName);
/**
* Return the user-visible name for this gesture's shape, like "triangle" or "line".
*/
QString shapeName() const;
/**
* Return true if this gesture is valid.
*
*/
bool isValid() const;
/**
* Return a string representation of this gesture.
* Return empty string if invalid.
* This function is mainly for use with config files.
*
* @see shapeName()
*/
QString toString() const;
/**
* Return an idealized SVG image of this gesture.
* Return an empty image if invalid.
* @param attributes SVG attributes to apply to the SVG "path" element that
* makes up the drawing of the gesture. By default, only a 'fill="none"'
* attribute will be set.
*/
QByteArray toSvg(const QString &attributes = QString()) const;
/**
* Return a difference measurement betwenn this gesture and the @p other
* gesture. Abort comparison if difference is larger than @p abortThreshold
* and return a very large difference in that case.
* Usual return values range from x to y //TODO: fill in x and y
*/
float distance(const KShapeGesture &other, float abortThreshold) const;
/**
* Set this gesture to the other gesture.
*/
KShapeGesture &operator=(const KShapeGesture &other);
/**
* Return whether this gesture is equal to the other gesture.
*/
bool operator==(const KShapeGesture &other) const;
/**
* Return the opposite of operator==()
*/
bool operator!=(const KShapeGesture &other) const;
/**
* Return an opaque value for use in hash tables
*/
uint hashable() const;
private:
KShapeGesturePrivate *const d;
};
inline uint qHash(const KShapeGesture &key)
{
return qHash(key.hashable());
}
class KRockerGesturePrivate;
class KRITAWIDGETUTILS_EXPORT KRockerGesture
{
public:
/**
* Create a new invalid rocker gesture.
*/
KRockerGesture();
/**
* Creates a new gesture consisting of given buttons.
- * @param description create gesture according to this
+ * @param hold create gesture according to this hold
+ * @param thenPush create gesture according to this push
*/
KRockerGesture(enum Qt::MouseButton hold, enum Qt::MouseButton thenPush);
/**
* Creates a new gesture from a string description.
* @param description create gesture according to this
*/
KRockerGesture(const QString &description);
/**
* Copies the given gesture.
* @param other gesture to copy
*/
KRockerGesture(const KRockerGesture &other);
/**
* Destructor.
*/
~KRockerGesture();
/**
* set button combination to trigger
*/
void setButtons(Qt::MouseButton hold, Qt::MouseButton thenPush);
/**
* Write the button combination to hold and thenPush
*/
void getButtons(Qt::MouseButton *hold, Qt::MouseButton *thenPush) const;
/**
* Return a user-friendly name of the button combination.
*/
QString rockerName() const;
/**
* Return a user-friendly name for the mouse button button
*/
static QString mouseButtonName(Qt::MouseButton button);
/**
* Return true if this gesture is valid.
*/
bool isValid() const;
/**
* Return a string representation of this gesture.
* Return an empty string if invalid.
* This function is mainly for use with config files.
*
* @see rockerName()
*/
QString toString() const;
/**
* Set this gesture to the other gesture.
*/
KRockerGesture &operator=(const KRockerGesture &other);
/**
* Return whether this gesture is equal to the other gesture.
*/
bool operator==(const KRockerGesture &other) const;
/**
* Return the opposite of operator==()
*/
bool operator!=(const KRockerGesture &other) const;
/**
* Return an opaque value for use in hash tables
*/
uint hashable() const;
private:
KRockerGesturePrivate *const d;
};
inline uint qHash(const KRockerGesture &key)
{
return qHash(key.hashable());
}
//KGESTURE_H
#endif
diff --git a/libs/widgetutils/xmlgui/kswitchlanguagedialog_p.h b/libs/widgetutils/xmlgui/kswitchlanguagedialog_p.h
index 573198b5e1..92880eeeca 100644
--- a/libs/widgetutils/xmlgui/kswitchlanguagedialog_p.h
+++ b/libs/widgetutils/xmlgui/kswitchlanguagedialog_p.h
@@ -1,94 +1,89 @@
/*
* This file is part of the KDE Libraries
* Copyright (C) 2007 Krzysztof Lichota (lichota@mimuw.edu.pl)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef _KSWITCHLANGUAGEDIALOG_H_
#define _KSWITCHLANGUAGEDIALOG_H_
#include <QDialog>
namespace KDEPrivate
{
class KSwitchLanguageDialogPrivate;
/**
* @short Standard "switch application language" dialog box.
*
* This class provides "switch application language" dialog box that is used
* in KHelpMenu
*
* @author Krzysztof Lichota (lichota@mimuw.edu.pl)
* @internal
*/
class KSwitchLanguageDialog : public QDialog
{
Q_OBJECT
public:
/**
* Constructor. Creates a fully featured "Switch application language" dialog box.
* Note that this dialog is made modeless in the KHelpMenu class so
* the users may expect a modeless dialog.
*
* @param parent The parent of the dialog box. You should use the
* toplevel window so that the dialog becomes centered.
- * @param name Internal name of the widget. This name in not used in the
- * caption.
- * @param modal If false, this widget will be modeless and must be
- * made visible using QWidget::show(). Otherwise it will be
- * modal and must be made visible using QWidget::exec()
*/
KSwitchLanguageDialog(QWidget *parent = 0);
~KSwitchLanguageDialog() override;
protected Q_SLOTS:
/**
* Activated when the Ok button has been clicked.
*/
virtual void slotOk();
void slotDefault();
/**
Called when one of language buttons changes state.
*/
virtual void languageOnButtonChanged(const QString &);
/**
Called to add one language button to dialog.
*/
virtual void slotAddLanguageButton();
/**
Called when "Remove" language button is clicked.
*/
virtual void removeButtonClicked();
private:
KSwitchLanguageDialogPrivate *const d;
friend class KSwitchLanguageDialogPrivate;
};
}
#endif
diff --git a/libs/widgetutils/xmlgui/ktoggletoolbaraction.h b/libs/widgetutils/xmlgui/ktoggletoolbaraction.h
index 63653ef29f..2110b6daf6 100644
--- a/libs/widgetutils/xmlgui/ktoggletoolbaraction.h
+++ b/libs/widgetutils/xmlgui/ktoggletoolbaraction.h
@@ -1,95 +1,98 @@
/* This file is part of the KDE libraries
Copyright (C) 1999 Reginald Stadlbauer <reggie@kde.org>
(C) 1999 Simon Hausmann <hausmann@kde.org>
(C) 2000 Nicolas Hadacek <haadcek@kde.org>
(C) 2000 Kurt Granroth <granroth@kde.org>
(C) 2000 Michael Koch <koch@kde.org>
(C) 2001 Holger Freyther <freyther@kde.org>
(C) 2002 Ellis Whitehead <ellis@kde.org>
(C) 2003 Andras Mantia <amantia@kde.org>
(C) 2005-2006 Hamish Rodda <rodda@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KTOGGLETOOLBARACTION_H
#define KTOGGLETOOLBARACTION_H
#include <ktoggleaction.h>
#include <kritawidgetutils_export.h>
class KToolBar;
/**
* An action that takes care of everything associated with
* showing or hiding a toolbar by a menu action. It will
* show or hide the toolbar with the given name when
* activated, and check or uncheck itself if the toolbar
* is manually shown or hidden.
*
* If you need to perform some additional action when the
* toolbar is shown or hidden, connect to the toggled(bool)
* signal. It will be emitted after the toolbar's
* visibility has changed, whenever it changes.
*/
class KRITAWIDGETUTILS_EXPORT KToggleToolBarAction : public KToggleAction
{
Q_OBJECT
public:
/**
* Create a KToggleToolbarAction that manages the toolbar
- * named toolBarName. This can be either the name of a
+ * named @p toolBarName. This can be either the name of a
* toolbar in an xml ui file, or a toolbar programmatically
* created with that name.
*
- * @param The action's parent object.
+ * @param toolBarName The toolbar name.
+ * @param text The toolbar hint text.
+ * @param parent The action's parent object.
*/
KToggleToolBarAction(const char *toolBarName, const QString &text, QObject *parent);
/**
- * Create a KToggleToolbarAction that manages the @param toolBar.
+ * Create a KToggleToolbarAction that manages the @p toolBar.
* This can be either the name of a toolbar in an xml ui file,
* or a toolbar programmatically created with that name.
*
* @param toolBar the toolbar to be managed
+ * @param text The action's text
* @param parent The action's parent object.
*/
KToggleToolBarAction(KToolBar *toolBar, const QString &text, QObject *parent);
/**
* Destroys toggle toolbar action.
*/
~KToggleToolBarAction() override;
/**
* Returns a pointer to the tool bar it manages.
*/
KToolBar *toolBar();
/**
* Reimplemented from @see QObject.
*/
bool eventFilter(QObject *watched, QEvent *event) override;
private Q_SLOTS:
void slotToggled(bool checked) override;
private:
class Private;
Private *const d;
};
#endif
diff --git a/libs/widgetutils/xmlgui/ktoolbar.h b/libs/widgetutils/xmlgui/ktoolbar.h
index 447a03f7cf..6931e61243 100644
--- a/libs/widgetutils/xmlgui/ktoolbar.h
+++ b/libs/widgetutils/xmlgui/ktoolbar.h
@@ -1,215 +1,215 @@
/* This file is part of the KDE libraries
Copyright (C) 2000 Reginald Stadlbauer (reggie@kde.org)
(C) 1997, 1998 Stephan Kulow (coolo@kde.org)
(C) 1997, 1998 Sven Radej (radej@kde.org)
(C) 1997, 1998 Mark Donohoe (donohoe@kde.org)
(C) 1997, 1998 Matthias Ettrich (ettrich@kde.org)
(C) 1999, 2000 Kurt Granroth (granroth@kde.org)
(C) 2005-2006 Hamish Rodda (rodda@kde.org)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KTOOLBAR_H
#define KTOOLBAR_H
#include <kritawidgetutils_export.h>
#include <QToolBar>
class QDomElement;
class KConfigGroup;
class KConfig;
class KMainWindow;
class KXMLGUIClient;
/**
* @short Floatable toolbar with auto resize.
*
* A KDE-style toolbar.
*
* KToolBar can be used as a standalone widget, but KMainWindow
* provides easy factories and management of one or more toolbars.
*
* KToolBar uses a global config group to load toolbar settings on
* construction. It will reread this config group on a
* KApplication::appearanceChanged() signal.
*
* KToolBar respects Kiosk settings (see the KAuthorized namespace in the
* KConfig framework). In particular, system administrators can prevent users
* from moving toolbars with the "movable_toolbars" action, and from showing or
* hiding toolbars with the "options_show_toolbar" action. For example, to
* disable both, add the following the application or global configuration:
* @verbatim
[KDE Action Restrictions][$i]
movable_toolbars=false
options_show_toolbar=false
@endverbatim
*
* @note If you can't depend on KXmlGui but you want to integrate with KDE, you can use QToolBar with:
* Set ToolButtonStyle to Qt::ToolButtonFollowStyle, this will make QToolBar use the settings for "Main Toolbar"
* Additionally set QToolBar::setProperty("otherToolbar", true) to use settings for "Other toolbars"
* Settings from "Other toolbars" will only work on widget styles derived from KStyle
* @author Reginald Stadlbauer <reggie@kde.org>, Stephan Kulow <coolo@kde.org>, Sven Radej <radej@kde.org>, Hamish Rodda <rodda@kde.org>.
*/
class KRITAWIDGETUTILS_EXPORT KToolBar : public QToolBar
{
Q_OBJECT
public:
/**
* Constructor.
*
* This constructor takes care of adding the toolbar to the mainwindow,
* if @p parent is a QMainWindow.
*
* Normally KDE applications do not call this directly, they either
* call KMainWindow::toolBar(name), or they use XML-GUI and specify
* toolbars using XML.
*
* @param objectName The QObject name of this toolbar, required so that QMainWindow can save and load the toolbar position,
* and so that KToolBar can find out if it's the main toolbar.
* @param parent The standard toolbar parent (usually a KMainWindow)
* @param readConfig whether to apply the configuration (global and application-specific)
*/
explicit KToolBar(const QString &objectName, QWidget *parent, bool readConfig = true);
/**
* Destroys the toolbar.
*/
~KToolBar() override;
/**
* Returns the main window that this toolbar is docked with.
*/
KMainWindow *mainWindow() const;
/**
* Convenience function to set icon size
*/
void setIconDimensions(int size);
/**
* Returns the default size for this type of toolbar.
*
* @return the default size for this type of toolbar.
*/
int iconSizeDefault() const; // KDE5: hide from public API. Doesn't make sense to export this, and it isn't used.
/**
* Save the toolbar settings to group @p configGroup in @p config.
*/
void saveSettings(KConfigGroup &cg);
/**
* Read the toolbar settings from group @p configGroup in @p config
* and apply them.
*/
void applySettings(const KConfigGroup &cg);
/**
* Adds an XML gui client that uses this toolbar
* @since 4.8.1
*/
void addXMLGUIClient(KXMLGUIClient *client);
/**
* Removes an XML gui client that uses this toolbar
* @since 4.8.5
*/
void removeXMLGUIClient(KXMLGUIClient *client);
/**
- * Load state from an XML @param element, called by KXMLGUIBuilder.
+ * Load state from an XML @p element, called by KXMLGUIBuilder.
*/
void loadState(const QDomElement &element);
/**
- * Save state into an XML @param element, called by KXMLGUIBuilder.
+ * Save state into an XML @p element, called by KXMLGUIBuilder.
*/
void saveState(QDomElement &element) const;
/**
* Reimplemented to support context menu activation on disabled tool buttons.
*/
bool eventFilter(QObject *watched, QEvent *event) override;
/**
* Returns whether the toolbars are currently editable (drag & drop of actions).
*/
static bool toolBarsEditable();
/**
* Enable or disable toolbar editing via drag & drop of actions. This is
* called by KEditToolbar and should generally be set to disabled whenever
* KEditToolbar is not active.
*/
static void setToolBarsEditable(bool editable);
/**
* Returns whether the toolbars are locked (i.e., moving of the toobars disallowed).
*/
static bool toolBarsLocked();
/**
* Allows you to lock and unlock all toolbars (i.e., disallow/allow moving of the toobars).
*/
static void setToolBarsLocked(bool locked);
/**
* Emits a dbus signal to tell all toolbars in all applications, that the user settings have
* changed.
* @since 5.0
*/
static void emitToolbarStyleChanged();
protected Q_SLOTS:
virtual void slotMovableChanged(bool movable);
protected:
void contextMenuEvent(QContextMenuEvent *) override;
void actionEvent(QActionEvent *) override;
// Draggable toolbar configuration
void dragEnterEvent(QDragEnterEvent *) override;
void dragMoveEvent(QDragMoveEvent *) override;
void dragLeaveEvent(QDragLeaveEvent *) override;
void dropEvent(QDropEvent *) override;
void mousePressEvent(QMouseEvent *) override;
void mouseMoveEvent(QMouseEvent *) override;
void mouseReleaseEvent(QMouseEvent *) override;
private:
class Private;
Private *const d;
Q_PRIVATE_SLOT(d, void slotAppearanceChanged())
Q_PRIVATE_SLOT(d, void slotContextAboutToShow())
Q_PRIVATE_SLOT(d, void slotContextAboutToHide())
Q_PRIVATE_SLOT(d, void slotContextLeft())
Q_PRIVATE_SLOT(d, void slotContextRight())
Q_PRIVATE_SLOT(d, void slotContextShowText())
Q_PRIVATE_SLOT(d, void slotContextTop())
Q_PRIVATE_SLOT(d, void slotContextBottom())
Q_PRIVATE_SLOT(d, void slotContextIcons())
Q_PRIVATE_SLOT(d, void slotContextText())
Q_PRIVATE_SLOT(d, void slotContextTextRight())
Q_PRIVATE_SLOT(d, void slotContextTextUnder())
Q_PRIVATE_SLOT(d, void slotContextIconSize())
Q_PRIVATE_SLOT(d, void slotLockToolBars(bool))
};
#endif
diff --git a/libs/widgetutils/xmlgui/ktoolbarhandler_p.h b/libs/widgetutils/xmlgui/ktoolbarhandler_p.h
index 7899b05635..5c5a1bf26c 100644
--- a/libs/widgetutils/xmlgui/ktoolbarhandler_p.h
+++ b/libs/widgetutils/xmlgui/ktoolbarhandler_p.h
@@ -1,72 +1,72 @@
/* This file is part of the KDE libraries
Copyright (C) 2002 Simon Hausmann <hausmann@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KTOOLBARHANDLER_H
#define KTOOLBARHANDLER_H
#include <QLinkedList>
#include <QObject>
#include <QPointer>
#include <kxmlguiclient.h>
class KXmlGuiWindow;
namespace KDEPrivate
{
class ToolBarHandler : public QObject, public KXMLGUIClient
{
Q_OBJECT
public:
/**
* Creates a new tool bar handler for the supplied
- * @param mainWindow.
+ * @p mainWindow.
*/
explicit ToolBarHandler(KXmlGuiWindow *mainWindow);
/**
* Creates a new tool bar handler for the supplied
- * @param mainWindow and with the supplied parent.
+ * @p mainWindow and with the supplied @p parent.
*/
ToolBarHandler(KXmlGuiWindow *mainWindow, QObject *parent);
/**
* Destroys the tool bar handler.
*/
~ToolBarHandler() override;
/**
* Returns the action which is responsible for the tool bar menu.
*/
QAction *toolBarMenuAction();
public Q_SLOTS:
void setupActions();
private:
class Private;
Private *const d;
Q_PRIVATE_SLOT(d, void clientAdded(KXMLGUIClient *))
};
} // namespace KDEPrivate
#endif // KTOOLBARHANDLER_H
diff --git a/libs/widgetutils/xmlgui/kxmlguibuilder.h b/libs/widgetutils/xmlgui/kxmlguibuilder.h
index 274d425238..ca88bd8820 100644
--- a/libs/widgetutils/xmlgui/kxmlguibuilder.h
+++ b/libs/widgetutils/xmlgui/kxmlguibuilder.h
@@ -1,94 +1,94 @@
/* This file is part of the KDE project
Copyright (C) 2000 Simon Hausmann <hausmann@kde.org>
David Faure <faure@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef kxmlguibuilder_h
#define kxmlguibuilder_h
#include <kritawidgetutils_export.h>
class KXMLGUIBuilderPrivate;
class KXMLGUIClient;
class QAction;
class QDomElement;
class QStringList;
class QWidget;
/**
* Implements the creation of the GUI (menubar, menus and toolbars)
* as requested by the GUI factory.
*
* The virtual methods are mostly for historical reasons, there isn't really
* a need to derive from KXMLGUIBuilder anymore.
*/
class KRITAWIDGETUTILS_EXPORT KXMLGUIBuilder
{
public:
explicit KXMLGUIBuilder(QWidget *widget);
virtual ~KXMLGUIBuilder();
/* @internal */
KXMLGUIClient *builderClient() const;
/* @internal */
void setBuilderClient(KXMLGUIClient *client);
/* @internal */
QWidget *widget();
virtual QStringList containerTags() const;
/**
* Creates a container (menubar/menu/toolbar/statusbar/separator/...)
* from an element in the XML file
*
* @param parent The parent for the container
* @param index The index where the container should be inserted
* into the parent container/widget
* @param element The element from the DOM tree describing the
* container (use it to access container specified
* attributes or child elements)
- * @param action The action created for this container; used for e.g. passing to removeContainer.
+ * @param containerAction The action created for this container; used for e.g. passing to removeContainer.
*/
virtual QWidget *createContainer(QWidget *parent, int index,
const QDomElement &element, QAction *&containerAction);
/**
* Removes the given (and previously via createContainer )
* created container.
*
*/
virtual void removeContainer(QWidget *container, QWidget *parent,
QDomElement &element, QAction *containerAction);
virtual QStringList customTags() const;
virtual QAction *createCustomElement(QWidget *parent, int index, const QDomElement &element);
virtual void removeCustomElement(QWidget *parent, QAction *action);
virtual void finalizeGUI(KXMLGUIClient *client);
protected:
virtual void virtual_hook(int id, void *data);
private:
KXMLGUIBuilderPrivate *const d;
};
#endif
diff --git a/packaging/linux/appimage/build-deps.sh b/packaging/linux/appimage/build-deps.sh
index 44f8914b31..001ccb03f8 100755
--- a/packaging/linux/appimage/build-deps.sh
+++ b/packaging/linux/appimage/build-deps.sh
@@ -1,83 +1,84 @@
#!/bin/bash
#
# Build all Krita's dependencies on Ubuntu 14.04.
#
# Prerequisites: cmake git build-essential libxcb-keysyms1-dev plus all deps for Qt5
#
# Halt on errors and be verbose about what we are doing
set -e
set -x
# Read in our parameters
export BUILD_PREFIX=$1
export KRITA_SOURCES=$2
# qjsonparser, used to add metadata to the plugins needs to work in a en_US.UTF-8 environment.
# That's not always the case, so make sure it is
export LC_ALL=en_US.UTF-8
export LANG=en_us.UTF-8
# We want to use $prefix/deps/usr/ for all our dependencies
export DEPS_INSTALL_PREFIX=$BUILD_PREFIX/deps/usr/
export DOWNLOADS_DIR=$BUILD_PREFIX/downloads/
# Setup variables needed to help everything find what we build
export LD_LIBRARY_PATH=$DEPS_INSTALL_PREFIX/lib:$LD_LIBRARY_PATH
export PATH=$DEPS_INSTALL_PREFIX/bin:$PATH
export PKG_CONFIG_PATH=$DEPS_INSTALL_PREFIX/share/pkgconfig:$DEPS_INSTALL_PREFIX/lib/pkgconfig:/usr/lib/pkgconfig:$PKG_CONFIG_PATH
export CMAKE_PREFIX_PATH=$DEPS_INSTALL_PREFIX:$CMAKE_PREFIX_PATH
# A krita build layout looks like this:
# krita/ -- the source directory
# downloads/ -- downloads of the dependencies from files.kde.org
# deps-build/ -- build directory for the dependencies
# deps/ -- the location for the built dependencies
# build/ -- build directory for krita itself
# krita.appdir/ -- install directory for krita and the dependencies
# Make sure our downloads directory exists
if [ ! -d $DOWNLOADS_DIR ] ; then
mkdir -p $DOWNLOADS_DIR
fi
# Make sure our build directory exists
if [ ! -d $BUILD_PREFIX/deps-build/ ] ; then
mkdir -p $BUILD_PREFIX/deps-build/
fi
# The 3rdparty dependency handling in Krita also requires the install directory to be pre-created
if [ ! -d $DEPS_INSTALL_PREFIX ] ; then
mkdir -p $DEPS_INSTALL_PREFIX
fi
# Switch to our build directory as we're basically ready to start building...
cd $BUILD_PREFIX/deps-build/
# Configure the dependencies for building
cmake $KRITA_SOURCES/3rdparty -DCMAKE_INSTALL_PREFIX=$DEPS_INSTALL_PREFIX -DINSTALL_ROOT=$DEPS_INSTALL_PREFIX -DEXTERNALS_DOWNLOAD_DIR=$DOWNLOADS_DIR
# Now start building everything we need, in the appropriate order
#cmake --build . --config RelWithDebInfo --target ext_png
#cmake --build . --config RelWithDebInfo --target ext_tiff
#cmake --build . --config RelWithDebInfo --target ext_jpeg
cmake --build . --config RelWithDebInfo --target ext_boost
cmake --build . --config RelWithDebInfo --target ext_eigen3
cmake --build . --config RelWithDebInfo --target ext_exiv2
cmake --build . --config RelWithDebInfo --target ext_fftw3
cmake --build . --config RelWithDebInfo --target ext_lcms2
cmake --build . --config RelWithDebInfo --target ext_ocio
cmake --build . --config RelWithDebInfo --target ext_openexr
cmake --build . --config RelWithDebInfo --target ext_vc
cmake --build . --config RelWithDebInfo --target ext_libraw
cmake --build . --config RelWithDebInfo --target ext_giflib
#cmake --build . --config RelWithDebInfo --target ext_gsl
cmake --build . --config RelWithDebInfo --target ext_python
#cmake --build . --config RelWithDebInfo --target ext_freetype
#cmake --build . --config RelWithDebInfo --target ext_fontconfig
cmake --build . --config RelWithDebInfo --target ext_qt
cmake --build . --config RelWithDebInfo --target ext_poppler
cmake --build . --config RelWithDebInfo --target ext_kcrash
cmake --build . --config RelWithDebInfo --target ext_gmic
cmake --build . --config RelWithDebInfo --target ext_sip
cmake --build . --config RelWithDebInfo --target ext_pyqt
+cmake --build . --config RelWithDebInfo --target ext_quazip
diff --git a/packaging/linux/appimage/build-gmic-qt.sh b/packaging/linux/appimage/build-gmic-qt.sh
index 64719a7117..70b8c61957 100755
--- a/packaging/linux/appimage/build-gmic-qt.sh
+++ b/packaging/linux/appimage/build-gmic-qt.sh
@@ -1,46 +1,47 @@
#!/bin/bash
# Halt on errors and be verbose about what we are doing
set -e
set -x
# Read in our parameters
export BUILD_PREFIX=$1
+export VERSION=2.4.2
# qjsonparser, used to add metadata to the plugins needs to work in a en_US.UTF-8 environment.
# That's not always the case, so make sure it is
export LC_ALL=en_US.UTF-8
export LANG=en_us.UTF-8
# We want to use $prefix/deps/usr/ for all our dependencies
export DEPS_INSTALL_PREFIX=$BUILD_PREFIX/deps/usr/
export DOWNLOADS_DIR=$BUILD_PREFIX/downloads/
# Setup variables needed to help everything find what we build
export LD_LIBRARY_PATH=$DEPS_INSTALL_PREFIX/lib:$LD_LIBRARY_PATH
export PATH=$DEPS_INSTALL_PREFIX/bin:$PATH
export PKG_CONFIG_PATH=$DEPS_INSTALL_PREFIX/share/pkgconfig:$DEPS_INSTALL_PREFIX/lib/pkgconfig:/usr/lib/pkgconfig:$PKG_CONFIG_PATH
export CMAKE_PREFIX_PATH=$DEPS_INSTALL_PREFIX:$CMAKE_PREFIX_PATH
# Switch to the build prefix
cd $BUILD_PREFIX
# G'Mic is built in build-deps.sh
# Therefore we just need to copy over the installation artifacts from that process
# First, make sure we have a clean slate to work from and setup the directory structure
rm -rf $BUILD_PREFIX/gmic_qt_krita.appdir/
mkdir -p $BUILD_PREFIX/gmic_qt_krita.appdir/usr/bin
mkdir -p $BUILD_PREFIX/gmic_qt_krita.appdir/usr/lib
mkdir -p $BUILD_PREFIX/gmic_qt_krita.appdir/usr/share
# Copy over the artifacts...
cp $DEPS_INSTALL_PREFIX/bin/gmic_krita_qt* $BUILD_PREFIX/gmic_qt_krita.appdir/usr/bin
cp $BUILD_PREFIX/deps-build/ext_gmic/gmic-qt/resources/gmic_hat.png $BUILD_PREFIX/gmic_qt_krita.appdir/gmic_krita_qt.png
# Now generate the Appimage for it
linuxdeployqt $BUILD_PREFIX/gmic_qt_krita.appdir/usr/bin/gmic_krita_qt.desktop -verbose=2 -bundle-non-qt-libs -appimage
# Make sure it has a consistent name too
-mv gmic_krita_qt-x86_64.AppImage gmic_krita_qt-x86_64.appimage
+mv gmic_krita_qt*x86_64.AppImage gmic_krita_qt-x86_64.appimage
diff --git a/packaging/linux/appimage/build-image.sh b/packaging/linux/appimage/build-image.sh
index 7f45b3236d..85795e0b3f 100755
--- a/packaging/linux/appimage/build-image.sh
+++ b/packaging/linux/appimage/build-image.sh
@@ -1,102 +1,102 @@
#!/bin/bash
# Halt on errors and be verbose about what we are doing
set -e
set -x
# Read in our parameters
export BUILD_PREFIX=$1
export KRITA_SOURCES=$2
# Save some frequently referenced locations in variables for ease of use / updating
export APPDIR=$BUILD_PREFIX/krita.appdir
export PLUGINS=$APPDIR/usr/lib/kritaplugins/
# qjsonparser, used to add metadata to the plugins needs to work in a en_US.UTF-8 environment.
# That's not always the case, so make sure it is
export LC_ALL=en_US.UTF-8
export LANG=en_us.UTF-8
# We want to use $prefix/deps/usr/ for all our dependencies
export DEPS_INSTALL_PREFIX=$BUILD_PREFIX/deps/usr/
export DOWNLOADS_DIR=$BUILD_PREFIX/downloads/
# Setup variables needed to help everything find what we built
export LD_LIBRARY_PATH=$DEPS_INSTALL_PREFIX/lib/:$DEPS_INSTALL_PREFIX/lib/x86_64-linux-gnu/:$APPDIR/usr/lib/:$LD_LIBRARY_PATH
export PATH=$DEPS_INSTALL_PREFIX/bin/:$PATH
export PKG_CONFIG_PATH=$DEPS_INSTALL_PREFIX/share/pkgconfig/:$DEPS_INSTALL_PREFIX/lib/pkgconfig/:/usr/lib/pkgconfig/:$PKG_CONFIG_PATH
export CMAKE_PREFIX_PATH=$DEPS_INSTALL_PREFIX:$CMAKE_PREFIX_PATH
export PYTHONPATH=$DEPS_INSTALL_PREFIX/sip/:$DEPS_INSTALL_PREFIX/lib/python3.5/site-packages/:$DEPS_INSTALL_PREFIX/lib/python3.5/
export PYTHONHOME=$DEPS_INSTALL_PREFIX
# Switch over to our build prefix
cd $BUILD_PREFIX
#
# Now we can get the process started!
#
# Step 0: place the translations where ki18n and Qt look for them
if [ -d $APPDIR/usr/share/locale ] ; then
mv $APPDIR/usr/share/locale $APPDIR/usr/share/krita
fi
# Step 1: Copy over all the resources provided by dependencies that we need
cp -r $DEPS_INSTALL_PREFIX/share/locale $APPDIR/usr/share/krita
cp -r $DEPS_INSTALL_PREFIX/share/kf5 $APPDIR/usr/share
cp -r $DEPS_INSTALL_PREFIX/share/mime $APPDIR/usr/share
cp -r $DEPS_INSTALL_PREFIX/lib/python3.5 $APPDIR/usr/lib
cp -r $DEPS_INSTALL_PREFIX/sip $APPDIR/usr/lib/
cp -r $DEPS_INSTALL_PREFIX/translations $APPDIR/usr/
# Step 2: Relocate x64 binaries from the architecture specific directory as required for Appimages
mv $APPDIR/usr/lib/x86_64-linux-gnu/* $APPDIR/usr/lib
rm -rf $APPDIR/usr/lib/x86_64-linux-gnu/
# Step 3: Update the rpath in the various plugins we have to make sure they'll be loadable in an Appimage context
for lib in $PLUGINS/*.so*; do
patchelf --set-rpath '$ORIGIN/..' $lib;
done
for lib in $APPDIR/usr/lib/python3.5/site-packages/PyQt5/*.so*; do
patchelf --set-rpath '$ORIGIN/../..' $lib;
done
for lib in $APPDIR/usr/lib/python3.5/lib-dynload/*.so*; do
patchelf --set-rpath '$ORIGIN/../..' $lib;
done
patchelf --set-rpath '$ORIGIN/../../../..' $APPDIR/usr/lib/qml/org/krita/draganddrop/libdraganddropplugin.so
patchelf --set-rpath '$ORIGIN/../../../..' $APPDIR/usr/lib/qml/org/krita/sketch/libkritasketchplugin.so
patchelf --set-rpath '$ORIGIN/../..' $APPDIR/usr/lib/krita-python-libs/PyKrita/krita.so
patchelf --set-rpath '$ORIGIN/../..' $APPDIR/usr/lib/sip/sip.so
-# Step 4: Build the image!!!
-linuxdeployqt $APPDIR/usr/share/applications/org.kde.krita.desktop \
- -executable=$APPDIR/usr/bin/krita \
- -qmldir=$DEPS_INSTALL_PREFIX/qml \
- -verbose=2 \
- -bundle-non-qt-libs \
- -extra-plugins=$PLUGINS,$APPDIR/usr/lib/krita-python-libs/PyKrita/krita.so,$APPDIR/usr/lib//qml/org/krita/sketch/libkritasketchplugin.so,$APPDIR/usr/lib/qml/org/krita/draganddrop/libdraganddropplugin.so \
- -appimage
-
# Step 5: Find out what version of Krita we built and give the Appimage a proper name
cd $BUILD_PREFIX/krita-build
KRITA_VERSION=$(grep "#define KRITA_VERSION_STRING" libs/version/kritaversion.h | cut -d '"' -f 2)
# Also find out the revision of Git we built
# Then use that to generate a combined name we'll distribute
cd $KRITA_SOURCES
if [[ -d .git ]]; then
GIT_REVISION=$(git rev-parse --short HEAD)
VERSION=$KRITA_VERSION-$GIT_REVISION
else
VERSION=$KRITA_VERSION
fi
# Return to our build root
cd $BUILD_PREFIX
+# Step 4: Build the image!!!
+linuxdeployqt $APPDIR/usr/share/applications/org.kde.krita.desktop \
+ -executable=$APPDIR/usr/bin/krita \
+ -qmldir=$DEPS_INSTALL_PREFIX/qml \
+ -verbose=2 \
+ -bundle-non-qt-libs \
+ -extra-plugins=$PLUGINS,$APPDIR/usr/lib/krita-python-libs/PyKrita/krita.so,$APPDIR/usr/lib//qml/org/krita/sketch/libkritasketchplugin.so,$APPDIR/usr/lib/qml/org/krita/draganddrop/libdraganddropplugin.so \
+ -appimage
+
# Generate a new name for the Appimage file and rename it accordingly
APPIMAGE=krita-"$VERSION"-x86_64.appimage
-mv Krita-x86_64.AppImage $APPIMAGE
+mv Krita*x86_64.AppImage $APPIMAGE
diff --git a/packaging/linux/snap/build.sh b/packaging/linux/snap/build.sh
new file mode 100755
index 0000000000..4c0ff92e32
--- /dev/null
+++ b/packaging/linux/snap/build.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+set -ex
+
+IMAGE=ubuntu:18.04
+CONTAINER=container-krita-snap
+
+function at_exit {
+ lxc stop $CONTAINER
+}
+
+lxc stop $CONTAINER || true
+
+lxc launch --ephemeral "$IMAGE" $CONTAINER
+sleep 4 # so network is up
+
+trap at_exit INT TERM EXIT
+
+lxc file push --recursive . $CONTAINER/workspace
+lxc exec $CONTAINER -- /workspace/build_in_container.sh
+lxc file pull --recursive $CONTAINER/workspace/result .
diff --git a/packaging/linux/snap/build_in_container.sh b/packaging/linux/snap/build_in_container.sh
new file mode 100755
index 0000000000..eb99551dda
--- /dev/null
+++ b/packaging/linux/snap/build_in_container.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+set -ex
+
+cd /workspace
+
+ping -c1 networkcheck.kde.org
+
+apt-key adv --keyserver keyserver.ubuntu.com --recv E6D4736255751E5D
+echo 'deb http://archive.neon.kde.org/unstable bionic main' > /etc/apt/sources.list.d/neon.list
+apt update
+
+snap install --edge --classic snapcraft
+
+snapcraft --version
+snapcraft --destructive-mode
+
+mkdir -p result
+mv *.snap result/
diff --git a/packaging/linux/snap/kf5-locale-gen b/packaging/linux/snap/kf5-locale-gen
new file mode 100755
index 0000000000..37bba2a46c
--- /dev/null
+++ b/packaging/linux/snap/kf5-locale-gen
@@ -0,0 +1,115 @@
+#!/usr/bin/perl
+#
+# Copyright 2019 Harald Sitter <sitter@kde.org>
+#
+# 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 3 of
+# the License or any later version accepted by the membership of
+# KDE e.V. (or its successor approved by the membership of KDE
+# e.V.), which shall act as a proxy defined in Section 14 of
+# version 3 of the license.
+#
+# 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, see <http://www.gnu.org/licenses/>.
+
+use strict;
+
+my $debug = 0;
+if (defined $ENV{'LOCALE_DEBUG'}) {
+ $debug = 1;
+}
+
+my $config_dir = ($ENV{'XDG_CONFIG_HOME'} or "$ENV{'HOME'}/.config");
+my $conf="$config_dir/klanguageoverridesrc";
+
+my @languages = ('C.UTF-8');
+
+sub debug {
+ print(@_) if $debug;
+}
+
+# Grab langs from languageoverrides file (in-app language switch feature).
+if (-e $conf) {
+ my $group;
+ open my $ini, '<', $conf or die "Can't open $conf: $!\n";
+ while (<$ini>) { # loops in $_
+ chomp;
+ my $keyword;
+ my $value;
+ if (/^\s*\[(.+)\].*/) {
+ $group = $1;
+ } elsif (/^([^=]+?)\s*=\s*(.*?)\s*$/) { # https://code-maven.com/slides/perl-programming/solution-parse-ini-file
+ if ($group ne 'Language') {
+ next;
+ }
+ $keyword = $1;
+ $value = $2;
+ my $pattern = '@ByteArray\((.+)\)';
+ (my $langs = $value) =~ s/$pattern/$1/g;
+ push @languages, split(/:/, $langs);
+ # print("-- keyword: $1 -- value: $2\n");
+ }
+ }
+ close($ini);
+}
+
+# Grab values from ENV.
+push @languages, ($ENV{'LANG'} or 'C.UTF-8');
+foreach my $key (sort keys %ENV) {
+ my $needle = 'LC_';
+ if (substr($key, 0, length($needle)) eq $needle) {
+ push @languages, $ENV{$key};
+ }
+}
+if (my $language = $ENV{'LANGUAGE'}) {
+ push @languages, split(':', $language);
+}
+push @languages, ($ENV{'LANG'} or 'C.UTF-8');
+
+# Uniqify; can't really use any perl modules as the base requirements of KF5 are
+# mostly perl core only.
+@languages = do { my %seen; grep { !$seen{$_}++ } @languages };
+
+# Generate as necessary.
+# Note: this actually generates invalid stuff as well (e.g. fr.UTF-8) while we
+# apparently do not need these there isn't much of a downside to generate them
+# lest something in glibc falls over if the language is entirely unknown in
+# the locpath. I should point out that we can't actually get from
+# language-only settings (as seen in klanguageoverrides) to full locales, so
+# there is no other option here anyway.
+my $env_locpath = $ENV{'LOCPATH'} or die "LOCPATH not set";
+my @locpaths = split(/:/, $env_locpath);
+my $master_locpath = $locpaths[0];
+foreach my $lang (@languages) {
+ my ($locale, $encoding) = split(/\./, $lang);
+ $encoding //= 'UTF-8';
+ debug("lang: $lang loc: $locale, char: $encoding\n");
+
+ my $found = 0;
+ foreach my $locpath (@locpaths) {
+ my $loc_target = "$locpath/$locale.$encoding";
+ debug("checking $loc_target\n");
+ if (-e $loc_target) {
+ debug("exists! skipping\n");
+ $found = 1;
+ last;
+ }
+ }
+ next if $found;
+ my $target = "$master_locpath/$locale.$encoding";
+
+ debug("generating $target\n");
+ # localedef will exit !0 for unknown reasons, even when everything was
+ # generated fine.
+ # may be: /snap/krita/32/usr/share/i18n/locales/iso14651_t1_common:7120: LC_COLLATE: symbol `pure-ta-zh' not known
+ system('localedef', '-i', $locale, '-f', $encoding, $target);
+}
+
+# use Data::Dumper;
+# print Dumper(@languages);
diff --git a/packaging/linux/snap/qt5-launch b/packaging/linux/snap/qt5-launch
index 389e499f7f..e92afec422 100755
--- a/packaging/linux/snap/qt5-launch
+++ b/packaging/linux/snap/qt5-launch
@@ -1,77 +1,167 @@
#!/bin/bash
if [ "$SNAP_ARCH" == "amd64" ]; then
ARCH='x86_64-linux-gnu'
elif [ "$SNAP_ARCH" == "armhf" ]; then
ARCH="arm-linux-gnueabihf"
else
ARCH="$SNAP_ARCH-linux-gnu"
fi
-export LD_LIBRARY_PATH=$SNAP/usr/lib/$ARCH:$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH=$SNAP/lib:$SNAP/lib/$ARCH:$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH=$SNAP/usr/lib:$SNAP/usr/lib/$ARCH:$LD_LIBRARY_PATH
# XKB config
export XKB_CONFIG_ROOT=$SNAP/usr/share/X11/xkb
# Mesa Libs
export LD_LIBRARY_PATH=$SNAP/usr/lib/$ARCH/mesa:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH=$SNAP/usr/lib/$ARCH/mesa-egl:$LD_LIBRARY_PATH
-# XDG Config
-export XDG_CONFIG_DIRS=$SNAP/etc:$XDG_CONFIG_DIRS
+# libGL Drivers
+export LIBGL_DRIVERS_PATH=$SNAP/usr/lib/$ARCH/dri
+export LD_LIBRARY_PATH=$LIBGL_DRIVERS_PATH:$LD_LIBRARY_PATH
-# Note: this doesn't seem to work, QML's LocalStorage either ignores
-# or fails to use $SNAP_USER_DATA if defined here
-export XDG_DATA_DIRS=$SNAP/usr/share:$XDG_DATA_DIRS
+# Grab proprietary nvidia mount when possible.
+# https://bugs.launchpad.net/snappy/+bug/1588192
+export LD_LIBRARY_PATH=/var/lib/snapd/lib/gl:$LD_LIBRARY_PATH
-# Font Config
-export FONTCONFIG_PATH=$SNAP/etc/fonts/config.d
-export FONTCONFIG_FILE=$SNAP/etc/fonts/fonts.conf
+# Pulseaudio plugins
+export LD_LIBRARY_PATH=$SNAP/usr/lib/$ARCH/pulseaudio:$LD_LIBRARY_PATH
-# Tell libGL where to find the drivers
-export LIBGL_DRIVERS_PATH=$SNAP/usr/lib/$ARCH/dri
+# XDG Config
+export XDG_CONFIG_DIRS=$SNAP/etc:${XDG_CONFIG_DIRS:-/etc/xdg}
-# Necessary for the SDK to find the translations directory
-export APP_DIR=$SNAP
+# Note: this doesn't seem to work, QML's LocalStorage either ignores
+# or fails to use $SNAP_USER_DATA if defined here
+export XDG_DATA_DIRS=$SNAP/usr/share:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}
# Set XDG_DATA_HOME to local path, dependent on snap version
-export XDG_DATA_HOME=$SNAP_USER_DATA/.local-$SNAP_VERSION/share
-export XDG_DATA_DIRS=$XDG_DATA_HOME:$XDG_DATA_DIRS
+export XDG_DATA_HOME=$SNAP_USER_DATA/.local/share
mkdir -p $XDG_DATA_HOME
-# Set cache folder to local path, dependent on snap version
-export XDG_CACHE_HOME=$SNAP_USER_DATA/.cache-$SNAP_VERSION
+export XDG_CONFIG_HOME=$SNAP_USER_DATA/.config
+mkdir -p $XDG_CONFIG_HOME
+
+if [ -e $SNAP_USER_DATA/.local/config/kdeglobals ]; then
+ # Migrate legacy path previously (incorrectly) used for configs.
+ # The current var $HOME/.config is in line with the XDG default.
+ cp -rv $SNAP_USER_DATA/.local/config/. $XDG_CONFIG_HOME
+ rm -rv $SNAP_USER_DATA/.local/config/
+fi
+
+export XDG_CACHE_HOME=$SNAP_USER_DATA/.cache
mkdir -p $XDG_CACHE_HOME
-# Not good, needed for fontconfig and themes
-ln -sf $SNAP/usr/share/{fontconfig,fonts,fonts-*,themes} $XDG_DATA_HOME
+export XDG_RUNTIME_DIR=$SNAP_USER_DATA/.local/var/run/$UID
+mkdir -p $XDG_RUNTIME_DIR
-# Qt Platform to Mir
-export QTCHOOSER_NO_GLOBAL_DIR=1
-export QT_SELECT=snappy-qt5
+# Font Config
+export FONTCONFIG_PATH=$SNAP/etc/fonts/config.d
+export FONTCONFIG_FILE=$XDG_CONFIG_HOME/fontconfig/fonts.conf
+
+REALHOME=`getent passwd $UID | cut -d ':' -f 6`
+# Keep an array of data dirs, for looping through them
+IFS=':' read -r -a data_dirs_array <<< "$XDG_DATA_DIRS"
+
+function make_user_fontconfig {
+ echo "<fontconfig>"
+ if [ -d $REALHOME/.local/share/fonts ]; then
+ echo " <dir>$REALHOME/.local/share/fonts</dir>"
+ fi
+ if [ -d $REALHOME/.fonts ]; then
+ echo " <dir>$REALHOME/.fonts</dir>"
+ fi
+ for d in "${data_dirs_array[@]}"; do
+ if [ -d "$d/fonts" ]; then
+ echo " <dir>$d/fonts</dir>"
+ fi
+ done
+ echo ' <include ignore_missing="yes">conf.d</include>'
+ # We need to include this default cachedir first so that caching
+ # works: without it, fontconfig will try to write to the real user home
+ # cachedir and be blocked by AppArmor.
+ echo ' <cachedir prefix="xdg">fontconfig</cachedir>'
+ if [ -d $REALHOME/.cache/fontconfig ]; then
+ echo " <cachedir>$REALHOME/.cache/fontconfig</cachedir>"
+ fi
+ echo "</fontconfig>"
+}
+
+# This relies on the desktop, x11, or unity7 interface from what I can tell.
+# We'll configure the in-snap fontconfig to look in the outside resoruces,
+# the outside resources are made readable by the aforementioned interfaces.
+rm -rf $XDG_DATA_HOME/{fontconfig,fonts,fonts-*,themes,.themes}
+mkdir -p $XDG_CONFIG_HOME/fontconfig
+make_user_fontconfig > $FONTCONFIG_FILE
# Qt Libs
export LD_LIBRARY_PATH=$SNAP/usr/lib/$ARCH/qt5/libs:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH=$SNAP/usr/lib/$ARCH/pulseaudio:$LD_LIBRARY_PATH
# Qt Modules
export QT_PLUGIN_PATH=$SNAP/usr/lib/$ARCH/qt5/plugins
export QML2_IMPORT_PATH=$QML2_IMPORT_PATH:$SNAP/usr/lib/$ARCH/qt5/qml
export QML2_IMPORT_PATH=$QML2_IMPORT_PATH:$SNAP/lib/$ARCH
# Removes Qt warning: Could not find a location
# of the system Compose files
export QTCOMPOSE=$SNAP/usr/share/X11/locale
-
-export DESKTOP_SESSION=ubuntu
-export XDG_SESSION_DESKTOP=ubuntu
-export XDG_CURRENT_DESKTOP=kde
-export QT_QPA_PLATFORMTHEME=appmenu-qt5
export QT_XKB_CONFIG_ROOT=/usr/share/X11/xkb
+# FIXME: it's unclear if we actually need to force plasma theming, even without
+# gtk integration theme Qt should probably figure out a sane style. Needs
+# testing though.
+export KDE_FULL_SESSION=true
+export DESKTOP_SESSION=${DESKTOP_SESSION:-/usr/share/xsessions/plasma}
+export XDG_SESSION_DESKTOP=${XDG_SESSION_DESKTOP:-KDE}
+export XDG_CURRENT_DESKTOP=${XDG_CURRENT_DESKTOP:-KDE}
+export QT_QPA_PLATFORMTHEME=${QT_QPA_PLATFORMTHEME:-kde}
+
# ensure that our HW/opengl libs are before the snap specific libs
export LD_LIBRARY_PATH=$SNAP_LIBRARY_PATH:$LD_LIBRARY_PATH
+# KDE specific
+## Do not start slaves through klauncher but fork them directly.
+export KDE_FORK_SLAVES=1
+## Neon PATCH! make KIO look for slaves in a dynamic location depending on $SNAP
+## FIXME: should be made a : separated list so we can look in $SNAP and $KF5!
+export KF5_LIBEXEC_DIR=$SNAP/usr/lib/$ARCH/libexec/kf5
+
+# Link icons into home so (xcursor) can find its cursor theme.
+mkdir -p $HOME/.icons
+ln -fs $SNAP/usr/share/icons/* $HOME/.icons
+
+# Make sure QtChooser isn't being terrible to us
+export QTCHOOSER_NO_GLOBAL_DIR=1
+export QT_SELECT=5
+# qtchooser hardcodes reference paths, we'll need to rewrite them properly
+[ -d $XDG_CONFIG_HOME/qtchooser ] || mkdir -p $XDG_CONFIG_HOME/qtchooser
+echo "$SNAP/usr/lib/qt5/bin" > $XDG_CONFIG_HOME/qtchooser/5.conf
+echo "$SNAP/usr/lib/$ARCH" >> $XDG_CONFIG_HOME/qtchooser/5.conf
+echo "$SNAP/usr/lib/qt5/bin" > $XDG_CONFIG_HOME/qtchooser/default.conf
+echo "$SNAP/usr/lib/$ARCH" >> $XDG_CONFIG_HOME/qtchooser/default.conf
+
+# This relies on qtbase patch
+# 0001-let-qlibraryinfo-fall-back-to-locate-qt.conf-via-XDG.patch
+# to make QLibraryInfo look in XDG_* locations for qt.conf. The paths configured
+# here are applied to everything that uses QLibraryInfo as final fallback and
+# has no XDG_* fallback before that. Currently the most interesting offender
+# is QtWebEngine which will not work unless the Data path is correctly set.
+cat << EOF > $XDG_CONFIG_HOME/qt.conf
+[Paths]
+Data = $SNAP/usr/share/qt5/
+Translations = $SNAP/usr/share/qt5/translations
+EOF
+
+# requires `locales, libc-bin` being stage-packages
+if [ -e $SNAP/usr/share/i18n ]; then
+ export I18NPATH=$SNAP/usr/share/i18n
+ locpath=$XDG_DATA_HOME/locale
+ mkdir -p $locpath
+ export LOCPATH=$locpath:/usr/lib/locale # core snap contains C.UTF-8 already
+ LC_ALL=C.UTF-8 $SNAP/bin/kf5-locale-gen || exit 1
+fi
+
cd $SNAP
exec "$@"
-
diff --git a/packaging/linux/snap/setup/gui/calligrakrita.png b/packaging/linux/snap/snap/gui/calligrakrita.png
similarity index 100%
rename from packaging/linux/snap/setup/gui/calligrakrita.png
rename to packaging/linux/snap/snap/gui/calligrakrita.png
diff --git a/packaging/linux/snap/setup/gui/krita.desktop b/packaging/linux/snap/snap/gui/krita.desktop
similarity index 98%
rename from packaging/linux/snap/setup/gui/krita.desktop
rename to packaging/linux/snap/snap/gui/krita.desktop
index af96944042..2d67fb2efe 100755
--- a/packaging/linux/snap/setup/gui/krita.desktop
+++ b/packaging/linux/snap/snap/gui/krita.desktop
@@ -1,137 +1,138 @@
[Desktop Entry]
Name=Krita
Name[af]=Krita
Name[ar]=كريتا
Name[bg]=Krita
Name[br]=Krita
Name[bs]=Krita
Name[ca]=Krita
Name[ca@valencia]=Krita
Name[cs]=Krita
Name[cy]=Krita
Name[da]=Krita
Name[de]=Krita
Name[el]=Krita
Name[en_GB]=Krita
Name[eo]=Krita
Name[es]=Krita
Name[et]=Krita
Name[eu]=Krita
Name[fi]=Krita
Name[fr]=Krita
Name[fy]=Krita
Name[ga]=Krita
Name[gl]=Krita
Name[he]=Krita
Name[hi]=केरिता
Name[hne]=केरिता
Name[hr]=Krita
Name[hu]=Krita
Name[ia]=Krita
Name[is]=Krita
Name[it]=Krita
Name[ja]=Krita
Name[kk]=Krita
Name[ko]=Krita
Name[lt]=Krita
Name[lv]=Krita
Name[mr]=क्रिटा
Name[ms]=Krita
Name[nb]=Krita
Name[nds]=Krita
Name[ne]=क्रिता
Name[nl]=Krita
Name[pl]=Krita
Name[pt]=Krita
Name[pt_BR]=Krita
Name[ro]=Krita
Name[ru]=Krita
Name[se]=Krita
Name[sk]=Krita
Name[sl]=Krita
Name[sv]=Krita
Name[ta]=கிரிட்டா
Name[tg]=Krita
Name[tr]=Krita
Name[ug]=Krita
Name[uk]=Krita
Name[uz]=Krita
Name[uz@cyrillic]=Krita
Name[wa]=Krita
Name[xh]=Krita
Name[x-test]=xxKritaxx
Name[zh_CN]=Krita
Name[zh_TW]=Krita
Exec=krita %F
GenericName=Digital Painting
GenericName[ar]=رسم رقمي
GenericName[bs]=Digitalno Bojenje
GenericName[ca]=Dibuix digital
GenericName[ca@valencia]=Dibuix digital
GenericName[cs]=Digitální malování
GenericName[da]=Digital tegning
GenericName[de]=Digitales Malen
GenericName[el]=Ψηφιακή ζωγραφική
GenericName[en_GB]=Digital Painting
GenericName[es]=Pintura digital
GenericName[et]=Digitaalne joonistamine
GenericName[eu]=Margolan digitala
GenericName[fi]=Digitaalimaalaus
GenericName[fr]=Peinture numérique
GenericName[gl]=Debuxo dixital
GenericName[hu]=Digitális festészet
GenericName[ia]=Pintura Digital
GenericName[is]=Stafræn málun
GenericName[it]=Pittura digitale
GenericName[ja]=デジタルペインティング
GenericName[kk]=Цифрлық сурет салу
GenericName[lt]=Skaitmeninis piešimas
GenericName[mr]=डिजिटल पेंटिंग
GenericName[nb]=Digital maling
GenericName[nl]=Digitaal schilderen
GenericName[pl]=Cyfrowe malowanie
GenericName[pt]=Pintura Digital
GenericName[pt_BR]=Pintura digital
GenericName[ru]=Цифровая живопись
GenericName[sk]=Digitálne maľovanie
GenericName[sl]=Digitalno slikanje
GenericName[sv]=Digital målning
GenericName[tr]=Sayısal Boyama
GenericName[ug]=سىفىرلىق رەسىم سىزغۇ
GenericName[uk]=Цифрове малювання
GenericName[x-test]=xxDigital Paintingxx
GenericName[zh_CN]=数字绘画
GenericName[zh_TW]=數位繪畫
MimeType=application/x-krita;image/openraster;application/x-krita-paintoppreset;
Comment=Pixel-based image manipulation program for the Calligra Suite
Comment[ar]=برنامج لتعديل الصور البكسليّة لطقم «كاليغرا»
Comment[ca]=Programa de manipulació d'imatges basades en píxels per a la Suite Calligra
Comment[ca@valencia]=Programa de manipulació d'imatges basades en píxels per a la Suite Calligra
Comment[de]=Pixelbasiertes Bildbearbeitungsprogramm für die Calligra-Suite
Comment[el]=Πρόγραμμα επεξεργασίας εικόνας με βάση εικονοστοιχεία για το Calligra Stage
Comment[en_GB]=Pixel-based image manipulation program for the Calligra Suite
Comment[es]=Programa de manipulación de imágenes basado en píxeles para la suite Calligra
Comment[et]=Calligra pikslipõhine pilditöötluse rakendus
Comment[eu]=Pixel-oinarridun irudiak manipulatzeko programa Calligra-Suiterako
+Comment[fi]=Bittikarttakuvankäsittelyohjelma Calligra-toimisto-ohjelmistoon
Comment[gl]=Programa da colección de Calligra para a manipulación de imaxes baseadas en píxeles.
Comment[is]=Myndvinnsluforrit fyrir Calligra-forritavöndulinn
Comment[it]=Programma di manipolazione delle immagini basato su pixel per Calligra Suite
Comment[nl]=Afbeeldingsbewerkingsprogramma gebaseerd op pixels voor de Calligra Suite
Comment[pl]=Program do obróbki obrazów na poziomie pikseli dla Pakietu Calligra
Comment[pt]='Plugin' de manipulação de imagens em pixels para o Calligra Stage
Comment[pt_BR]=Programa de manipulação de imagens baseado em pixels para o Calligra Suite
Comment[ru]=Программа редактирования пиксельной анимации для the Calligra Suite
Comment[sk]=Program na manipuláciu s pixelmi pre Calligra Suite
Comment[sv]=Bildpunktsbaserat bildbehandlingsprogram för Calligra-sviten
Comment[tr]=Calligra Suite için Pixel tabanlı görüntü düzenleme programı
Comment[uk]=Програма для роботи із растровими зображеннями для комплексу програм Calligra
Comment[x-test]=xxPixel-based image manipulation program for the Calligra Suitexx
Comment[zh_CN]=Calligra 套件的像素图像处理程序
Comment[zh_TW]=Calligra 套件中基於像素的影像處理程式
Type=Application
Icon=${SNAP}/meta/gui/calligrakrita.png
Categories=Qt;KDE;Graphics;
X-KDE-NativeMimeType=application/x-krita
X-KDE-ExtraNativeMimeTypes=
StartupNotify=true
X-Krita-Version=28
diff --git a/packaging/linux/snap/snapcraft.yaml b/packaging/linux/snap/snapcraft.yaml
index 8073f19582..b340df2904 100644
--- a/packaging/linux/snap/snapcraft.yaml
+++ b/packaging/linux/snap/snapcraft.yaml
@@ -1,141 +1,148 @@
name: krita
-version: 3.0.91-snap15
+version: 4.1.7.101
summary: Krita is the digital painting studio for artists
description: Krita is a creative application for raster images. Whether you want to create
from scratch or work with existing images, Krita is for you. You can work with
photos or scanned images, or start with a blank slate. Krita supports most
graphics tablets out of the box.
-
+base: core18
apps:
krita:
command: qt5-launch usr/bin/krita
- plugs: [x11, unity7, home, opengl, network, network-bind, removable-media]
+ plugs: [x11, unity7, home, opengl, network, network-bind, removable-media, desktop, desktop-legacy]
parts:
- qt:
- plugin: nil
- stage-packages:
- - libqt5concurrent5
- - libqt5core5a
- - libqt5dbus5
- - libqt5gui5
- - libqt5network5
- - libqt5printsupport5
- - libqt5svg5
- - libqt5widgets5
- - libqt5x11extras5
- - libqt5xml5
-
- kdeframeworks:
- plugin: nil
- stage-packages:
- - libkf5archive5
- - libkf5completion5
- - libkf5configcore5
- - libkf5configgui5
- - libkf5coreaddons5
- - libkf5guiaddons5
- - libkf5i18n5
- - libkf5itemviews5
- - libkf5widgetsaddons5
- - libkf5windowsystem5
- - libkf5crash5
- after: [qt]
-
krita:
plugin: cmake
-# Using -DKDE_NO_DEBUG_OUTPUT was causing compilation failure for some reason
-# configflags: [-DCMAKE_INSTALL_PREFIX=/usr, -DQT_NO_DEBUG=1, -DCMAKE_CXX_FLAGS="-DKDE_NO_DEBUG_OUTPUT"]
- configflags: [-DCMAKE_INSTALL_PREFIX=/usr, -DQT_NO_DEBUG=1]
- source: http://download.kde.org/unstable/krita/3.0.91/krita-3.0.91.tar.gz
+ configflags:
+ - "-DCMAKE_INSTALL_PREFIX=/usr"
+ - "-DCMAKE_BUILD_TYPE=Release"
+ - "-DENABLE_TESTING=OFF"
+ - "-DBUILD_TESTING=OFF"
+ - "-DKDE_SKIP_TEST_SETTINGS=ON"
+ source: https://download.kde.org/stable/krita/4.1.7/krita-4.1.7.101.tar.gz
# Use these instead to build from the git source
-# source: git://anongit.kde.org/krita.git
+# source: https://anongit.kde.org/krita.git
# source-type: git
-# source-branch: krita/3.0
+# source-branch: master
build-packages:
+ - gettext
- build-essential
- cmake
- libboost-dev
- libboost-system-dev
- libeigen3-dev
- libexiv2-dev
- libfftw3-dev
- libfontconfig1-dev
- libfreetype6-dev
- libgl1-mesa-dev
- libglew-dev
- libglib2.0-dev
- libglu1-mesa-dev
- libgsf-1-dev
- libgsl-dev
- libjpeg-dev
- liblcms2-dev
- libopenexr-dev
- - libpng12-dev
- - libpoppler-qt4-dev
+ - libpng-dev
+ - libpoppler-qt5-dev
- libtiff5-dev
- libvc-dev
- libopencolorio-dev
- libx11-dev
- libxml2-dev
- libxslt1-dev
- libxi-dev
- pkg-config
- - pkg-kde-tools
- vc-dev
- zlib1g-dev
- - libkdcraw-dev
+ - libkf5kdcraw-dev
- shared-mime-info
- libopenimageio-dev
- extra-cmake-modules
- libkf5archive-dev
- libkf5coreaddons-dev
- libkf5guiaddons-dev
- libkf5itemmodels-dev
- libkf5itemviews-dev
- libkf5widgetsaddons-dev
- libkf5i18n-dev
- libkf5windowsystem-dev
- libkf5completion-dev
- libkf5iconthemes-dev
- libkf5kiocore5
- libqt5svg5-dev
- libqt5x11extras5-dev
- libqt5opengl5-dev
- stage-packages:
- - libboost-system1.58.0
- - libexiv2-14
+
+ runtime:
+ plugin: nil
+ stage-packages:
+ - libexiv2-26
- libfftw3-double3
- libgomp1
- - libgsl2
+ - libgsl23
- libilmbase12
- libjpeg8
- liblcms2-2
- libopencolorio1v5
- libopenexr22
- - libpng12-0
+ - libpng16-16
- libstdc++6
- libtiff5
- libx11-6
- libxcb1
- libxi6
- zlib1g
- - libraw15
- - libkf5crash5
- libpoppler-qt5-1
- after: [qt, kdeframeworks]
-
- integration:
+ - shared-mime-info
+ - libboost-system1.65.1
+ - librtmp1
+ - libqt5multimedia5
+ - libqt5quickwidgets5
+ - libkf5archive5
+ - libkf5completion5
+ - libkf5configcore5
+ - libkf5configgui5
+ - libkf5coreaddons5
+ - libkf5guiaddons5
+ - libkf5i18n5
+ - libkf5itemviews5
+ - libkf5widgetsaddons5
+ - libkf5windowsystem5
+ - libkf5crash5
+ - libqt5concurrent5
+ - libqt5core5a
+ - libqt5dbus5
+ - libqt5gui5
+ - libqt5network5
+ - libqt5printsupport5
+ - libqt5svg5
+ - libqt5widgets5
+ - libqt5x11extras5
+ - libqt5xml5
+ - locales
+ - libc-bin
+ prime:
+ - "-usr/share/wallpapers/*"
+ - "-usr/share/fonts/*"
+
+ plasma-integration:
plugin: nil
stage-packages:
- - ttf-ubuntu-font-family
- snap:
- - usr/share
- - -usr/share/doc
+ - plasma-integration
+ - kde-style-breeze
+ - breeze-icon-theme
+ - kio # runtime slaves for kio
+ prime:
+ - "-usr/share/wallpapers/*"
+ - "-usr/share/fonts/*"
launcher:
- plugin: copy
- files:
+ plugin: dump
+ source: .
+ organize:
qt5-launch: bin/qt5-launch
-
+ kf5-locale-gen: bin/kf5-locale-gen
diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector_base.cpp b/plugins/dockers/advancedcolorselector/kis_color_selector_base.cpp
index f3b27d578d..5098ac89d6 100644
--- a/plugins/dockers/advancedcolorselector/kis_color_selector_base.cpp
+++ b/plugins/dockers/advancedcolorselector/kis_color_selector_base.cpp
@@ -1,560 +1,560 @@
/*
* Copyright (c) 2010 Adam Celarek <kdedev at xibo dot at>
*
* 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
*/
#include "kis_color_selector_base.h"
#include <QMouseEvent>
#include <QApplication>
#include <QDesktopWidget>
#include <QTimer>
#include <QCursor>
#include <QPainter>
#include <QMimeData>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <ksharedconfig.h>
#include "KoColorSpace.h"
#include "KoColorSpaceRegistry.h"
#include "kis_canvas2.h"
#include "kis_canvas_resource_provider.h"
#include "kis_node.h"
#include "KisViewManager.h"
#include <KisView.h>
#include "kis_image.h"
#include "kis_global.h"
#include "kis_display_color_converter.h"
#include <resources/KoGamutMask.h>
class KisColorPreviewPopup : public QWidget {
public:
KisColorPreviewPopup(KisColorSelectorBase* parent)
: QWidget(parent), m_parent(parent)
{
setWindowFlags(Qt::ToolTip | Qt::NoDropShadowWindowHint);
setQColor(QColor(0,0,0));
m_baseColor = QColor(0,0,0,0);
m_previousColor = QColor(0,0,0,0);
m_lastUsedColor = QColor(0,0,0,0);
}
void show()
{
updatePosition();
QWidget::show();
}
void updatePosition()
{
QPoint parentPos = m_parent->mapToGlobal(QPoint(0,0));
QRect availRect = QApplication::desktop()->availableGeometry(this);
QPoint targetPos;
if ( parentPos.x() - 100 > availRect.x() ) {
targetPos = QPoint(parentPos.x() - 100, parentPos.y());
} else if ( parentPos.x() + m_parent->width() + 100 < availRect.right()) {
targetPos = m_parent->mapToGlobal(QPoint(m_parent->width(), 0));
} else if ( parentPos.y() - 100 > availRect.y() ) {
targetPos = QPoint(parentPos.x(), parentPos.y() - 100);
} else {
targetPos = QPoint(parentPos.x(), parentPos.y() + m_parent->height());
}
setGeometry(targetPos.x(), targetPos.y(), 100, 150);
setAttribute(Qt::WA_TranslucentBackground);
}
void setQColor(const QColor& color)
{
m_color = color;
update();
}
void setPreviousColor()
{
m_previousColor = m_baseColor;
}
void setBaseColor(const QColor& color)
{
m_baseColor = color;
update();
}
void setLastUsedColor(const QColor& color)
{
m_lastUsedColor = color;
update();
}
protected:
void paintEvent(QPaintEvent *e) override {
Q_UNUSED(e);
QPainter p(this);
p.fillRect(0, 0, width(), width(), m_color);
p.fillRect(50, width(), width(), height(), m_previousColor);
p.fillRect(0, width(), 50, height(), m_lastUsedColor);
}
void enterEvent(QEvent *e) override {
QWidget::enterEvent(e);
m_parent->tryHideAllPopups();
}
void leaveEvent(QEvent *e) override {
QWidget::leaveEvent(e);
m_parent->tryHideAllPopups();
}
private:
KisColorSelectorBase* m_parent;
QColor m_color;
QColor m_baseColor;
QColor m_previousColor;
QColor m_lastUsedColor;
};
KisColorSelectorBase::KisColorSelectorBase(QWidget *parent) :
QWidget(parent),
m_canvas(0),
m_popup(0),
m_parent(0),
m_colorUpdateAllowed(true),
m_colorUpdateSelf(false),
m_hideTimer(new QTimer(this)),
m_popupOnMouseOver(false),
m_popupOnMouseClick(true),
m_colorSpace(0),
m_isPopup(false),
m_hideOnMouseClick(false),
m_colorPreviewPopup(new KisColorPreviewPopup(this))
{
m_hideTimer->setInterval(0);
m_hideTimer->setSingleShot(true);
connect(m_hideTimer, SIGNAL(timeout()), this, SLOT(hidePopup()));
using namespace std::placeholders; // For _1 placeholder
auto function = std::bind(&KisColorSelectorBase::slotUpdateColorAndPreview, this, _1);
m_updateColorCompressor.reset(new ColorCompressorType(20 /* ms */, function));
}
KisColorSelectorBase::~KisColorSelectorBase()
{
delete m_popup;
delete m_colorPreviewPopup;
}
void KisColorSelectorBase::setPopupBehaviour(bool onMouseOver, bool onMouseClick)
{
m_popupOnMouseClick = onMouseClick;
m_popupOnMouseOver = onMouseOver;
if(onMouseClick) {
m_popupOnMouseOver = false;
}
if(m_popupOnMouseOver) {
setMouseTracking(true);
}
}
void KisColorSelectorBase::setColorSpace(const KoColorSpace *colorSpace)
{
m_colorSpace = colorSpace;
}
void KisColorSelectorBase::setCanvas(KisCanvas2 *canvas)
{
if (m_canvas) {
m_canvas->disconnectCanvasObserver(this);
}
m_canvas = canvas;
if (m_canvas) {
connect(m_canvas->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)),
SLOT(canvasResourceChanged(int,QVariant)), Qt::UniqueConnection);
connect(m_canvas->displayColorConverter(), SIGNAL(displayConfigurationChanged()),
- SLOT(reset()));
+ SLOT(reset()), Qt::UniqueConnection);
connect(canvas->imageView()->resourceProvider(), SIGNAL(sigFGColorUsed(KoColor)),
this, SLOT(updateLastUsedColorPreview(KoColor)), Qt::UniqueConnection);
if (m_canvas->viewManager() && m_canvas->viewManager()->resourceProvider()) {
setColor(Acs::currentColor(m_canvas->viewManager()->resourceProvider(), Acs::Foreground));
}
}
if (m_popup) {
m_popup->setCanvas(canvas);
}
reset();
}
void KisColorSelectorBase::unsetCanvas()
{
if (m_popup) {
m_popup->unsetCanvas();
}
m_canvas = 0;
}
void KisColorSelectorBase::mousePressEvent(QMouseEvent* event)
{
event->accept();
//this boolean here is to check if the colour selector is updating the resource, so it won't update itself when the resource is updated//
if (m_colorUpdateSelf==false)
{m_colorUpdateSelf=true;}
if(!m_isPopup && m_popupOnMouseClick &&
event->button() == Qt::MidButton) {
lazyCreatePopup();
int x = event->globalX();
int y = event->globalY();
int popupsize = m_popup->width();
x-=popupsize/2;
y-=popupsize/2;
QRect availRect = QApplication::desktop()->availableGeometry(this);
if(x<availRect.x())
x = availRect.x();
if(y<availRect.y())
y = availRect.y();
if(x+m_popup->width()>availRect.x()+availRect.width())
x = availRect.x()+availRect.width()-m_popup->width();
if(y+m_popup->height()>availRect.y()+availRect.height())
y = availRect.y()+availRect.height()-m_popup->height();
m_popup->move(x, y);
m_popup->setHidingTime(200);
showPopup(DontMove);
} else if (m_isPopup && event->button() == Qt::MidButton) {
if (m_colorPreviewPopup) {
m_colorPreviewPopup->hide();
}
hide();
} else {
showColorPreview();
event->ignore();
}
}
void KisColorSelectorBase::mouseReleaseEvent(QMouseEvent *e) {
Q_UNUSED(e);
if (e->button() == Qt::MidButton) {
e->accept();
} else if (m_isPopup &&
(m_hideOnMouseClick && !m_popupOnMouseOver) &&
!m_hideTimer->isActive()) {
if (m_colorPreviewPopup) {
m_colorPreviewPopup->hide();
}
hide();
}
}
void KisColorSelectorBase::enterEvent(QEvent *e)
{
if (m_popup && m_popup->isVisible()) {
m_popup->m_hideTimer->stop();
}
if (m_isPopup && m_hideTimer->isActive()) {
m_hideTimer->stop();
}
// do not show the popup when boxed in
// the configuration dialog (m_canvas == 0)
if (m_canvas &&
!m_isPopup && m_popupOnMouseOver &&
(!m_popup || m_popup->isHidden())) {
lazyCreatePopup();
const QRect availRect = QApplication::desktop()->availableGeometry(this);
QPoint proposedTopLeft = rect().center() - m_popup->rect().center();
proposedTopLeft = mapToGlobal(proposedTopLeft);
QRect popupRect = QRect(proposedTopLeft, m_popup->size());
popupRect = kisEnsureInRect(popupRect, availRect);
m_popup->setGeometry(popupRect);
m_popup->setHidingTime(200);
showPopup(DontMove);
}
QWidget::enterEvent(e);
}
void KisColorSelectorBase::leaveEvent(QEvent *e)
{
tryHideAllPopups();
QWidget::leaveEvent(e);
}
void KisColorSelectorBase::keyPressEvent(QKeyEvent *)
{
if (m_isPopup) {
hidePopup();
}
}
void KisColorSelectorBase::dragEnterEvent(QDragEnterEvent *e)
{
if(e->mimeData()->hasColor())
e->acceptProposedAction();
if(e->mimeData()->hasText() && QColor(e->mimeData()->text()).isValid())
e->acceptProposedAction();
}
void KisColorSelectorBase::dropEvent(QDropEvent *e)
{
QColor color;
if(e->mimeData()->hasColor()) {
color = qvariant_cast<QColor>(e->mimeData()->colorData());
}
else if(e->mimeData()->hasText()) {
color.setNamedColor(e->mimeData()->text());
if(!color.isValid())
return;
}
KoColor kocolor(color , KoColorSpaceRegistry::instance()->rgb8());
updateColor(kocolor, Acs::Foreground, true);
}
void KisColorSelectorBase::updateColor(const KoColor &color, Acs::ColorRole role, bool needsExplicitColorReset)
{
commitColor(color, role);
if (needsExplicitColorReset) {
setColor(color);
}
}
void KisColorSelectorBase::requestUpdateColorAndPreview(const KoColor &color, Acs::ColorRole role)
{
m_updateColorCompressor->start(qMakePair(color, role));
}
void KisColorSelectorBase::slotUpdateColorAndPreview(QPair<KoColor, Acs::ColorRole> color)
{
updateColorPreview(color.first);
updateColor(color.first, color.second, false);
}
void KisColorSelectorBase::setColor(const KoColor& color)
{
Q_UNUSED(color);
}
void KisColorSelectorBase::setHidingTime(int time)
{
KIS_ASSERT_RECOVER_NOOP(m_isPopup);
m_hideTimer->setInterval(time);
}
void KisColorSelectorBase::lazyCreatePopup()
{
if (!m_popup) {
m_popup = createPopup();
Q_ASSERT(m_popup);
m_popup->setParent(this);
/**
* On Linux passing Qt::X11BypassWindowManagerHint makes
* the window never hide if one switches it with Alt+Tab
* or something like that. The window also don't get any
* mouse-enter/leave events. So we have to make it normal
* window (which is visible to the window manager and
* appears in the tasks bar). Therefore we don't use it
* anyomore.
*/
m_popup->setWindowFlags(Qt::FramelessWindowHint |
Qt::Window |
Qt::NoDropShadowWindowHint);
m_popup->m_parent = this;
m_popup->m_isPopup=true;
}
m_popup->setCanvas(m_canvas);
m_popup->updateSettings();
}
void KisColorSelectorBase::showPopup(Move move)
{
// This slot may be called by some action,
// so we need to be able to handle it
lazyCreatePopup();
QPoint cursorPos = QCursor::pos();
if (move == MoveToMousePosition) {
m_popup->move(QPoint(cursorPos.x()-m_popup->width()/2, cursorPos.y()-m_popup->height()/2));
QRect rc = m_popup->geometry();
if (rc.x() < 0) rc.setX(0);
if (rc.y() < 0) rc.setY(0);
m_popup->setGeometry(rc);
}
if (m_colorPreviewPopup) {
m_colorPreviewPopup->hide();
}
m_popup->show();
m_popup->m_colorPreviewPopup->show();
}
void KisColorSelectorBase::hidePopup()
{
KIS_ASSERT_RECOVER_RETURN(m_isPopup);
m_colorPreviewPopup->hide();
hide();
}
void KisColorSelectorBase::commitColor(const KoColor& color, Acs::ColorRole role)
{
if (!m_canvas)
return;
m_colorUpdateAllowed=false;
if (role == Acs::Foreground)
m_canvas->resourceManager()->setForegroundColor(color);
else
m_canvas->resourceManager()->setBackgroundColor(color);
m_colorUpdateAllowed=true;
}
void KisColorSelectorBase::showColorPreview()
{
if(m_colorPreviewPopup->isHidden()) {
m_colorPreviewPopup->show();
}
}
void KisColorSelectorBase::updateColorPreview(const KoColor &color)
{
m_colorPreviewPopup->setQColor(converter()->toQColor(color));
}
void KisColorSelectorBase::canvasResourceChanged(int key, const QVariant &v)
{
if (key == KoCanvasResourceProvider::ForegroundColor || key == KoCanvasResourceProvider::BackgroundColor) {
KoColor realColor(v.value<KoColor>());
updateColorPreview(realColor);
if (m_colorUpdateAllowed && !m_colorUpdateSelf) {
setColor(realColor);
}
}
}
const KoColorSpace* KisColorSelectorBase::colorSpace() const
{
return converter()->paintingColorSpace();
}
void KisColorSelectorBase::updateSettings()
{
if(m_popup) {
m_popup->updateSettings();
}
KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector");
int zoomSelectorOptions = (int) cfg.readEntry("zoomSelectorOptions", 0) ;
if (zoomSelectorOptions == 0) {
setPopupBehaviour(false, true); // middle mouse button click will open zoom selector
} else if (zoomSelectorOptions == 1) {
setPopupBehaviour(true, false); // move over will open the zoom selector
}
else
{
setPopupBehaviour(false, false); // do not show zoom selector
}
if(m_isPopup) {
m_hideOnMouseClick = cfg.readEntry("hidePopupOnClickCheck", false);
const int zoomSize = cfg.readEntry("zoomSize", 280);
resize(zoomSize, zoomSize);
}
reset();
}
void KisColorSelectorBase::reset()
{
update();
}
void KisColorSelectorBase::updateBaseColorPreview(const KoColor &color)
{
m_colorPreviewPopup->setBaseColor(converter()->toQColor(color));
}
void KisColorSelectorBase::updatePreviousColorPreview()
{
m_colorPreviewPopup->setPreviousColor();
}
void KisColorSelectorBase::updateLastUsedColorPreview(const KoColor &color)
{
m_colorPreviewPopup->setLastUsedColor(converter()->toQColor(color));
}
KisDisplayColorConverter* KisColorSelectorBase::converter() const
{
return m_canvas ?
m_canvas->displayColorConverter() :
KisDisplayColorConverter::dumbConverterInstance();
}
void KisColorSelectorBase::tryHideAllPopups()
{
if (m_colorPreviewPopup->isVisible()) {
m_colorUpdateSelf=false; //this is for allowing advanced selector to listen to outside colour-change events.
m_colorPreviewPopup->hide();
}
if (m_popup && m_popup->isVisible()) {
m_popup->m_hideTimer->start();
}
if (m_isPopup && !m_hideTimer->isActive()) {
m_hideTimer->start();
}
}
void KisColorSelectorBase::mouseMoveEvent(QMouseEvent *event)
{
event->accept();
}
diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector_container.cpp b/plugins/dockers/advancedcolorselector/kis_color_selector_container.cpp
index 6a4fc834d7..aaa2094b2e 100644
--- a/plugins/dockers/advancedcolorselector/kis_color_selector_container.cpp
+++ b/plugins/dockers/advancedcolorselector/kis_color_selector_container.cpp
@@ -1,244 +1,251 @@
/*
* Copyright (c) 2010 Adam Celarek <kdedev at xibo dot at>
*
* 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
*/
#include "kis_color_selector_container.h"
#include "kis_color_selector.h"
#include "kis_my_paint_shade_selector.h"
#include "kis_minimal_shade_selector.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QAction>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <ksharedconfig.h>
#include <kactioncollection.h>
#include <KisDocument.h>
#include <KisGamutMaskToolbar.h>
#include "KisViewManager.h"
#include "kis_canvas2.h"
#include "kis_canvas_resource_provider.h"
#include "kis_node_manager.h"
#include "kis_node.h"
#include "kis_paint_device.h"
#include "kis_action_registry.h"
KisColorSelectorContainer::KisColorSelectorContainer(QWidget *parent) :
QWidget(parent),
m_colorSelector(new KisColorSelector(this)),
m_myPaintShadeSelector(new KisMyPaintShadeSelector(this)),
m_minimalShadeSelector(new KisMinimalShadeSelector(this)),
m_shadeSelector(m_myPaintShadeSelector),
m_gamutMaskToolbar(new KisGamutMaskToolbar(this)),
+ m_showColorSelector(true),
m_canvas(0)
{
m_widgetLayout = new QBoxLayout(QBoxLayout::TopToBottom, this);
m_widgetLayout->setSpacing(0);
m_widgetLayout->setMargin(0);
m_gamutMaskToolbar->setContentsMargins(0, 0, 0, 5);
m_gamutMaskToolbar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
m_colorSelector->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_myPaintShadeSelector->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_minimalShadeSelector->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_widgetLayout->addWidget(m_gamutMaskToolbar);
m_widgetLayout->addWidget(m_colorSelector);
m_widgetLayout->addWidget(m_myPaintShadeSelector);
m_widgetLayout->addWidget(m_minimalShadeSelector);
m_gamutMaskToolbar->hide();
m_myPaintShadeSelector->hide();
m_minimalShadeSelector->hide();
connect(m_colorSelector,SIGNAL(settingsButtonClicked()), SIGNAL(openSettings()));
connect(this, SIGNAL(settingsChanged()), m_colorSelector, SLOT(updateSettings()));
connect(this, SIGNAL(settingsChanged()), m_myPaintShadeSelector, SLOT(updateSettings()));
connect(this, SIGNAL(settingsChanged()), this, SLOT(updateSettings()));
connect(this, SIGNAL(settingsChanged()), m_minimalShadeSelector, SLOT(updateSettings()));
m_colorSelAction = KisActionRegistry::instance()->makeQAction("show_color_selector", this);
connect(m_colorSelAction, SIGNAL(triggered()), m_colorSelector, SLOT(showPopup()), Qt::UniqueConnection);
m_mypaintAction = KisActionRegistry::instance()->makeQAction("show_mypaint_shade_selector", this);
connect(m_mypaintAction, SIGNAL(triggered()), m_myPaintShadeSelector, SLOT(showPopup()), Qt::UniqueConnection);
m_minimalAction = KisActionRegistry::instance()->makeQAction("show_minimal_shade_selector", this);
connect(m_minimalAction, SIGNAL(triggered()), m_minimalShadeSelector, SLOT(showPopup()), Qt::UniqueConnection);
}
void KisColorSelectorContainer::unsetCanvas()
{
m_colorSelector->hasAtLeastOneDocument(doesAtleastOneDocumentExist());
m_colorSelector->unsetCanvas();
m_myPaintShadeSelector->unsetCanvas();
m_minimalShadeSelector->unsetCanvas();
m_canvas = 0;
}
bool KisColorSelectorContainer::doesAtleastOneDocumentExist()
{
if (m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->document() ) {
if (m_canvas->viewManager()->document()->image()->height() == 0) {
return false;
} else {
return true;
}
} else {
return false;
}
}
void KisColorSelectorContainer::slotUpdateIcons()
{
m_colorSelector->updateIcons();
}
void KisColorSelectorContainer::setCanvas(KisCanvas2* canvas)
{
if (m_canvas) {
m_canvas->disconnectCanvasObserver(this);
m_canvas->viewManager()->nodeManager()->disconnect(this);
KActionCollection *ac = m_canvas->viewManager()->actionCollection();
ac->takeAction(ac->action("show_color_selector"));
ac->takeAction(ac->action("show_mypaint_shade_selector"));
ac->takeAction(ac->action("show_minimal_shade_selector"));
}
m_canvas = canvas;
m_colorSelector->setCanvas(canvas);
m_myPaintShadeSelector->setCanvas(canvas);
m_minimalShadeSelector->setCanvas(canvas);
m_colorSelector->hasAtLeastOneDocument(doesAtleastOneDocumentExist());
if (m_canvas && m_canvas->viewManager()) {
if (m_canvas->viewManager()->nodeManager()) {
connect(m_canvas->viewManager()->nodeManager(), SIGNAL(sigLayerActivated(KisLayerSP)), SLOT(reactOnLayerChange()), Qt::UniqueConnection);
}
connect(m_canvas->viewManager()->resourceProvider(), SIGNAL(sigGamutMaskChanged(KoGamutMask*)),
- m_colorSelector, SLOT(slotGamutMaskSet(KoGamutMask*)));
+ m_colorSelector, SLOT(slotGamutMaskSet(KoGamutMask*)), Qt::UniqueConnection);
connect(m_canvas->viewManager()->resourceProvider(), SIGNAL(sigGamutMaskUnset()),
- m_colorSelector, SLOT(slotGamutMaskUnset()));
+ m_colorSelector, SLOT(slotGamutMaskUnset()), Qt::UniqueConnection);
connect(m_canvas->viewManager()->resourceProvider(), SIGNAL(sigGamutMaskPreviewUpdate()),
- m_colorSelector, SLOT(slotGamutMaskPreviewUpdate()));
+ m_colorSelector, SLOT(slotGamutMaskPreviewUpdate()), Qt::UniqueConnection);
m_gamutMaskToolbar->connectMaskSignals(m_canvas->viewManager()->resourceProvider());
// gamut mask connections
- connect(m_gamutMaskToolbar, SIGNAL(sigGamutMaskToggle(bool)), m_colorSelector, SLOT(slotGamutMaskToggle(bool)));
+ connect(m_gamutMaskToolbar, SIGNAL(sigGamutMaskToggle(bool)), m_colorSelector, SLOT(slotGamutMaskToggle(bool)), Qt::UniqueConnection);
KActionCollection* actionCollection = canvas->viewManager()->actionCollection();
actionCollection->addAction("show_color_selector", m_colorSelAction);
actionCollection->addAction("show_mypaint_shade_selector", m_mypaintAction);
actionCollection->addAction("show_minimal_shade_selector", m_minimalAction);
}
}
void KisColorSelectorContainer::updateSettings()
{
KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector");
m_onDockerResizeSetting = (int)cfg.readEntry("onDockerResize", 0);
+ m_showColorSelector = (bool) cfg.readEntry("showColorSelector", true);
+
+ if (m_showColorSelector) {
+ m_colorSelector->show();
+
+ if (m_colorSelector->configuration().mainType == KisColorSelectorConfiguration::Wheel) {
+ m_gamutMaskToolbar->show();
+ } else {
+ m_gamutMaskToolbar->hide();
+ }
+ } else {
+ m_colorSelector->hide();
+ m_gamutMaskToolbar->hide();
+ }
QString type = cfg.readEntry("shadeSelectorType", "Minimal");
QWidget* newShadeSelector;
if(type=="MyPaint")
newShadeSelector = m_myPaintShadeSelector;
else if (type=="Minimal")
newShadeSelector = m_minimalShadeSelector;
else
newShadeSelector = 0;
if(m_shadeSelector!=newShadeSelector && m_shadeSelector!=0) {
m_shadeSelector->hide();
}
m_shadeSelector=newShadeSelector;
if(m_shadeSelector!=0)
m_shadeSelector->show();
-
-
- if (m_colorSelector->configuration().mainType == KisColorSelectorConfiguration::Wheel) {
- m_gamutMaskToolbar->show();
- } else {
- m_gamutMaskToolbar->hide();
- }
-
}
void KisColorSelectorContainer::reactOnLayerChange()
{
if (m_canvas) {
KisNodeSP node = m_canvas->viewManager()->resourceProvider()->currentNode();
if (node) {
KisPaintDeviceSP device = node->paintDevice();
if (device) {
m_colorSelAction->setEnabled(true);
m_mypaintAction->setEnabled(true);
m_minimalAction->setEnabled(true);
}
else {
// m_colorSelAction->setEnabled(false);
// m_mypaintAction->setEnabled(false);
// m_minimalAction->setEnabled(false);
}
}
}
}
void KisColorSelectorContainer::resizeEvent(QResizeEvent * e)
{
if(m_shadeSelector!=0) {
int minimumHeightForBothWidgets = m_colorSelector->minimumHeight()+m_shadeSelector->minimumHeight()+30; //+30 for the buttons (temporarily)
if(height()<minimumHeightForBothWidgets && m_onDockerResizeSetting== 1) { // 1 option is hide shade selector
m_shadeSelector->hide();
}
else {
m_shadeSelector->show();
}
// m_onDockerResizeSetting==0 is allow horizontal layout
if(height() < width() && m_onDockerResizeSetting==0 && m_shadeSelector!=m_minimalShadeSelector) {
m_widgetLayout->setDirection(QBoxLayout::LeftToRight);
}
else {
m_widgetLayout->setDirection(QBoxLayout::TopToBottom);
}
}
QWidget::resizeEvent(e);
}
diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector_container.h b/plugins/dockers/advancedcolorselector/kis_color_selector_container.h
index fc98414e32..0892b74a95 100644
--- a/plugins/dockers/advancedcolorselector/kis_color_selector_container.h
+++ b/plugins/dockers/advancedcolorselector/kis_color_selector_container.h
@@ -1,75 +1,76 @@
/*
* Copyright (c) 2010 Adam Celarek <kdedev at xibo dot at>
*
* 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
*/
#ifndef KIS_COLOR_SELECTOR_CONTAINER_H
#define KIS_COLOR_SELECTOR_CONTAINER_H
#include <QWidget>
#include <QPointer>
#include <kis_canvas2.h>
class KisColorSelector;
class KisMyPaintShadeSelector;
class KisMinimalShadeSelector;
class QBoxLayout;
class QAction;
class KisGamutMaskToolbar;
class KisColorSelectorContainer : public QWidget
{
Q_OBJECT
public:
explicit KisColorSelectorContainer(QWidget *parent = 0);
void setCanvas(KisCanvas2* canvas);
void unsetCanvas();
bool doesAtleastOneDocumentExist();
enum ShadeSelectorType{MyPaintSelector, MinimalSelector, NoSelector};
public Q_SLOTS:
void slotUpdateIcons();
Q_SIGNALS:
void openSettings();
void settingsChanged();
protected Q_SLOTS:
void updateSettings();
void reactOnLayerChange();
protected:
void resizeEvent(QResizeEvent *) override;
private:
KisColorSelector* m_colorSelector;
KisMyPaintShadeSelector* m_myPaintShadeSelector;
KisMinimalShadeSelector* m_minimalShadeSelector;
QWidget* m_shadeSelector;
KisGamutMaskToolbar* m_gamutMaskToolbar;
int m_onDockerResizeSetting;
+ bool m_showColorSelector;
QBoxLayout* m_widgetLayout;
QAction * m_colorSelAction;
QAction * m_mypaintAction;
QAction * m_minimalAction;
QPointer<KisCanvas2> m_canvas;
};
#endif // KIS_COLOR_SELECTOR_CONTAINER_H
diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector_ng_docker_widget.cpp b/plugins/dockers/advancedcolorselector/kis_color_selector_ng_docker_widget.cpp
index f8a9cdf02c..511701c76a 100644
--- a/plugins/dockers/advancedcolorselector/kis_color_selector_ng_docker_widget.cpp
+++ b/plugins/dockers/advancedcolorselector/kis_color_selector_ng_docker_widget.cpp
@@ -1,221 +1,285 @@
/*
* Copyright (c) 2010 Adam Celarek <kdedev at xibo dot at>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_color_selector_ng_docker_widget.h"
#include "ui_wdg_color_selector_settings.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
+#include <QToolButton>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <ksharedconfig.h>
+#include <kis_icon_utils.h>
#include <QAction>
#include <kactioncollection.h>
#include "kis_canvas2.h"
#include "KisViewManager.h"
#include "kis_node_manager.h"
#include "kis_canvas_resource_provider.h"
#include "kis_color_space_selector.h"
#include "kis_preference_set_registry.h"
#include "kis_node.h"
#include "kis_paint_device.h"
#include "kis_color_history.h"
#include "kis_common_colors.h"
#include "kis_color_selector_settings.h"
#include "kis_color_selector_container.h"
#include "kis_action_registry.h"
KisColorSelectorNgDockerWidget::KisColorSelectorNgDockerWidget(QWidget *parent) :
QWidget(parent),
m_colorHistoryAction(0),
m_commonColorsAction(0),
+ m_widgetLayout(0),
+ m_mainLayout(0),
+ m_horizontalPatchesContainer(0),
+ m_sidebarLayout(0),
m_verticalColorPatchesLayout(0),
m_horizontalColorPatchesLayout(0),
+ m_fallbackSettingsButton(new QToolButton(this)),
m_canvas(0)
{
setAutoFillBackground(true);
m_colorSelectorContainer = new KisColorSelectorContainer(this);
m_colorHistoryWidget = new KisColorHistory(this);
m_commonColorsWidget = new KisCommonColors(this);
//default settings
//remember to also change the default in the ui file
//shade selector
+ // fallback settings button when the color selector is disabled
+ m_fallbackSettingsButton->setIcon(KisIconUtils::loadIcon("configure"));
+ m_fallbackSettingsButton->setIconSize(QSize(22,22));
+ m_fallbackSettingsButton->setAutoRaise(true);
+ m_fallbackSettingsButton->hide();
+
//layout
+ m_widgetLayout = new QHBoxLayout();
+ m_widgetLayout->setSpacing(0);
+ m_widgetLayout->setMargin(0);
+
+ m_mainLayout = new QVBoxLayout();
+ m_mainLayout->setSpacing(0);
+ m_mainLayout->setMargin(0);
+
+ m_horizontalPatchesContainer = new QHBoxLayout();
+ m_horizontalPatchesContainer->setSpacing(0);
+ m_horizontalPatchesContainer->setMargin(0);
+
+ m_sidebarLayout = new QVBoxLayout();
+ m_sidebarLayout->setSpacing(0);
+ m_sidebarLayout->setMargin(0);
+
m_verticalColorPatchesLayout = new QHBoxLayout();
m_verticalColorPatchesLayout->setSpacing(0);
m_verticalColorPatchesLayout->setMargin(0);
- m_verticalColorPatchesLayout->addWidget(m_colorSelectorContainer);
- m_horizontalColorPatchesLayout = new QVBoxLayout(this);
+ m_horizontalColorPatchesLayout = new QVBoxLayout();
m_horizontalColorPatchesLayout->setSpacing(0);
m_horizontalColorPatchesLayout->setMargin(0);
- m_horizontalColorPatchesLayout->addLayout(m_verticalColorPatchesLayout);
+
+ m_horizontalPatchesContainer->addLayout(m_horizontalColorPatchesLayout);
+
+ m_mainLayout->addWidget(m_colorSelectorContainer);
+ m_mainLayout->addLayout(m_horizontalPatchesContainer);
+
+ m_sidebarLayout->addLayout(m_verticalColorPatchesLayout);
+
+ m_widgetLayout->addLayout(m_mainLayout);
+ m_widgetLayout->addLayout(m_sidebarLayout);
+ setLayout(m_widgetLayout);
updateLayout();
connect(m_colorSelectorContainer, SIGNAL(openSettings()), this, SLOT(openSettings()));
//emit settingsChanged() if the settings are changed in krita preferences
KisPreferenceSetRegistry *preferenceSetRegistry = KisPreferenceSetRegistry::instance();
KisColorSelectorSettingsFactory* factory =
dynamic_cast<KisColorSelectorSettingsFactory*>(preferenceSetRegistry->get("KisColorSelectorSettingsFactory"));
Q_ASSERT(factory);
connect(&(factory->repeater), SIGNAL(settingsUpdated()), this, SIGNAL(settingsChanged()), Qt::UniqueConnection);
connect(this, SIGNAL(settingsChanged()), this, SLOT(updateLayout()), Qt::UniqueConnection);
connect(this, SIGNAL(settingsChanged()), m_commonColorsWidget, SLOT(updateSettings()), Qt::UniqueConnection);
connect(this, SIGNAL(settingsChanged()), m_colorHistoryWidget, SLOT(updateSettings()), Qt::UniqueConnection);
connect(this, SIGNAL(settingsChanged()), m_colorSelectorContainer, SIGNAL(settingsChanged()), Qt::UniqueConnection);
connect(this, SIGNAL(settingsChanged()), this, SLOT(update()), Qt::UniqueConnection);
emit settingsChanged();
m_colorHistoryAction = KisActionRegistry::instance()->makeQAction("show_color_history", this);
connect(m_colorHistoryAction, SIGNAL(triggered()), m_colorHistoryWidget, SLOT(showPopup()), Qt::UniqueConnection);
m_commonColorsAction = KisActionRegistry::instance()->makeQAction("show_common_colors", this);
connect(m_commonColorsAction, SIGNAL(triggered()), m_commonColorsWidget, SLOT(showPopup()), Qt::UniqueConnection);
+ connect(m_fallbackSettingsButton, SIGNAL(clicked()), this, SLOT(openSettings()));
}
void KisColorSelectorNgDockerWidget::unsetCanvas()
{
m_canvas = 0;
m_commonColorsWidget->unsetCanvas();
m_colorHistoryWidget->unsetCanvas();
m_colorSelectorContainer->unsetCanvas();
}
void KisColorSelectorNgDockerWidget::setCanvas(KisCanvas2 *canvas)
{
if (m_canvas) {
m_canvas->disconnect(this);
KActionCollection *ac = m_canvas->viewManager()->actionCollection();
ac->takeAction(ac->action("show_color_history"));
ac->takeAction(ac->action("show_common_colors"));
}
m_canvas = canvas;
m_commonColorsWidget->setCanvas(canvas);
m_colorHistoryWidget->setCanvas(canvas);
m_colorSelectorContainer->setCanvas(canvas);
if (m_canvas && m_canvas->viewManager()) {
if (m_canvas->viewManager()->nodeManager()) {
connect(m_canvas->viewManager()->nodeManager(), SIGNAL(sigLayerActivated(KisLayerSP)), SLOT(reactOnLayerChange()), Qt::UniqueConnection);
}
KActionCollection* actionCollection = canvas->viewManager()->actionCollection();
actionCollection->addAction("show_color_history", m_colorHistoryAction);
actionCollection->addAction("show_common_colors", m_commonColorsAction);
- connect(m_canvas->viewManager()->mainWindow(), SIGNAL(themeChanged()), m_colorSelectorContainer, SLOT(slotUpdateIcons()));
+ connect(m_canvas->viewManager()->mainWindow(), SIGNAL(themeChanged()), m_colorSelectorContainer, SLOT(slotUpdateIcons()), Qt::UniqueConnection);
}
reactOnLayerChange();
}
void KisColorSelectorNgDockerWidget::openSettings()
{
if (!m_canvas) return;
KisColorSelectorSettingsDialog settings;
if(settings.exec()==QDialog::Accepted) {
emit settingsChanged();
}
}
void KisColorSelectorNgDockerWidget::updateLayout()
{
KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector");
+ bool showColorSelector = (bool) cfg.readEntry("showColorSelector", true);
//color patches
bool m_lastColorsShow = cfg.readEntry("lastUsedColorsShow", true);
KisColorPatches::Direction m_lastColorsDirection;
if(cfg.readEntry("lastUsedColorsAlignment", false))
m_lastColorsDirection=KisColorPatches::Vertical;
else
m_lastColorsDirection=KisColorPatches::Horizontal;
bool m_commonColorsShow = cfg.readEntry("commonColorsShow", true);
KisColorPatches::Direction m_commonColorsDirection;
if(cfg.readEntry("commonColorsAlignment", false))
m_commonColorsDirection=KisColorPatches::Vertical;
else
m_commonColorsDirection=KisColorPatches::Horizontal;
-
m_verticalColorPatchesLayout->removeWidget(m_colorHistoryWidget);
m_verticalColorPatchesLayout->removeWidget(m_commonColorsWidget);
m_horizontalColorPatchesLayout->removeWidget(m_colorHistoryWidget);
m_horizontalColorPatchesLayout->removeWidget(m_commonColorsWidget);
+ m_sidebarLayout->removeWidget(m_fallbackSettingsButton);
+ m_mainLayout->removeWidget(m_fallbackSettingsButton);
+
if(m_lastColorsShow==false)
m_colorHistoryWidget->hide();
else
m_colorHistoryWidget->show();
if(m_commonColorsShow==false) {
m_commonColorsWidget->hide();
}
else {
m_commonColorsWidget->show();
}
+
+ bool fallbackSettingsButtonVertical = true;
+
if(m_lastColorsShow && m_lastColorsDirection==KisColorPatches::Vertical) {
m_verticalColorPatchesLayout->addWidget(m_colorHistoryWidget);
}
if(m_commonColorsShow && m_commonColorsDirection==KisColorPatches::Vertical) {
m_verticalColorPatchesLayout->addWidget(m_commonColorsWidget);
}
if(m_lastColorsShow && m_lastColorsDirection==KisColorPatches::Horizontal) {
m_horizontalColorPatchesLayout->addWidget(m_colorHistoryWidget);
+ fallbackSettingsButtonVertical = false;
}
if(m_commonColorsShow && m_commonColorsDirection==KisColorPatches::Horizontal) {
m_horizontalColorPatchesLayout->addWidget(m_commonColorsWidget);
+ fallbackSettingsButtonVertical = false;
+ }
+
+ // prefer the vertical column, if patch components have different layout
+ if (m_commonColorsDirection != m_lastColorsDirection) {
+ fallbackSettingsButtonVertical = true;
+ }
+
+ if (!showColorSelector) {
+ if (fallbackSettingsButtonVertical) {
+ m_sidebarLayout->addWidget(m_fallbackSettingsButton);
+ } else {
+ m_horizontalPatchesContainer->addWidget(m_fallbackSettingsButton);
+ }
+
+ m_fallbackSettingsButton->show();
+ } else {
+ m_fallbackSettingsButton->hide();
}
updateGeometry();
}
void KisColorSelectorNgDockerWidget::reactOnLayerChange()
{
/**
* Trigger the update for the case if some legacy code needs it.
* Now the node's color space is managed by the
* KisDisplayColorConverter and KisColorSelectorBase objects, so
* technically this call is not needed anymore. Please remove it
* when you are totally sure this will not break something.
*/
emit settingsChanged();
}
diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector_ng_docker_widget.h b/plugins/dockers/advancedcolorselector/kis_color_selector_ng_docker_widget.h
index b94dd23275..1b39587a4c 100644
--- a/plugins/dockers/advancedcolorselector/kis_color_selector_ng_docker_widget.h
+++ b/plugins/dockers/advancedcolorselector/kis_color_selector_ng_docker_widget.h
@@ -1,67 +1,75 @@
/*
* Copyright (c) 2010 Adam Celarek <kdedev at xibo dot at>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_COLOR_SELECTOR_NG_DOCKER_WIDGET_H
#define KIS_COLOR_SELECTOR_NG_DOCKER_WIDGET_H
#include <QWidget>
#include <QPointer>
+#include <QToolButton>
+
#include <kis_canvas2.h>
class QAction;
class KisCommonColors;
class KisColorHistory;
class KisColorSelectorContainer;
class QVBoxLayout;
class QHBoxLayout;
class KisColorSelectorNgDockerWidget : public QWidget
{
Q_OBJECT
public:
explicit KisColorSelectorNgDockerWidget(QWidget *parent = 0);
void setCanvas(KisCanvas2* canvas);
void unsetCanvas();
public Q_SLOTS:
void openSettings();
Q_SIGNALS:
void settingsChanged();
protected Q_SLOTS:
void updateLayout();
void reactOnLayerChange();
private:
KisColorSelectorContainer* m_colorSelectorContainer;
KisColorHistory* m_colorHistoryWidget;
KisCommonColors* m_commonColorsWidget;
QAction * m_colorHistoryAction;
QAction * m_commonColorsAction;
+ QHBoxLayout* m_widgetLayout;
+ QVBoxLayout* m_mainLayout;
+ QHBoxLayout* m_horizontalPatchesContainer;
+ QVBoxLayout* m_sidebarLayout;
+
QHBoxLayout* m_verticalColorPatchesLayout; // vertical color patches should be added here
QVBoxLayout* m_horizontalColorPatchesLayout;//horizontal ----------"----------------------
+ QToolButton* m_fallbackSettingsButton;
QPointer<KisCanvas2> m_canvas;
};
#endif
diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector_settings.cpp b/plugins/dockers/advancedcolorselector/kis_color_selector_settings.cpp
index c648ec5ed4..3c3be144d0 100644
--- a/plugins/dockers/advancedcolorselector/kis_color_selector_settings.cpp
+++ b/plugins/dockers/advancedcolorselector/kis_color_selector_settings.cpp
@@ -1,632 +1,632 @@
/*
* Copyright (C) 2010 Celarek Adam <kdedev at xibo dot at>
*
* 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_color_selector_settings.h"
#include "ui_wdg_color_selector_settings.h"
#include <QVBoxLayout>
#include <QDialogButtonBox>
#include <QPushButton>
#include <QComboBox>
#include <kconfiggroup.h>
#include <kis_icon.h>
#include "KoColorSpace.h"
#include "KoColorSpaceRegistry.h"
#include "KoColorProfile.h"
#include "kis_color_selector_combo_box.h"
#include "kis_color_selector.h"
#include "kis_config.h"
KisColorSelectorSettings::KisColorSelectorSettings(QWidget *parent) :
KisPreferenceSet(parent),
ui(new Ui::KisColorSelectorSettings)
{
ui->setupUi(this);
resize(minimumSize());
ui->colorSelectorConfiguration->setColorSpace(ui->colorSpace->currentColorSpace());
ui->useDifferentColorSpaceCheckbox->setChecked(false);
connect(ui->useDifferentColorSpaceCheckbox, SIGNAL(clicked(bool)), this, SLOT(useDifferentColorSpaceChecked(bool)));
/* color docker selector drop down */
ui->dockerColorSettingsComboBox->addItem(i18n("Advanced Color Selector"));
//ui->dockerColorSettingsComboBox->addItem(i18n("Color Sliders"));
ui->dockerColorSettingsComboBox->addItem(i18n("Color Hotkeys"));
ui->dockerColorSettingsComboBox->setCurrentIndex(0); // start off seeing advanced color selector properties
connect( ui->dockerColorSettingsComboBox, SIGNAL(currentIndexChanged(int)),this, SLOT(changedColorDocker(int)));
changedColorDocker(0);
/* advanced color docker options */
ui->dockerResizeOptionsComboBox->addItem(i18n("Change to a Horizontal Layout"));
ui->dockerResizeOptionsComboBox->addItem(i18n("Hide Shade Selector"));
ui->dockerResizeOptionsComboBox->addItem(i18n("Do Nothing"));
ui->dockerResizeOptionsComboBox->setCurrentIndex(0);
ui->zoomSelectorOptionComboBox->addItem(i18n("When Pressing Middle Mouse Button"));
ui->zoomSelectorOptionComboBox->addItem(i18n("On Mouse Over"));
ui->zoomSelectorOptionComboBox->addItem(i18n("Never"));
ui->zoomSelectorOptionComboBox->setCurrentIndex(0);
ui->colorSelectorTypeComboBox->addItem(i18n("HSV"));
ui->colorSelectorTypeComboBox->addItem(i18n("HSL"));
ui->colorSelectorTypeComboBox->addItem(i18n("HSI"));
ui->colorSelectorTypeComboBox->addItem(i18n("HSY'"));
ui->colorSelectorTypeComboBox->setCurrentIndex(0);
connect( ui->colorSelectorTypeComboBox, SIGNAL(currentIndexChanged(int)),this, SLOT(changedACSColorSelectorType(int)));
changedACSColorSelectorType(0); // initialize everything to HSV at the start
ui->ACSshadeSelectorMyPaintColorModelComboBox->addItem(i18n("HSV"));
ui->ACSshadeSelectorMyPaintColorModelComboBox->addItem(i18n("HSL"));
ui->ACSshadeSelectorMyPaintColorModelComboBox->addItem(i18n("HSI"));
ui->ACSshadeSelectorMyPaintColorModelComboBox->addItem(i18n("HSY'"));
ui->ACSshadeSelectorMyPaintColorModelComboBox->setCurrentIndex(0);
ui->ACSShadeSelectorTypeComboBox->addItem(i18n("MyPaint"));
ui->ACSShadeSelectorTypeComboBox->addItem(i18n("Minimal"));
ui->ACSShadeSelectorTypeComboBox->addItem(i18n("Do Not Show"));
ui->ACSShadeSelectorTypeComboBox->setCurrentIndex(0);
changedACSShadeSelectorType(0); // show/hide UI elements for MyPaint settings
connect( ui->ACSShadeSelectorTypeComboBox, SIGNAL(currentIndexChanged(int)),this, SLOT(changedACSShadeSelectorType(int)));
ui->commonColorsAlignVertical->setChecked(true);
ui->commonColorsAlignHorizontal->setChecked(true);
connect( ui->commonColorsAlignHorizontal, SIGNAL(toggled(bool)), this, SLOT(changedACSColorAlignment(bool)));
connect( ui->lastUsedColorsAlignHorizontal, SIGNAL(toggled(bool)), this, SLOT(changedACSLastUsedColorAlignment(bool)));
changedACSColorAlignment(ui->commonColorsAlignHorizontal->isChecked());
changedACSLastUsedColorAlignment(ui->lastUsedColorsAlignHorizontal->isChecked());
connect(ui->colorSpace, SIGNAL(colorSpaceChanged(const KoColorSpace*)),
ui->colorSelectorConfiguration, SLOT(setColorSpace(const KoColorSpace*)));
connect(this, SIGNAL(hsxchanged(int)),
ui->colorSelectorConfiguration, SLOT(setList(int)));
connect(ui->minimalShadeSelectorLineCount, SIGNAL(valueChanged(int)),
ui->minimalShadeSelectorLineSettings, SLOT(setLineCount(int)));
connect(ui->minimalShadeSelectorLineSettings, SIGNAL(lineCountChanged(int)),
ui->minimalShadeSelectorLineCount, SLOT(setValue(int)));
connect(ui->minimalShadeSelectorAsGradient, SIGNAL(toggled(bool)),
ui->minimalShadeSelectorLineSettings, SIGNAL(setGradient(bool)));
connect(ui->minimalShadeSelectorAsColorPatches, SIGNAL(toggled(bool)),
ui->minimalShadeSelectorLineSettings, SIGNAL(setPatches(bool)));
connect(ui->minimalShadeSelectorLineHeight, SIGNAL(valueChanged(int)),
ui->minimalShadeSelectorLineSettings, SIGNAL(setLineHeight(int)));
connect(ui->minimalShadeSelectorPatchesPerLine, SIGNAL(valueChanged(int)),
ui->minimalShadeSelectorLineSettings, SIGNAL(setPatchCount(int)));
}
KisColorSelectorSettings::~KisColorSelectorSettings()
{
delete ui;
}
QString KisColorSelectorSettings::id()
{
return QString("advancedColorSelector");
}
QString KisColorSelectorSettings::name()
{
return header();
}
QString KisColorSelectorSettings::header()
{
return QString(i18n("Color Selector Settings"));
}
QIcon KisColorSelectorSettings::icon()
{
return KisIconUtils::loadIcon("extended_color_selector");
}
void KisColorSelectorSettings::savePreferences() const
{
// write cfg
KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector");
KConfigGroup hsxcfg = KSharedConfig::openConfig()->group("hsxColorSlider");
KConfigGroup hotkeycfg = KSharedConfig::openConfig()->group("colorhotkeys");
// advanced color selector
cfg.writeEntry("onDockerResize", ui->dockerResizeOptionsComboBox->currentIndex());
cfg.writeEntry("zoomSelectorOptions", ui->zoomSelectorOptionComboBox->currentIndex() );
cfg.writeEntry("zoomSize", ui->popupSize->value());
+ cfg.writeEntry("showColorSelector", ui->chkShowColorSelector->isChecked());
bool useCustomColorSpace = ui->useDifferentColorSpaceCheckbox->isChecked();
const KoColorSpace* colorSpace = useCustomColorSpace ? ui->colorSpace->currentColorSpace() : 0;
KisConfig kisconfig(false);
kisconfig.setCustomColorSelectorColorSpace(colorSpace);
//color patches
cfg.writeEntry("lastUsedColorsShow", ui->lastUsedColorsShow->isChecked());
cfg.writeEntry("lastUsedColorsAlignment", ui->lastUsedColorsAlignVertical->isChecked());
cfg.writeEntry("lastUsedColorsScrolling", ui->lastUsedColorsAllowScrolling->isChecked());
cfg.writeEntry("lastUsedColorsNumCols", ui->lastUsedColorsNumCols->value());
cfg.writeEntry("lastUsedColorsNumRows", ui->lastUsedColorsNumRows->value());
cfg.writeEntry("lastUsedColorsCount", ui->lastUsedColorsPatchCount->value());
cfg.writeEntry("lastUsedColorsWidth", ui->lastUsedColorsWidth->value());
cfg.writeEntry("lastUsedColorsHeight", ui->lastUsedColorsHeight->value());
cfg.writeEntry("commonColorsShow", ui->commonColorsShow->isChecked());
cfg.writeEntry("commonColorsAlignment", ui->commonColorsAlignVertical->isChecked());
cfg.writeEntry("commonColorsScrolling", ui->commonColorsAllowScrolling->isChecked());
cfg.writeEntry("commonColorsNumCols", ui->commonColorsNumCols->value());
cfg.writeEntry("commonColorsNumRows", ui->commonColorsNumRows->value());
cfg.writeEntry("commonColorsCount", ui->commonColorsPatchCount->value());
cfg.writeEntry("commonColorsWidth", ui->commonColorsWidth->value());
cfg.writeEntry("commonColorsHeight", ui->commonColorsHeight->value());
cfg.writeEntry("commonColorsAutoUpdate", ui->commonColorsAutoUpdate->isChecked());
//shade selector
int shadeSelectorTypeIndex = ui->ACSShadeSelectorTypeComboBox->currentIndex();
if(shadeSelectorTypeIndex == 0) {
cfg.writeEntry("shadeSelectorType", "MyPaint");
} else if (shadeSelectorTypeIndex == 1) {
cfg.writeEntry("shadeSelectorType", "Minimal");
} else {
cfg.writeEntry("shadeSelectorType", "Hidden");
}
cfg.writeEntry("shadeSelectorUpdateOnRightClick", ui->shadeSelectorUpdateOnRightClick->isChecked());
cfg.writeEntry("shadeSelectorUpdateOnForeground", ui->shadeSelectorUpdateOnForeground->isChecked());
cfg.writeEntry("shadeSelectorUpdateOnLeftClick", ui->shadeSelectorUpdateOnLeftClick->isChecked());
cfg.writeEntry("shadeSelectorUpdateOnBackground", ui->shadeSelectorUpdateOnBackground->isChecked());
cfg.writeEntry("hidePopupOnClickCheck", ui->hidePopupOnClickCheck->isChecked());
//mypaint model
int shadeMyPaintComboBoxIndex = ui->ACSshadeSelectorMyPaintColorModelComboBox->currentIndex();
if (shadeMyPaintComboBoxIndex == 0 ) {
cfg.writeEntry("shadeMyPaintType", "HSV");
} else if (shadeMyPaintComboBoxIndex == 1 ) {
cfg.writeEntry("shadeMyPaintType", "HSL");
} else if (shadeMyPaintComboBoxIndex == 2 ) {
cfg.writeEntry("shadeMyPaintType", "HSI");
} else { // HSY
cfg.writeEntry("shadeMyPaintType", "HSY");
}
cfg.writeEntry("minimalShadeSelectorAsGradient", ui->minimalShadeSelectorAsGradient->isChecked());
cfg.writeEntry("minimalShadeSelectorPatchCount", ui->minimalShadeSelectorPatchesPerLine->value());
cfg.writeEntry("minimalShadeSelectorLineConfig", ui->minimalShadeSelectorLineSettings->toString());
cfg.writeEntry("minimalShadeSelectorLineHeight", ui->minimalShadeSelectorLineHeight->value());
//color selector
KisColorSelectorComboBox* cstw = dynamic_cast<KisColorSelectorComboBox*>(ui->colorSelectorConfiguration);
cfg.writeEntry("colorSelectorConfiguration", cstw->configuration().toString());
cfg.writeEntry("hsxSettingType", ui->colorSelectorTypeComboBox->currentIndex());
//luma//
cfg.writeEntry("lumaR", ui->l_lumaR->value());
cfg.writeEntry("lumaG", ui->l_lumaG->value());
cfg.writeEntry("lumaB", ui->l_lumaB->value());
cfg.writeEntry("gamma", ui->SP_Gamma->value());
//slider//
hsxcfg.writeEntry("hsvH", ui->csl_hsvH->isChecked());
hsxcfg.writeEntry("hsvS", ui->csl_hsvS->isChecked());
hsxcfg.writeEntry("hsvV", ui->csl_hsvV->isChecked());
hsxcfg.writeEntry("hslH", ui->csl_hslH->isChecked());
hsxcfg.writeEntry("hslS", ui->csl_hslS->isChecked());
hsxcfg.writeEntry("hslL", ui->csl_hslL->isChecked());
hsxcfg.writeEntry("hsiH", ui->csl_hsiH->isChecked());
hsxcfg.writeEntry("hsiS", ui->csl_hsiS->isChecked());
hsxcfg.writeEntry("hsiI", ui->csl_hsiI->isChecked());
hsxcfg.writeEntry("hsyH", ui->csl_hsyH->isChecked());
hsxcfg.writeEntry("hsyS", ui->csl_hsyS->isChecked());
hsxcfg.writeEntry("hsyY", ui->csl_hsyY->isChecked());
//hotkeys//
hotkeycfg.writeEntry("steps_lightness", ui->sb_lightness->value());
hotkeycfg.writeEntry("steps_saturation", ui->sb_saturation->value());
hotkeycfg.writeEntry("steps_hue", ui->sb_hue->value());
hotkeycfg.writeEntry("steps_redgreen", ui->sb_rg->value());
hotkeycfg.writeEntry("steps_blueyellow", ui->sb_by->value());
emit settingsChanged();
}
//void KisColorSelectorSettings::changeEvent(QEvent *e)
//{
// QDialog::changeEvent(e);
// switch (e->type()) {
// case QEvent::LanguageChange:
// ui->retranslateUi(this);
// break;
// default:
// break;
// }
//}
void KisColorSelectorSettings::changedColorDocker(int index)
{
// having a situation where too many sections are visible makes the window too large. turn all off before turning more on
ui->colorSliderOptions->hide();
ui->advancedColorSelectorOptions->hide();
ui->hotKeyOptions->hide();
if (index == 0) { // advanced color selector options selected
ui->advancedColorSelectorOptions->show();
ui->colorSliderOptions->hide();
ui->hotKeyOptions->hide();
}
// else if (index == 1) { // color slider options selected
// ui->advancedColorSelectorOptions->hide();
// ui->hotKeyOptions->hide();
// ui->colorSliderOptions->show();
// }
else {
ui->colorSliderOptions->hide();
ui->advancedColorSelectorOptions->hide();
ui->hotKeyOptions->show();
}
}
void KisColorSelectorSettings::changedACSColorSelectorType(int index)
{
ui->lumaCoefficientGroupbox->setVisible(false);
if (index == 0) { // HSV
ui->ACSTypeDescriptionLabel->setText(i18n("Values goes from black to white, or black to the most saturated colour. Saturation, in turn, goes from the most saturated colour to white, grey or black."));
}
else if (index == 1) { // HSL
ui->ACSTypeDescriptionLabel->setText(i18n("Lightness goes from black to white, with middle grey being equal to the most saturated colour."));
}
else if (index == 2) { // HSI
ui->ACSTypeDescriptionLabel->setText(i18n("Intensity maps to the sum of rgb components"));
}
else { // HSY'
ui->ACSTypeDescriptionLabel->setText(i18n("Luma(Y') is weighted by its coefficients which are configurable. Default values are set to 'rec 709'."));
ui->lumaCoefficientGroupbox->setVisible(true);
}
ui->colorSelectorConfiguration->update();
emit hsxchanged(index);
}
void KisColorSelectorSettings::changedACSColorAlignment(bool toggled)
{
// this slot is tied to the horizontal radio button's state being changed
// you can infer the vertical state
ui->lbl_commonColorsNumCols->setDisabled(toggled);
ui->commonColorsNumCols->setDisabled(toggled);
ui->lbl_commonColorsNumRows->setEnabled(toggled);
ui->commonColorsNumRows->setEnabled(toggled);
}
void KisColorSelectorSettings::changedACSLastUsedColorAlignment(bool toggled)
{
// this slot is tied to the horizontal radio button's state being changed
// you can infer the vertical state
ui->lbl_lastUsedNumCols->setDisabled(toggled);
ui->lastUsedColorsNumCols->setDisabled(toggled);
ui->lbl_lastUsedNumRows->setEnabled(toggled);
ui->lastUsedColorsNumRows->setEnabled(toggled);
}
-
-
void KisColorSelectorSettings::changedACSShadeSelectorType(int index)
{
if (index == 0) { // MyPaint
ui->minimalShadeSelectorGroup->hide();
ui->myPaintColorModelLabel->show();
ui->ACSshadeSelectorMyPaintColorModelComboBox->show();
} else if (index == 1) { // Minimal
ui->minimalShadeSelectorGroup->show();
ui->myPaintColorModelLabel->hide();
ui->ACSshadeSelectorMyPaintColorModelComboBox->hide();
}else { // do not show
ui->minimalShadeSelectorGroup->hide();
ui->myPaintColorModelLabel->hide();
ui->ACSshadeSelectorMyPaintColorModelComboBox->hide();
}
}
void KisColorSelectorSettings::useDifferentColorSpaceChecked(bool enabled)
{
ui->colorSpace->setEnabled(enabled);
}
void KisColorSelectorSettings::loadPreferences()
{
//read cfg
//don't forget to also add a new entry to the default preferences
KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector");
KConfigGroup hsxcfg = KSharedConfig::openConfig()->group("hsxColorSlider");
KConfigGroup hotkeycfg = KSharedConfig::openConfig()->group("colorhotkeys");
// Advanced color selector
ui->dockerResizeOptionsComboBox->setCurrentIndex( (int)cfg.readEntry("onDockerResize", 0) );
ui->zoomSelectorOptionComboBox->setCurrentIndex( (int) cfg.readEntry("zoomSelectorOptions", 0) );
ui->popupSize->setValue(cfg.readEntry("zoomSize", 280));
-
+ ui->chkShowColorSelector->setChecked((bool) cfg.readEntry("showColorSelector", true));
{
KisConfig kisconfig(true);
const KoColorSpace *cs = kisconfig.customColorSelectorColorSpace();
if (cs) {
ui->useDifferentColorSpaceCheckbox->setChecked(true);
ui->colorSpace->setEnabled(true);
ui->colorSpace->setCurrentColorSpace(cs);
} else {
ui->useDifferentColorSpaceCheckbox->setChecked(false);
ui->colorSpace->setEnabled(false);
}
}
//color patches
ui->lastUsedColorsShow->setChecked(cfg.readEntry("lastUsedColorsShow", true));
bool a = cfg.readEntry("lastUsedColorsAlignment", true);
ui->lastUsedColorsAlignVertical->setChecked(a);
ui->lastUsedColorsAlignHorizontal->setChecked(!a);
ui->lastUsedColorsAllowScrolling->setChecked(cfg.readEntry("lastUsedColorsScrolling", true));
ui->lastUsedColorsNumCols->setValue(cfg.readEntry("lastUsedColorsNumCols", 1));
ui->lastUsedColorsNumRows->setValue(cfg.readEntry("lastUsedColorsNumRows", 1));
ui->lastUsedColorsPatchCount->setValue(cfg.readEntry("lastUsedColorsCount", 20));
ui->lastUsedColorsWidth->setValue(cfg.readEntry("lastUsedColorsWidth", 16));
ui->lastUsedColorsHeight->setValue(cfg.readEntry("lastUsedColorsHeight", 16));
ui->commonColorsShow->setChecked(cfg.readEntry("commonColorsShow", true));
a = cfg.readEntry("commonColorsAlignment", false);
ui->commonColorsAlignVertical->setChecked(a);
ui->commonColorsAlignHorizontal->setChecked(!a);
ui->commonColorsAllowScrolling->setChecked(cfg.readEntry("commonColorsScrolling", true));
ui->commonColorsNumCols->setValue(cfg.readEntry("commonColorsNumCols", 1));
ui->commonColorsNumRows->setValue(cfg.readEntry("commonColorsNumRows", 1));
ui->commonColorsPatchCount->setValue(cfg.readEntry("commonColorsCount", 12));
ui->commonColorsWidth->setValue(cfg.readEntry("commonColorsWidth", 16));
ui->commonColorsHeight->setValue(cfg.readEntry("commonColorsHeight", 16));
ui->commonColorsAutoUpdate->setChecked(cfg.readEntry("commonColorsAutoUpdate", false));
//shade selector
QString shadeSelectorType=cfg.readEntry("shadeSelectorType", "Minimal");
if ( shadeSelectorType == "MyPaint") {
ui->ACSShadeSelectorTypeComboBox->setCurrentIndex(0);
} else if (shadeSelectorType == "Minimal") {
ui->ACSShadeSelectorTypeComboBox->setCurrentIndex(1);
} else { // Hidden
ui->ACSShadeSelectorTypeComboBox->setCurrentIndex(2);
}
ui->shadeSelectorUpdateOnRightClick->setChecked(cfg.readEntry("shadeSelectorUpdateOnRightClick", false));
ui->shadeSelectorUpdateOnLeftClick->setChecked(cfg.readEntry("shadeSelectorUpdateOnLeftClick", false));
ui->shadeSelectorUpdateOnForeground->setChecked(cfg.readEntry("shadeSelectorUpdateOnForeground", true));
ui->shadeSelectorUpdateOnBackground->setChecked(cfg.readEntry("shadeSelectorUpdateOnBackground", true));
ui->hidePopupOnClickCheck->setChecked(cfg.readEntry("hidePopupOnClickCheck", false));
QString shadeMyPaintType = cfg.readEntry("shadeMyPaintType", "HSV");
if (shadeMyPaintType == "HSV" ) {
ui->ACSshadeSelectorMyPaintColorModelComboBox->setCurrentIndex(0);
} else if (shadeMyPaintType == "HSL" ) {
ui->ACSshadeSelectorMyPaintColorModelComboBox->setCurrentIndex(1);
} else if (shadeMyPaintType == "HSI" ) {
ui->ACSshadeSelectorMyPaintColorModelComboBox->setCurrentIndex(2);
} else { // HSY
ui->ACSshadeSelectorMyPaintColorModelComboBox->setCurrentIndex(3);
}
bool asGradient = cfg.readEntry("minimalShadeSelectorAsGradient", true);
if(asGradient) ui->minimalShadeSelectorAsGradient->setChecked(true);
else ui->minimalShadeSelectorAsColorPatches->setChecked(true);
ui->minimalShadeSelectorPatchesPerLine->setValue(cfg.readEntry("minimalShadeSelectorPatchCount", 10));
ui->minimalShadeSelectorLineSettings->fromString(cfg.readEntry("minimalShadeSelectorLineConfig", "0|0.2|0|0|0|0|0;1|0|1|1|0|0|0;2|0|-1|1|0|0|0;"));
ui->minimalShadeSelectorLineHeight->setValue(cfg.readEntry("minimalShadeSelectorLineHeight", 10));
int hsxSettingType= (int)cfg.readEntry("hsxSettingType", 0);
ui->colorSelectorTypeComboBox->setCurrentIndex(hsxSettingType);
//color selector
KisColorSelectorComboBox* cstw = dynamic_cast<KisColorSelectorComboBox*>(ui->colorSelectorConfiguration);
cstw->setConfiguration(KisColorSelectorConfiguration::fromString(cfg.readEntry("colorSelectorConfiguration", "3|0|5|0"))); // triangle selector
//luma values//
ui->l_lumaR->setValue(cfg.readEntry("lumaR", 0.2126));
ui->l_lumaG->setValue(cfg.readEntry("lumaG", 0.7152));
ui->l_lumaB->setValue(cfg.readEntry("lumaB", 0.0722));
ui->SP_Gamma->setValue(cfg.readEntry("gamma", 2.2));
//color sliders//
ui->csl_hsvH->setChecked(hsxcfg.readEntry("hsvH", false));
ui->csl_hsvS->setChecked(hsxcfg.readEntry("hsvS", false));
ui->csl_hsvV->setChecked(hsxcfg.readEntry("hsvV", false));
ui->csl_hslH->setChecked(hsxcfg.readEntry("hslH", true));
ui->csl_hslS->setChecked(hsxcfg.readEntry("hslS", true));
ui->csl_hslL->setChecked(hsxcfg.readEntry("hslL", true));
ui->csl_hsiH->setChecked(hsxcfg.readEntry("hsiH", false));
ui->csl_hsiS->setChecked(hsxcfg.readEntry("hsiS", false));
ui->csl_hsiI->setChecked(hsxcfg.readEntry("hsiI", false));
ui->csl_hsyH->setChecked(hsxcfg.readEntry("hsyH", false));
ui->csl_hsyS->setChecked(hsxcfg.readEntry("hsyS", false));
ui->csl_hsyY->setChecked(hsxcfg.readEntry("hsyY", false));
//hotkeys//
ui->sb_lightness->setValue(hotkeycfg.readEntry("steps_lightness", 10));
ui->sb_saturation->setValue(hotkeycfg.readEntry("steps_saturation", 10));
ui->sb_hue->setValue(hotkeycfg.readEntry("steps_hue", 36));
ui->sb_rg->setValue(hotkeycfg.readEntry("steps_redgreen", 10));
ui->sb_by->setValue(hotkeycfg.readEntry("steps_blueyellow", 10));
}
void KisColorSelectorSettings::loadDefaultPreferences()
{
//set defaults
//if you change something, don't forget that loadPreferences should be kept in sync
// advanced color selector docker
ui->dockerResizeOptionsComboBox->setCurrentIndex(0);
ui->zoomSelectorOptionComboBox->setCurrentIndex(0);
ui->popupSize->setValue(280);
+ ui->chkShowColorSelector->setChecked(true);
ui->useDifferentColorSpaceCheckbox->setChecked(false);
ui->colorSpace->setCurrentColorModel(KoID("RGBA"));
ui->colorSpace->setCurrentColorDepth(KoID("U8"));
ui->colorSpace->setCurrentProfile(KoColorSpaceRegistry::instance()->rgb8()->profile()->name());
//color patches
ui->lastUsedColorsShow->setChecked(true);
ui->lastUsedColorsAlignVertical->setChecked(true);
ui->lastUsedColorsAlignHorizontal->setChecked(false);
ui->lastUsedColorsAllowScrolling->setChecked(true);
ui->lastUsedColorsNumCols->setValue(1);
ui->lastUsedColorsNumRows->setValue(1);
ui->lastUsedColorsPatchCount->setValue(20);
ui->lastUsedColorsWidth->setValue(16);
ui->lastUsedColorsHeight->setValue(16);
ui->commonColorsShow->setChecked(true);
ui->commonColorsAlignVertical->setChecked(false);
ui->commonColorsAlignHorizontal->setChecked(true);
ui->commonColorsAllowScrolling->setChecked(true);
ui->commonColorsNumCols->setValue(1);
ui->commonColorsNumRows->setValue(1);
ui->commonColorsPatchCount->setValue(12);
ui->commonColorsWidth->setValue(16);
ui->commonColorsHeight->setValue(16);
ui->commonColorsAutoUpdate->setChecked(false);
//shade selector
ui->ACSShadeSelectorTypeComboBox->setCurrentIndex(1); // Minimal
ui->ACSshadeSelectorMyPaintColorModelComboBox->setCurrentIndex(0);
ui->shadeSelectorUpdateOnRightClick->setChecked(false);
ui->shadeSelectorUpdateOnLeftClick->setChecked(false);
ui->shadeSelectorUpdateOnForeground->setChecked(true);
ui->shadeSelectorUpdateOnBackground->setChecked(true);
bool asGradient = true;
if(asGradient) ui->minimalShadeSelectorAsGradient->setChecked(true);
else ui->minimalShadeSelectorAsColorPatches->setChecked(true);
ui->minimalShadeSelectorPatchesPerLine->setValue(10);
ui->minimalShadeSelectorLineSettings->fromString("0|0.2|0|0|0|0|0;1|0|1|1|0|0|0;2|0|-1|1|0|0|0;");
ui->minimalShadeSelectorLineHeight->setValue(10);
// set advanced color selector to use HSV
ui->colorSelectorTypeComboBox->setCurrentIndex(0);
KisColorSelectorComboBox* cstw = dynamic_cast<KisColorSelectorComboBox*>(ui->colorSelectorConfiguration);
cstw->setConfiguration(KisColorSelectorConfiguration("3|0|5|0")); // triangle selector
//luma//
ui->l_lumaR->setValue(0.2126);
ui->l_lumaG->setValue(0.7152);
ui->l_lumaB->setValue(0.0722);
ui->SP_Gamma->setValue(2.2);
//color sliders//
ui->csl_hsvH->setChecked(false);
ui->csl_hsvS->setChecked(false);
ui->csl_hsvV->setChecked(false);
ui->csl_hslH->setChecked(true);
ui->csl_hslS->setChecked(true);
ui->csl_hslL->setChecked(true);
ui->csl_hsiH->setChecked(false);
ui->csl_hsiS->setChecked(false);
ui->csl_hsiI->setChecked(false);
ui->csl_hsyH->setChecked(false);
ui->csl_hsyS->setChecked(false);
ui->csl_hsyY->setChecked(false);
//hotkeys//
ui->sb_lightness->setValue(10);
ui->sb_saturation->setValue(10);
ui->sb_hue->setValue(36);
ui->sb_rg->setValue(10);
ui->sb_by->setValue(10);
}
KisColorSelectorSettingsDialog::KisColorSelectorSettingsDialog(QWidget *parent) :
QDialog(parent),
m_widget(new KisColorSelectorSettings(this))
{
QLayout* l = new QVBoxLayout(this);
l->addWidget(m_widget);
m_widget->loadPreferences();
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel|QDialogButtonBox::RestoreDefaults,
Qt::Horizontal,
this);
l->addWidget(buttonBox);
connect(buttonBox, SIGNAL(accepted()), m_widget, SLOT(savePreferences()));
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
connect(buttonBox->button(QDialogButtonBox::RestoreDefaults),
SIGNAL(clicked()), m_widget, SLOT(loadDefaultPreferences()));
}
diff --git a/plugins/dockers/advancedcolorselector/wdg_color_selector_settings.ui b/plugins/dockers/advancedcolorselector/wdg_color_selector_settings.ui
index 1228962083..e0ffc90df6 100644
--- a/plugins/dockers/advancedcolorselector/wdg_color_selector_settings.ui
+++ b/plugins/dockers/advancedcolorselector/wdg_color_selector_settings.ui
@@ -1,1560 +1,1572 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>KisColorSelectorSettings</class>
<widget class="QWidget" name="KisColorSelectorSettings">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
- <width>721</width>
+ <width>723</width>
<height>1108</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Color Selector Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_16">
<item>
<layout class="QFormLayout" name="dockerSelectForm">
<property name="labelAlignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="verticalSpacing">
<number>6</number>
</property>
<property name="bottomMargin">
<number>20</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="dockerColorSettingsLabel">
<property name="text">
<string>Docker:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="dockerColorSettingsComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTabWidget" name="advancedColorSelectorOptions">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>280</height>
</size>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab_typeshape">
<attribute name="title">
<string>Color Selector</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
- <layout class="QHBoxLayout" name="horizontalLayout_6">
- <item>
- <layout class="QVBoxLayout" name="verticalLayout_5">
- <item>
- <widget class="KisColorSelectorComboBox" name="colorSelectorConfiguration">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="verticalSpacer_2">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::MinimumExpanding</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>5</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </item>
- <item>
- <layout class="QVBoxLayout" name="verticalLayout_2">
- <property name="spacing">
- <number>6</number>
- </property>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_12">
- <property name="spacing">
- <number>12</number>
- </property>
- <property name="topMargin">
- <number>6</number>
- </property>
- <property name="rightMargin">
- <number>6</number>
- </property>
- <item>
- <layout class="QVBoxLayout" name="verticalLayout_3">
- <item>
- <widget class="QLabel" name="l_type">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="layoutDirection">
- <enum>Qt::LeftToRight</enum>
- </property>
- <property name="text">
- <string>Color &amp;Model Type: </string>
- </property>
- <property name="alignment">
- <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
- </property>
- <property name="buddy">
- <cstring>colorSelectorConfiguration</cstring>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QComboBox" name="colorSelectorTypeComboBox">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QLabel" name="ACSTypeDescriptionLabel">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string notr="true">Type Description goes here</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QGroupBox" name="lumaCoefficientGroupbox">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="title">
- <string>Luma Coefficients</string>
- </property>
- <layout class="QHBoxLayout" name="horizontalLayout_15">
+ <widget class="QGroupBox" name="chkShowColorSelector">
+ <property name="title">
+ <string>Show color selector</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_13">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_6">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
<item>
- <layout class="QGridLayout" name="gridLayout_5">
- <property name="leftMargin">
- <number>0</number>
+ <widget class="KisColorSelectorComboBox" name="colorSelectorConfiguration">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
</property>
- <property name="rightMargin">
- <number>0</number>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
</property>
- <property name="verticalSpacing">
+ <property name="sizeType">
+ <enum>QSizePolicy::MinimumExpanding</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>5</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_12">
+ <property name="spacing">
+ <number>12</number>
+ </property>
+ <property name="topMargin">
<number>6</number>
</property>
- <item row="0" column="0">
- <widget class="QLabel" name="redlabel">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Red': </string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- </item>
- <item row="0" column="3">
- <widget class="KisDoubleParseSpinBox" name="l_lumaG">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>50</width>
- <height>0</height>
- </size>
- </property>
- <property name="decimals">
- <number>4</number>
- </property>
- </widget>
- </item>
- <item row="0" column="2">
- <widget class="QLabel" name="greenlabel">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Green':</string>
- </property>
- </widget>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QLabel" name="l_type">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string>Color &amp;Model Type: </string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="buddy">
+ <cstring>colorSelectorConfiguration</cstring>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="colorSelectorTypeComboBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
</item>
- <item row="0" column="4">
- <widget class="QLabel" name="bluelabel">
+ <item>
+ <widget class="QLabel" name="ACSTypeDescriptionLabel">
<property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
- <string>Blue':</string>
+ <string notr="true">Type Description goes here</string>
</property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="KisDoubleParseSpinBox" name="l_lumaR">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>50</width>
- <height>0</height>
- </size>
- </property>
- <property name="decimals">
- <number>4</number>
- </property>
- </widget>
- </item>
- <item row="0" column="5">
- <widget class="KisDoubleParseSpinBox" name="l_lumaB">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>50</width>
- <height>0</height>
- </size>
- </property>
- <property name="decimals">
- <number>4</number>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="KisDoubleParseSpinBox" name="SP_Gamma">
- <property name="toolTip">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This sets the gamma value that the linearised HSY Luminosity is crunched with. 1 makes the selector fully linear, 2.2 is a practical default value.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
- </property>
- <property name="decimals">
- <number>1</number>
- </property>
- <property name="minimum">
- <double>-3.000000000000000</double>
- </property>
- <property name="maximum">
- <double>3.000000000000000</double>
- </property>
- <property name="singleStep">
- <double>0.100000000000000</double>
- </property>
- <property name="value">
- <double>2.200000000000000</double>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="gamma_label">
- <property name="text">
- <string>Gamma:</string>
+ <property name="wordWrap">
+ <bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
+ <item>
+ <widget class="QGroupBox" name="lumaCoefficientGroupbox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Luma Coefficients</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_15">
+ <item>
+ <layout class="QGridLayout" name="gridLayout_5">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>6</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="redlabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Red': </string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="KisDoubleParseSpinBox" name="l_lumaG">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>50</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="decimals">
+ <number>4</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="greenlabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Green':</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="4">
+ <widget class="QLabel" name="bluelabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Blue':</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="KisDoubleParseSpinBox" name="l_lumaR">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>50</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="decimals">
+ <number>4</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="5">
+ <widget class="KisDoubleParseSpinBox" name="l_lumaB">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>50</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="decimals">
+ <number>4</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="KisDoubleParseSpinBox" name="SP_Gamma">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This sets the gamma value that the linearised HSY Luminosity is crunched with. 1 makes the selector fully linear, 2.2 is a practical default value.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="decimals">
+ <number>1</number>
+ </property>
+ <property name="minimum">
+ <double>-3.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>3.000000000000000</double>
+ </property>
+ <property name="singleStep">
+ <double>0.100000000000000</double>
+ </property>
+ <property name="value">
+ <double>2.200000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="gamma_label">
+ <property name="text">
+ <string>Gamma:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
</layout>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
</item>
<item>
<widget class="QGroupBox" name="useDifferentColorSpaceCheckbox">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Color Selector Uses Different Color Space than Ima&amp;ge</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_10">
<item>
<widget class="KisColorSpaceSelector" name="colorSpace" native="true">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_9">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_general">
<attribute name="title">
<string>Behavior</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_15">
<item>
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>When Docker Resizes: </string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="zoomSelectorOptionComboBox"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Show Zoom Selector UI: </string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Zoom Selector Size: </string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="KisIntParseSpinBox" name="popupSize">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="suffix">
<string> px</string>
</property>
<property name="minimum">
<number>100</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
<property name="singleStep">
<number>10</number>
</property>
<property name="value">
<number>260</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="dockerResizeOptionsComboBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="hidePopupOnClickCheck">
<property name="text">
<string>Hide Popup on click.</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabShadeSelector">
<attribute name="title">
<string>Shade Selector</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_9">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_11">
<item>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Type:</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="myPaintColorModelLabel">
<property name="text">
<string>Color model:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="ACSShadeSelectorTypeComboBox"/>
</item>
<item row="0" column="3">
<widget class="QComboBox" name="ACSshadeSelectorMyPaintColorModelComboBox"/>
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_7">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_7">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Update Selector When:</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="shadeSelectorUpdateOnRightClick">
<property name="text">
<string>Right clicking on shade selector</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="shadeSelectorUpdateOnLeftClick">
<property name="text">
<string>Left clicking on shade selector</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="shadeSelectorUpdateOnForeground">
<property name="toolTip">
<string>this doesn't include a color change by the shade selector</string>
</property>
<property name="text">
<string>Foreground color changes</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="shadeSelectorUpdateOnBackground">
<property name="toolTip">
<string>this doesn't include a color change by the shade selector</string>
</property>
<property name="text">
<string>Background color change</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="minimalShadeSelectorGroup">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Minimal Shade Selector</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QLabel" name="label_15">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Display:</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="minimalShadeSelectorAsGradient">
<property name="text">
<string>&amp;Gradient</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="minimalShadeSelectorAsColorPatches">
<property name="text">
<string>Colo&amp;r Patches</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="KisShadeSelectorLinesSettings" name="minimalShadeSelectorLineSettings">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>59</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Line Count: </string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="KisIntParseSpinBox" name="minimalShadeSelectorLineCount">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>10</number>
</property>
<property name="value">
<number>3</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Line Height: </string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="KisIntParseSpinBox" name="minimalShadeSelectorLineHeight">
<property name="suffix">
<string> px</string>
</property>
<property name="minimum">
<number>8</number>
</property>
<property name="maximum">
<number>99</number>
</property>
<property name="value">
<number>16</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="patchesPerLineLabel">
<property name="text">
<string>Patches Per Line: </string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="KisIntParseSpinBox" name="minimalShadeSelectorPatchesPerLine"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_ColorHistory">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<attribute name="title">
<string>Color History</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_11">
<item>
<widget class="QGroupBox" name="lastUsedColorsShow">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Show Color Histor&amp;y</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_12">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Patch Options</string>
</property>
<layout class="QFormLayout" name="formLayout_4">
<item row="0" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Height:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="KisIntParseSpinBox" name="lastUsedColorsWidth">
<property name="suffix">
<string> px</string>
</property>
<property name="prefix">
<string/>
</property>
<property name="minimum">
<number>16</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Width: </string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="KisIntParseSpinBox" name="lastUsedColorsHeight">
<property name="suffix">
<string> px</string>
</property>
<property name="minimum">
<number>16</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Max Patches: </string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="KisIntParseSpinBox" name="lastUsedColorsPatchCount">
<property name="minimum">
<number>1</number>
</property>
<property name="value">
<number>30</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Layout</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="verticalSpacing">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QRadioButton" name="lastUsedColorsAlignVertical">
<property name="text">
<string>&amp;Vertical</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="lbl_lastUsedNumCols">
<property name="text">
<string>Colu&amp;mns:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>lastUsedColorsNumCols</cstring>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="KisIntParseSpinBox" name="lastUsedColorsNumCols">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>20</number>
</property>
<property name="value">
<number>2</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QRadioButton" name="lastUsedColorsAlignHorizontal">
<property name="text">
<string>Hori&amp;zontal</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="lbl_lastUsedNumRows">
<property name="text">
<string>&amp;Rows:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>lastUsedColorsNumRows</cstring>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="KisIntParseSpinBox" name="lastUsedColorsNumRows">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>20</number>
</property>
<property name="value">
<number>3</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="lastUsedColorsAllowScrolling">
<property name="text">
<string>Allow scrolling</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_6">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_colorsFromImage">
<attribute name="title">
<string>Colors from Image</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_14">
<item>
<widget class="QGroupBox" name="commonColorsShow">
<property name="enabled">
<bool>true</bool>
</property>
<property name="title">
<string>Show Colors from the ima&amp;ge</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_13">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Patch Options</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Height:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="KisIntParseSpinBox" name="commonColorsWidth">
<property name="suffix">
<string> px</string>
</property>
<property name="prefix">
<string/>
</property>
<property name="minimum">
<number>16</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Width:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="KisIntParseSpinBox" name="commonColorsHeight">
<property name="suffix">
<string> px</string>
</property>
<property name="minimum">
<number>16</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Max Patches: </string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="KisIntParseSpinBox" name="commonColorsPatchCount">
<property name="minimum">
<number>1</number>
</property>
<property name="value">
<number>30</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Layout</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="horizontalSpacing">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QRadioButton" name="commonColorsAlignVertical">
<property name="text">
<string>&amp;Vertical</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="lbl_commonColorsNumCols">
<property name="text">
<string>Colu&amp;mns:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>commonColorsNumCols</cstring>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="KisIntParseSpinBox" name="commonColorsNumCols">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>20</number>
</property>
<property name="value">
<number>2</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QRadioButton" name="commonColorsAlignHorizontal">
<property name="text">
<string>Hori&amp;zontal</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="lbl_commonColorsNumRows">
<property name="text">
<string>&amp;Rows:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>commonColorsNumRows</cstring>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="KisIntParseSpinBox" name="commonColorsNumRows">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>20</number>
</property>
<property name="value">
<number>3</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="commonColorsAutoUpdate">
<property name="toolTip">
<string>this can be slow on big images</string>
</property>
<property name="text">
<string>Update after every stroke</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="commonColorsAllowScrolling">
<property name="text">
<string>Allow scrolling</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_7">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QWidget" name="colorSliderOptions" native="true">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupHsv">
<property name="title">
<string>HSV Sliders to Show</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="csl_hsvH">
<property name="text">
<string>Hue</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="csl_hsvS">
<property name="text">
<string>Saturation</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="csl_hsvV">
<property name="text">
<string>Value</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupHsl">
<property name="title">
<string>HSL Sliders to Show</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QCheckBox" name="csl_hslH">
<property name="text">
<string>Hue</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="csl_hslS">
<property name="text">
<string>Saturation</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="csl_hslL">
<property name="text">
<string>Lightness</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupHsi">
<property name="title">
<string>HSI Sliders to Show</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QCheckBox" name="csl_hsiH">
<property name="text">
<string>Hue</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="csl_hsiS">
<property name="text">
<string>Saturation</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="csl_hsiI">
<property name="text">
<string>Intensity</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupHsy">
<property name="title">
<string>HSY' Sliders to Show</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QCheckBox" name="csl_hsyH">
<property name="text">
<string>Hue</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="csl_hsyS">
<property name="text">
<string>Saturation</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="csl_hsyY">
<property name="text">
<string>Luma</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="hotKeyOptions" native="true">
<layout class="QVBoxLayout" name="hotkeyOptions">
<item>
<widget class="QGroupBox" name="hsy">
<property name="title">
<string>Lightness, Saturation and Hue hotkey steps</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_8">
<property name="text">
<string>Lightness: </string>
</property>
</widget>
</item>
<item>
<widget class="KisIntParseSpinBox" name="sb_lightness">
<property name="suffix">
<string> steps</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_13">
<property name="text">
<string>Saturation: </string>
</property>
</widget>
</item>
<item>
<widget class="KisIntParseSpinBox" name="sb_saturation">
<property name="suffix">
<string> steps</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_17">
<property name="text">
<string>Hue: </string>
</property>
</widget>
</item>
<item>
<widget class="KisIntParseSpinBox" name="sb_hue">
<property name="suffix">
<string> steps</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="horizontalGroupBox_2">
<property name="title">
<string>YUV Redder/Greener/Bluer/Yellower hotkey steps</string>
</property>
<layout class="QHBoxLayout" name="yuv">
<item>
<widget class="QLabel" name="label_18">
<property name="text">
<string>Redder/Greener: </string>
</property>
</widget>
</item>
<item>
<widget class="KisIntParseSpinBox" name="sb_rg">
<property name="suffix">
<string> steps</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_19">
<property name="text">
<string>Bluer/Yellower: </string>
</property>
</widget>
</item>
<item>
<widget class="KisIntParseSpinBox" name="sb_by">
<property name="suffix">
<string> steps</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KisDoubleParseSpinBox</class>
<extends>QDoubleSpinBox</extends>
<header>kis_double_parse_spin_box.h</header>
</customwidget>
<customwidget>
<class>KisColorSpaceSelector</class>
<extends>QWidget</extends>
<header>widgets/kis_color_space_selector.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>KisIntParseSpinBox</class>
<extends>QSpinBox</extends>
<header>kis_int_parse_spin_box.h</header>
</customwidget>
<customwidget>
<class>KisColorSelectorComboBox</class>
<extends>QComboBox</extends>
<header>kis_color_selector_combo_box.h</header>
</customwidget>
<customwidget>
<class>KisShadeSelectorLinesSettings</class>
<extends>QComboBox</extends>
<header>kis_shade_selector_lines_settings.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>minimalShadeSelectorAsColorPatches</sender>
<signal>toggled(bool)</signal>
<receiver>minimalShadeSelectorPatchesPerLine</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>194</x>
<y>393</y>
</hint>
<hint type="destinationlabel">
<x>520</x>
<y>463</y>
</hint>
</hints>
</connection>
</connections>
</ui>
diff --git a/plugins/dockers/animation/kis_animation_utils.cpp b/plugins/dockers/animation/kis_animation_utils.cpp
index 0f0c528f4d..15f8611f26 100644
--- a/plugins/dockers/animation/kis_animation_utils.cpp
+++ b/plugins/dockers/animation/kis_animation_utils.cpp
@@ -1,345 +1,350 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_animation_utils.h"
#include "kundo2command.h"
#include "kis_algebra_2d.h"
#include "kis_image.h"
#include "kis_node.h"
#include "kis_keyframe_channel.h"
#include "kis_post_execution_undo_adapter.h"
#include "kis_global.h"
#include "kis_tool_utils.h"
#include "kis_image_animation_interface.h"
#include "kis_command_utils.h"
#include "kis_processing_applicator.h"
#include "kis_transaction.h"
namespace KisAnimationUtils {
const QString addFrameActionName = i18n("New Frame");
const QString duplicateFrameActionName = i18n("Copy Frame");
const QString removeFrameActionName = i18n("Remove Frame");
const QString removeFramesActionName = i18n("Remove Frames");
const QString lazyFrameCreationActionName = i18n("Auto Frame Mode");
const QString dropFramesActionName = i18n("Drop Frames");
const QString showLayerActionName = i18n("Show in Timeline");
const QString newLayerActionName = i18n("New Layer");
const QString addExistingLayerActionName = i18n("Add Existing Layer");
const QString removeLayerActionName = i18n("Remove Layer");
const QString addOpacityKeyframeActionName = i18n("Add opacity keyframe");
const QString addTransformKeyframeActionName = i18n("Add transform keyframe");
const QString removeOpacityKeyframeActionName = i18n("Remove opacity keyframe");
const QString removeTransformKeyframeActionName = i18n("Remove transform keyframe");
KUndo2Command* createKeyframeCommand(KisImageSP image, KisNodeSP node, const QString &channelId, int time, bool copy, KUndo2Command *parentCommand) {
KUndo2Command *cmd = new KisCommandUtils::LambdaCommand(
copy ? kundo2_i18n("Copy Keyframe") :
kundo2_i18n("Add Keyframe"),
parentCommand,
[image, node, channelId, time, copy] () mutable -> KUndo2Command* {
bool result = false;
QScopedPointer<KUndo2Command> cmd(new KUndo2Command());
KisKeyframeChannel *channel = node->getKeyframeChannel(channelId);
bool createdChannel = false;
if (!channel) {
node->enableAnimation();
channel = node->getKeyframeChannel(channelId, true);
if (!channel) return nullptr;
createdChannel = true;
}
if (copy) {
if (!channel->keyframeAt(time)) {
KisKeyframeSP srcFrame = channel->activeKeyframeAt(time);
channel->copyKeyframe(srcFrame, time, cmd.data());
result = true;
}
} else {
if (channel->keyframeAt(time) && !createdChannel) {
if (image->animationInterface()->currentTime() == time && channelId == KisKeyframeChannel::Content.id()) {
//shortcut: clearing the image instead
KisPaintDeviceSP device = node->paintDevice();
if (device) {
+ const QRect dirtyRect = device->extent();
+
KisTransaction transaction(kundo2_i18n("Clear"), device, cmd.data());
device->clear();
(void) transaction.endAndTake(); // saved as 'parent'
+
+ node->setDirty(dirtyRect);
+
result = true;
}
}
} else {
channel->addKeyframe(time, cmd.data());
result = true;
}
}
return result ? new KisCommandUtils::SkipFirstRedoWrapper(cmd.take()) : nullptr;
});
return cmd;
}
void createKeyframeLazy(KisImageSP image, KisNodeSP node, const QString &channelId, int time, bool copy) {
KUndo2Command *cmd = createKeyframeCommand(image, node, channelId, time, copy);
KisProcessingApplicator::runSingleCommandStroke(image, cmd, KisStrokeJobData::BARRIER);
}
void removeKeyframes(KisImageSP image, const FrameItemList &frames) {
KIS_SAFE_ASSERT_RECOVER_RETURN(!image->locked());
KUndo2Command *cmd = new KisCommandUtils::LambdaCommand(
kundo2_i18np("Remove Keyframe",
"Remove Keyframes",
frames.size()),
[image, frames] () {
bool result = false;
QScopedPointer<KUndo2Command> cmd(new KUndo2Command());
Q_FOREACH (const FrameItem &item, frames) {
const int time = item.time;
KisNodeSP node = item.node;
KisKeyframeChannel *channel = 0;
if (node) {
channel = node->getKeyframeChannel(item.channel);
}
if (!channel) continue;
KisKeyframeSP keyframe = channel->keyframeAt(time);
if (!keyframe) continue;
channel->deleteKeyframe(keyframe, cmd.data());
result = true;
}
return result ? new KisCommandUtils::SkipFirstRedoWrapper(cmd.take()) : 0;
});
KisProcessingApplicator::runSingleCommandStroke(image, cmd, KisStrokeJobData::BARRIER);
}
void removeKeyframe(KisImageSP image, KisNodeSP node, const QString &channel, int time) {
QVector<FrameItem> frames;
frames << FrameItem(node, channel, time);
removeKeyframes(image, frames);
}
struct LessOperator {
LessOperator(const QPoint &offset)
: m_columnCoeff(-KisAlgebra2D::signPZ(offset.x())),
m_rowCoeff(-1000000 * KisAlgebra2D::signZZ(offset.y()))
{
}
bool operator()(const QModelIndex &lhs, const QModelIndex &rhs) {
return
m_columnCoeff * lhs.column() + m_rowCoeff * lhs.row() <
m_columnCoeff * rhs.column() + m_rowCoeff * rhs.row();
}
private:
int m_columnCoeff;
int m_rowCoeff;
};
void sortPointsForSafeMove(QModelIndexList *points, const QPoint &offset)
{
std::sort(points->begin(), points->end(), LessOperator(offset));
}
bool supportsContentFrames(KisNodeSP node)
{
return node->inherits("KisPaintLayer") || node->inherits("KisFilterMask") || node->inherits("KisTransparencyMask") || node->inherits("KisSelectionBasedLayer");
}
void swapOneFrameItem(const FrameItem &src, const FrameItem &dst, KUndo2Command *parentCommand)
{
const int srcTime = src.time;
KisNodeSP srcNode = src.node;
KisKeyframeChannel *srcChannel = srcNode->getKeyframeChannel(src.channel);
const int dstTime = dst.time;
KisNodeSP dstNode = dst.node;
KisKeyframeChannel *dstChannel = dstNode->getKeyframeChannel(dst.channel, true);
if (srcNode == dstNode) {
if (!srcChannel) return; // TODO: add warning!
srcChannel->swapFrames(srcTime, dstTime, parentCommand);
} else {
if (!srcChannel || !dstChannel) return; // TODO: add warning!
dstChannel->swapExternalKeyframe(srcChannel, srcTime, dstTime, parentCommand);
}
}
void moveOneFrameItem(const FrameItem &src, const FrameItem &dst, bool copy, bool moveEmptyFrames, KUndo2Command *parentCommand)
{
const int srcTime = src.time;
KisNodeSP srcNode = src.node;
KisKeyframeChannel *srcChannel = srcNode->getKeyframeChannel(src.channel);
const int dstTime = dst.time;
KisNodeSP dstNode = dst.node;
KisKeyframeChannel *dstChannel = dstNode->getKeyframeChannel(dst.channel, true);
if (srcNode == dstNode) {
if (!srcChannel) return; // TODO: add warning!
KisKeyframeSP srcKeyframe = srcChannel->keyframeAt(srcTime);
KisKeyframeSP dstKeyFrame = srcChannel->keyframeAt(dstTime);
if (srcKeyframe) {
if (copy) {
srcChannel->copyKeyframe(srcKeyframe, dstTime, parentCommand);
} else {
srcChannel->moveKeyframe(srcKeyframe, dstTime, parentCommand);
}
} else {
if (dstKeyFrame && moveEmptyFrames && !copy) {
//Destination is effectively replaced by an empty frame.
dstChannel->deleteKeyframe(dstKeyFrame, parentCommand);
}
}
} else {
if (!srcChannel || !dstChannel) return; // TODO: add warning!
KisKeyframeSP srcKeyframe = srcChannel->keyframeAt(srcTime);
if (!srcKeyframe) return; // TODO: add warning!
dstChannel->copyExternalKeyframe(srcChannel, srcTime, dstTime, parentCommand);
if (!copy) {
srcChannel->deleteKeyframe(srcKeyframe, parentCommand);
}
}
}
KUndo2Command* createMoveKeyframesCommand(const FrameItemList &srcFrames,
const FrameItemList &dstFrames,
bool copy,
bool moveEmpty,
KUndo2Command *parentCommand)
{
FrameMovePairList srcDstPairs;
for (int i = 0; i < srcFrames.size(); i++) {
srcDstPairs << std::make_pair(srcFrames[i], dstFrames[i]);
}
return createMoveKeyframesCommand(srcDstPairs, copy, moveEmpty, parentCommand);
}
KUndo2Command* createMoveKeyframesCommand(const FrameMovePairList &srcDstPairs,
bool copy,
bool moveEmptyFrames,
KUndo2Command *parentCommand)
{
KUndo2Command *cmd = new KisCommandUtils::LambdaCommand(
!copy ?
kundo2_i18np("Move Keyframe",
"Move %1 Keyframes",
srcDstPairs.size()) :
kundo2_i18np("Copy Keyframe",
"Copy %1 Keyframes",
srcDstPairs.size()),
parentCommand,
[srcDstPairs, copy, moveEmptyFrames] () -> KUndo2Command* {
bool result = false;
QScopedPointer<KUndo2Command> cmd(new KUndo2Command());
using MoveChain = QList<FrameItem>;
QHash<FrameItem, MoveChain> moveMap;
Q_FOREACH (const FrameMovePair &pair, srcDstPairs) {
moveMap.insert(pair.first, {pair.second});
}
auto it = moveMap.begin();
while (it != moveMap.end()) {
MoveChain &chain = it.value();
const FrameItem &previousFrame = chain.last();
auto tailIt = moveMap.find(previousFrame);
if (tailIt == it || tailIt == moveMap.end()) {
++it;
continue;
}
chain.append(tailIt.value());
tailIt = moveMap.erase(tailIt);
// no incrementing! we are going to check the new tail now!
}
for (it = moveMap.begin(); it != moveMap.end(); ++it) {
MoveChain &chain = it.value();
chain.prepend(it.key());
KIS_SAFE_ASSERT_RECOVER(chain.size() > 1) { continue; }
bool isCycle = false;
if (chain.last() == chain.first()) {
isCycle = true;
chain.takeLast();
}
auto frameIt = chain.rbegin();
FrameItem dstItem = *frameIt++;
while (frameIt != chain.rend()) {
FrameItem srcItem = *frameIt++;
if (!isCycle) {
moveOneFrameItem(srcItem, dstItem, copy, moveEmptyFrames, cmd.data());
} else {
swapOneFrameItem(srcItem, dstItem, cmd.data());
}
dstItem = srcItem;
result = true;
}
}
return result ? new KisCommandUtils::SkipFirstRedoWrapper(cmd.take()) : 0;
});
return cmd;
}
QDebug operator<<(QDebug dbg, const FrameItem &item)
{
dbg.nospace() << "FrameItem(" << item.node->name() << ", " << item.channel << ", " << item.time << ")";
return dbg.space();
}
}
diff --git a/plugins/dockers/animation/timeline_frames_view.cpp b/plugins/dockers/animation/timeline_frames_view.cpp
index 67a7b9a803..b1e3baed51 100644
--- a/plugins/dockers/animation/timeline_frames_view.cpp
+++ b/plugins/dockers/animation/timeline_frames_view.cpp
@@ -1,1520 +1,1521 @@
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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 "timeline_frames_view.h"
#include "timeline_frames_model.h"
#include "timeline_ruler_header.h"
#include "timeline_layers_header.h"
#include "timeline_insert_keyframe_dialog.h"
#include "timeline_frames_item_delegate.h"
#include <QPainter>
#include <QApplication>
#include <QDropEvent>
#include <QMenu>
#include <QScrollBar>
#include <QScroller>
#include <QDrag>
#include <QInputDialog>
#include <QClipboard>
#include <QMimeData>
+#include "config-qtmultimedia.h"
#include "KSharedConfig"
#include "KisKineticScroller.h"
#include "kis_zoom_button.h"
#include "kis_icon_utils.h"
#include "kis_animation_utils.h"
#include "kis_custom_modifiers_catcher.h"
#include "kis_action.h"
#include "kis_signal_compressor.h"
#include "kis_time_range.h"
#include "kis_color_label_selector_widget.h"
#include "kis_slider_spin_box.h"
#include <KisImportExportManager.h>
#include <kis_signals_blocker.h>
#include <kis_image_config.h>
#include <KoFileDialog.h>
#include <KoIconToolTip.h>
typedef QPair<QRect, QModelIndex> QItemViewPaintPair;
typedef QList<QItemViewPaintPair> QItemViewPaintPairs;
struct TimelineFramesView::Private
{
Private(TimelineFramesView *_q)
: q(_q),
fps(1),
zoomStillPointIndex(-1),
zoomStillPointOriginalOffset(0),
dragInProgress(false),
dragWasSuccessful(false),
modifiersCatcher(0),
selectionChangedCompressor(300, KisSignalCompressor::FIRST_INACTIVE)
{}
TimelineFramesView *q;
TimelineFramesModel *model;
TimelineRulerHeader *horizontalRuler;
TimelineLayersHeader *layersHeader;
int fps;
int zoomStillPointIndex;
int zoomStillPointOriginalOffset;
QPoint initialDragPanValue;
QPoint initialDragPanPos;
QToolButton *addLayersButton;
KisAction *showHideLayerAction;
QToolButton *audioOptionsButton;
KisColorLabelSelectorWidget *colorSelector;
QWidgetAction *colorSelectorAction;
KisColorLabelSelectorWidget *multiframeColorSelector;
QWidgetAction *multiframeColorSelectorAction;
QMenu *audioOptionsMenu;
QAction *openAudioAction;
QAction *audioMuteAction;
KisSliderSpinBox *volumeSlider;
QMenu *layerEditingMenu;
QMenu *existingLayersMenu;
TimelineInsertKeyframeDialog *insertKeyframeDialog;
KisZoomButton *zoomDragButton;
bool dragInProgress;
bool dragWasSuccessful;
KisCustomModifiersCatcher *modifiersCatcher;
QPoint lastPressedPosition;
Qt::KeyboardModifiers lastPressedModifier;
KisSignalCompressor selectionChangedCompressor;
QStyleOptionViewItem viewOptionsV4() const;
QItemViewPaintPairs draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const;
QPixmap renderToPixmap(const QModelIndexList &indexes, QRect *r) const;
KoIconToolTip tip;
KisActionManager *actionMan = 0;
};
TimelineFramesView::TimelineFramesView(QWidget *parent)
: QTableView(parent),
m_d(new Private(this))
{
m_d->modifiersCatcher = new KisCustomModifiersCatcher(this);
m_d->modifiersCatcher->addModifier("pan-zoom", Qt::Key_Space);
m_d->modifiersCatcher->addModifier("offset-frame", Qt::Key_Alt);
setCornerButtonEnabled(false);
setSelectionBehavior(QAbstractItemView::SelectItems);
setSelectionMode(QAbstractItemView::ExtendedSelection);
setItemDelegate(new TimelineFramesItemDelegate(this));
setDragEnabled(true);
setDragDropMode(QAbstractItemView::DragDrop);
setAcceptDrops(true);
setDropIndicatorShown(true);
setDefaultDropAction(Qt::MoveAction);
m_d->horizontalRuler = new TimelineRulerHeader(this);
this->setHorizontalHeader(m_d->horizontalRuler);
connect(m_d->horizontalRuler, SIGNAL(sigInsertColumnLeft()), SLOT(slotInsertKeyframeColumnLeft()));
connect(m_d->horizontalRuler, SIGNAL(sigInsertColumnRight()), SLOT(slotInsertKeyframeColumnRight()));
connect(m_d->horizontalRuler, SIGNAL(sigInsertMultipleColumns()), SLOT(slotInsertMultipleKeyframeColumns()));
connect(m_d->horizontalRuler, SIGNAL(sigRemoveColumns()), SLOT(slotRemoveSelectedColumns()));
connect(m_d->horizontalRuler, SIGNAL(sigRemoveColumnsAndShift()), SLOT(slotRemoveSelectedColumnsAndShift()));
connect(m_d->horizontalRuler, SIGNAL(sigInsertHoldColumns()), SLOT(slotInsertHoldFrameColumn()));
connect(m_d->horizontalRuler, SIGNAL(sigRemoveHoldColumns()), SLOT(slotRemoveHoldFrameColumn()));
connect(m_d->horizontalRuler, SIGNAL(sigInsertHoldColumnsCustom()), SLOT(slotInsertMultipleHoldFrameColumns()));
connect(m_d->horizontalRuler, SIGNAL(sigRemoveHoldColumnsCustom()), SLOT(slotRemoveMultipleHoldFrameColumns()));
connect(m_d->horizontalRuler, SIGNAL(sigMirrorColumns()), SLOT(slotMirrorColumns()));
connect(m_d->horizontalRuler, SIGNAL(sigCopyColumns()), SLOT(slotCopyColumns()));
connect(m_d->horizontalRuler, SIGNAL(sigCutColumns()), SLOT(slotCutColumns()));
connect(m_d->horizontalRuler, SIGNAL(sigPasteColumns()), SLOT(slotPasteColumns()));
m_d->layersHeader = new TimelineLayersHeader(this);
m_d->layersHeader->setSectionResizeMode(QHeaderView::Fixed);
m_d->layersHeader->setDefaultSectionSize(24);
m_d->layersHeader->setMinimumWidth(60);
m_d->layersHeader->setHighlightSections(true);
this->setVerticalHeader(m_d->layersHeader);
connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), SLOT(slotUpdateInfiniteFramesCount()));
connect(horizontalScrollBar(), SIGNAL(sliderReleased()), SLOT(slotUpdateInfiniteFramesCount()));
/********** New Layer Menu ***********************************************************/
m_d->addLayersButton = new QToolButton(this);
m_d->addLayersButton->setAutoRaise(true);
m_d->addLayersButton->setIcon(KisIconUtils::loadIcon("addlayer"));
m_d->addLayersButton->setIconSize(QSize(20, 20));
m_d->addLayersButton->setPopupMode(QToolButton::InstantPopup);
m_d->layerEditingMenu = new QMenu(this);
m_d->layerEditingMenu->addAction(KisAnimationUtils::newLayerActionName, this, SLOT(slotAddNewLayer()));
m_d->existingLayersMenu = m_d->layerEditingMenu->addMenu(KisAnimationUtils::addExistingLayerActionName);
m_d->layerEditingMenu->addSeparator();
m_d->layerEditingMenu->addAction(KisAnimationUtils::removeLayerActionName, this, SLOT(slotRemoveLayer()));
connect(m_d->existingLayersMenu, SIGNAL(aboutToShow()), SLOT(slotUpdateLayersMenu()));
connect(m_d->existingLayersMenu, SIGNAL(triggered(QAction*)), SLOT(slotAddExistingLayer(QAction*)));
connect(m_d->layersHeader, SIGNAL(sigRequestContextMenu(QPoint)), SLOT(slotLayerContextMenuRequested(QPoint)));
m_d->addLayersButton->setMenu(m_d->layerEditingMenu);
/********** Audio Channel Menu *******************************************************/
m_d->audioOptionsButton = new QToolButton(this);
m_d->audioOptionsButton->setAutoRaise(true);
m_d->audioOptionsButton->setIcon(KisIconUtils::loadIcon("audio-none"));
m_d->audioOptionsButton->setIconSize(QSize(20, 20)); // very small on windows if not explicitly set
m_d->audioOptionsButton->setPopupMode(QToolButton::InstantPopup);
m_d->audioOptionsMenu = new QMenu(this);
#ifndef HAVE_QT_MULTIMEDIA
m_d->audioOptionsMenu->addSection(i18nc("@item:inmenu", "Audio playback is not supported in this build!"));
#endif
m_d->openAudioAction= new QAction("XXX", this);
connect(m_d->openAudioAction, SIGNAL(triggered()), this, SLOT(slotSelectAudioChannelFile()));
m_d->audioOptionsMenu->addAction(m_d->openAudioAction);
m_d->audioMuteAction = new QAction(i18nc("@item:inmenu", "Mute"), this);
m_d->audioMuteAction->setCheckable(true);
connect(m_d->audioMuteAction, SIGNAL(triggered(bool)), SLOT(slotAudioChannelMute(bool)));
m_d->audioOptionsMenu->addAction(m_d->audioMuteAction);
m_d->audioOptionsMenu->addAction(i18nc("@item:inmenu", "Remove audio"), this, SLOT(slotAudioChannelRemove()));
m_d->audioOptionsMenu->addSeparator();
m_d->volumeSlider = new KisSliderSpinBox(this);
m_d->volumeSlider->setRange(0, 100);
m_d->volumeSlider->setSuffix("%");
m_d->volumeSlider->setPrefix(i18nc("@item:inmenu, slider", "Volume:"));
m_d->volumeSlider->setSingleStep(1);
m_d->volumeSlider->setPageStep(10);
m_d->volumeSlider->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
connect(m_d->volumeSlider, SIGNAL(valueChanged(int)), SLOT(slotAudioVolumeChanged(int)));
QWidgetAction *volumeAction = new QWidgetAction(m_d->audioOptionsMenu);
volumeAction->setDefaultWidget(m_d->volumeSlider);
m_d->audioOptionsMenu->addAction(volumeAction);
m_d->audioOptionsButton->setMenu(m_d->audioOptionsMenu);
/********** Frame Editing Context Menu ***********************************************/
m_d->colorSelector = new KisColorLabelSelectorWidget(this);
m_d->colorSelectorAction = new QWidgetAction(this);
m_d->colorSelectorAction->setDefaultWidget(m_d->colorSelector);
connect(m_d->colorSelector, &KisColorLabelSelectorWidget::currentIndexChanged, this, &TimelineFramesView::slotColorLabelChanged);
m_d->multiframeColorSelector = new KisColorLabelSelectorWidget(this);
m_d->multiframeColorSelectorAction = new QWidgetAction(this);
m_d->multiframeColorSelectorAction->setDefaultWidget(m_d->multiframeColorSelector);
connect(m_d->multiframeColorSelector, &KisColorLabelSelectorWidget::currentIndexChanged, this, &TimelineFramesView::slotColorLabelChanged);
/********** Insert Keyframes Dialog **************************************************/
m_d->insertKeyframeDialog = new TimelineInsertKeyframeDialog(this);
/********** Zoom Button **************************************************************/
m_d->zoomDragButton = new KisZoomButton(this);
m_d->zoomDragButton->setAutoRaise(true);
m_d->zoomDragButton->setIcon(KisIconUtils::loadIcon("zoom-horizontal"));
m_d->zoomDragButton->setIconSize(QSize(20, 20)); // this icon is very small on windows if no explicitly set
m_d->zoomDragButton->setToolTip(i18nc("@info:tooltip", "Zoom Timeline. Hold down and drag left or right."));
m_d->zoomDragButton->setPopupMode(QToolButton::InstantPopup);
connect(m_d->zoomDragButton, SIGNAL(zoomLevelChanged(qreal)), SLOT(slotZoomButtonChanged(qreal)));
connect(m_d->zoomDragButton, SIGNAL(zoomStarted(qreal)), SLOT(slotZoomButtonPressed(qreal)));
setFramesPerSecond(12);
setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
{
QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(this);
if( scroller ) {
connect(scroller, SIGNAL(stateChanged(QScroller::State)),
this, SLOT(slotScrollerStateChanged(QScroller::State)));
}
}
connect(&m_d->selectionChangedCompressor, SIGNAL(timeout()),
SLOT(slotSelectionChanged()));
connect(&m_d->selectionChangedCompressor, SIGNAL(timeout()),
SLOT(slotUpdateFrameActions()));
{
QClipboard *cb = QApplication::clipboard();
connect(cb, SIGNAL(dataChanged()), SLOT(slotUpdateFrameActions()));
}
}
TimelineFramesView::~TimelineFramesView()
{
}
void TimelineFramesView::setShowInTimeline(KisAction *action)
{
m_d->showHideLayerAction = action;
m_d->layerEditingMenu->addAction(m_d->showHideLayerAction);
}
void TimelineFramesView::setActionManager(KisActionManager *actionManager)
{
m_d->actionMan = actionManager;
m_d->horizontalRuler->setActionManager(actionManager);
if (actionManager) {
KisAction *action = 0;
action = m_d->actionMan->createAction("add_blank_frame");
connect(action, SIGNAL(triggered()), SLOT(slotAddBlankFrame()));
action = m_d->actionMan->createAction("add_duplicate_frame");
connect(action, SIGNAL(triggered()), SLOT(slotAddDuplicateFrame()));
action = m_d->actionMan->createAction("insert_keyframe_left");
connect(action, SIGNAL(triggered()), SLOT(slotInsertKeyframeLeft()));
action = m_d->actionMan->createAction("insert_keyframe_right");
connect(action, SIGNAL(triggered()), SLOT(slotInsertKeyframeRight()));
action = m_d->actionMan->createAction("insert_multiple_keyframes");
connect(action, SIGNAL(triggered()), SLOT(slotInsertMultipleKeyframes()));
action = m_d->actionMan->createAction("remove_frames_and_pull");
connect(action, SIGNAL(triggered()), SLOT(slotRemoveSelectedFramesAndShift()));
action = m_d->actionMan->createAction("remove_frames");
connect(action, SIGNAL(triggered()), SLOT(slotRemoveSelectedFrames()));
action = m_d->actionMan->createAction("insert_hold_frame");
connect(action, SIGNAL(triggered()), SLOT(slotInsertHoldFrame()));
action = m_d->actionMan->createAction("insert_multiple_hold_frames");
connect(action, SIGNAL(triggered()), SLOT(slotInsertMultipleHoldFrames()));
action = m_d->actionMan->createAction("remove_hold_frame");
connect(action, SIGNAL(triggered()), SLOT(slotRemoveHoldFrame()));
action = m_d->actionMan->createAction("remove_multiple_hold_frames");
connect(action, SIGNAL(triggered()), SLOT(slotRemoveMultipleHoldFrames()));
action = m_d->actionMan->createAction("mirror_frames");
connect(action, SIGNAL(triggered()), SLOT(slotMirrorFrames()));
action = m_d->actionMan->createAction("copy_frames_to_clipboard");
connect(action, SIGNAL(triggered()), SLOT(slotCopyFrames()));
action = m_d->actionMan->createAction("cut_frames_to_clipboard");
connect(action, SIGNAL(triggered()), SLOT(slotCutFrames()));
action = m_d->actionMan->createAction("paste_frames_from_clipboard");
connect(action, SIGNAL(triggered()), SLOT(slotPasteFrames()));
action = m_d->actionMan->createAction("set_start_time");
connect(action, SIGNAL(triggered()), SLOT(slotSetStartTimeToCurrentPosition()));
action = m_d->actionMan->createAction("set_end_time");
connect(action, SIGNAL(triggered()), SLOT(slotSetEndTimeToCurrentPosition()));
action = m_d->actionMan->createAction("update_playback_range");
connect(action, SIGNAL(triggered()), SLOT(slotUpdatePlackbackRange()));
}
}
void resizeToMinimalSize(QAbstractButton *w, int minimalSize) {
QSize buttonSize = w->sizeHint();
if (buttonSize.height() > minimalSize) {
buttonSize = QSize(minimalSize, minimalSize);
}
w->resize(buttonSize);
}
void TimelineFramesView::updateGeometries()
{
QTableView::updateGeometries();
const int availableHeight = m_d->horizontalRuler->height();
const int margin = 2;
const int minimalSize = availableHeight - 2 * margin;
resizeToMinimalSize(m_d->addLayersButton, minimalSize);
resizeToMinimalSize(m_d->audioOptionsButton, minimalSize);
resizeToMinimalSize(m_d->zoomDragButton, minimalSize);
int x = 2 * margin;
int y = (availableHeight - minimalSize) / 2;
m_d->addLayersButton->move(x, 2 * y);
m_d->audioOptionsButton->move(x + minimalSize + 2 * margin, 2 * y);
const int availableWidth = m_d->layersHeader->width();
x = availableWidth - margin - minimalSize;
m_d->zoomDragButton->move(x, 2 * y);
}
void TimelineFramesView::setModel(QAbstractItemModel *model)
{
TimelineFramesModel *framesModel = qobject_cast<TimelineFramesModel*>(model);
m_d->model = framesModel;
QTableView::setModel(model);
connect(m_d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
this, SLOT(slotHeaderDataChanged(Qt::Orientation,int,int)));
connect(m_d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
this, SLOT(slotDataChanged(QModelIndex,QModelIndex)));
connect(m_d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
this, SLOT(slotReselectCurrentIndex()));
connect(m_d->model, SIGNAL(sigInfiniteTimelineUpdateNeeded()),
this, SLOT(slotUpdateInfiniteFramesCount()));
connect(m_d->model, SIGNAL(sigAudioChannelChanged()),
this, SLOT(slotUpdateAudioActions()));
connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
&m_d->selectionChangedCompressor, SLOT(start()));
connect(m_d->model, SIGNAL(sigEnsureRowVisible(int)), SLOT(slotEnsureRowVisible(int)));
slotUpdateAudioActions();
}
void TimelineFramesView::setFramesPerSecond(int fps)
{
m_d->fps = fps;
m_d->horizontalRuler->setFramePerSecond(fps);
// For some reason simple update sometimes doesn't work here, so
// reset the whole header
//
// m_d->horizontalRuler->reset();
}
void TimelineFramesView::slotZoomButtonPressed(qreal staticPoint)
{
m_d->zoomStillPointIndex =
qIsNaN(staticPoint) ? currentIndex().column() : staticPoint;
const int w = m_d->horizontalRuler->defaultSectionSize();
m_d->zoomStillPointOriginalOffset =
w * m_d->zoomStillPointIndex -
horizontalScrollBar()->value();
}
void TimelineFramesView::slotZoomButtonChanged(qreal zoomLevel)
{
if (m_d->horizontalRuler->setZoom(zoomLevel)) {
slotUpdateInfiniteFramesCount();
const int w = m_d->horizontalRuler->defaultSectionSize();
horizontalScrollBar()->setValue(w * m_d->zoomStillPointIndex - m_d->zoomStillPointOriginalOffset);
viewport()->update();
}
}
void TimelineFramesView::slotColorLabelChanged(int label)
{
Q_FOREACH(QModelIndex index, selectedIndexes()) {
m_d->model->setData(index, label, TimelineFramesModel::FrameColorLabelIndexRole);
}
KisImageConfig(false).setDefaultFrameColorLabel(label);
}
void TimelineFramesView::slotSelectAudioChannelFile()
{
if (!m_d->model) return;
QString defaultDir = QStandardPaths::writableLocation(QStandardPaths::MusicLocation);
const QString currentFile = m_d->model->audioChannelFileName();
QDir baseDir = QFileInfo(currentFile).absoluteDir();
if (baseDir.exists()) {
defaultDir = baseDir.absolutePath();
}
const QString result = KisImportExportManager::askForAudioFileName(defaultDir, this);
const QFileInfo info(result);
if (info.exists()) {
m_d->model->setAudioChannelFileName(info.absoluteFilePath());
}
}
void TimelineFramesView::slotAudioChannelMute(bool value)
{
if (!m_d->model) return;
if (value != m_d->model->isAudioMuted()) {
m_d->model->setAudioMuted(value);
}
}
void TimelineFramesView::slotUpdateIcons()
{
m_d->addLayersButton->setIcon(KisIconUtils::loadIcon("addlayer"));
m_d->audioOptionsButton->setIcon(KisIconUtils::loadIcon("audio-none"));
m_d->zoomDragButton->setIcon(KisIconUtils::loadIcon("zoom-horizontal"));
}
void TimelineFramesView::slotAudioChannelRemove()
{
if (!m_d->model) return;
m_d->model->setAudioChannelFileName(QString());
}
void TimelineFramesView::slotUpdateAudioActions()
{
if (!m_d->model) return;
const QString currentFile = m_d->model->audioChannelFileName();
if (currentFile.isEmpty()) {
m_d->openAudioAction->setText(i18nc("@item:inmenu", "Open audio..."));
} else {
QFileInfo info(currentFile);
m_d->openAudioAction->setText(i18nc("@item:inmenu", "Change audio (%1)...", info.fileName()));
}
m_d->audioMuteAction->setChecked(m_d->model->isAudioMuted());
QIcon audioIcon;
if (currentFile.isEmpty()) {
audioIcon = KisIconUtils::loadIcon("audio-none");
} else {
if (m_d->model->isAudioMuted()) {
audioIcon = KisIconUtils::loadIcon("audio-volume-mute");
} else {
audioIcon = KisIconUtils::loadIcon("audio-volume-high");
}
}
m_d->audioOptionsButton->setIcon(audioIcon);
m_d->volumeSlider->setEnabled(!m_d->model->isAudioMuted());
KisSignalsBlocker b(m_d->volumeSlider);
m_d->volumeSlider->setValue(qRound(m_d->model->audioVolume() * 100.0));
}
void TimelineFramesView::slotAudioVolumeChanged(int value)
{
m_d->model->setAudioVolume(qreal(value) / 100.0);
}
void TimelineFramesView::slotUpdateInfiniteFramesCount()
{
if (horizontalScrollBar()->isSliderDown()) return;
const int sectionWidth = m_d->horizontalRuler->defaultSectionSize();
const int calculatedIndex =
(horizontalScrollBar()->value() +
m_d->horizontalRuler->width() - 1) / sectionWidth;
m_d->model->setLastVisibleFrame(calculatedIndex);
}
void TimelineFramesView::slotScrollerStateChanged( QScroller::State state ) {
KisKineticScroller::updateCursor(this, state);
}
void TimelineFramesView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
{
QTableView::currentChanged(current, previous);
if (previous.column() != current.column()) {
m_d->model->setData(previous, false, TimelineFramesModel::ActiveFrameRole);
m_d->model->setData(current, true, TimelineFramesModel::ActiveFrameRole);
}
}
QItemSelectionModel::SelectionFlags TimelineFramesView::selectionCommand(const QModelIndex &index,
const QEvent *event) const
{
// WARNING: Copy-pasted from KisNodeView! Please keep in sync!
/**
* Qt has a bug: when we Ctrl+click on an item, the item's
* selections gets toggled on mouse *press*, whereas usually it is
* done on mouse *release*. Therefore the user cannot do a
* Ctrl+D&D with the default configuration. This code fixes the
* problem by manually returning QItemSelectionModel::NoUpdate
* flag when the user clicks on an item and returning
* QItemSelectionModel::Toggle on release.
*/
if (event &&
(event->type() == QEvent::MouseButtonPress ||
event->type() == QEvent::MouseButtonRelease) &&
index.isValid()) {
const QMouseEvent *mevent = static_cast<const QMouseEvent*>(event);
if (mevent->button() == Qt::RightButton &&
selectionModel()->selectedIndexes().contains(index)) {
// Allow calling context menu for multiple layers
return QItemSelectionModel::NoUpdate;
}
if (event->type() == QEvent::MouseButtonPress &&
(mevent->modifiers() & Qt::ControlModifier)) {
return QItemSelectionModel::NoUpdate;
}
if (event->type() == QEvent::MouseButtonRelease &&
(mevent->modifiers() & Qt::ControlModifier)) {
return QItemSelectionModel::Toggle;
}
}
return QAbstractItemView::selectionCommand(index, event);
}
void TimelineFramesView::slotSelectionChanged()
{
int minColumn = std::numeric_limits<int>::max();
int maxColumn = std::numeric_limits<int>::min();
foreach (const QModelIndex &idx, selectedIndexes()) {
if (idx.column() > maxColumn) {
maxColumn = idx.column();
}
if (idx.column() < minColumn) {
minColumn = idx.column();
}
}
KisTimeRange range;
if (maxColumn > minColumn) {
range = KisTimeRange(minColumn, maxColumn - minColumn + 1);
}
m_d->model->setPlaybackRange(range);
}
void TimelineFramesView::slotReselectCurrentIndex()
{
QModelIndex index = currentIndex();
currentChanged(index, index);
}
void TimelineFramesView::slotEnsureRowVisible(int row)
{
QModelIndex index = currentIndex();
if (!index.isValid() || row < 0) return;
index = m_d->model->index(row, index.column());
scrollTo(index);
}
void TimelineFramesView::slotDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
{
if (m_d->model->isPlaybackActive()) return;
int selectedColumn = -1;
for (int j = topLeft.column(); j <= bottomRight.column(); j++) {
QVariant value = m_d->model->data(
m_d->model->index(topLeft.row(), j),
TimelineFramesModel::ActiveFrameRole);
if (value.isValid() && value.toBool()) {
selectedColumn = j;
break;
}
}
QModelIndex index = currentIndex();
if (!index.isValid() && selectedColumn < 0) {
return;
}
if (selectedColumn == -1) {
selectedColumn = index.column();
}
if (selectedColumn != index.column() && !m_d->dragInProgress) {
int row= index.isValid() ? index.row() : 0;
selectionModel()->setCurrentIndex(m_d->model->index(row, selectedColumn), QItemSelectionModel::ClearAndSelect);
}
}
void TimelineFramesView::slotHeaderDataChanged(Qt::Orientation orientation, int first, int last)
{
Q_UNUSED(first);
Q_UNUSED(last);
if (orientation == Qt::Horizontal) {
const int newFps = m_d->model->headerData(0, Qt::Horizontal, TimelineFramesModel::FramesPerSecondRole).toInt();
if (newFps != m_d->fps) {
setFramesPerSecond(newFps);
}
}
}
void TimelineFramesView::rowsInserted(const QModelIndex& parent, int start, int end)
{
QTableView::rowsInserted(parent, start, end);
}
inline bool isIndexDragEnabled(QAbstractItemModel *model, const QModelIndex &index) {
return (model->flags(index) & Qt::ItemIsDragEnabled);
}
QStyleOptionViewItem TimelineFramesView::Private::viewOptionsV4() const
{
QStyleOptionViewItem option = q->viewOptions();
option.locale = q->locale();
option.locale.setNumberOptions(QLocale::OmitGroupSeparator);
option.widget = q;
return option;
}
QItemViewPaintPairs TimelineFramesView::Private::draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const
{
Q_ASSERT(r);
QRect &rect = *r;
const QRect viewportRect = q->viewport()->rect();
QItemViewPaintPairs ret;
for (int i = 0; i < indexes.count(); ++i) {
const QModelIndex &index = indexes.at(i);
const QRect current = q->visualRect(index);
if (current.intersects(viewportRect)) {
ret += qMakePair(current, index);
rect |= current;
}
}
rect &= viewportRect;
return ret;
}
QPixmap TimelineFramesView::Private::renderToPixmap(const QModelIndexList &indexes, QRect *r) const
{
Q_ASSERT(r);
QItemViewPaintPairs paintPairs = draggablePaintPairs(indexes, r);
if (paintPairs.isEmpty())
return QPixmap();
QPixmap pixmap(r->size());
pixmap.fill(Qt::transparent);
QPainter painter(&pixmap);
QStyleOptionViewItem option = viewOptionsV4();
option.state |= QStyle::State_Selected;
for (int j = 0; j < paintPairs.count(); ++j) {
option.rect = paintPairs.at(j).first.translated(-r->topLeft());
const QModelIndex &current = paintPairs.at(j).second;
//adjustViewOptionsForIndex(&option, current);
q->itemDelegate(current)->paint(&painter, option, current);
}
return pixmap;
}
void TimelineFramesView::startDrag(Qt::DropActions supportedActions)
{
QModelIndexList indexes = selectionModel()->selectedIndexes();
if (!indexes.isEmpty() && m_d->modifiersCatcher->modifierPressed("offset-frame")) {
QVector<int> rows;
int leftmostColumn = std::numeric_limits<int>::max();
Q_FOREACH (const QModelIndex &index, indexes) {
leftmostColumn = qMin(leftmostColumn, index.column());
if (!rows.contains(index.row())) {
rows.append(index.row());
}
}
const int lastColumn = m_d->model->columnCount() - 1;
selectionModel()->clear();
Q_FOREACH (const int row, rows) {
QItemSelection sel(m_d->model->index(row, leftmostColumn), m_d->model->index(row, lastColumn));
selectionModel()->select(sel, QItemSelectionModel::Select);
}
supportedActions = Qt::MoveAction;
{
QModelIndexList indexes = selectedIndexes();
for(int i = indexes.count() - 1 ; i >= 0; --i) {
if (!isIndexDragEnabled(m_d->model, indexes.at(i)))
indexes.removeAt(i);
}
selectionModel()->clear();
if (indexes.count() > 0) {
QMimeData *data = m_d->model->mimeData(indexes);
if (!data)
return;
QRect rect;
QPixmap pixmap = m_d->renderToPixmap(indexes, &rect);
rect.adjust(horizontalOffset(), verticalOffset(), 0, 0);
QDrag *drag = new QDrag(this);
drag->setPixmap(pixmap);
drag->setMimeData(data);
drag->setHotSpot(m_d->lastPressedPosition - rect.topLeft());
drag->exec(supportedActions, Qt::MoveAction);
setCurrentIndex(currentIndex());
}
}
} else {
/**
* Workaround for Qt5's bug: if we start a dragging action right during
* Shift-selection, Qt will get crazy. We cannot workaround it easily,
* because we would need to fork mouseMoveEvent() for that (where the
* decision about drag state is done). So we just abort dragging in that
* case.
*
* BUG:373067
*/
if (m_d->lastPressedModifier & Qt::ShiftModifier) {
return;
}
/**
* Workaround for Qt5's bugs:
*
* 1) Qt doesn't treat selection the selection on D&D
* correctly, so we save it in advance and restore
* afterwards.
*
* 2) There is a private variable in QAbstractItemView:
* QAbstractItemView::Private::currentSelectionStartIndex.
* It is initialized *only* when the setCurrentIndex() is called
* explicitly on the view object, not on the selection model.
* Therefore we should explicitly call setCurrentIndex() after
* D&D, even if it already has *correct* value!
*
* 2) We should also call selectionModel()->select()
* explicitly. There are two reasons for it: 1) Qt doesn't
* maintain selection over D&D; 2) when reselecting single
* element after D&D, Qt goes crazy, because it tries to
* read *global* keyboard modifiers. Therefore if we are
* dragging with Shift or Ctrl pressed it'll get crazy. So
* just reset it explicitly.
*/
QModelIndexList selectionBefore = selectionModel()->selectedIndexes();
QModelIndex currentBefore = selectionModel()->currentIndex();
// initialize a global status variable
m_d->dragWasSuccessful = false;
QAbstractItemView::startDrag(supportedActions);
QModelIndex newCurrent;
QPoint selectionOffset;
if (m_d->dragWasSuccessful) {
newCurrent = currentIndex();
selectionOffset = QPoint(newCurrent.column() - currentBefore.column(),
newCurrent.row() - currentBefore.row());
} else {
newCurrent = currentBefore;
selectionOffset = QPoint();
}
setCurrentIndex(newCurrent);
selectionModel()->clearSelection();
Q_FOREACH (const QModelIndex &idx, selectionBefore) {
QModelIndex newIndex =
model()->index(idx.row() + selectionOffset.y(),
idx.column() + selectionOffset.x());
selectionModel()->select(newIndex, QItemSelectionModel::Select);
}
}
}
void TimelineFramesView::dragEnterEvent(QDragEnterEvent *event)
{
m_d->dragInProgress = true;
m_d->model->setScrubState(true);
QTableView::dragEnterEvent(event);
}
void TimelineFramesView::dragMoveEvent(QDragMoveEvent *event)
{
m_d->dragInProgress = true;
m_d->model->setScrubState(true);
QTableView::dragMoveEvent(event);
if (event->isAccepted()) {
QModelIndex index = indexAt(event->pos());
if (!m_d->model->canDropFrameData(event->mimeData(), index)) {
event->ignore();
} else {
selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
}
}
}
void TimelineFramesView::dropEvent(QDropEvent *event)
{
m_d->dragInProgress = false;
m_d->model->setScrubState(false);
if (event->keyboardModifiers() & Qt::ControlModifier) {
event->setDropAction(Qt::CopyAction);
}
QAbstractItemView::dropEvent(event);
m_d->dragWasSuccessful = event->isAccepted();
}
void TimelineFramesView::dragLeaveEvent(QDragLeaveEvent *event)
{
m_d->dragInProgress = false;
m_d->model->setScrubState(false);
QAbstractItemView::dragLeaveEvent(event);
}
void TimelineFramesView::createFrameEditingMenuActions(QMenu *menu, bool addFrameCreationActions)
{
slotUpdateFrameActions();
// calculate if selection range is set. This will determine if the update playback range is available
QSet<int> rows;
int minColumn = 0;
int maxColumn = 0;
calculateSelectionMetrics(minColumn, maxColumn, rows);
bool selectionExists = minColumn != maxColumn;
if (selectionExists) {
KisActionManager::safePopulateMenu(menu, "update_playback_range", m_d->actionMan);
} else {
KisActionManager::safePopulateMenu(menu, "set_start_time", m_d->actionMan);
KisActionManager::safePopulateMenu(menu, "set_end_time", m_d->actionMan);
}
menu->addSeparator();
KisActionManager::safePopulateMenu(menu, "cut_frames_to_clipboard", m_d->actionMan);
KisActionManager::safePopulateMenu(menu, "copy_frames_to_clipboard", m_d->actionMan);
KisActionManager::safePopulateMenu(menu, "paste_frames_from_clipboard", m_d->actionMan);
menu->addSeparator();
{ //Frames submenu.
QMenu *frames = menu->addMenu(i18nc("@item:inmenu", "Keyframes"));
KisActionManager::safePopulateMenu(frames, "insert_keyframe_left", m_d->actionMan);
KisActionManager::safePopulateMenu(frames, "insert_keyframe_right", m_d->actionMan);
frames->addSeparator();
KisActionManager::safePopulateMenu(frames, "insert_multiple_keyframes", m_d->actionMan);
}
{ //Holds submenu.
QMenu *hold = menu->addMenu(i18nc("@item:inmenu", "Hold Frames"));
KisActionManager::safePopulateMenu(hold, "insert_hold_frame", m_d->actionMan);
KisActionManager::safePopulateMenu(hold, "remove_hold_frame", m_d->actionMan);
hold->addSeparator();
KisActionManager::safePopulateMenu(hold, "insert_multiple_hold_frames", m_d->actionMan);
KisActionManager::safePopulateMenu(hold, "remove_multiple_hold_frames", m_d->actionMan);
}
menu->addSeparator();
KisActionManager::safePopulateMenu(menu, "remove_frames", m_d->actionMan);
KisActionManager::safePopulateMenu(menu, "remove_frames_and_pull", m_d->actionMan);
menu->addSeparator();
if (addFrameCreationActions) {
KisActionManager::safePopulateMenu(menu, "add_blank_frame", m_d->actionMan);
KisActionManager::safePopulateMenu(menu, "add_duplicate_frame", m_d->actionMan);
menu->addSeparator();
}
}
void TimelineFramesView::mousePressEvent(QMouseEvent *event)
{
QPersistentModelIndex index = indexAt(event->pos());
if (m_d->modifiersCatcher->modifierPressed("pan-zoom")) {
if (event->button() == Qt::RightButton) {
// TODO: try calculate index under mouse cursor even when
// it is outside any visible row
qreal staticPoint = index.isValid() ? index.column() : currentIndex().column();
m_d->zoomDragButton->beginZoom(event->pos(), staticPoint);
} else if (event->button() == Qt::LeftButton) {
m_d->initialDragPanPos = event->pos();
m_d->initialDragPanValue =
QPoint(horizontalScrollBar()->value(),
verticalScrollBar()->value());
}
event->accept();
} else if (event->button() == Qt::RightButton) {
int numSelectedItems = selectionModel()->selectedIndexes().size();
if (index.isValid() &&
numSelectedItems <= 1 &&
m_d->model->data(index, TimelineFramesModel::FrameEditableRole).toBool()) {
model()->setData(index, true, TimelineFramesModel::ActiveLayerRole);
model()->setData(index, true, TimelineFramesModel::ActiveFrameRole);
setCurrentIndex(index);
if (model()->data(index, TimelineFramesModel::FrameExistsRole).toBool() ||
model()->data(index, TimelineFramesModel::SpecialKeyframeExists).toBool()) {
{
KisSignalsBlocker b(m_d->colorSelector);
QVariant colorLabel = index.data(TimelineFramesModel::FrameColorLabelIndexRole);
int labelIndex = colorLabel.isValid() ? colorLabel.toInt() : 0;
m_d->colorSelector->setCurrentIndex(labelIndex);
}
QMenu menu;
createFrameEditingMenuActions(&menu, false);
menu.addSeparator();
menu.addAction(m_d->colorSelectorAction);
menu.exec(event->globalPos());
} else {
{
KisSignalsBlocker b(m_d->colorSelector);
const int labelIndex = KisImageConfig(true).defaultFrameColorLabel();
m_d->colorSelector->setCurrentIndex(labelIndex);
}
QMenu menu;
createFrameEditingMenuActions(&menu, true);
menu.addSeparator();
menu.addAction(m_d->colorSelectorAction);
menu.exec(event->globalPos());
}
} else if (numSelectedItems > 1) {
int labelIndex = -1;
bool haveFrames = false;
Q_FOREACH(QModelIndex index, selectedIndexes()) {
haveFrames |= index.data(TimelineFramesModel::FrameExistsRole).toBool();
QVariant colorLabel = index.data(TimelineFramesModel::FrameColorLabelIndexRole);
if (colorLabel.isValid()) {
if (labelIndex == -1) {
// First label
labelIndex = colorLabel.toInt();
} else if (labelIndex != colorLabel.toInt()) {
// Mixed colors in selection
labelIndex = -1;
break;
}
}
}
if (haveFrames) {
KisSignalsBlocker b(m_d->multiframeColorSelector);
m_d->multiframeColorSelector->setCurrentIndex(labelIndex);
}
QMenu menu;
createFrameEditingMenuActions(&menu, false);
menu.addSeparator();
KisActionManager::safePopulateMenu(&menu, "mirror_frames", m_d->actionMan);
menu.addSeparator();
menu.addAction(m_d->multiframeColorSelectorAction);
menu.exec(event->globalPos());
}
} else if (event->button() == Qt::MidButton) {
QModelIndex index = model()->buddy(indexAt(event->pos()));
if (index.isValid()) {
QStyleOptionViewItem option = viewOptions();
option.rect = visualRect(index);
// The offset of the headers is needed to get the correct position inside the view.
m_d->tip.showTip(this, event->pos() + QPoint(verticalHeader()->width(), horizontalHeader()->height()), option, index);
}
event->accept();
} else {
if (index.isValid()) {
m_d->model->setLastClickedIndex(index);
}
m_d->lastPressedPosition =
QPoint(horizontalOffset(), verticalOffset()) + event->pos();
m_d->lastPressedModifier = event->modifiers();
QAbstractItemView::mousePressEvent(event);
}
}
void TimelineFramesView::mouseMoveEvent(QMouseEvent *e)
{
if (m_d->modifiersCatcher->modifierPressed("pan-zoom")) {
if (e->buttons() & Qt::RightButton) {
m_d->zoomDragButton->continueZoom(e->pos());
} else if (e->buttons() & Qt::LeftButton) {
QPoint diff = e->pos() - m_d->initialDragPanPos;
QPoint offset = QPoint(m_d->initialDragPanValue.x() - diff.x(),
m_d->initialDragPanValue.y() - diff.y());
const int height = m_d->layersHeader->defaultSectionSize();
horizontalScrollBar()->setValue(offset.x());
verticalScrollBar()->setValue(offset.y() / height);
}
e->accept();
} else if (e->buttons() == Qt::MidButton) {
QModelIndex index = model()->buddy(indexAt(e->pos()));
if (index.isValid()) {
QStyleOptionViewItem option = viewOptions();
option.rect = visualRect(index);
// The offset of the headers is needed to get the correct position inside the view.
m_d->tip.showTip(this, e->pos() + QPoint(verticalHeader()->width(), horizontalHeader()->height()), option, index);
}
e->accept();
} else {
m_d->model->setScrubState(true);
QTableView::mouseMoveEvent(e);
}
}
void TimelineFramesView::mouseReleaseEvent(QMouseEvent *e)
{
if (m_d->modifiersCatcher->modifierPressed("pan-zoom")) {
e->accept();
} else {
m_d->model->setScrubState(false);
QTableView::mouseReleaseEvent(e);
}
}
void TimelineFramesView::wheelEvent(QWheelEvent *e)
{
QModelIndex index = currentIndex();
int column= -1;
if (index.isValid()) {
column= index.column() + ((e->delta() > 0) ? 1 : -1);
}
if (column >= 0 && !m_d->dragInProgress) {
setCurrentIndex(m_d->model->index(index.row(), column));
}
}
void TimelineFramesView::slotUpdateLayersMenu()
{
QAction *action = 0;
m_d->existingLayersMenu->clear();
QVariant value = model()->headerData(0, Qt::Vertical, TimelineFramesModel::OtherLayersRole);
if (value.isValid()) {
TimelineFramesModel::OtherLayersList list = value.value<TimelineFramesModel::OtherLayersList>();
int i = 0;
Q_FOREACH (const TimelineFramesModel::OtherLayer &l, list) {
action = m_d->existingLayersMenu->addAction(l.name);
action->setData(i++);
}
}
}
void TimelineFramesView::slotUpdateFrameActions()
{
if (!m_d->actionMan) return;
const QModelIndexList editableIndexes = calculateSelectionSpan(false, true);
const bool hasEditableFrames = !editableIndexes.isEmpty();
bool hasExistingFrames = false;
Q_FOREACH (const QModelIndex &index, editableIndexes) {
if (model()->data(index, TimelineFramesModel::FrameExistsRole).toBool()) {
hasExistingFrames = true;
break;
}
}
auto enableAction = [this] (const QString &id, bool value) {
KisAction *action = m_d->actionMan->actionByName(id);
KIS_SAFE_ASSERT_RECOVER_RETURN(action);
action->setEnabled(value);
};
enableAction("add_blank_frame", hasEditableFrames);
enableAction("add_duplicate_frame", hasEditableFrames);
enableAction("insert_keyframe_left", hasEditableFrames);
enableAction("insert_keyframe_right", hasEditableFrames);
enableAction("insert_multiple_keyframes", hasEditableFrames);
enableAction("remove_frames", hasEditableFrames && hasExistingFrames);
enableAction("remove_frames_and_pull", hasEditableFrames);
enableAction("insert_hold_frame", hasEditableFrames);
enableAction("insert_multiple_hold_frames", hasEditableFrames);
enableAction("remove_hold_frame", hasEditableFrames);
enableAction("remove_multiple_hold_frames", hasEditableFrames);
enableAction("mirror_frames", hasEditableFrames && editableIndexes.size() > 1);
enableAction("copy_frames_to_clipboard", true);
enableAction("cut_frames_to_clipboard", hasEditableFrames);
QClipboard *cp = QApplication::clipboard();
const QMimeData *data = cp->mimeData();
enableAction("paste_frames_from_clipboard", data && data->hasFormat("application/x-krita-frame"));
//TODO: update column actions!
}
void TimelineFramesView::slotSetStartTimeToCurrentPosition()
{
m_d->model->setFullClipRangeStart(this->currentIndex().column());
}
void TimelineFramesView::slotSetEndTimeToCurrentPosition()
{
m_d->model->setFullClipRangeEnd(this->currentIndex().column());
}
void TimelineFramesView::slotUpdatePlackbackRange()
{
QSet<int> rows;
int minColumn = 0;
int maxColumn = 0;
calculateSelectionMetrics(minColumn, maxColumn, rows);
m_d->model->setFullClipRangeStart(minColumn);
m_d->model->setFullClipRangeEnd(maxColumn);
}
void TimelineFramesView::slotLayerContextMenuRequested(const QPoint &globalPos)
{
m_d->layerEditingMenu->exec(globalPos);
}
void TimelineFramesView::slotAddNewLayer()
{
QModelIndex index = currentIndex();
const int newRow = index.isValid() ? index.row() : 0;
model()->insertRow(newRow);
}
void TimelineFramesView::slotAddExistingLayer(QAction *action)
{
QVariant value = action->data();
if (value.isValid()) {
QModelIndex index = currentIndex();
const int newRow = index.isValid() ? index.row() + 1 : 0;
m_d->model->insertOtherLayer(value.toInt(), newRow);
}
}
void TimelineFramesView::slotRemoveLayer()
{
QModelIndex index = currentIndex();
if (!index.isValid()) return;
model()->removeRow(index.row());
}
void TimelineFramesView::slotAddBlankFrame()
{
QModelIndex index = currentIndex();
if (!index.isValid() ||
!m_d->model->data(index, TimelineFramesModel::FrameEditableRole).toBool()) {
return;
}
m_d->model->createFrame(index);
}
void TimelineFramesView::slotAddDuplicateFrame()
{
QModelIndex index = currentIndex();
if (!index.isValid() ||
!m_d->model->data(index, TimelineFramesModel::FrameEditableRole).toBool()) {
return;
}
m_d->model->copyFrame(index);
}
void TimelineFramesView::calculateSelectionMetrics(int &minColumn, int &maxColumn, QSet<int> &rows) const
{
minColumn = std::numeric_limits<int>::max();
maxColumn = std::numeric_limits<int>::min();
Q_FOREACH (const QModelIndex &index, selectionModel()->selectedIndexes()) {
if (!m_d->model->data(index, TimelineFramesModel::FrameEditableRole).toBool()) continue;
rows.insert(index.row());
minColumn = qMin(minColumn, index.column());
maxColumn = qMax(maxColumn, index.column());
}
}
void TimelineFramesView::insertKeyframes(int count, int timing, TimelineDirection direction, bool entireColumn)
{
QSet<int> rows;
int minColumn = 0, maxColumn = 0;
calculateSelectionMetrics(minColumn, maxColumn, rows);
if (count <= 0) { //Negative count? Use number of selected frames.
count = qMax(1, maxColumn - minColumn + 1);
}
const int insertionColumn =
direction == TimelineDirection::RIGHT ?
maxColumn + 1 : minColumn;
if (entireColumn) {
rows.clear();
for (int i = 0; i < m_d->model->rowCount(); i++) {
if (!m_d->model->data(m_d->model->index(i, insertionColumn), TimelineFramesModel::FrameEditableRole).toBool()) continue;
rows.insert(i);
}
}
if (!rows.isEmpty()) {
m_d->model->insertFrames(insertionColumn, rows.toList(), count, timing);
}
}
void TimelineFramesView::insertMultipleKeyframes(bool entireColumn)
{
int count, timing;
TimelineDirection direction;
if (m_d->insertKeyframeDialog->promptUserSettings(count, timing, direction)) {
insertKeyframes(count, timing, direction, entireColumn);
}
}
QModelIndexList TimelineFramesView::calculateSelectionSpan(bool entireColumn, bool editableOnly) const
{
QModelIndexList indexes;
if (entireColumn) {
QSet<int> rows;
int minColumn = 0;
int maxColumn = 0;
calculateSelectionMetrics(minColumn, maxColumn, rows);
rows.clear();
for (int i = 0; i < m_d->model->rowCount(); i++) {
if (editableOnly &&
!m_d->model->data(m_d->model->index(i, minColumn), TimelineFramesModel::FrameEditableRole).toBool()) continue;
for (int column = minColumn; column <= maxColumn; column++) {
indexes << m_d->model->index(i, column);
}
}
} else {
Q_FOREACH (const QModelIndex &index, selectionModel()->selectedIndexes()) {
if (!editableOnly || m_d->model->data(index, TimelineFramesModel::FrameEditableRole).toBool()) {
indexes << index;
}
}
}
return indexes;
}
void TimelineFramesView::slotRemoveSelectedFrames(bool entireColumn, bool pull)
{
const QModelIndexList selectedIndices = calculateSelectionSpan(entireColumn);
if (!selectedIndices.isEmpty()) {
if (pull) {
m_d->model->removeFramesAndOffset(selectedIndices);
} else {
m_d->model->removeFrames(selectedIndices);
}
}
}
void TimelineFramesView::insertOrRemoveHoldFrames(int count, bool entireColumn)
{
QModelIndexList indexes;
if (!entireColumn) {
Q_FOREACH (const QModelIndex &index, selectionModel()->selectedIndexes()) {
if (m_d->model->data(index, TimelineFramesModel::FrameEditableRole).toBool()) {
indexes << index;
}
}
} else {
const int column = selectionModel()->currentIndex().column();
for (int i = 0; i < m_d->model->rowCount(); i++) {
const QModelIndex index = m_d->model->index(i, column);
if (m_d->model->data(index, TimelineFramesModel::FrameEditableRole).toBool()) {
indexes << index;
}
}
}
if (!indexes.isEmpty()) {
m_d->model->insertHoldFrames(indexes, count);
}
}
void TimelineFramesView::insertOrRemoveMultipleHoldFrames(bool insertion, bool entireColumn)
{
bool ok = false;
const int count = QInputDialog::getInt(this,
i18nc("@title:window", "Insert or Remove Hold Frames"),
i18nc("@label:spinbox", "Enter number of frames"),
insertion ?
m_d->insertKeyframeDialog->defaultTimingOfAddedFrames() :
m_d->insertKeyframeDialog->defaultNumberOfHoldFramesToRemove(),
1, 10000, 1, &ok);
if (ok) {
if (insertion) {
m_d->insertKeyframeDialog->setDefaultTimingOfAddedFrames(count);
insertOrRemoveHoldFrames(count, entireColumn);
} else {
m_d->insertKeyframeDialog->setDefaultNumberOfHoldFramesToRemove(count);
insertOrRemoveHoldFrames(-count, entireColumn);
}
}
}
void TimelineFramesView::slotMirrorFrames(bool entireColumn)
{
const QModelIndexList indexes = calculateSelectionSpan(entireColumn);
if (!indexes.isEmpty()) {
m_d->model->mirrorFrames(indexes);
}
}
void TimelineFramesView::cutCopyImpl(bool entireColumn, bool copy)
{
const QModelIndexList selectedIndices = calculateSelectionSpan(entireColumn, !copy);
if (selectedIndices.isEmpty()) return;
int minColumn = std::numeric_limits<int>::max();
int minRow = std::numeric_limits<int>::max();
Q_FOREACH (const QModelIndex &index, selectedIndices) {
minRow = qMin(minRow, index.row());
minColumn = qMin(minColumn, index.column());
}
const QModelIndex baseIndex = m_d->model->index(minRow, minColumn);
QMimeData *data = m_d->model->mimeDataExtended(selectedIndices,
baseIndex,
copy ?
TimelineFramesModel::CopyFramesPolicy :
TimelineFramesModel::MoveFramesPolicy);
if (data) {
QClipboard *cb = QApplication::clipboard();
cb->setMimeData(data);
}
}
void TimelineFramesView::slotPasteFrames(bool entireColumn)
{
const QModelIndex currentIndex =
!entireColumn ? this->currentIndex() : m_d->model->index(0, this->currentIndex().column());
if (!currentIndex.isValid()) return;
QClipboard *cb = QApplication::clipboard();
const QMimeData *data = cb->mimeData();
if (data && data->hasFormat("application/x-krita-frame")) {
bool dataMoved = false;
bool result = m_d->model->dropMimeDataExtended(data, Qt::MoveAction, currentIndex, &dataMoved);
if (result && dataMoved) {
cb->clear();
}
}
}
bool TimelineFramesView::viewportEvent(QEvent *event)
{
if (event->type() == QEvent::ToolTip && model()) {
QHelpEvent *he = static_cast<QHelpEvent *>(event);
QModelIndex index = model()->buddy(indexAt(he->pos()));
if (index.isValid()) {
QStyleOptionViewItem option = viewOptions();
option.rect = visualRect(index);
// The offset of the headers is needed to get the correct position inside the view.
m_d->tip.showTip(this, he->pos() + QPoint(verticalHeader()->width(), horizontalHeader()->height()), option, index);
return true;
}
}
return QTableView::viewportEvent(event);
}
diff --git a/plugins/dockers/arrangedocker/arrange_docker_widget.ui b/plugins/dockers/arrangedocker/arrange_docker_widget.ui
index 4090b5ef1d..bc72a55cb3 100644
--- a/plugins/dockers/arrangedocker/arrange_docker_widget.ui
+++ b/plugins/dockers/arrangedocker/arrange_docker_widget.ui
@@ -1,533 +1,509 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ArrangeDockerWidget</class>
<widget class="QWidget" name="ArrangeDockerWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>303</width>
<height>426</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QFrame" name="disabledLabel">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
- <property name="leftMargin">
- <number>4</number>
- </property>
- <property name="topMargin">
- <number>5</number>
- </property>
- <property name="rightMargin">
- <number>5</number>
- </property>
- <property name="bottomMargin">
- <number>5</number>
- </property>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Activate the Select Shapes Tool to arrange objects.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="buttons">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout" columnstretch="0,0,0,0,0,0,0">
- <property name="leftMargin">
- <number>4</number>
- </property>
- <property name="topMargin">
- <number>7</number>
- </property>
- <property name="bottomMargin">
- <number>7</number>
- </property>
- <property name="spacing">
- <number>5</number>
- </property>
<item row="0" column="0" colspan="7">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Align</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item row="11" column="5">
<widget class="QToolButton" name="group">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QToolButton" name="hDistributeRight">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QToolButton" name="hDistributeLeft">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
</widget>
</item>
<item row="5" column="4">
<widget class="QToolButton" name="vDistributeTop">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QToolButton" name="hDistributeCenter">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
</widget>
</item>
<item row="10" column="0" colspan="4">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Order</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item row="5" column="6">
<widget class="QToolButton" name="vDistributeBottom">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QToolButton" name="vDistributeGaps">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QToolButton" name="hDistributeGaps">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
</widget>
</item>
<item row="7" column="0" colspan="7">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Spacing</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item row="10" column="5" colspan="2">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Group</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="rightAlign">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="QToolButton" name="raiseLevel">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
</widget>
</item>
<item row="1" column="6">
<widget class="QToolButton" name="bottomAlign">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
</widget>
</item>
<item row="5" column="5">
<widget class="QToolButton" name="vDistributeCenter">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
</widget>
</item>
<item row="1" column="5">
<widget class="QToolButton" name="vCenterAlign">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QToolButton" name="topAlign">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
</widget>
</item>
<item row="11" column="3">
<widget class="QToolButton" name="sendBack">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
</widget>
</item>
<item row="11" column="2">
<widget class="QToolButton" name="lowerLevel">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QToolButton" name="leftAlign">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
</widget>
</item>
<item row="11" column="0">
<widget class="QToolButton" name="bringToFront">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QToolButton" name="hCenterAlign">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
</widget>
</item>
<item row="11" column="6">
<widget class="QToolButton" name="ungroup">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
</widget>
</item>
<item row="3" column="0" colspan="7">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Distribute</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item row="9" column="0" colspan="7">
<widget class="Line" name="line_3">
<property name="minimumSize">
<size>
<width>0</width>
<height>15</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="6" column="0" colspan="7">
<widget class="Line" name="line_2">
<property name="minimumSize">
<size>
<width>0</width>
<height>15</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="2" column="0" colspan="7">
<widget class="Line" name="line">
<property name="minimumSize">
<size>
<width>0</width>
<height>15</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
diff --git a/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.cpp b/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.cpp
index 5b162b0caa..30b487ea76 100644
--- a/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.cpp
+++ b/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.cpp
@@ -1,459 +1,459 @@
/*
* Copyright (c) 2009 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <kis_debug.h>
#include <klocalizedstring.h>
#include <KoCanvasResourceProvider.h>
#include <KoResourceServerProvider.h>
#include <KoResourceServerObserver.h>
#include <KoResourceServerAdapter.h>
#include <KoCanvasBase.h>
#include <kis_canvas2.h>
#include <KoColor.h>
#include <resources/KoGamutMask.h>
#include <kis_icon_utils.h>
#include <KisPart.h>
#include <kis_shape_layer.h>
#include <kis_types.h>
#include <KisDocument.h>
#include <kis_node_selection_adapter.h>
#include <kis_group_layer.h>
#include <KisView.h>
#include <KoResourceItemChooser.h>
#include <kis_display_color_converter.h>
#include <QWidget>
#include <QMenu>
#include <QButtonGroup>
#include <QRegExpValidator>
#include <QRegExp>
#include <QFileInfo>
#include "artisticcolorselector_dock.h"
#include <KisViewManager.h>
#include <kis_canvas_resource_provider.h>
#include <kis_arcs_constants.h>
#include <KisGamutMaskToolbar.h>
#include "ui_wdgArtisticColorSelector.h"
#include "ui_wdgARCSSettings.h"
#include "ui_wdgWheelPreferencesPopup.h"
class KisMainWindow;
struct ArtisticColorSelectorUI: public QWidget, public Ui_wdgArtisticColorSelector
{
ArtisticColorSelectorUI() {
setupUi(this);
}
};
struct ARCSSettingsUI: public QWidget, public Ui_wdgARCSSettings
{
ARCSSettingsUI() {
setupUi(this);
}
};
struct WheelPreferencesPopupUI: public QWidget, public Ui_wdgWheelPreferencesPopup
{
WheelPreferencesPopupUI() {
setupUi(this);
}
};
ArtisticColorSelectorDock::ArtisticColorSelectorDock()
: QDockWidget(i18n("Artistic Color Selector"))
, m_canvas(nullptr)
, m_resourceProvider(0)
, m_selectedMask(nullptr)
{
setEnabled(false);
m_hsxButtons = new QButtonGroup();
m_preferencesUI = new ARCSSettingsUI();
m_wheelPrefsUI = new WheelPreferencesPopupUI();
m_selectorUI = new ArtisticColorSelectorUI();
QPixmap hueStepsPixmap = KisIconUtils::loadIcon("wheel-sectors").pixmap(16,16);
QPixmap saturationStepsPixmap = KisIconUtils::loadIcon("wheel-rings").pixmap(16,16);
QPixmap valueScaleStepsPixmap = KisIconUtils::loadIcon("wheel-light").pixmap(16,16);
QIcon infinityIcon = KisIconUtils::loadIcon("infinity");
m_infinityPixmap = infinityIcon.pixmap(16,16);
m_selectorUI->colorSelector->loadSettings();
m_selectorUI->bnWheelPrefs->setIcon(KisIconUtils::loadIcon("wheel-sectors"));
m_selectorUI->bnWheelPrefs->setPopupWidget(m_wheelPrefsUI);
m_selectorUI->bnDockerPrefs->setPopupWidget(m_preferencesUI);
m_selectorUI->bnDockerPrefs->setIcon(KisIconUtils::loadIcon("configure"));
//preferences
m_hsxButtons->addButton(m_preferencesUI->bnHsy, KisColor::HSY);
m_hsxButtons->addButton(m_preferencesUI->bnHsi, KisColor::HSI);
m_hsxButtons->addButton(m_preferencesUI->bnHsl, KisColor::HSL);
m_hsxButtons->addButton(m_preferencesUI->bnHsv, KisColor::HSV);
m_wheelPrefsUI->bnInverseSat->setChecked(m_selectorUI->colorSelector->isSaturationInverted());
m_wheelPrefsUI->labelHueSteps->setPixmap(hueStepsPixmap);
m_wheelPrefsUI->labelSaturationSteps->setPixmap(saturationStepsPixmap);
m_wheelPrefsUI->labelValueScaleSteps->setPixmap(valueScaleStepsPixmap);
m_wheelPrefsUI->numHueSteps->setRange(MIN_NUM_UI_HUE_PIECES, MAX_NUM_HUE_PIECES);
m_wheelPrefsUI->numSaturationSteps->setRange(MIN_NUM_SATURATION_RINGS, MAX_NUM_SATURATION_RINGS);
m_wheelPrefsUI->numValueScaleSteps->setRange(MIN_NUM_UI_LIGHT_PIECES, MAX_NUM_LIGHT_PIECES);
m_wheelPrefsUI->bnInfHueSteps->setIcon(infinityIcon);
m_wheelPrefsUI->bnInfValueScaleSteps->setIcon(infinityIcon);
m_wheelPrefsUI->bnInfHueSteps->setToolTip(i18n("Continuous Mode"));
m_wheelPrefsUI->bnInfValueScaleSteps->setToolTip(i18n("Continuous Mode"));
int selectorHueSteps = m_selectorUI->colorSelector->getNumPieces();
if (selectorHueSteps == 1) {
m_wheelPrefsUI->bnInfHueSteps->setChecked(true);
} else {
m_wheelPrefsUI->bnInfHueSteps->setChecked(false);
}
m_wheelPrefsUI->numHueSteps->setValue(selectorHueSteps);
m_wheelPrefsUI->numSaturationSteps->setValue(m_selectorUI->colorSelector->getNumRings());
int selectorValueScaleSteps = m_selectorUI->colorSelector->getNumLightPieces();
if (selectorValueScaleSteps == 1) {
m_wheelPrefsUI->bnInfValueScaleSteps->setChecked(true);
} else {
m_wheelPrefsUI->bnInfValueScaleSteps->setChecked(false);
}
m_wheelPrefsUI->numValueScaleSteps->setValue(m_selectorUI->colorSelector->getNumLightPieces());
m_preferencesUI->bnDefInfHueSteps->setIcon(infinityIcon);
m_preferencesUI->bnDefInfValueScaleSteps->setIcon(infinityIcon);
m_preferencesUI->labelDefHueSteps->setPixmap(hueStepsPixmap);
m_preferencesUI->labelDefSaturationSteps->setPixmap(saturationStepsPixmap);
m_preferencesUI->labelDefValueScaleSteps->setPixmap(valueScaleStepsPixmap);
m_preferencesUI->defaultHueSteps->setRange(MIN_NUM_HUE_PIECES, MAX_NUM_HUE_PIECES);
m_preferencesUI->defaultSaturationSteps->setRange(MIN_NUM_SATURATION_RINGS, MAX_NUM_SATURATION_RINGS);
m_preferencesUI->defaultValueScaleSteps->setRange(MIN_NUM_LIGHT_PIECES, MAX_NUM_LIGHT_PIECES);
m_preferencesUI->defaultHueSteps->setValue(m_selectorUI->colorSelector->getDefaultHueSteps());
m_preferencesUI->defaultSaturationSteps->setValue(m_selectorUI->colorSelector->getDefaultSaturationSteps());
m_preferencesUI->defaultValueScaleSteps->setValue(m_selectorUI->colorSelector->getDefaultValueScaleSteps());
m_preferencesUI->showBgColor->setChecked(m_selectorUI->colorSelector->getShowBgColor());
m_preferencesUI->showValueScaleNumbers->setChecked(m_selectorUI->colorSelector->getShowValueScaleNumbers());
m_preferencesUI->enforceGamutMask->setChecked(m_selectorUI->colorSelector->enforceGamutMask());
m_preferencesUI->permissiveGamutMask->setChecked(!m_selectorUI->colorSelector->enforceGamutMask());
m_preferencesUI->spLumaR->setValue(m_selectorUI->colorSelector->lumaR());
m_preferencesUI->spLumaG->setValue(m_selectorUI->colorSelector->lumaG());
m_preferencesUI->spLumaB->setValue(m_selectorUI->colorSelector->lumaB());
m_preferencesUI->spLumaGamma->setValue(m_selectorUI->colorSelector->lumaGamma());
switch(m_selectorUI->colorSelector->getColorSpace())
{
case KisColor::HSV: { m_preferencesUI->bnHsv->setChecked(true); } break;
case KisColor::HSI: { m_preferencesUI->bnHsi->setChecked(true); } break;
case KisColor::HSL: { m_preferencesUI->bnHsl->setChecked(true); } break;
case KisColor::HSY: { m_preferencesUI->bnHsy->setChecked(true); } break;
}
if (m_selectorUI->colorSelector->getColorSpace() == KisColor::HSY) {
m_preferencesUI->lumaCoefficientBox->show();
} else {
m_preferencesUI->lumaCoefficientBox->hide();
}
connect(m_wheelPrefsUI->numValueScaleSteps , SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged()));
connect(m_wheelPrefsUI->numHueSteps , SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged()));
connect(m_wheelPrefsUI->numSaturationSteps , SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged()));
connect(m_wheelPrefsUI->bnInverseSat , SIGNAL(clicked(bool)) , SLOT(slotPreferenceChanged()));
connect(m_wheelPrefsUI->bnInfHueSteps , SIGNAL(clicked(bool)) , SLOT(slotPreferenceChanged()));
connect(m_wheelPrefsUI->bnInfValueScaleSteps, SIGNAL(clicked(bool)) , SLOT(slotPreferenceChanged()));
connect(m_wheelPrefsUI->bnDefault , SIGNAL(clicked(bool)) , SLOT(slotResetDefaultSettings()));
connect(m_preferencesUI->defaultHueSteps , SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged()));
connect(m_preferencesUI->defaultSaturationSteps, SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged()));
connect(m_preferencesUI->defaultValueScaleSteps, SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged()));
connect(m_preferencesUI->bnDefInfHueSteps , SIGNAL(clicked(bool)) , SLOT(slotPreferenceChanged()));
connect(m_preferencesUI->bnDefInfValueScaleSteps, SIGNAL(clicked(bool)) , SLOT(slotPreferenceChanged()));
connect(m_preferencesUI->showBgColor , SIGNAL(toggled(bool)) , SLOT(slotPreferenceChanged()));
connect(m_preferencesUI->showValueScaleNumbers, SIGNAL(toggled(bool)) , SLOT(slotPreferenceChanged()));
connect(m_preferencesUI->enforceGamutMask , SIGNAL(toggled(bool)) , SLOT(slotPreferenceChanged()));
connect(m_preferencesUI->spLumaR , SIGNAL(valueChanged(qreal)), SLOT(slotColorSpaceSelected()));
connect(m_preferencesUI->spLumaG , SIGNAL(valueChanged(qreal)), SLOT(slotColorSpaceSelected()));
connect(m_preferencesUI->spLumaB , SIGNAL(valueChanged(qreal)), SLOT(slotColorSpaceSelected()));
connect(m_preferencesUI->spLumaGamma , SIGNAL(valueChanged(qreal)), SLOT(slotColorSpaceSelected()));
connect(m_selectorUI->colorSelector , SIGNAL(sigFgColorChanged(KisColor)) , SLOT(slotFgColorChanged(KisColor)));
connect(m_selectorUI->colorSelector , SIGNAL(sigBgColorChanged(KisColor)) , SLOT(slotBgColorChanged(KisColor)));
// gamut mask connections
connect(m_selectorUI->gamutMaskToolbar, SIGNAL(sigGamutMaskToggle(bool)), SLOT(slotGamutMaskToggle(bool)));
connect(m_hsxButtons , SIGNAL(buttonClicked(int)) , SLOT(slotColorSpaceSelected()));
setWidget(m_selectorUI);
}
ArtisticColorSelectorDock::~ArtisticColorSelectorDock()
{
m_selectorUI->colorSelector->saveSettings();
delete m_hsxButtons;
}
void ArtisticColorSelectorDock::setViewManager(KisViewManager* kisview)
{
m_resourceProvider = kisview->resourceProvider();
m_selectorUI->colorSelector->setFgColor(m_resourceProvider->resourceManager()->foregroundColor());
m_selectorUI->colorSelector->setBgColor(m_resourceProvider->resourceManager()->backgroundColor());
connect(m_resourceProvider, SIGNAL(sigGamutMaskChanged(KoGamutMask*)),
- this, SLOT(slotGamutMaskSet(KoGamutMask*)));
+ this, SLOT(slotGamutMaskSet(KoGamutMask*)), Qt::UniqueConnection);
connect(m_resourceProvider, SIGNAL(sigGamutMaskUnset()),
- this, SLOT(slotGamutMaskUnset()));
+ this, SLOT(slotGamutMaskUnset()), Qt::UniqueConnection);
connect(m_resourceProvider, SIGNAL(sigGamutMaskPreviewUpdate()),
- this, SLOT(slotGamutMaskPreviewUpdate()));
+ this, SLOT(slotGamutMaskPreviewUpdate()), Qt::UniqueConnection);
m_selectorUI->gamutMaskToolbar->connectMaskSignals(m_resourceProvider);
}
void ArtisticColorSelectorDock::slotCanvasResourceChanged(int key, const QVariant& value)
{
if(key == KoCanvasResourceProvider::ForegroundColor)
m_selectorUI->colorSelector->setFgColor(value.value<KoColor>());
if(key == KoCanvasResourceProvider::BackgroundColor)
m_selectorUI->colorSelector->setBgColor(value.value<KoColor>());
}
void ArtisticColorSelectorDock::slotFgColorChanged(const KisColor& color)
{
m_resourceProvider->resourceManager()->setForegroundColor(
KoColor(color.toKoColor(), m_resourceProvider->resourceManager()->foregroundColor().colorSpace())
);
}
void ArtisticColorSelectorDock::slotBgColorChanged(const KisColor& color)
{
m_resourceProvider->resourceManager()->setBackgroundColor(
KoColor(color.toKoColor(), m_resourceProvider->resourceManager()->backgroundColor().colorSpace())
);
}
void ArtisticColorSelectorDock::slotColorSpaceSelected()
{
KisColor::Type type = static_cast<KisColor::Type>(
m_hsxButtons->id(m_hsxButtons->checkedButton()));
m_selectorUI->colorSelector->setColorSpace(type);
if (type == KisColor::HSY) {
m_preferencesUI->lumaCoefficientBox->show();
} else {
m_preferencesUI->lumaCoefficientBox->hide();
}
m_selectorUI->colorSelector->setLumaCoefficients(
m_preferencesUI->spLumaR->value(),
m_preferencesUI->spLumaG->value(),
m_preferencesUI->spLumaB->value(),
m_preferencesUI->spLumaGamma->value()
);
}
void ArtisticColorSelectorDock::slotPreferenceChanged()
{
int hueSteps = DEFAULT_HUE_STEPS;
if (m_wheelPrefsUI->bnInfHueSteps->isChecked()) {
m_wheelPrefsUI->numHueSteps->setEnabled(false);
hueSteps = 1;
} else {
m_wheelPrefsUI->numHueSteps->setEnabled(true);
hueSteps = m_wheelPrefsUI->numHueSteps->value();
}
m_selectorUI->colorSelector->setNumPieces(hueSteps);
m_selectorUI->colorSelector->setNumRings(m_wheelPrefsUI->numSaturationSteps->value());
int valueScaleSteps;
if (m_wheelPrefsUI->bnInfValueScaleSteps->isChecked()) {
m_wheelPrefsUI->numValueScaleSteps->setEnabled(false);
valueScaleSteps = 1;
} else {
valueScaleSteps = m_wheelPrefsUI->numValueScaleSteps->value();
m_wheelPrefsUI->numValueScaleSteps->setEnabled(true);
}
m_selectorUI->colorSelector->setNumLightPieces(valueScaleSteps);
int defHueSteps;
if (m_preferencesUI->bnDefInfHueSteps->isChecked()) {
m_preferencesUI->defaultHueSteps->setEnabled(false);
defHueSteps = 1;
} else {
m_preferencesUI->defaultHueSteps->setEnabled(true);
defHueSteps = m_preferencesUI->defaultHueSteps->value();
}
m_selectorUI->colorSelector->setDefaultHueSteps(defHueSteps);
m_selectorUI->colorSelector->setDefaultSaturationSteps(m_preferencesUI->defaultSaturationSteps->value());
int defValueScaleSteps;
if (m_preferencesUI->bnDefInfValueScaleSteps->isChecked()) {
m_preferencesUI->defaultValueScaleSteps->setEnabled(false);
defValueScaleSteps = 1;
} else {
m_preferencesUI->defaultValueScaleSteps->setEnabled(true);
defValueScaleSteps = m_preferencesUI->defaultValueScaleSteps->value();
}
m_selectorUI->colorSelector->setDefaultValueScaleSteps(defValueScaleSteps);
m_selectorUI->colorSelector->setShowBgColor(m_preferencesUI->showBgColor->isChecked());
m_selectorUI->colorSelector->setShowValueScaleNumbers(m_preferencesUI->showValueScaleNumbers->isChecked());
m_selectorUI->colorSelector->setEnforceGamutMask(m_preferencesUI->enforceGamutMask->isChecked());
m_selectorUI->colorSelector->setInverseSaturation(m_wheelPrefsUI->bnInverseSat->isChecked());
}
void ArtisticColorSelectorDock::slotResetDefaultSettings()
{
quint32 hueSteps = m_selectorUI->colorSelector->getDefaultHueSteps();
quint32 saturationSteps = m_selectorUI->colorSelector->getDefaultSaturationSteps();
quint32 valueScaleSteps = m_selectorUI->colorSelector->getDefaultValueScaleSteps();
m_selectorUI->colorSelector->setNumRings(saturationSteps);
m_wheelPrefsUI->numSaturationSteps->blockSignals(true);
m_wheelPrefsUI->numSaturationSteps->setValue(saturationSteps);
m_wheelPrefsUI->numSaturationSteps->blockSignals(false);
m_selectorUI->colorSelector->setNumPieces(hueSteps);
m_wheelPrefsUI->numHueSteps->blockSignals(true);
m_wheelPrefsUI->numHueSteps->setValue(hueSteps);
m_wheelPrefsUI->numHueSteps->blockSignals(false);
if (hueSteps == 1) {
m_wheelPrefsUI->numHueSteps->setEnabled(false);
m_wheelPrefsUI->bnInfHueSteps->setChecked(true);
} else {
m_wheelPrefsUI->numHueSteps->setEnabled(true);
m_wheelPrefsUI->bnInfHueSteps->setChecked(false);
}
m_selectorUI->colorSelector->setNumLightPieces(valueScaleSteps);
m_wheelPrefsUI->numValueScaleSteps->blockSignals(true);
m_wheelPrefsUI->numValueScaleSteps->setValue(valueScaleSteps);
m_wheelPrefsUI->numValueScaleSteps->blockSignals(false);
if (valueScaleSteps == 1) {
m_wheelPrefsUI->numValueScaleSteps->setEnabled(false);
m_wheelPrefsUI->bnInfValueScaleSteps->setChecked(true);
} else {
m_wheelPrefsUI->numValueScaleSteps->setEnabled(true);
m_wheelPrefsUI->bnInfValueScaleSteps->setChecked(false);
}
}
void ArtisticColorSelectorDock::slotGamutMaskToggle(bool checked)
{
bool b = (!m_selectedMask) ? false : checked;
if (b == true) {
m_selectorUI->colorSelector->setGamutMask(m_selectedMask);
}
m_selectorUI->colorSelector->setGamutMaskOn(b);
}
void ArtisticColorSelectorDock::setCanvas(KoCanvasBase *canvas)
{
if (!canvas) {
return;
}
m_canvas = dynamic_cast<KisCanvas2*>(canvas);
if (m_canvas) {
m_canvas->disconnectCanvasObserver(this);
}
if (m_canvas) {
connect(m_canvas->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)),
- SLOT(slotCanvasResourceChanged(int,QVariant)));
+ SLOT(slotCanvasResourceChanged(int,QVariant)), Qt::UniqueConnection);
connect(m_canvas->displayColorConverter(), SIGNAL(displayConfigurationChanged()),
- SLOT(slotSelectorSettingsChanged()));
+ SLOT(slotSelectorSettingsChanged()), Qt::UniqueConnection);
m_selectorUI->colorSelector->setColorConverter(m_canvas->displayColorConverter());
setEnabled(true);
}
}
void ArtisticColorSelectorDock::unsetCanvas()
{
setEnabled(false);
m_canvas = nullptr;
m_selectorUI->colorSelector->setColorConverter(KisDisplayColorConverter::dumbConverterInstance());
}
void ArtisticColorSelectorDock::slotGamutMaskSet(KoGamutMask *mask)
{
if (!mask) {
return;
}
m_selectedMask = mask;
if (m_selectedMask) {
m_selectorUI->colorSelector->setGamutMask(m_selectedMask);
slotGamutMaskToggle(true);
} else {
slotGamutMaskToggle(false);
}
}
void ArtisticColorSelectorDock::slotGamutMaskUnset()
{
if (!m_selectedMask) {
return;
}
m_selectedMask = nullptr;
slotGamutMaskToggle(false);
m_selectorUI->colorSelector->setGamutMask(m_selectedMask);
}
void ArtisticColorSelectorDock::slotGamutMaskPreviewUpdate()
{
m_selectorUI->colorSelector->setDirty();
}
void ArtisticColorSelectorDock::slotSelectorSettingsChanged()
{
m_selectorUI->colorSelector->setDirty();
}
diff --git a/plugins/dockers/defaultdockers/kis_layer_box.cpp b/plugins/dockers/defaultdockers/kis_layer_box.cpp
index fed744d849..bedb9c7f2f 100644
--- a/plugins/dockers/defaultdockers/kis_layer_box.cpp
+++ b/plugins/dockers/defaultdockers/kis_layer_box.cpp
@@ -1,1083 +1,1082 @@
/*
* kis_layer_box.cc - part of Krita aka Krayon aka KimageShop
*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (C) 2006 Gábor Lehel <illissius@gmail.com>
* Copyright (C) 2007 Thomas Zander <zander@kde.org>
* Copyright (C) 2007 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2011 José Luis Vergara <pentalis@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_layer_box.h"
#include <QToolButton>
#include <QLayout>
#include <QMouseEvent>
#include <QPainter>
#include <QPoint>
#include <QRect>
#include <QString>
#include <QToolTip>
#include <QWidget>
#include <QComboBox>
#include <QCheckBox>
#include <QVBoxLayout>
#include <QPixmap>
#include <QList>
#include <QVector>
#include <QLabel>
#include <QMenu>
#include <QWidgetAction>
#include <kis_debug.h>
#include <klocalizedstring.h>
#include <kis_icon.h>
#include <KisNodeView.h>
#include <KoColorSpace.h>
#include <KoCompositeOpRegistry.h>
#include <KisDocument.h>
#include <kis_types.h>
#include <kis_image.h>
#include <kis_paint_device.h>
#include <kis_layer.h>
#include <kis_group_layer.h>
#include <kis_mask.h>
#include <kis_node.h>
#include <kis_base_node.h>
#include <kis_composite_ops_model.h>
#include <kis_keyframe_channel.h>
#include <kis_image_animation_interface.h>
#include <KoProperties.h>
#include "kis_action.h"
#include "kis_action_manager.h"
#include "widgets/kis_cmb_composite.h"
#include "widgets/kis_slider_spin_box.h"
#include "KisViewManager.h"
#include "kis_node_manager.h"
#include "kis_node_model.h"
#include "canvas/kis_canvas2.h"
#include "kis_dummies_facade_base.h"
#include "kis_shape_controller.h"
#include "kis_selection_mask.h"
#include "kis_config.h"
#include "KisView.h"
#include "krita_utils.h"
#include "sync_button_and_action.h"
#include "kis_color_label_selector_widget.h"
#include "kis_signals_blocker.h"
#include "kis_color_filter_combo.h"
#include "kis_node_filter_proxy_model.h"
#include "kis_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 <QProxyStyle>
class KisLayerBoxStyle : public QProxyStyle
{
public:
KisLayerBoxStyle(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 KisLayerBox::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 KisLayerBox::addActionToMenu(QMenu *menu, const QString &id)
{
if (m_canvas) {
menu->addAction(m_canvas->viewManager()->actionManager()->actionByName(id));
}
}
KisLayerBox::KisLayerBox()
: QDockWidget(i18n("Layers"))
, m_canvas(0)
, m_wdgLayerBox(new Ui_WdgLayerBox)
, m_thumbnailCompressor(500, KisSignalCompressor::FIRST_INACTIVE)
, m_colorLabelCompressor(900, KisSignalCompressor::FIRST_INACTIVE)
, 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 KisLayerBoxStyle(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 <b>Layers</b> 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, &KisLayerBox::slotAboutToRemoveRows);
connect(m_wdgLayerBox->cmbFilter, SIGNAL(selectedColorsChanged()), SLOT(updateLayerFiltering()));
setEnabled(false);
connect(&m_thumbnailCompressor, SIGNAL(timeout()), SLOT(updateThumbnail()));
connect(&m_colorLabelCompressor, SIGNAL(timeout()), SLOT(updateAvailableLabels()));
// 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->setIconSize(QSize(13, 13));
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()));
}
KisLayerBox::~KisLayerBox()
{
delete m_wdgLayerBox;
}
void expandNodesRecursively(KisNodeSP root, QPointer<KisNodeFilterProxyModel> filteringModel, KisNodeView *nodeView)
{
if (!root) return;
if (filteringModel.isNull()) return;
if (!nodeView) return;
nodeView->blockSignals(true);
KisNodeSP node = root->firstChild();
while (node) {
QModelIndex idx = filteringModel->indexFromNode(node);
if (idx.isValid()) {
nodeView->setExpanded(idx, !node->collapsed());
}
if (!node->collapsed() && node->childCount() > 0) {
expandNodesRecursively(node, filteringModel, nodeView);
}
node = node->nextSibling();
}
nodeView->blockSignals(false);
}
void KisLayerBox::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 KisLayerBox::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()));
}
void KisLayerBox::setCanvas(KoCanvasBase *canvas)
{
if (m_canvas == canvas)
return;
setEnabled(canvas != 0);
if (m_canvas) {
m_canvas->disconnectCanvasObserver(this);
m_nodeModel->setDummiesFacade(0, 0, 0, 0, 0, 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<KisCanvas2*>(canvas);
if (m_canvas) {
m_image = m_canvas->image();
connect(m_image, SIGNAL(sigImageUpdated(QRect)), &m_thumbnailCompressor, SLOT(start()));
KisDocument* doc = static_cast<KisDocument*>(m_canvas->imageView()->document());
KisShapeController *kritaShapeController =
dynamic_cast<KisShapeController*>(doc->shapeController());
KisDummiesFacadeBase *kritaDummiesFacade =
static_cast<KisDummiesFacadeBase*>(kritaShapeController);
m_selectionActionsAdapter.reset(new KisSelectionActionsAdapter(m_canvas->viewManager()->selectionManager()));
m_nodeModel->setDummiesFacade(kritaDummiesFacade,
m_image,
kritaShapeController,
m_nodeManager->nodeSelectionAdapter(),
m_nodeManager->nodeInsertionAdapter(),
m_selectionActionsAdapter.data(),
m_nodeManager->nodeDisplayModeAdapter());
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 -> KisLayerBox
connect(m_nodeManager, SIGNAL(sigUiNeedChangeActiveNode(KisNodeSP)),
this, SLOT(setCurrentNode(KisNodeSP)));
connect(m_nodeManager,
SIGNAL(sigUiNeedChangeSelectedNodes(QList<KisNodeSP>)),
SLOT(slotNodeManagerChangedSelection(QList<KisNodeSP>)));
}
else {
setCurrentNode(m_canvas->imageView()->currentNode());
}
// Connection KisLayerBox -> KisNodeManager (isolate layer)
connect(m_nodeModel, SIGNAL(toggleIsolateActiveNode()),
m_nodeManager, SLOT(toggleIsolateActiveNode()));
KisImageAnimationInterface *animation = m_image->animationInterface();
connect(animation, &KisImageAnimationInterface::sigUiTimeChanged, this, &KisLayerBox::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 KisLayerBox::unsetCanvas()
{
setEnabled(false);
if (m_canvas) {
m_newLayerMenu->clear();
}
m_filteringModel->unsetDummiesFacade();
disconnect(m_image, 0, this, 0);
disconnect(m_nodeManager, 0, this, 0);
disconnect(m_nodeModel, 0, m_nodeManager, 0);
m_nodeManager->slotSetSelectedNodes(KisNodeList());
m_canvas = 0;
}
void KisLayerBox::notifyImageDeleted()
{
setCanvas(0);
}
void KisLayerBox::updateUI()
{
if (!m_canvas) return;
if (!m_nodeManager) return;
KisNodeSP activeNode = m_nodeManager->activeNode();
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, &KisLayerBox::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);
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<const KisGroupLayer*>(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 KisLayerBox::setCurrentNode(KisNodeSP node)
{
m_filteringModel->setActiveNode(node);
QModelIndex index = node ? m_filteringModel->indexFromNode(node) : QModelIndex();
m_filteringModel->setData(index, true, KisNodeModel::ActiveRole);
updateUI();
}
void KisLayerBox::slotModelReset()
{
if(m_nodeModel->hasDummiesFacade()) {
QItemSelection selection;
Q_FOREACH (const KisNodeSP node, m_nodeManager->selectedNodes()) {
const QModelIndex &idx = m_filteringModel->indexFromNode(node);
if(idx.isValid()){
QItemSelectionRange selectionRange(idx);
selection << selectionRange;
}
}
m_wdgLayerBox->listLayers->selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect);
}
updateUI();
}
void KisLayerBox::slotSetCompositeOp(const KoCompositeOp* compositeOp)
{
KoID opId = KoCompositeOpRegistry::instance().getKoID(compositeOp->id());
m_wdgLayerBox->cmbComposite->blockSignals(true);
m_wdgLayerBox->cmbComposite->selectCompositeOp(opId);
m_wdgLayerBox->cmbComposite->blockSignals(false);
}
// range: 0-100
void KisLayerBox::slotSetOpacity(double opacity)
{
Q_ASSERT(opacity >= 0 && opacity <= 100);
m_wdgLayerBox->doubleOpacity->blockSignals(true);
m_wdgLayerBox->doubleOpacity->setValue(opacity);
m_wdgLayerBox->doubleOpacity->blockSignals(false);
}
void KisLayerBox::slotContextMenuRequested(const QPoint &pos, const QModelIndex &index)
{
KisNodeList nodes = m_nodeManager->selectedNodes();
KisNodeSP activeNode = m_nodeManager->activeNode();
if (nodes.isEmpty() || !activeNode) return;
if (m_canvas) {
QMenu menu;
const bool singleLayer = nodes.size() == 1;
if (index.isValid()) {
menu.addAction(m_propertiesAction);
if (singleLayer) {
addActionToMenu(&menu, "layer_style");
}
{
KisSignalsBlocker b(m_colorSelector);
m_colorSelector->setCurrentIndex(singleLayer ? activeNode->colorLabelIndex() : -1);
}
menu.addAction(m_colorSelectorAction);
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 KisLayerBox::slotMinimalView()
{
m_wdgLayerBox->listLayers->setDisplayMode(KisNodeView::MinimalMode);
}
void KisLayerBox::slotDetailedView()
{
m_wdgLayerBox->listLayers->setDisplayMode(KisNodeView::DetailedMode);
}
void KisLayerBox::slotThumbnailView()
{
m_wdgLayerBox->listLayers->setDisplayMode(KisNodeView::ThumbnailMode);
}
void KisLayerBox::slotRmClicked()
{
if (!m_canvas) return;
m_nodeManager->removeNode();
}
void KisLayerBox::slotRaiseClicked()
{
if (!m_canvas) return;
m_nodeManager->raiseNode();
}
void KisLayerBox::slotLowerClicked()
{
if (!m_canvas) return;
m_nodeManager->lowerNode();
}
void KisLayerBox::slotPropertiesClicked()
{
if (!m_canvas) return;
if (KisNodeSP active = m_nodeManager->activeNode()) {
m_nodeManager->nodeProperties(active);
}
}
void KisLayerBox::slotCompositeOpChanged(int index)
{
Q_UNUSED(index);
if (!m_canvas) return;
QString compositeOp = m_wdgLayerBox->cmbComposite->selectedCompositeOp().id();
m_nodeManager->nodeCompositeOpChanged(m_nodeManager->activeColorSpace()->compositeOp(compositeOp));
}
void KisLayerBox::slotOpacityChanged()
{
if (!m_canvas) return;
m_blockOpacityUpdate = true;
m_nodeManager->nodeOpacityChanged(m_newOpacity, true);
m_blockOpacityUpdate = false;
}
void KisLayerBox::slotOpacitySliderMoved(qreal opacity)
{
m_newOpacity = opacity;
m_opacityDelayTimer.start(200);
}
void KisLayerBox::slotCollapsed(const QModelIndex &index)
{
KisNodeSP node = m_filteringModel->nodeFromIndex(index);
if (node) {
node->setCollapsed(true);
}
}
void KisLayerBox::slotExpanded(const QModelIndex &index)
{
KisNodeSP node = m_filteringModel->nodeFromIndex(index);
if (node) {
node->setCollapsed(false);
}
}
void KisLayerBox::slotSelectOpaque()
{
if (!m_canvas) return;
QAction *action = m_canvas->viewManager()->actionManager()->actionByName("selectopaque");
if (action) {
action->trigger();
}
}
void KisLayerBox::slotNodeCollapsedChanged()
{
expandNodesRecursively(m_image->rootLayer(), m_filteringModel, m_wdgLayerBox->listLayers);
}
inline bool isSelectionMask(KisNodeSP node)
{
return dynamic_cast<KisSelectionMask*>(node.data());
}
KisNodeSP KisLayerBox::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 KisLayerBox::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<KisNodeSP> masks = m_image->rootLayer()->childNodes(QStringList("KisSelectionMask"), properties);
if (!masks.isEmpty()) {
globalSelectionMask = dynamic_cast<KisSelectionMask*>(masks.first().data());
}
}
// try to find at least any selection mask
if (!globalSelectionMask) {
KoProperties properties;
QList<KisNodeSP> masks = m_image->rootLayer()->childNodes(QStringList("KisSelectionMask"), properties);
if (!masks.isEmpty()) {
globalSelectionMask = dynamic_cast<KisSelectionMask*>(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 KisLayerBox::selectionChanged(const QModelIndexList selection)
{
if (!m_nodeManager) return;
/**
* When the user clears the extended selection by clicking on the
* empty area of the docker, the selection should be reset on to
* the active layer, which might be even unselected(!).
*/
if (selection.isEmpty() && m_nodeManager->activeNode()) {
QModelIndex selectedIndex =
m_filteringModel->indexFromNode(m_nodeManager->activeNode());
m_wdgLayerBox->listLayers->selectionModel()->
setCurrentIndex(selectedIndex, QItemSelectionModel::ClearAndSelect);
return;
}
QList<KisNodeSP> selectedNodes;
Q_FOREACH (const QModelIndex &idx, selection) {
selectedNodes << m_filteringModel->nodeFromIndex(idx);
}
m_nodeManager->slotSetSelectedNodes(selectedNodes);
updateUI();
}
void KisLayerBox::slotAboutToRemoveRows(const QModelIndex &parent, int start, int end)
{
/**
* Qt has changed its behavior when deleting an item. Previously
* the selection priority was on the next item in the list, and
* now it has shanged to the previous item. Here we just adjust
* the selected item after the node removal. Please take care that
* this method overrides what was done by the corresponding method
* of QItemSelectionModel, which *has already done* its work. That
* is why we use (start - 1) and (end + 1) in the activation
* condition.
*
* See bug: https://bugs.kde.org/show_bug.cgi?id=345601
*/
QModelIndex currentIndex = m_wdgLayerBox->listLayers->currentIndex();
QAbstractItemModel *model = m_filteringModel;
if (currentIndex.isValid() && parent == currentIndex.parent()
&& currentIndex.row() >= start - 1 && currentIndex.row() <= end + 1) {
QModelIndex old = currentIndex;
if (model && end < model->rowCount(parent) - 1) // there are rows left below the change
currentIndex = model->index(end + 1, old.column(), parent);
else if (start > 0) // there are rows left above the change
currentIndex = model->index(start - 1, old.column(), parent);
else // there are no rows left in the table
currentIndex = QModelIndex();
if (currentIndex.isValid() && currentIndex != old) {
m_wdgLayerBox->listLayers->setCurrentIndex(currentIndex);
}
}
}
void KisLayerBox::slotNodeManagerChangedSelection(const KisNodeList &nodes)
{
if (!m_nodeManager) return;
QModelIndexList newSelection;
Q_FOREACH(KisNodeSP node, nodes) {
newSelection << m_filteringModel->indexFromNode(node);
}
QItemSelectionModel *model = m_wdgLayerBox->listLayers->selectionModel();
if (KritaUtils::compareListsUnordered(newSelection, model->selectedIndexes())) {
return;
}
QItemSelection selection;
Q_FOREACH(const QModelIndex &idx, newSelection) {
selection.select(idx, idx);
}
model->select(selection, QItemSelectionModel::ClearAndSelect);
}
void KisLayerBox::updateThumbnail()
{
m_wdgLayerBox->listLayers->updateNode(m_wdgLayerBox->listLayers->currentIndex());
}
void KisLayerBox::slotRenameCurrentNode()
{
m_wdgLayerBox->listLayers->edit(m_wdgLayerBox->listLayers->currentIndex());
}
void KisLayerBox::slotColorLabelChanged(int label)
{
KisNodeList nodes = m_nodeManager->selectedNodes();
Q_FOREACH(KisNodeSP node, nodes) {
auto applyLabelFunc =
[label](KisNodeSP node) {
node->setColorLabelIndex(label);
};
KisLayerUtils::recursiveApplyNodes(node, applyLabelFunc);
}
}
void KisLayerBox::updateAvailableLabels()
{
if (!m_image) return;
m_wdgLayerBox->cmbFilter->updateAvailableLabels(m_image->root());
}
void KisLayerBox::updateLayerFiltering()
{
m_filteringModel->setAcceptedLabels(m_wdgLayerBox->cmbFilter->selectedColors());
}
void KisLayerBox::slotKeyframeChannelAdded(KisKeyframeChannel *channel)
{
if (channel->id() == KisKeyframeChannel::Opacity.id()) {
watchOpacityChannel(channel);
}
}
void KisLayerBox::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 KisLayerBox::slotOpacityKeyframeChanged(KisKeyframeSP keyframe)
{
Q_UNUSED(keyframe);
if (m_blockOpacityUpdate) return;
updateUI();
}
void KisLayerBox::slotOpacityKeyframeMoved(KisKeyframeSP keyframe, int fromTime)
{
Q_UNUSED(fromTime);
slotOpacityKeyframeChanged(keyframe);
}
void KisLayerBox::slotImageTimeChanged(int time)
{
Q_UNUSED(time);
updateUI();
}
void KisLayerBox::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 KisLayerBox::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_kis_layer_box.cpp"
diff --git a/plugins/dockers/defaultdockers/wdglayerbox.ui b/plugins/dockers/defaultdockers/wdglayerbox.ui
index 174977e988..30a68514b9 100644
--- a/plugins/dockers/defaultdockers/wdglayerbox.ui
+++ b/plugins/dockers/defaultdockers/wdglayerbox.ui
@@ -1,330 +1,349 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WdgLayerBox</class>
<widget class="QWidget" name="WdgLayerBox">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>220</width>
<height>384</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>1</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="hbox2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="KisCompositeOpComboBox" name="cmbComposite">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Blending Mode</string>
</property>
<property name="whatsThis">
<string>Select the blending mode for the layer.</string>
</property>
</widget>
</item>
<item>
- <widget class="KisColorFilterCombo" name="cmbFilter"/>
+ <widget class="KisColorFilterCombo" name="cmbFilter">
+ <property name="iconSize">
+ <size>
+ <width>22</width>
+ <height>22</height>
+ </size>
+ </property>
+ </widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="opacityLayout" stretch="0,0,0">
<property name="topMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="opacityLabel">
<property name="text">
<string>Opacity:</string>
</property>
</widget>
</item>
<item>
<widget class="KisDoubleSliderSpinBox" name="doubleOpacity" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Layer Opacity</string>
</property>
<property name="whatsThis">
<string>Adjust the transparency of the layer</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="configureLayerDockerToolbar">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
<property name="text">
<string>...</string>
</property>
+ <property name="iconSize">
+ <size>
+ <width>22</width>
+ <height>22</height>
+ </size>
+ </property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="KisNodeView" name="listLayers" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="hbox1" stretch="0,0,0,0,0,1,0">
<property name="spacing">
<number>2</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="KisToolButton" name="bnAdd">
<property name="minimumSize">
<size>
<width>0</width>
<height>28</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>28</height>
</size>
</property>
<property name="text">
<string>...</string>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="bnDuplicate">
<property name="minimumSize">
<size>
<width>28</width>
<height>28</height>
</size>
</property>
<property name="toolTip">
<string>Duplicate layer or mask</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="bnLower">
<property name="minimumSize">
<size>
<width>28</width>
<height>28</height>
</size>
</property>
<property name="toolTip">
<string>Move layer or mask down</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="bnRaise">
<property name="minimumSize">
<size>
<width>28</width>
<height>28</height>
</size>
</property>
<property name="toolTip">
<string>Move layer or mask up</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="bnProperties">
<property name="minimumSize">
<size>
<width>28</width>
<height>28</height>
</size>
</property>
<property name="toolTip">
<string>View or change the layer properties</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="spacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>28</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="bnDelete">
<property name="minimumSize">
<size>
<width>28</width>
<height>28</height>
</size>
</property>
<property name="toolTip">
<string>Delete the layer or mask</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KisCompositeOpComboBox</class>
<extends>QComboBox</extends>
<header>widgets/kis_cmb_composite.h</header>
</customwidget>
<customwidget>
<class>KisDoubleSliderSpinBox</class>
<extends>QWidget</extends>
<header>kis_slider_spin_box.h</header>
</customwidget>
<customwidget>
<class>KisNodeView</class>
<extends></extends>
<header location="global">KisNodeView.h</header>
</customwidget>
<customwidget>
<class>KisToolButton</class>
<extends>QToolButton</extends>
<header>kis_tool_button.h</header>
</customwidget>
<customwidget>
<class>KisColorFilterCombo</class>
<extends>QComboBox</extends>
<header>kis_color_filter_combo.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>cmbComposite</tabstop>
<tabstop>bnDuplicate</tabstop>
<tabstop>bnLower</tabstop>
<tabstop>bnRaise</tabstop>
<tabstop>bnProperties</tabstop>
<tabstop>bnDelete</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>
diff --git a/plugins/dockers/gamutmask/gamutmask_dock.cpp b/plugins/dockers/gamutmask/gamutmask_dock.cpp
index 63ef163d12..fa4ecc8d06 100644
--- a/plugins/dockers/gamutmask/gamutmask_dock.cpp
+++ b/plugins/dockers/gamutmask/gamutmask_dock.cpp
@@ -1,629 +1,629 @@
/*
* Copyright (c) 2018 Anna Medonosova <anna.medonosova@gmail.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <kis_debug.h>
#include <klocalizedstring.h>
#include <KoCanvasResourceProvider.h>
#include <KoResourceServerProvider.h>
#include <KoResourceServerObserver.h>
#include <KoResourceServerAdapter.h>
#include <KoCanvasBase.h>
#include <KoColor.h>
#include <resources/KoGamutMask.h>
#include <kis_icon_utils.h>
#include <KisPart.h>
#include <kis_shape_layer.h>
#include <kis_types.h>
#include <KisDocument.h>
#include <kis_node_selection_adapter.h>
#include <kis_group_layer.h>
#include <KisView.h>
#include <KoResourceItemChooser.h>
#include <QWidget>
#include <QMenu>
#include <QButtonGroup>
#include <QRegularExpressionValidator>
#include <QRegularExpression>
#include <QFileInfo>
#include <QMessageBox>
#include "gamutmask_dock.h"
#include <KisViewManager.h>
#include <kis_canvas_resource_provider.h>
#include <KoColorBackground.h>
#include <KoShapeStroke.h>
#include <ctime>
#include "ui_wdgGamutMaskChooser.h"
class KisMainWindow;
struct GamutMaskChooserUI: public QWidget, public Ui_wdgGamutMaskChooser
{
GamutMaskChooserUI() {
setupUi(this);
}
};
GamutMaskDock::GamutMaskDock()
: QDockWidget(i18n("Gamut Masks"))
, m_resourceProvider(0)
, m_selfClosingTemplate(false)
, m_externalTemplateClose(false)
, m_creatingNewMask(false)
, m_templatePrevSaved(false)
, m_selfSelectingMask(false)
, m_selectedMask(nullptr)
, m_maskDocument(nullptr)
, m_view(nullptr)
{
m_dockerUI = new GamutMaskChooserUI();
m_dockerUI->bnMaskEditor->setIcon(KisIconUtils::loadIcon("dirty-preset"));
m_dockerUI->bnMaskDelete->setIcon(KisIconUtils::loadIcon("deletelayer"));
m_dockerUI->bnMaskNew->setIcon(KisIconUtils::loadIcon("list-add"));
m_dockerUI->bnMaskDuplicate->setIcon(KisIconUtils::loadIcon("duplicatelayer"));
m_dockerUI->maskPropertiesBox->setVisible(false);
m_dockerUI->bnSaveMask->setIcon(KisIconUtils::loadIcon("document-save"));
m_dockerUI->bnCancelMaskEdit->setIcon(KisIconUtils::loadIcon("dialog-cancel"));
m_dockerUI->bnPreviewMask->setIcon(KisIconUtils::loadIcon("visible"));
QRegularExpression maskTitleRegex("^[-_\\(\\)\\sA-Za-z0-9]+$");
QRegularExpressionValidator* m_maskTitleValidator = new QRegularExpressionValidator(maskTitleRegex, this);
m_dockerUI->maskTitleEdit->setValidator(m_maskTitleValidator);
KoResourceServer<KoGamutMask>* rServer = KoResourceServerProvider::instance()->gamutMaskServer();
rServer->addObserver(this);
// gamut mask connections
connect(m_dockerUI->bnSaveMask , SIGNAL(clicked()) , SLOT(slotGamutMaskSave()));
connect(m_dockerUI->bnCancelMaskEdit , SIGNAL(clicked()) , SLOT(slotGamutMaskCancelEdit()));
connect(m_dockerUI->bnPreviewMask , SIGNAL(clicked()) , SLOT(slotGamutMaskPreview()));
connect(m_dockerUI->bnMaskEditor , SIGNAL(clicked()) , SLOT(slotGamutMaskEdit()));
connect(m_dockerUI->maskChooser, SIGNAL(sigGamutMaskSelected(KoGamutMask*)), SLOT(slotGamutMaskSelected(KoGamutMask*)));
connect(m_dockerUI->bnMaskNew , SIGNAL(clicked()) , SLOT(slotGamutMaskCreateNew()));
connect(m_dockerUI->bnMaskDelete , SIGNAL(clicked()) , SLOT(slotGamutMaskDelete()));
connect(m_dockerUI->bnMaskDuplicate , SIGNAL(clicked()) , SLOT(slotGamutMaskDuplicate()));
setWidget(m_dockerUI);
}
GamutMaskDock::~GamutMaskDock()
{
KoResourceServer<KoGamutMask>* rServer = KoResourceServerProvider::instance()->gamutMaskServer();
rServer->removeObserver(this);
}
void GamutMaskDock::setViewManager(KisViewManager* kisview)
{
m_resourceProvider = kisview->resourceProvider();
selectMask(m_resourceProvider->currentGamutMask());
- connect(this, SIGNAL(sigGamutMaskSet(KoGamutMask*)), m_resourceProvider, SLOT(slotGamutMaskActivated(KoGamutMask*)));
- connect(this, SIGNAL(sigGamutMaskChanged(KoGamutMask*)), m_resourceProvider, SLOT(slotGamutMaskActivated(KoGamutMask*)));
- connect(this, SIGNAL(sigGamutMaskUnset()), m_resourceProvider, SLOT(slotGamutMaskUnset()));
- connect(this, SIGNAL(sigGamutMaskPreviewUpdate()), m_resourceProvider, SLOT(slotGamutMaskPreviewUpdate()));
- connect(KisPart::instance(), SIGNAL(sigDocumentRemoved(QString)), this, SLOT(slotDocumentRemoved(QString)));
+ connect(this, SIGNAL(sigGamutMaskSet(KoGamutMask*)), m_resourceProvider, SLOT(slotGamutMaskActivated(KoGamutMask*)), Qt::UniqueConnection);
+ connect(this, SIGNAL(sigGamutMaskChanged(KoGamutMask*)), m_resourceProvider, SLOT(slotGamutMaskActivated(KoGamutMask*)), Qt::UniqueConnection);
+ connect(this, SIGNAL(sigGamutMaskUnset()), m_resourceProvider, SLOT(slotGamutMaskUnset()), Qt::UniqueConnection);
+ connect(this, SIGNAL(sigGamutMaskPreviewUpdate()), m_resourceProvider, SLOT(slotGamutMaskPreviewUpdate()), Qt::UniqueConnection);
+ connect(KisPart::instance(), SIGNAL(sigDocumentRemoved(QString)), this, SLOT(slotDocumentRemoved(QString)), Qt::UniqueConnection);
}
void GamutMaskDock::slotGamutMaskEdit()
{
if (!m_selectedMask) {
return;
}
openMaskEditor();
}
bool GamutMaskDock::openMaskEditor()
{
if (!m_selectedMask) {
return false;
}
// find the template resource first, so we can abort the action early on
QString maskTemplateFile = KoResourcePaths::findResource("ko_gamutmasks", "GamutMaskTemplate.kra");
if (maskTemplateFile.isEmpty() || maskTemplateFile.isNull() || !QFile::exists(maskTemplateFile)) {
dbgPlugins << "GamutMaskDock::openMaskEditor(): maskTemplateFile (" << maskTemplateFile << ") was not found on the system";
getUserFeedback(i18n("Could not open gamut mask for editing."),
i18n("The editor template was not found."),
QMessageBox::Ok, QMessageBox::Ok, QMessageBox::Critical);
return false;
}
m_dockerUI->maskPropertiesBox->setVisible(true);
m_dockerUI->maskPropertiesBox->setEnabled(true);
m_dockerUI->editControlsBox->setEnabled(false);
m_dockerUI->editControlsBox->setVisible(false);
m_dockerUI->maskTitleEdit->setText(m_selectedMask->title());
m_dockerUI->maskDescriptionEdit->setPlainText(m_selectedMask->description());
m_maskDocument = KisPart::instance()->createDocument();
KisPart::instance()->addDocument(m_maskDocument);
m_maskDocument->openUrl(QUrl::fromLocalFile(maskTemplateFile), KisDocument::DontAddToRecent);
// template document needs a proper autogenerated filename,
// to avoid collision with other documents,
// otherwise bugs happen when slotDocumentRemoved is called
// (e.g. user closes another view, the template stays open, but the edit operation is canceled)
m_maskDocument->setInfiniteAutoSaveInterval();
QString maskPath = QString("%1%2%3_%4.kra")
.arg(QDir::tempPath())
.arg(QDir::separator())
.arg("GamutMaskTemplate")
.arg(std::time(nullptr));
m_maskDocument->setUrl(QUrl::fromLocalFile(maskPath));
m_maskDocument->setLocalFilePath(maskPath);
KisShapeLayerSP shapeLayer = getShapeLayer();
// pass only copies of shapes to the layer,
// so the originals don't disappear from the mask later
for (KoShape *shape: m_selectedMask->koShapes()) {
KoShape* newShape = shape->cloneShape();
newShape->setStroke(KoShapeStrokeModelSP());
newShape->setBackground(QSharedPointer<KoColorBackground>(new KoColorBackground(QColor(255,255,255))));
shapeLayer->addShape(newShape);
}
m_maskDocument->setPreActivatedNode(shapeLayer);
// set document as active
KisMainWindow* mainWindow = KisPart::instance()->currentMainwindow();
KIS_ASSERT(mainWindow);
m_view = mainWindow->addViewAndNotifyLoadingCompleted(m_maskDocument);
KIS_ASSERT(m_view);
for(KisView *view: KisPart::instance()->views()) {
if (view->document() == m_maskDocument) {
view->activateWindow();
break;
}
}
connect(m_view->viewManager(), SIGNAL(viewChanged()), this, SLOT(slotViewChanged()));
connect(m_maskDocument, SIGNAL(completed()), this, SLOT(slotDocumentSaved()));
return true;
}
void GamutMaskDock::cancelMaskEdit()
{
if (m_creatingNewMask) {
deleteMask();
}
if (m_selectedMask) {
m_selectedMask->clearPreview();
if (m_resourceProvider->currentGamutMask() == m_selectedMask) {
emit sigGamutMaskChanged(m_selectedMask);
}
}
closeMaskDocument();
}
void GamutMaskDock::selectMask(KoGamutMask *mask, bool notifyItemChooser)
{
if (!mask) {
return;
}
m_selectedMask = mask;
if (notifyItemChooser) {
m_selfSelectingMask = true;
m_dockerUI->maskChooser->setCurrentResource(m_selectedMask);
m_selfSelectingMask = false;
}
emit sigGamutMaskSet(m_selectedMask);
}
bool GamutMaskDock::saveSelectedMaskResource()
{
if (!m_selectedMask || !m_maskDocument) {
return false;
}
bool maskSaved = false;
if (m_selectedMask) {
QList<KoShape*> shapes = getShapesFromLayer();
if (shapes.count() > 0) {
m_selectedMask->setMaskShapes(shapes);
m_selectedMask->setImage(
m_maskDocument->image()->convertToQImage(m_maskDocument->image()->bounds()
, m_maskDocument->image()->profile()
)
);
m_selectedMask->setDescription(m_dockerUI->maskDescriptionEdit->toPlainText());
m_selectedMask->clearPreview();
m_selectedMask->save();
maskSaved = true;
} else {
getUserFeedback(i18n("Saving of gamut mask '%1' was aborted.", m_selectedMask->title()),
i18n("<p>The mask template is invalid.</p>"
"<p>Please check that:"
"<ul>"
"<li>your template contains a vector layer named 'maskShapesLayer'</li>"
"<li>there are one or more vector shapes on the 'maskShapesLayer'</li>"
"</ul></p>"
),
QMessageBox::Ok, QMessageBox::Ok);
}
}
return maskSaved;
}
void GamutMaskDock::deleteMask()
{
KoResourceServer<KoGamutMask>* rServer = KoResourceServerProvider::instance()->gamutMaskServer();
rServer->removeResourceAndBlacklist(m_selectedMask);
m_selectedMask = nullptr;
}
int GamutMaskDock::getUserFeedback(QString text, QString informativeText,
QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton,
QMessageBox::Icon severity)
{
QMessageBox msgBox;
msgBox.setWindowTitle(i18nc("@title:window", "Krita"));
msgBox.setText(QString("<p><b>%1</b></p>").arg(text));
msgBox.setInformativeText(informativeText);
msgBox.setStandardButtons(buttons);
msgBox.setDefaultButton(defaultButton);
msgBox.setIcon(severity);
int res = msgBox.exec();
return res;
}
int GamutMaskDock::saveOrCancel(QMessageBox::StandardButton defaultAction)
{
int response = 0;
if (m_maskDocument->isModified()) {
response = getUserFeedback(i18n("Gamut mask <b>'%1'</b> has been modified.", m_selectedMask->title()),
i18n("Do you want to save it?"),
QMessageBox::Cancel | QMessageBox::Close | QMessageBox::Save, defaultAction);
} else if (m_templatePrevSaved && defaultAction != QMessageBox::Close) {
response = QMessageBox::Save;
} else if (!m_templatePrevSaved) {
response = QMessageBox::Close;
} else {
response = defaultAction;
}
switch (response) {
case QMessageBox::Save : {
slotGamutMaskSave();
break;
}
case QMessageBox::Close : {
cancelMaskEdit();
break;
}
}
return response;
}
KoGamutMask *GamutMaskDock::createMaskResource(KoGamutMask* sourceMask, QString newTitle)
{
m_creatingNewMask = true;
KoGamutMask* newMask = nullptr;
if (sourceMask) {
newMask = new KoGamutMask(sourceMask);
newMask->setImage(sourceMask->image());
} else {
newMask = new KoGamutMask();
QString defaultPreviewPath = KoResourcePaths::findResource("ko_gamutmasks", "empty_mask_preview.png");
KIS_SAFE_ASSERT_RECOVER_NOOP(!(defaultPreviewPath.isEmpty() || defaultPreviewPath.isNull() || !QFile::exists(defaultPreviewPath)));
newMask->setImage(QImage(defaultPreviewPath, "PNG"));
}
QPair<QString,QFileInfo> maskFile = resolveMaskTitle(newTitle);
QString maskTitle = maskFile.first;
QFileInfo fileInfo = maskFile.second;
newMask->setTitle(maskTitle);
newMask->setFilename(fileInfo.filePath());
newMask->setValid(true);
KoResourceServer<KoGamutMask>* rServer = KoResourceServerProvider::instance()->gamutMaskServer();
rServer->removeFromBlacklist(newMask);
rServer->addResource(newMask, false);
return newMask;
}
QPair<QString, QFileInfo> GamutMaskDock::resolveMaskTitle(QString suggestedTitle)
{
KoResourceServer<KoGamutMask>* rServer = KoResourceServerProvider::instance()->gamutMaskServer();
QString saveLocation = rServer->saveLocation();
QString processedTitle = suggestedTitle.trimmed();
QString resourceName = processedTitle;
while (rServer->resourceByName(resourceName)) {
resourceName = resourceName + QString(" (Copy)");
}
QString maskTitle = resourceName;
QString maskFile = maskTitle + ".kgm";
QString path = saveLocation + maskFile.replace(QRegularExpression("\\s+"), "_");
QFileInfo fileInfo(path);
return QPair<QString, QFileInfo>(maskTitle, fileInfo);
}
void GamutMaskDock::closeMaskDocument()
{
if (!m_externalTemplateClose) {
if (m_maskDocument) {
// set the document to not modified to bypass confirmation dialog
// the close is already confirmed
m_maskDocument->setModified(false);
m_maskDocument->closeUrl();
m_view->closeView();
m_view->deleteLater();
// set a flag that we are doing it ourselves, so the docker does not react to
// removing signal from KisPart
m_selfClosingTemplate = true;
KisPart::instance()->removeView(m_view);
KisPart::instance()->removeDocument(m_maskDocument);
m_selfClosingTemplate = false;
}
}
m_dockerUI->maskPropertiesBox->setVisible(false);
m_dockerUI->editControlsBox->setVisible(true);
m_dockerUI->editControlsBox->setEnabled(true);
disconnect(m_view->viewManager(), SIGNAL(viewChanged()), this, SLOT(slotViewChanged()));
disconnect(m_maskDocument, SIGNAL(completed()), this, SLOT(slotDocumentSaved()));
// the template file is meant as temporary, if the user saved it, delete now
if (QFile::exists(m_maskDocument->localFilePath())) {
QFile::remove(m_maskDocument->localFilePath());
}
m_maskDocument = nullptr;
m_view = nullptr;
m_creatingNewMask = false;
m_templatePrevSaved = false;
}
QList<KoShape*> GamutMaskDock::getShapesFromLayer()
{
KisShapeLayerSP shapeLayer = getShapeLayer();
// create a deep copy of the shapes to save in the mask,
// otherwise they vanish when the template closes
QList<KoShape*> newShapes;
if (shapeLayer) {
for (KoShape* sh: shapeLayer->shapes()) {
KoShape* newShape = sh->cloneShape();
KoShapeStrokeSP border(new KoShapeStroke(0.5f, Qt::white));
newShape->setStroke(border);
newShape->setBackground(QSharedPointer<KoColorBackground>(new KoColorBackground(QColor(255,255,255,0))));
newShapes.append(newShape);
}
}
return newShapes;
}
KisShapeLayerSP GamutMaskDock::getShapeLayer()
{
KisNodeSP node = m_maskDocument->image()->rootLayer()->findChildByName("maskShapesLayer");
return KisShapeLayerSP(dynamic_cast<KisShapeLayer*>(node.data()));
}
void GamutMaskDock::slotGamutMaskSave()
{
if (!m_selectedMask || !m_maskDocument) {
return;
}
QString newTitle = m_dockerUI->maskTitleEdit->text();
if (m_selectedMask->title() != newTitle) {
// title has changed, rename
KoGamutMask* newMask = createMaskResource(m_selectedMask, newTitle);
// delete old mask and select new
deleteMask();
selectMask(newMask);
}
bool maskSaved = saveSelectedMaskResource();
if (maskSaved) {
emit sigGamutMaskSet(m_selectedMask);
closeMaskDocument();
}
}
void GamutMaskDock::slotGamutMaskCancelEdit()
{
if (!m_selectedMask) {
return;
}
saveOrCancel(QMessageBox::Close);
}
void GamutMaskDock::slotGamutMaskPreview()
{
if (!m_selectedMask) {
return;
}
m_selectedMask->setPreviewMaskShapes(getShapesFromLayer());
emit sigGamutMaskPreviewUpdate();
}
void GamutMaskDock::slotGamutMaskSelected(KoGamutMask *mask)
{
if (!m_selfSelectingMask) {
if (m_maskDocument) {
int res = saveOrCancel();
if (res == QMessageBox::Cancel) {
return;
}
}
selectMask(mask, false);
}
}
void GamutMaskDock::setCanvas(KoCanvasBase *canvas)
{
setEnabled(canvas != 0);
}
void GamutMaskDock::unsetCanvas()
{
setEnabled(false);
}
void GamutMaskDock::unsetResourceServer()
{
KoResourceServer<KoGamutMask>* rServer = KoResourceServerProvider::instance()->gamutMaskServer();
rServer->removeObserver(this);
}
void GamutMaskDock::removingResource(KoGamutMask *resource)
{
// if deleting previously set mask, notify selectors to unset their mask
if (resource == m_resourceProvider->currentGamutMask()) {
emit sigGamutMaskUnset();
m_selectedMask = nullptr;
}
}
void GamutMaskDock::resourceChanged(KoGamutMask *resource)
{
// if currently set mask has been changed, notify selectors
if (resource == m_resourceProvider->currentGamutMask()) {
selectMask(resource);
}
}
void GamutMaskDock::slotGamutMaskCreateNew()
{
KoGamutMask* newMask = createMaskResource(nullptr, "new mask");
selectMask(newMask);
bool editorOpened = openMaskEditor();
if (!editorOpened) {
deleteMask();
}
}
void GamutMaskDock::slotGamutMaskDuplicate()
{
if (!m_selectedMask) {
return;
}
KoGamutMask* newMask = createMaskResource(m_selectedMask, m_selectedMask->title());
selectMask(newMask);
bool editorOpened = openMaskEditor();
if (!editorOpened) {
deleteMask();
}
}
void GamutMaskDock::slotGamutMaskDelete()
{
if (!m_selectedMask) {
return;
}
int res = getUserFeedback(i18n("Are you sure you want to delete mask <b>'%1'</b>?"
, m_selectedMask->title()));
if (res == QMessageBox::Yes) {
deleteMask();
}
}
void GamutMaskDock::slotDocumentRemoved(QString filename)
{
if (!m_maskDocument) {
return;
}
m_externalTemplateClose = true;
// we do not want to run this if it is we who close the file
if (!m_selfClosingTemplate) {
// KisPart called, that a document will be removed
// if it's ours, cancel the mask edit operation
if (m_maskDocument->url().toLocalFile() == filename) {
m_maskDocument->waitForSavingToComplete();
saveOrCancel();
}
}
m_externalTemplateClose = false;
}
void GamutMaskDock::slotViewChanged()
{
if (!m_maskDocument || !m_view) {
return;
}
if (m_view->viewManager()->document() == m_maskDocument) {
m_dockerUI->maskPropertiesBox->setEnabled(true);
} else {
m_dockerUI->maskPropertiesBox->setEnabled(false);
}
}
void GamutMaskDock::slotDocumentSaved()
{
m_templatePrevSaved = true;
}
diff --git a/plugins/dockers/overview/overviewdocker_dock.cpp b/plugins/dockers/overview/overviewdocker_dock.cpp
index 3b549be975..2aa777ee70 100644
--- a/plugins/dockers/overview/overviewdocker_dock.cpp
+++ b/plugins/dockers/overview/overviewdocker_dock.cpp
@@ -1,86 +1,148 @@
/*
* Copyright (c) 2009 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "overviewdocker_dock.h"
#include "overviewwidget.h"
#include <QLabel>
#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QToolButton>
#include <QStatusBar>
+#include <kis_slider_spin_box.h>
#include <klocalizedstring.h>
#include "kis_canvas2.h"
#include <KisViewManager.h>
+#include <kactioncollection.h>
+#include <kis_action.h>
#include <kis_zoom_manager.h>
#include "kis_image.h"
#include "kis_paint_device.h"
#include "kis_signal_compressor.h"
-
+#include "kis_canvas_controller.h"
+#include "kis_icon_utils.h"
OverviewDockerDock::OverviewDockerDock( )
: QDockWidget(i18n("Overview"))
- , m_zoomSlider(0)
- , m_canvas(0)
+ , m_zoomSlider(nullptr)
+ , m_rotateSlider(nullptr)
+ , m_mirrorCanvas(nullptr)
+ , m_canvas(nullptr)
{
QWidget *page = new QWidget(this);
m_layout = new QVBoxLayout(page);
+ m_horizontalLayout = new QHBoxLayout();
m_overviewWidget = new OverviewWidget(this);
m_overviewWidget->setMinimumHeight(50);
m_overviewWidget->setBackgroundRole(QPalette::AlternateBase);
m_overviewWidget->setAutoFillBackground(true); // paints background role before paint()
m_layout->addWidget(m_overviewWidget, 1);
setWidget(page);
}
void OverviewDockerDock::setCanvas(KoCanvasBase * canvas)
{
if(m_canvas == canvas)
return;
- setEnabled(canvas != 0);
+ setEnabled(canvas != nullptr);
if (m_canvas) {
m_canvas->disconnectCanvasObserver(this);
m_canvas->image()->disconnect(this);
}
if (m_zoomSlider) {
m_layout->removeWidget(m_zoomSlider);
delete m_zoomSlider;
- m_zoomSlider = 0;
+ m_zoomSlider = nullptr;
+ }
+
+ if (m_rotateSlider) {
+ m_horizontalLayout->removeWidget(m_rotateSlider);
+ delete m_rotateSlider;
+ m_rotateSlider = nullptr;
+ }
+
+ if (m_mirrorCanvas) {
+ m_horizontalLayout->removeWidget(m_mirrorCanvas);
+ delete m_mirrorCanvas;
+ m_mirrorCanvas = nullptr;
}
+ m_layout->removeItem(m_horizontalLayout);
+
m_canvas = dynamic_cast<KisCanvas2*>(canvas);
m_overviewWidget->setCanvas(canvas);
if (m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->zoomController() && m_canvas->viewManager()->zoomController()->zoomAction()) {
m_zoomSlider = m_canvas->viewManager()->zoomController()->zoomAction()->createWidget(m_canvas->imageView()->KisView::statusBar());
m_layout->addWidget(m_zoomSlider);
+
+ m_rotateSlider = new KisDoubleSliderSpinBox();
+ m_rotateSlider->setRange(-180, 180, 2);
+ m_rotateSlider->setValue(m_canvas->rotationAngle());
+ m_rotateSlider->setPrefix(i18n("Rotation: "));
+ m_rotateSlider->setSuffix("°");
+ connect(m_rotateSlider, SIGNAL(valueChanged(qreal)), this, SLOT(rotateCanvasView(qreal)), Qt::UniqueConnection);
+ m_mirrorCanvas = new QToolButton();
+ QList<QAction *> actions = m_canvas->viewManager()->actionCollection()->actions();
+ Q_FOREACH(QAction* action, actions) {
+ if (action->objectName()=="mirror_canvas") {
+ m_mirrorCanvas->setDefaultAction(action);
+ }
+ }
+ m_horizontalLayout->addWidget(m_mirrorCanvas);
+ m_horizontalLayout->addWidget(m_rotateSlider);
+ m_layout->addLayout(m_horizontalLayout);
}
}
void OverviewDockerDock::unsetCanvas()
{
setEnabled(false);
- m_canvas = 0;
+ m_canvas = nullptr;
m_overviewWidget->unsetCanvas();
}
+void OverviewDockerDock::rotateCanvasView(qreal rotation)
+{
+ KisCanvasController *canvasController =
+ dynamic_cast<KisCanvasController*>(m_canvas->viewManager()->canvasBase()->canvasController());
+ if (canvasController) {
+ canvasController->rotateCanvas(rotation-m_canvas->rotationAngle());
+ }
+}
+
+void OverviewDockerDock::updateSlider()
+{
+ qreal rotation = m_canvas->rotationAngle();
+ if (rotation > 180) {
+ rotation = rotation - 360;
+ } else if (rotation < -180) {
+ rotation = rotation + 360;
+ }
+ if (m_rotateSlider->value() != rotation) {
+ m_rotateSlider->setValue(rotation);
+ }
+}
+
diff --git a/plugins/dockers/overview/overviewdocker_dock.h b/plugins/dockers/overview/overviewdocker_dock.h
index d9995751bc..0270b04478 100644
--- a/plugins/dockers/overview/overviewdocker_dock.h
+++ b/plugins/dockers/overview/overviewdocker_dock.h
@@ -1,46 +1,56 @@
/*
* Copyright (c) 2009 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _OVERVIEW_DOCK_H_
#define _OVERVIEW_DOCK_H_
#include <QPointer>
#include <QDockWidget>
+#include <kis_slider_spin_box.h>
#include <KoCanvasObserverBase.h>
#include <kis_canvas2.h>
class QVBoxLayout;
+class QHBoxLayout;
+class QToolButton;
class OverviewWidget;
class OverviewDockerDock : public QDockWidget, public KoCanvasObserverBase {
Q_OBJECT
public:
OverviewDockerDock();
QString observerName() override { return "OverviewDockerDock"; }
void setCanvas(KoCanvasBase *canvas) override;
void unsetCanvas() override;
+public Q_SLOTS:
+ void rotateCanvasView(qreal rotation);
+ void updateSlider();
+
private:
QVBoxLayout *m_layout;
+ QHBoxLayout *m_horizontalLayout;
OverviewWidget *m_overviewWidget;
QWidget *m_zoomSlider;
+ KisDoubleSliderSpinBox *m_rotateSlider;
+ QToolButton *m_mirrorCanvas;
QPointer<KisCanvas2> m_canvas;
};
#endif
diff --git a/plugins/dockers/overview/overviewwidget.cc b/plugins/dockers/overview/overviewwidget.cc
index 8ba6d6f4e2..ac662c721a 100644
--- a/plugins/dockers/overview/overviewwidget.cc
+++ b/plugins/dockers/overview/overviewwidget.cc
@@ -1,373 +1,375 @@
/*
* Copyright (c) 2009 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2014 Sven Langkamp <sven.langkamp@gmail.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "overviewwidget.h"
#include <QMouseEvent>
#include <QPainter>
#include <QCursor>
#include <QMutex>
#include <KoCanvasController.h>
#include <KoZoomController.h>
#include <kis_canvas2.h>
#include <KisViewManager.h>
#include <kis_image.h>
#include <kis_paint_device.h>
#include <kis_signal_compressor.h>
#include <kis_config.h>
#include "kis_idle_watcher.h"
#include "krita_utils.h"
#include "kis_painter.h"
#include <KoUpdater.h>
#include "kis_transform_worker.h"
#include "kis_filter_strategy.h"
#include <KoColorSpaceRegistry.h>
#include <QApplication>
const qreal oversample = 2.;
const int thumbnailTileDim = 128;
struct OverviewThumbnailStrokeStrategy::Private {
class ProcessData : public KisStrokeJobData
{
public:
ProcessData(KisPaintDeviceSP _dev, KisPaintDeviceSP _thumbDev, const QSize& _thumbnailSize, const QRect &_rect)
: KisStrokeJobData(CONCURRENT),
dev(_dev), thumbDev(_thumbDev), thumbnailSize(_thumbnailSize), tileRect(_rect)
{}
KisPaintDeviceSP dev;
KisPaintDeviceSP thumbDev;
QSize thumbnailSize;
QRect tileRect;
};
class FinishProcessing : public KisStrokeJobData
{
public:
- FinishProcessing(KisPaintDeviceSP _thumbDev)
+ FinishProcessing(KisPaintDeviceSP _thumbDev, const QSize& _thumbnailSize)
: KisStrokeJobData(SEQUENTIAL),
- thumbDev(_thumbDev)
+ thumbDev(_thumbDev), thumbnailSize(_thumbnailSize)
{}
KisPaintDeviceSP thumbDev;
+ QSize thumbnailSize;
};
};
OverviewWidget::OverviewWidget(QWidget * parent)
: QWidget(parent)
, m_canvas(0)
, m_dragging(false)
, m_imageIdleWatcher(250)
{
setMouseTracking(true);
KisConfig cfg(true);
m_outlineColor = qApp->palette().color(QPalette::Highlight);
}
OverviewWidget::~OverviewWidget()
{
}
void OverviewWidget::setCanvas(KoCanvasBase * canvas)
{
if (m_canvas) {
m_canvas->image()->disconnect(this);
}
m_canvas = dynamic_cast<KisCanvas2*>(canvas);
if (m_canvas) {
m_imageIdleWatcher.setTrackedImage(m_canvas->image());
connect(&m_imageIdleWatcher, &KisIdleWatcher::startedIdleMode, this, &OverviewWidget::generateThumbnail);
connect(m_canvas->image(), SIGNAL(sigImageUpdated(QRect)),SLOT(startUpdateCanvasProjection()));
connect(m_canvas->image(), SIGNAL(sigSizeChanged(QPointF,QPointF)),SLOT(startUpdateCanvasProjection()));
connect(m_canvas->canvasController()->proxyObject, SIGNAL(canvasOffsetXChanged(int)), this, SLOT(update()), Qt::UniqueConnection);
generateThumbnail();
}
}
QSize OverviewWidget::recalculatePreviewSize()
{
QSize imageSize(m_canvas->image()->bounds().size());
const qreal hScale = 1.0 * this->width() / imageSize.width();
const qreal vScale = 1.0 * this->height() / imageSize.height();
m_previewScale = qMin(hScale, vScale);
return imageSize * m_previewScale;
}
QPointF OverviewWidget::previewOrigin()
{
const QSize previewSize = recalculatePreviewSize();
return QPointF((width() - previewSize.width()) / 2.0f, (height() - previewSize.height()) / 2.0f);
}
QPolygonF OverviewWidget::previewPolygon()
{
if (m_canvas) {
const QRectF &canvasRect = QRectF(m_canvas->canvasWidget()->rect());
return canvasToPreviewTransform().map(canvasRect);
}
return QPolygonF();
}
QTransform OverviewWidget::previewToCanvasTransform()
{
QTransform previewToImage =
QTransform::fromTranslate(-this->width() / 2.0, -this->height() / 2.0) *
QTransform::fromScale(1.0 / m_previewScale, 1.0 / m_previewScale) *
QTransform::fromTranslate(m_canvas->image()->width() / 2.0, m_canvas->image()->height() / 2.0);
return previewToImage * m_canvas->coordinatesConverter()->imageToWidgetTransform();
}
QTransform OverviewWidget::canvasToPreviewTransform()
{
return previewToCanvasTransform().inverted();
}
void OverviewWidget::startUpdateCanvasProjection()
{
m_imageIdleWatcher.startCountdown();
}
void OverviewWidget::showEvent(QShowEvent *event)
{
Q_UNUSED(event);
m_imageIdleWatcher.startCountdown();
}
void OverviewWidget::resizeEvent(QResizeEvent *event)
{
Q_UNUSED(event);
if (m_canvas) {
if (!m_oldPixmap.isNull()) {
QSize newSize = recalculatePreviewSize();
m_pixmap = m_oldPixmap.scaled(newSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
m_imageIdleWatcher.startCountdown();
}
}
void OverviewWidget::mousePressEvent(QMouseEvent* event)
{
if (m_canvas) {
QPointF previewPos = event->pos();
if (!previewPolygon().containsPoint(previewPos, Qt::WindingFill)) {
const QRect& canvasRect = m_canvas->canvasWidget()->rect();
const QPointF newCanvasPos = previewToCanvasTransform().map(previewPos) -
QPointF(canvasRect.width() / 2.0f, canvasRect.height() / 2.0f);
m_canvas->canvasController()->pan(newCanvasPos.toPoint());
}
m_lastPos = previewPos;
m_dragging = true;
}
event->accept();
update();
}
void OverviewWidget::mouseMoveEvent(QMouseEvent* event)
{
if (m_dragging) {
QPointF previewPos = event->pos();
const QPointF lastCanvasPos = previewToCanvasTransform().map(m_lastPos);
const QPointF newCanvasPos = previewToCanvasTransform().map(event->pos());
QPointF diff = newCanvasPos - lastCanvasPos;
m_canvas->canvasController()->pan(diff.toPoint());
m_lastPos = previewPos;
}
event->accept();
}
void OverviewWidget::mouseReleaseEvent(QMouseEvent* event)
{
m_dragging = false;
event->accept();
update();
}
void OverviewWidget::wheelEvent(QWheelEvent* event)
{
float delta = event->delta();
if (delta > 0) {
m_canvas->viewManager()->zoomController()->zoomAction()->zoomIn();
} else {
m_canvas->viewManager()->zoomController()->zoomAction()->zoomOut();
}
}
void OverviewWidget::generateThumbnail()
{
if (isVisible()) {
QMutexLocker locker(&mutex);
if (m_canvas) {
QSize previewSize = recalculatePreviewSize();
if(previewSize.isValid()){
KisImageSP image = m_canvas->image();
if (!strokeId.isNull()) {
image->cancelStroke(strokeId);
strokeId.clear();
}
OverviewThumbnailStrokeStrategy* stroke = new OverviewThumbnailStrokeStrategy(image);
connect(stroke, SIGNAL(thumbnailUpdated(QImage)), this, SLOT(updateThumbnail(QImage)));
strokeId = image->startStroke(stroke);
KisPaintDeviceSP dev = image->projection();
KisPaintDeviceSP thumbDev = new KisPaintDevice(dev->colorSpace());
//creating a special stroke that computes thumbnail image in small chunks that can be quickly interrupted
//if user starts painting
QList<KisStrokeJobData*> jobs = OverviewThumbnailStrokeStrategy::createJobsData(dev, image->bounds(), thumbDev, previewSize);
Q_FOREACH (KisStrokeJobData *jd, jobs) {
image->addJob(strokeId, jd);
}
image->endStroke(strokeId);
}
}
}
}
void OverviewWidget::updateThumbnail(QImage pixmap)
{
m_pixmap = QPixmap::fromImage(pixmap);
m_oldPixmap = m_pixmap.copy();
update();
}
void OverviewWidget::paintEvent(QPaintEvent* event)
{
QWidget::paintEvent(event);
if (m_canvas) {
QPainter p(this);
const QSize previewSize = recalculatePreviewSize();
const QRectF previewRect = QRectF(previewOrigin(), previewSize);
p.drawPixmap(previewRect.toRect(), m_pixmap);
QRect r = rect();
QPolygonF outline;
outline << r.topLeft() << r.topRight() << r.bottomRight() << r.bottomLeft();
QPen pen;
pen.setColor(m_outlineColor);
pen.setStyle(Qt::DashLine);
p.setPen(pen);
p.drawPolygon(outline.intersected(previewPolygon()));
pen.setStyle(Qt::SolidLine);
p.setPen(pen);
p.drawPolygon(previewPolygon());
}
}
OverviewThumbnailStrokeStrategy::OverviewThumbnailStrokeStrategy(KisImageWSP image)
: KisSimpleStrokeStrategy("OverviewThumbnail"), m_image(image)
{
enableJob(KisSimpleStrokeStrategy::JOB_INIT, true, KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE);
enableJob(KisSimpleStrokeStrategy::JOB_DOSTROKE);
//enableJob(KisSimpleStrokeStrategy::JOB_FINISH);
enableJob(KisSimpleStrokeStrategy::JOB_CANCEL, true, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE);
setRequestsOtherStrokesToEnd(false);
setClearsRedoOnStart(false);
setCanForgetAboutMe(true);
}
QList<KisStrokeJobData *> OverviewThumbnailStrokeStrategy::createJobsData(KisPaintDeviceSP dev, const QRect& imageRect, KisPaintDeviceSP thumbDev, const QSize& thumbnailSize)
{
QSize thumbnailOversampledSize = oversample * thumbnailSize;
if ((thumbnailOversampledSize.width() > imageRect.width()) || (thumbnailOversampledSize.height() > imageRect.height())) {
thumbnailOversampledSize.scale(imageRect.size(), Qt::KeepAspectRatio);
}
QVector<QRect> tileRects = KritaUtils::splitRectIntoPatches(QRect(QPoint(0, 0), thumbnailOversampledSize), QSize(thumbnailTileDim, thumbnailTileDim));
QList<KisStrokeJobData*> jobsData;
Q_FOREACH (const QRect &tileRectangle, tileRects) {
jobsData << new OverviewThumbnailStrokeStrategy::Private::ProcessData(dev, thumbDev, thumbnailOversampledSize, tileRectangle);
}
- jobsData << new OverviewThumbnailStrokeStrategy::Private::FinishProcessing(thumbDev);
+ jobsData << new OverviewThumbnailStrokeStrategy::Private::FinishProcessing(thumbDev, thumbnailSize);
return jobsData;
}
OverviewThumbnailStrokeStrategy::~OverviewThumbnailStrokeStrategy()
{
}
void OverviewThumbnailStrokeStrategy::initStrokeCallback()
{
}
void OverviewThumbnailStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
{
Private::ProcessData *d_pd = dynamic_cast<Private::ProcessData*>(data);
if (d_pd) {
//we aren't going to use oversample capability of createThumbnailDevice because it recomputes exact bounds for each small patch, which is
//slow. We'll handle scaling separately.
KisPaintDeviceSP thumbnailTile = d_pd->dev->createThumbnailDeviceOversampled(d_pd->thumbnailSize.width(), d_pd->thumbnailSize.height(), 1, m_image->bounds(), d_pd->tileRect);
{
QMutexLocker locker(&m_thumbnailMergeMutex);
KisPainter gc(d_pd->thumbDev);
gc.bitBlt(QPoint(d_pd->tileRect.x(), d_pd->tileRect.y()), thumbnailTile, d_pd->tileRect);
}
return;
}
Private::FinishProcessing *d_fp = dynamic_cast<Private::FinishProcessing*>(data);
if (d_fp) {
QImage overviewImage;
KoDummyUpdater updater;
KisTransformWorker worker(d_fp->thumbDev, 1 / oversample, 1 / oversample, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
&updater, KisFilterStrategyRegistry::instance()->value("Bilinear"));
worker.run();
- overviewImage = d_fp->thumbDev->convertToQImage(KoColorSpaceRegistry::instance()->rgb8()->profile());
+ overviewImage = d_fp->thumbDev->convertToQImage(KoColorSpaceRegistry::instance()->rgb8()->profile(),
+ QRect(QPoint(0,0), d_fp->thumbnailSize));
emit thumbnailUpdated(overviewImage);
return;
}
}
void OverviewThumbnailStrokeStrategy::finishStrokeCallback()
{
}
void OverviewThumbnailStrokeStrategy::cancelStrokeCallback()
{
}
diff --git a/plugins/dockers/palettedocker/palettedocker_dock.cpp b/plugins/dockers/palettedocker/palettedocker_dock.cpp
index 386c23b24c..5b9c30e75f 100644
--- a/plugins/dockers/palettedocker/palettedocker_dock.cpp
+++ b/plugins/dockers/palettedocker/palettedocker_dock.cpp
@@ -1,371 +1,383 @@
/*
* Copyright (c) 2013 Sven Langkamp <sven.langkamp@gmail.com>
*
* 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 "palettedocker_dock.h"
#include <QPainter>
#include <QGridLayout>
#include <QTableView>
#include <QHeaderView>
#include <QWheelEvent>
#include <QColorDialog>
#include <QCompleter>
#include <QComboBox>
#include <QAction>
#include <QMenu>
#include <QCheckBox>
#include <QFormLayout>
#include <QLineEdit>
#include <squeezedcombobox.h>
#include <klocalizedstring.h>
#include <KoResourceServerProvider.h>
#include <KoColorSpaceRegistry.h>
#include <KoFileDialog.h>
#include <kis_icon.h>
#include <kis_config.h>
#include <kis_node_manager.h>
#include <kis_workspace_resource.h>
#include <kis_canvas_resource_provider.h>
#include <KisMainWindow.h>
#include <KisViewManager.h>
#include <kis_display_color_converter.h>
#include <kis_canvas2.h>
#include <KoDialog.h>
#include <kis_color_button.h>
#include <KisDocument.h>
#include <KisPart.h>
#include <KisPaletteModel.h>
#include <KisPaletteDelegate.h>
#include <kis_palette_view.h>
#include <KisPaletteListWidget.h>
#include <KisPaletteEditor.h>
#include <dialogs/KisDlgPaletteEditor.h>
#include "ui_wdgpalettedock.h"
PaletteDockerDock::PaletteDockerDock( )
: QDockWidget(i18n("Palette"))
, m_ui(new Ui_WdgPaletteDock())
, m_model(new KisPaletteModel(this))
, m_paletteChooser(new KisPaletteListWidget(this))
, m_view(Q_NULLPTR)
, m_resourceProvider(Q_NULLPTR)
, m_rServer(KoResourceServerProvider::instance()->paletteServer())
, m_activeDocument(Q_NULLPTR)
, m_paletteEditor(new KisPaletteEditor)
, m_actAdd(new QAction(KisIconUtils::loadIcon("list-add"), i18n("Add a color")))
, m_actRemove(new QAction(KisIconUtils::loadIcon("edit-delete"), i18n("Delete color")))
, m_actModify(new QAction(KisIconUtils::loadIcon("edit-rename"), i18n("Modify this spot")))
, m_actEditPalette(new QAction(KisIconUtils::loadIcon("groupLayer"), i18n("Edit this palette")))
+ , m_colorSelfUpdate(false)
{
QWidget *mainWidget = new QWidget(this);
setWidget(mainWidget);
m_ui->setupUi(mainWidget);
m_ui->bnAdd->setDefaultAction(m_actAdd.data());
m_ui->bnRemove->setDefaultAction(m_actRemove.data());
m_ui->bnRename->setDefaultAction(m_actModify.data());
m_ui->bnEditPalette->setDefaultAction(m_actEditPalette.data());
// to make sure their icons have the same size
m_ui->bnRemove->setIconSize(QSize(16, 16));
m_ui->bnRename->setIconSize(QSize(16, 16));
m_ui->bnAdd->setIconSize(QSize(16, 16));
m_ui->bnEditPalette->setIconSize(QSize(16, 16));
m_ui->paletteView->setPaletteModel(m_model);
m_ui->paletteView->setAllowModification(true);
m_ui->cmbNameList->setCompanionView(m_ui->paletteView);
m_paletteEditor->setPaletteModel(m_model);
connect(m_actAdd.data(), SIGNAL(triggered()), SLOT(slotAddColor()));
connect(m_actRemove.data(), SIGNAL(triggered()), SLOT(slotRemoveColor()));
connect(m_actModify.data(), SIGNAL(triggered()), SLOT(slotEditEntry()));
connect(m_actEditPalette.data(), SIGNAL(triggered()), SLOT(slotEditPalette()));
connect(m_ui->paletteView, SIGNAL(sigIndexSelected(QModelIndex)),
SLOT(slotPaletteIndexSelected(QModelIndex)));
connect(m_ui->paletteView, SIGNAL(clicked(QModelIndex)),
SLOT(slotPaletteIndexClicked(QModelIndex)));
connect(m_ui->paletteView, SIGNAL(doubleClicked(QModelIndex)),
SLOT(slotPaletteIndexDoubleClicked(QModelIndex)));
m_viewContextMenu.addAction(m_actModify.data());
m_viewContextMenu.addAction(m_actRemove.data());
connect(m_ui->paletteView, SIGNAL(pressed(QModelIndex)), SLOT(slotContextMenu(QModelIndex)));
m_paletteChooser->setAllowModification(true);
connect(m_paletteChooser, SIGNAL(sigPaletteSelected(KoColorSet*)), SLOT(slotSetColorSet(KoColorSet*)));
connect(m_paletteChooser, SIGNAL(sigAddPalette()), SLOT(slotAddPalette()));
connect(m_paletteChooser, SIGNAL(sigImportPalette()), SLOT(slotImportPalette()));
connect(m_paletteChooser, SIGNAL(sigRemovePalette(KoColorSet*)), SLOT(slotRemovePalette(KoColorSet*)));
connect(m_paletteChooser, SIGNAL(sigExportPalette(KoColorSet*)), SLOT(slotExportPalette(KoColorSet*)));
m_ui->bnColorSets->setIcon(KisIconUtils::loadIcon("hi16-palette_library"));
m_ui->bnColorSets->setToolTip(i18n("Choose palette"));
m_ui->bnColorSets->setPopupWidget(m_paletteChooser);
KisConfig cfg(true);
QString defaultPaletteName = cfg.defaultPalette();
KoColorSet* defaultPalette = m_rServer->resourceByName(defaultPaletteName);
if (defaultPalette) {
slotSetColorSet(defaultPalette);
} else {
m_ui->bnAdd->setEnabled(false);
m_ui->bnRename->setEnabled(false);
m_ui->bnRemove->setEnabled(false);
m_ui->bnEditPalette->setEnabled(false);
m_ui->paletteView->setAllowModification(false);
}
}
PaletteDockerDock::~PaletteDockerDock()
{ }
void PaletteDockerDock::setViewManager(KisViewManager* kisview)
{
m_view = kisview;
m_resourceProvider = kisview->resourceProvider();
connect(m_resourceProvider, SIGNAL(sigSavingWorkspace(KisWorkspaceResource*)),
SLOT(saveToWorkspace(KisWorkspaceResource*)));
connect(m_resourceProvider, SIGNAL(sigLoadingWorkspace(KisWorkspaceResource*)),
SLOT(loadFromWorkspace(KisWorkspaceResource*)));
connect(m_resourceProvider, SIGNAL(sigFGColorChanged(KoColor)),
- m_ui->paletteView, SLOT(slotFGColorChanged(KoColor)));
+ this, SLOT(slotFGColorResourceChanged(KoColor)));
kisview->nodeManager()->disconnect(m_model);
}
void PaletteDockerDock::slotContextMenu(const QModelIndex &)
{
if (QApplication::mouseButtons() == Qt::RightButton) {
m_viewContextMenu.exec(QCursor::pos());
}
}
void PaletteDockerDock::slotAddPalette()
{
m_paletteEditor->addPalette();
}
void PaletteDockerDock::slotRemovePalette(KoColorSet *cs)
{
m_paletteEditor->removePalette(cs);
}
void PaletteDockerDock::slotImportPalette()
{
m_paletteEditor->importPalette();
}
void PaletteDockerDock::slotExportPalette(KoColorSet *palette)
{
KoFileDialog dialog(this, KoFileDialog::SaveFile, "Save Palette");
dialog.setDefaultDir(palette->filename());
dialog.setMimeTypeFilters(QStringList() << "krita/x-colorset");
QString newPath;
bool isStandAlone = palette->isGlobal();
QString oriPath = palette->filename();
if ((newPath = dialog.filename()).isEmpty()) { return; }
palette->setFilename(newPath);
palette->setIsGlobal(true);
palette->save();
palette->setFilename(oriPath);
palette->setIsGlobal(isStandAlone);
}
void PaletteDockerDock::setCanvas(KoCanvasBase *canvas)
{
setEnabled(canvas != Q_NULLPTR);
if (canvas) {
KisCanvas2 *cv = qobject_cast<KisCanvas2*>(canvas);
m_ui->paletteView->setDisplayRenderer(cv->displayColorConverter()->displayRendererInterface());
}
if (m_activeDocument) {
for (KoColorSet * &cs : m_activeDocument->paletteList()) {
KoColorSet *tmpAddr = cs;
cs = new KoColorSet(*cs);
m_rServer->removeResourceFromServer(tmpAddr);
}
}
if (m_view && m_view->document()) {
m_activeDocument = m_view->document();
m_paletteEditor->setView(m_view);
for (KoColorSet *cs : m_activeDocument->paletteList()) {
m_rServer->addResource(cs);
}
}
if (!m_currentColorSet) {
slotSetColorSet(Q_NULLPTR);
}
}
void PaletteDockerDock::unsetCanvas()
{
setEnabled(false);
m_ui->paletteView->setDisplayRenderer(Q_NULLPTR);
m_paletteEditor->setView(Q_NULLPTR);
for (KoResource *r : m_rServer->resources()) {
KoColorSet *c = static_cast<KoColorSet*>(r);
if (!c->isGlobal()) {
m_rServer->removeResourceFromServer(c);
}
}
if (!m_currentColorSet) {
slotSetColorSet(Q_NULLPTR);
}
}
void PaletteDockerDock::slotSetColorSet(KoColorSet* colorSet)
{
if (colorSet && colorSet->isEditable()) {
m_ui->bnAdd->setEnabled(true);
m_ui->bnRename->setEnabled(true);
m_ui->bnRemove->setEnabled(true);
m_ui->bnEditPalette->setEnabled(true);
m_ui->paletteView->setAllowModification(true);
} else {
m_ui->bnAdd->setEnabled(false);
m_ui->bnRename->setEnabled(false);
m_ui->bnRemove->setEnabled(false);
m_ui->bnEditPalette->setEnabled(false);
m_ui->paletteView->setAllowModification(false);
}
m_currentColorSet = colorSet;
m_model->setPalette(colorSet);
if (colorSet) {
KisConfig cfg(true);
cfg.setDefaultPalette(colorSet->name());
m_ui->lblPaletteName->setTextElideMode(Qt::ElideLeft);
m_ui->lblPaletteName->setText(colorSet->name());
} else {
m_ui->lblPaletteName->setText("");
}
}
void PaletteDockerDock::slotEditPalette()
{
KisDlgPaletteEditor dlg;
if (!m_currentColorSet) { return; }
dlg.setPaletteModel(m_model);
dlg.setView(m_view);
if (dlg.exec() != QDialog::Accepted){ return; }
slotSetColorSet(m_currentColorSet); // update GUI
}
void PaletteDockerDock::slotAddColor()
{
if (m_resourceProvider) {
m_paletteEditor->addEntry(m_resourceProvider->fgColor());
}
}
void PaletteDockerDock::slotRemoveColor()
{
QModelIndex index = m_ui->paletteView->currentIndex();
if (!index.isValid()) {
return;
}
m_paletteEditor->removeEntry(index);
m_ui->bnRemove->setEnabled(false);
}
void PaletteDockerDock::setFGColorByPalette(const KisSwatch &entry)
{
if (m_resourceProvider) {
+ m_colorSelfUpdate = true;
m_resourceProvider->setFGColor(entry.color());
+ m_colorSelfUpdate = false;
}
}
void PaletteDockerDock::saveToWorkspace(KisWorkspaceResource* workspace)
{
if (!m_currentColorSet.isNull()) {
workspace->setProperty("palette", m_currentColorSet->name());
}
}
void PaletteDockerDock::loadFromWorkspace(KisWorkspaceResource* workspace)
{
if (workspace->hasProperty("palette")) {
KoResourceServer<KoColorSet>* rServer = KoResourceServerProvider::instance()->paletteServer();
KoColorSet* colorSet = rServer->resourceByName(workspace->getString("palette"));
if (colorSet) {
slotSetColorSet(colorSet);
}
}
}
+void PaletteDockerDock::slotFGColorResourceChanged(const KoColor &color)
+{
+ if (!m_colorSelfUpdate) {
+ m_ui->paletteView->slotFGColorResourceChanged(color);
+ }
+}
+
void PaletteDockerDock::slotPaletteIndexSelected(const QModelIndex &index)
{
bool occupied = qvariant_cast<bool>(index.data(KisPaletteModel::CheckSlotRole));
if (occupied) {
if (!qvariant_cast<bool>(index.data(KisPaletteModel::IsGroupNameRole))) {
m_ui->bnRemove->setEnabled(true);
KisSwatch entry = m_model->getEntry(index);
setFGColorByPalette(entry);
}
}
if (!m_currentColorSet->isEditable()) { return; }
m_ui->bnRemove->setEnabled(occupied);
}
void PaletteDockerDock::slotPaletteIndexClicked(const QModelIndex &index)
{
if (!(qvariant_cast<bool>(index.data(KisPaletteModel::CheckSlotRole)))) {
setEntryByForeground(index);
}
}
void PaletteDockerDock::slotPaletteIndexDoubleClicked(const QModelIndex &index)
{
m_paletteEditor->modifyEntry(index);
}
void PaletteDockerDock::setEntryByForeground(const QModelIndex &index)
{
m_paletteEditor->setEntry(m_resourceProvider->fgColor(), index);
if (m_currentColorSet->isEditable()) {
m_ui->bnRemove->setEnabled(true);
}
}
void PaletteDockerDock::slotEditEntry()
{
QModelIndex index = m_ui->paletteView->currentIndex();
if (!index.isValid()) {
return;
}
m_paletteEditor->modifyEntry(index);
}
void PaletteDockerDock::slotNameListSelection(const KoColor &color)
{
+ m_colorSelfUpdate = true;
m_resourceProvider->setFGColor(color);
+ m_colorSelfUpdate = false;
}
diff --git a/plugins/dockers/palettedocker/palettedocker_dock.h b/plugins/dockers/palettedocker/palettedocker_dock.h
index c56fe112c4..3776d58bbe 100644
--- a/plugins/dockers/palettedocker/palettedocker_dock.h
+++ b/plugins/dockers/palettedocker/palettedocker_dock.h
@@ -1,111 +1,115 @@
/*
* Copyright (c) 2013 Sven Langkamp <sven.langkamp@gmail.com>
*
* 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 PALETTEDOCKER_DOCK_H
#define PALETTEDOCKER_DOCK_H
#include <QDockWidget>
#include <QModelIndex>
#include <QScopedPointer>
#include <QVector>
#include <QPointer>
#include <QPair>
#include <QAction>
#include <QMenu>
#include <KoResourceServerObserver.h>
#include <KoResourceServer.h>
#include <resources/KoColorSet.h>
#include <kis_canvas2.h>
#include <kis_mainwindow_observer.h>
#include <KisView.h>
class KisViewManager;
class KisCanvasResourceProvider;
class KisWorkspaceResource;
class KisPaletteListWidget;
class KisPaletteModel;
class KisPaletteEditor;
class Ui_WdgPaletteDock;
class PaletteDockerDock : public QDockWidget, public KisMainwindowObserver
{
Q_OBJECT
public:
PaletteDockerDock();
~PaletteDockerDock() override;
public: // QDockWidget
void setCanvas(KoCanvasBase *canvas) override;
void unsetCanvas() override;
public: // KisMainWindowObserver
void setViewManager(KisViewManager* kisview) override;
private Q_SLOTS:
void slotContextMenu(const QModelIndex &);
void slotAddPalette();
void slotRemovePalette(KoColorSet *);
void slotImportPalette();
void slotExportPalette(KoColorSet *);
void slotAddColor();
void slotRemoveColor();
void slotEditEntry();
void slotEditPalette();
void slotPaletteIndexSelected(const QModelIndex &index);
void slotPaletteIndexClicked(const QModelIndex &index);
void slotPaletteIndexDoubleClicked(const QModelIndex &index);
void slotNameListSelection(const KoColor &color);
void slotSetColorSet(KoColorSet* colorSet);
void saveToWorkspace(KisWorkspaceResource* workspace);
void loadFromWorkspace(KisWorkspaceResource* workspace);
+ void slotFGColorResourceChanged(const KoColor& color);
+
private:
void setEntryByForeground(const QModelIndex &index);
void setFGColorByPalette(const KisSwatch &entry);
private /* member variables */:
QScopedPointer<Ui_WdgPaletteDock> m_ui;
KisPaletteModel *m_model;
KisPaletteListWidget *m_paletteChooser;
QPointer<KisViewManager> m_view;
KisCanvasResourceProvider *m_resourceProvider;
KoResourceServer<KoColorSet> * const m_rServer;
QPointer<KisDocument> m_activeDocument;
QPointer<KoColorSet> m_currentColorSet;
QScopedPointer<KisPaletteEditor> m_paletteEditor;
QScopedPointer<QAction> m_actAdd;
QScopedPointer<QAction> m_actRemove;
QScopedPointer<QAction> m_actModify;
QScopedPointer<QAction> m_actEditPalette;
QMenu m_viewContextMenu;
+
+ bool m_colorSelfUpdate;
};
#endif
diff --git a/plugins/dockers/specificcolorselector/kis_specific_color_selector_widget.cc b/plugins/dockers/specificcolorselector/kis_specific_color_selector_widget.cc
index 9e6c056a1c..4aacf59369 100644
--- a/plugins/dockers/specificcolorselector/kis_specific_color_selector_widget.cc
+++ b/plugins/dockers/specificcolorselector/kis_specific_color_selector_widget.cc
@@ -1,269 +1,269 @@
/*
* Copyright (c) 2008 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2015 Moritz Molch <kde@moritzmolch.de>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_specific_color_selector_widget.h"
#include <QLabel>
#include <QVBoxLayout>
#include <QCheckBox>
#include <QSpacerItem>
#include <klocalizedstring.h>
#include <kconfiggroup.h>
#include <kconfig.h>
#include <ksharedconfig.h>
#include <KoChannelInfo.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorModelStandardIds.h>
#include <kis_color_input.h>
#include <KoColorProfile.h>
#include <kis_debug.h>
#include <kis_color_space_selector.h>
#include <kis_signal_compressor.h>
#include <kis_display_color_converter.h>
#include <kis_popup_button.h>
#include <kis_icon_utils.h>
#include "ui_wdgSpecificColorSelectorWidget.h"
KisSpecificColorSelectorWidget::KisSpecificColorSelectorWidget(QWidget* parent)
: QWidget(parent)
, m_colorSpace(0)
, m_spacer(0)
, m_updateCompressor(new KisSignalCompressor(10, KisSignalCompressor::POSTPONE, this))
, m_customColorSpaceSelected(false)
, m_displayConverter(0)
{
m_ui = new Ui_wdgSpecificColorSelectorWidget();
m_ui->setupUi(this);
m_updateAllowed = true;
connect(m_updateCompressor, SIGNAL(timeout()), SLOT(updateTimeout()));
m_colorspaceSelector = new KisColorSpaceSelector(this);
connect(m_colorspaceSelector, SIGNAL(colorSpaceChanged(const KoColorSpace*)), this, SLOT(setCustomColorSpace(const KoColorSpace*)));
m_ui->colorspacePopupButton->setPopupWidget(m_colorspaceSelector);
connect(m_ui->chkUsePercentage, SIGNAL(toggled(bool)), this, SLOT(onChkUsePercentageChanged(bool)));
KConfigGroup cfg = KSharedConfig::openConfig()->group(QString());
m_ui->chkUsePercentage->setChecked(cfg.readEntry("SpecificColorSelector/UsePercentage", false));
m_ui->chkUsePercentage->setIcon(KisIconUtils::loadIcon("ratio"));
m_colorspaceSelector->showColorBrowserButton(false);
m_spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding);
m_ui->slidersLayout->addItem(m_spacer);
}
KisSpecificColorSelectorWidget::~KisSpecificColorSelectorWidget()
{
KConfigGroup cfg = KSharedConfig::openConfig()->group(QString());
cfg.writeEntry("SpecificColorSelector/UsePercentage", m_ui->chkUsePercentage->isChecked());
}
bool KisSpecificColorSelectorWidget::customColorSpaceUsed()
{
return m_customColorSpaceSelected;
}
void KisSpecificColorSelectorWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
if (m_colorSpace) {
QString elidedColorspaceName = m_ui->colorspacePopupButton->fontMetrics().elidedText(
m_colorSpace->name(), Qt::ElideRight,
m_ui->colorspacePopupButton->width()
);
m_ui->colorspacePopupButton->setText(elidedColorspaceName);
}
}
void KisSpecificColorSelectorWidget::setDisplayConverter(KisDisplayColorConverter *displayConverter)
{
const bool needsForceUpdate = m_displayConverter != displayConverter;
m_displayConverter = displayConverter;
if (m_displayConverter) {
m_converterConnection.clear();
- m_converterConnection.addConnection(m_displayConverter, SIGNAL(displayConfigurationChanged()), this, SLOT(rereadCurrentColorSpace()));
+ m_converterConnection.addConnection(m_displayConverter, SIGNAL(displayConfigurationChanged()), this, SLOT(rereadCurrentColorSpace()), Qt::UniqueConnection);
}
rereadCurrentColorSpace(needsForceUpdate);
}
void KisSpecificColorSelectorWidget::rereadCurrentColorSpace(bool force)
{
if (m_displayConverter && !m_customColorSpaceSelected) {
m_colorSpace = m_displayConverter->paintingColorSpace();
}
setColorSpace(m_colorSpace, force);
setColor(m_color);
}
void KisSpecificColorSelectorWidget::setColorSpace(const KoColorSpace* cs, bool force)
{
Q_ASSERT(cs);
dbgPlugins << cs->id() << " " << cs->profile()->name();
if (*m_colorSpace == *cs && !force) {
Q_FOREACH (KisColorInput* input, m_inputs) {
input->update();
}
return;
}
if (cs->colorDepthId() == Integer8BitsColorDepthID || cs->colorDepthId() == Integer16BitsColorDepthID) {
m_ui->chkUsePercentage->setVisible(true);
} else {
m_ui->chkUsePercentage->setVisible(false);
}
m_colorSpace = KoColorSpaceRegistry::instance()->colorSpace(cs->colorModelId().id(), cs->colorDepthId().id(), cs->profile());
Q_ASSERT(m_colorSpace);
Q_ASSERT(*m_colorSpace == *cs);
QString elidedColorspaceName = m_ui->colorspacePopupButton->fontMetrics().elidedText(
m_colorSpace->name(), Qt::ElideRight,
m_ui->colorspacePopupButton->width()
);
m_ui->colorspacePopupButton->setText(elidedColorspaceName);
m_color = KoColor(m_color, m_colorSpace);
Q_FOREACH (KisColorInput* input, m_inputs) {
delete input;
}
m_inputs.clear();
m_ui->slidersLayout->removeItem(m_spacer);
QList<KoChannelInfo *> channels = KoChannelInfo::displayOrderSorted(m_colorSpace->channels());
KoColorDisplayRendererInterface *displayRenderer =
m_displayConverter ?
m_displayConverter->displayRendererInterface() :
KisDisplayColorConverter::dumbConverterInstance()->displayRendererInterface();
Q_FOREACH (KoChannelInfo* channel, channels) {
if (channel->channelType() == KoChannelInfo::COLOR) {
KisColorInput* input = 0;
switch (channel->channelValueType()) {
case KoChannelInfo::UINT8:
case KoChannelInfo::UINT16:
case KoChannelInfo::UINT32: {
input = new KisIntegerColorInput(this, channel, &m_color, displayRenderer, m_ui->chkUsePercentage->isChecked());
}
break;
case KoChannelInfo::FLOAT16:
case KoChannelInfo::FLOAT32: {
input = new KisFloatColorInput(this, channel, &m_color, displayRenderer);
}
break;
default:
Q_ASSERT(false);
input = 0;
}
if (input) {
connect(input, SIGNAL(updated()), this, SLOT(update()));
connect(this, SIGNAL(updated()), input, SLOT(update()));
m_inputs.append(input);
m_ui->slidersLayout->addWidget(input);
}
}
}
QList<QLabel*> labels;
int labelWidth = 0;
Q_FOREACH (KisColorInput* input, m_inputs) {
Q_FOREACH (QLabel* label, input->findChildren<QLabel*>()) {
labels.append(label);
labelWidth = qMax(labelWidth, label->sizeHint().width());
}
}
Q_FOREACH (QLabel *label, labels) {
label->setMinimumWidth(labelWidth);
}
bool allChannels8Bit = true;
Q_FOREACH (KoChannelInfo* channel, channels) {
if (channel->channelType() == KoChannelInfo::COLOR && channel->channelValueType() != KoChannelInfo::UINT8) {
allChannels8Bit = false;
}
}
if (allChannels8Bit) {
KisColorInput* input = new KisHexColorInput(this, &m_color, displayRenderer);
m_inputs.append(input);
m_ui->slidersLayout->addWidget(input);
connect(input, SIGNAL(updated()), this, SLOT(update()));
connect(this, SIGNAL(updated()), input, SLOT(update()));
}
m_ui->slidersLayout->addItem(m_spacer);
m_colorspaceSelector->blockSignals(true);
m_colorspaceSelector->setCurrentColorSpace(cs);
m_colorspaceSelector->blockSignals(false);
m_updateAllowed = false;
emit(updated());
m_updateAllowed = true;
}
void KisSpecificColorSelectorWidget::update()
{
if (m_updateAllowed) {
m_updateCompressor->start();
}
}
void KisSpecificColorSelectorWidget::setColor(const KoColor& c)
{
m_updateAllowed = false;
m_color.fromKoColor(c);
emit(updated());
m_updateAllowed = true;
}
void KisSpecificColorSelectorWidget::updateTimeout()
{
emit(colorChanged(m_color));
}
void KisSpecificColorSelectorWidget::setCustomColorSpace(const KoColorSpace *colorSpace)
{
m_customColorSpaceSelected = true;
setColorSpace(colorSpace);
setColor(m_color);
}
void KisSpecificColorSelectorWidget::onChkUsePercentageChanged(bool isChecked)
{
for (auto input: m_inputs) {
input->setPercentageWise(isChecked);
}
emit(updated());
}
diff --git a/plugins/extensions/animationrenderer/AnimationRenderer.cpp b/plugins/extensions/animationrenderer/AnimationRenderer.cpp
index 7556a9adcd..bd54aeb685 100644
--- a/plugins/extensions/animationrenderer/AnimationRenderer.cpp
+++ b/plugins/extensions/animationrenderer/AnimationRenderer.cpp
@@ -1,205 +1,222 @@
/*
* Copyright (c) 2016 Boudewijn Rempt <boud@valdyas.org>
*
* 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 "AnimationRenderer.h"
#include <QMessageBox>
#include <klocalizedstring.h>
#include <kpluginfactory.h>
#include <kis_image.h>
#include <KisViewManager.h>
#include <KoUpdater.h>
#include <kis_node_manager.h>
#include <kis_image_manager.h>
#include <kis_action.h>
#include <kis_image_animation_interface.h>
#include <kis_properties_configuration.h>
#include <kis_config.h>
#include <KisDocument.h>
#include <KisMimeDatabase.h>
#include <kis_time_range.h>
#include <KisImportExportManager.h>
#include "DlgAnimationRenderer.h"
#include <dialogs/KisAsyncAnimationFramesSaveDialog.h>
K_PLUGIN_FACTORY_WITH_JSON(AnimaterionRendererFactory, "kritaanimationrenderer.json", registerPlugin<AnimaterionRenderer>();)
AnimaterionRenderer::AnimaterionRenderer(QObject *parent, const QVariantList &)
: KisActionPlugin(parent)
{
// Shows the big dialog
KisAction *action = createAction("render_animation");
action->setActivationFlags(KisAction::IMAGE_HAS_ANIMATION);
connect(action, SIGNAL(triggered()), this, SLOT(slotRenderAnimation()));
// Re-renders the image sequence as defined in the last render
action = createAction("render_image_sequence_again");
action->setActivationFlags(KisAction::IMAGE_HAS_ANIMATION);
connect(action, SIGNAL(triggered()), this, SLOT(slotRenderSequenceAgain()));
}
AnimaterionRenderer::~AnimaterionRenderer()
{
}
void AnimaterionRenderer::slotRenderAnimation()
{
KisImageWSP image = viewManager()->image();
if (!image) return;
if (!image->animationInterface()->hasAnimation()) return;
KisDocument *doc = viewManager()->document();
DlgAnimationRenderer dlgAnimationRenderer(doc, viewManager()->mainWindow());
dlgAnimationRenderer.setCaption(i18n("Render Animation"));
KisConfig kisConfig(true);
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
cfg->fromXML(kisConfig.exportConfiguration("IMAGESEQUENCE"));
dlgAnimationRenderer.setSequenceConfiguration(cfg);
cfg->clearProperties();
cfg->fromXML(kisConfig.exportConfiguration("ANIMATION_RENDERER"));
dlgAnimationRenderer.setVideoConfiguration(cfg);
cfg->clearProperties();
cfg->fromXML(kisConfig.exportConfiguration("FFMPEG_CONFIG"));
dlgAnimationRenderer.setEncoderConfiguration(cfg);
// update the UI to show the selected export options
dlgAnimationRenderer.updateExportUIOptions();
if (dlgAnimationRenderer.exec() == QDialog::Accepted) {
KisPropertiesConfigurationSP sequenceConfig = dlgAnimationRenderer.getSequenceConfiguration();
kisConfig.setExportConfiguration("IMAGESEQUENCE", sequenceConfig);
QString mimetype = sequenceConfig->getString("mimetype");
QString extension = KisMimeDatabase::suffixesForMimeType(mimetype).first();
QString baseFileName = QString("%1/%2.%3").arg(sequenceConfig->getString("directory"))
.arg(sequenceConfig->getString("basename"))
.arg(extension);
+ /**
+ * Check if the dimensions make sense before we even try to batch save.
+ */
+ KisPropertiesConfigurationSP encoderConfig = dlgAnimationRenderer.getEncoderConfiguration();
+ if ((image->height()%2 || image->width()%2)
+ && (encoderConfig->getString("mimetype") == "video/mp4" ||
+ encoderConfig->getString("mimetype") == "video/x-matroska")) {
+ QString m = "Mastroska (.mkv)";
+ if (encoderConfig->getString("mimetype")== "video/mp4") {
+ m = "Mpeg4 (.mp4)";
+ }
+ qWarning() << m <<"requires width and height to be even, resize and try again!";
+ doc->setErrorMessage(i18n("%1 requires width and height to be even numbers. Please resize or crop the image before exporting.", m));
+ QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not render animation:\n%1", doc->errorMessage()));
+ return;
+ }
const bool batchMode = false; // TODO: fetch correctly!
+
KisAsyncAnimationFramesSaveDialog exporter(doc->image(),
KisTimeRange::fromTime(sequenceConfig->getInt("first_frame"), sequenceConfig->getInt("last_frame")),
baseFileName,
sequenceConfig->getInt("sequence_start"),
dlgAnimationRenderer.getFrameExportConfiguration());
exporter.setBatchMode(batchMode);
KisAsyncAnimationFramesSaveDialog::Result result =
exporter.regenerateRange(viewManager()->mainWindow()->viewManager());
// the folder could have been read-only or something else could happen
if (result == KisAsyncAnimationFramesSaveDialog::RenderComplete) {
QString savedFilesMask = exporter.savedFilesMask();
KisPropertiesConfigurationSP videoConfig = dlgAnimationRenderer.getVideoConfiguration();
if (videoConfig) {
kisConfig.setExportConfiguration("ANIMATION_RENDERER", videoConfig);
KisPropertiesConfigurationSP encoderConfig = dlgAnimationRenderer.getEncoderConfiguration();
if (encoderConfig) {
kisConfig.setExportConfiguration("FFMPEG_CONFIG", encoderConfig);
encoderConfig->setProperty("savedFilesMask", savedFilesMask);
}
const QString fileName = videoConfig->getString("filename");
QString resultFile = fileName;
KIS_SAFE_ASSERT_RECOVER_NOOP(QFileInfo(resultFile).isAbsolute())
{
const QFileInfo info(resultFile);
QDir dir(info.absolutePath());
if (!dir.exists()) {
dir.mkpath(info.absolutePath());
}
KIS_SAFE_ASSERT_RECOVER_NOOP(dir.exists());
}
QSharedPointer<KisImportExportFilter> encoder = dlgAnimationRenderer.encoderFilter();
encoder->setMimeType(mimetype.toLatin1());
QFile fi(resultFile);
KisImportExportFilter::ConversionStatus res;
if (!fi.open(QIODevice::WriteOnly)) {
qWarning() << "Could not open" << fi.fileName() << "for writing!";
doc->setErrorMessage(i18n("Could not open %1 for writing!", fi.fileName()));
res = KisImportExportFilter::CreationError;
}
else {
encoder->setFilename(fi.fileName());
res = encoder->convert(doc, &fi, encoderConfig);
fi.close();
}
if (res != KisImportExportFilter::OK) {
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not render animation:\n%1", doc->errorMessage()));
}
if (videoConfig->getBool("delete_sequence", false)) {
QDir d(sequenceConfig->getString("directory"));
QStringList sequenceFiles = d.entryList(QStringList() << sequenceConfig->getString("basename") + "*." + extension, QDir::Files);
Q_FOREACH(const QString &f, sequenceFiles) {
d.remove(f);
}
}
}
} else if (result == KisAsyncAnimationFramesSaveDialog::RenderFailed) {
viewManager()->mainWindow()->viewManager()->showFloatingMessage(i18n("Failed to render animation frames!"), QIcon());
}
}
}
void AnimaterionRenderer::slotRenderSequenceAgain()
{
KisImageWSP image = viewManager()->image();
if (!image) return;
if (!image->animationInterface()->hasAnimation()) return;
KisDocument *doc = viewManager()->document();
KisConfig kisConfig(false);
KisPropertiesConfigurationSP sequenceConfig = new KisPropertiesConfiguration();
sequenceConfig->fromXML(kisConfig.exportConfiguration("IMAGESEQUENCE"));
QString mimetype = sequenceConfig->getString("mimetype");
QString extension = KisMimeDatabase::suffixesForMimeType(mimetype).first();
QString baseFileName = QString("%1/%2.%3").arg(sequenceConfig->getString("directory"))
.arg(sequenceConfig->getString("basename"))
.arg(extension);
const bool batchMode = false; // TODO: fetch correctly!
KisAsyncAnimationFramesSaveDialog exporter(doc->image(),
KisTimeRange::fromTime(sequenceConfig->getInt("first_frame"), sequenceConfig->getInt("last_frame")),
baseFileName,
sequenceConfig->getInt("sequence_start"),
0);
exporter.setBatchMode(batchMode);
bool success = exporter.regenerateRange(0) == KisAsyncAnimationFramesSaveDialog::RenderComplete;
KIS_SAFE_ASSERT_RECOVER_NOOP(success);
}
#include "AnimationRenderer.moc"
diff --git a/plugins/extensions/animationrenderer/DlgAnimationRenderer.cpp b/plugins/extensions/animationrenderer/DlgAnimationRenderer.cpp
index 6f5c5c988e..22115851df 100644
--- a/plugins/extensions/animationrenderer/DlgAnimationRenderer.cpp
+++ b/plugins/extensions/animationrenderer/DlgAnimationRenderer.cpp
@@ -1,635 +1,662 @@
/*
* Copyright (c) 2016 Boudewijn Rempt <boud@valdyas.org>
*
* 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 "DlgAnimationRenderer.h"
#include <QStandardPaths>
#include <QPluginLoader>
#include <QJsonObject>
#include <QMessageBox>
#include <QStringList>
#include <QProcess>
#include <klocalizedstring.h>
#include <kpluginfactory.h>
#include <KoResourcePaths.h>
#include <kis_properties_configuration.h>
#include <kis_debug.h>
#include <KisMimeDatabase.h>
#include <KoJsonTrader.h>
#include <KisImportExportFilter.h>
#include <kis_image.h>
#include <kis_image_animation_interface.h>
#include <kis_time_range.h>
#include <KisImportExportManager.h>
#include <kis_config_widget.h>
#include <KisDocument.h>
#include <QHBoxLayout>
#include <kis_config.h>
#include <kis_file_name_requester.h>
#include <KoDialog.h>
#include "kis_slider_spin_box.h"
#include "kis_acyclic_signal_connector.h"
DlgAnimationRenderer::DlgAnimationRenderer(KisDocument *doc, QWidget *parent)
: KoDialog(parent)
, m_image(doc->image())
, m_doc(doc)
, m_defaultFileName(QFileInfo(doc->url().toLocalFile()).completeBaseName())
{
KisConfig cfg(true);
setCaption(i18n("Render Animation"));
setButtons(Ok | Cancel);
setDefaultButton(Ok);
if (m_defaultFileName.isEmpty()) {
m_defaultFileName = i18n("Untitled");
}
m_page = new WdgAnimationRenderer(this);
m_page->layout()->setMargin(0);
m_page->dirRequester->setMode(KoFileDialog::OpenDirectory);
QString lastLocation = cfg.readEntry<QString>("AnimationRenderer/last_sequence_export_location", QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
m_page->dirRequester->setFileName(lastLocation);
m_page->intStart->setMinimum(doc->image()->animationInterface()->fullClipRange().start());
m_page->intStart->setMaximum(doc->image()->animationInterface()->fullClipRange().end());
m_page->intStart->setValue(doc->image()->animationInterface()->playbackRange().start());
m_page->intEnd->setMinimum(doc->image()->animationInterface()->fullClipRange().start());
//m_page->intEnd->setMaximum(doc->image()->animationInterface()->fullClipRange().end()); // animators sometimes want to export after end frame
m_page->intEnd->setValue(doc->image()->animationInterface()->playbackRange().end());
m_page->intHeight->setMinimum(1);
m_page->intHeight->setMaximum(10000);
m_page->intHeight->setValue(doc->image()->height());
m_page->intWidth->setMinimum(1);
m_page->intWidth->setMaximum(10000);
m_page->intWidth->setValue(doc->image()->width());
// try to lock the width and height being updated
KisAcyclicSignalConnector *constrainsConnector = new KisAcyclicSignalConnector(this);
constrainsConnector->createCoordinatedConnector()->connectBackwardInt(m_page->intWidth, SIGNAL(valueChanged(int)), this, SLOT(slotLockAspectRatioDimensionsWidth(int)));
constrainsConnector->createCoordinatedConnector()->connectForwardInt(m_page->intHeight, SIGNAL(valueChanged(int)), this, SLOT(slotLockAspectRatioDimensionsHeight(int)));
m_page->intFramesPerSecond->setValue(doc->image()->animationInterface()->framerate());
QFileInfo audioFileInfo(doc->image()->animationInterface()->audioChannelFileName());
const bool hasAudio = audioFileInfo.exists();
m_page->chkIncludeAudio->setEnabled(hasAudio);
m_page->chkIncludeAudio->setChecked(hasAudio && !doc->image()->animationInterface()->isAudioMuted());
QStringList mimes = KisImportExportManager::supportedMimeTypes(KisImportExportManager::Export);
mimes.sort();
Q_FOREACH(const QString &mime, mimes) {
QString description = KisMimeDatabase::descriptionForMimeType(mime);
if (description.isEmpty()) {
description = mime;
}
m_page->cmbMimetype->addItem(description, mime);
if (mime == "image/png") {
m_page->cmbMimetype->setCurrentIndex(m_page->cmbMimetype->count() - 1);
}
}
setMainWidget(m_page);
QList<QPluginLoader *>list = KoJsonTrader::instance()->query("Krita/AnimationExporter", "");
Q_FOREACH(QPluginLoader *loader, list) {
QJsonObject json = loader->metaData().value("MetaData").toObject();
QStringList mimetypes = json.value("X-KDE-Export").toString().split(",");
Q_FOREACH(const QString &mime, mimetypes) {
KLibFactory *factory = qobject_cast<KLibFactory *>(loader->instance());
if (!factory) {
warnUI << loader->errorString();
continue;
}
QObject* obj = factory->create<KisImportExportFilter>(0);
if (!obj || !obj->inherits("KisImportExportFilter")) {
delete obj;
continue;
}
QSharedPointer<KisImportExportFilter>filter(static_cast<KisImportExportFilter*>(obj));
if (!filter) {
delete obj;
continue;
}
m_renderFilters.append(filter);
QString description = KisMimeDatabase::descriptionForMimeType(mime);
if (description.isEmpty()) {
description = mime;
}
m_page->cmbRenderType->addItem(description, mime);
}
}
m_page->videoFilename->setMode(KoFileDialog::SaveFile);
m_page->videoFilename->setStartDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
qDeleteAll(list);
connect(m_page->bnExportOptions, SIGNAL(clicked()), this, SLOT(sequenceMimeTypeSelected()));
connect(m_page->bnRenderOptions, SIGNAL(clicked()), this, SLOT(selectRenderOptions()));
m_page->ffmpegLocation->setFileName(findFFMpeg());
m_page->ffmpegLocation->setMode(KoFileDialog::OpenFile);
connect(m_page->ffmpegLocation, SIGNAL(fileSelected(QString)), this, SLOT(ffmpegLocationChanged(QString)));
m_page->cmbRenderType->setCurrentIndex(cfg.readEntry<int>("AnimationRenderer/render_type", 0));
connect(m_page->shouldExportOnlyImageSequence, SIGNAL(toggled(bool)), this, SLOT(slotExportTypeChanged()));
connect(m_page->shouldExportOnlyVideo, SIGNAL(toggled(bool)), this, SLOT(slotExportTypeChanged()));
connect(m_page->shouldExportAll, SIGNAL(toggled(bool)), this, SLOT(slotExportTypeChanged()));
updateExportUIOptions();
// connect and cold init
connect(m_page->cmbRenderType, SIGNAL(currentIndexChanged(int)), this, SLOT(selectRenderType(int)));
selectRenderType(m_page->cmbRenderType->currentIndex());
resize(m_page->sizeHint());
}
DlgAnimationRenderer::~DlgAnimationRenderer()
{
KisConfig cfg(false);
cfg.writeEntry<QString>("AnimationRenderer/last_sequence_export_location", m_page->dirRequester->fileName());
cfg.writeEntry<int>("AnimationRenderer/render_type", m_page->cmbRenderType->currentIndex());
cfg.setCustomFFMpegPath(m_page->ffmpegLocation->fileName());
if (m_encoderConfigWidget) {
m_encoderConfigWidget->setParent(0);
m_encoderConfigWidget->deleteLater();
}
if (m_frameExportConfigWidget) {
m_frameExportConfigWidget->setParent(0);
m_frameExportConfigWidget->deleteLater();
}
delete m_page;
}
QString DlgAnimationRenderer::fetchRenderingDirectory() const
{
QString result = m_page->dirRequester->fileName();
if (m_page->shouldExportOnlyVideo->isChecked()) {
const QFileInfo info(fetchRenderingFileName());
if (info.isAbsolute()) {
result = info.absolutePath();
}
}
return result;
}
QString DlgAnimationRenderer::fetchRenderingFileName() const
{
QString filename = m_page->videoFilename->fileName();
if (QFileInfo(filename).completeSuffix().isEmpty()) {
QString mimetype = m_page->cmbRenderType->itemData(m_page->cmbRenderType->currentIndex()).toString();
filename += "." + KisMimeDatabase::suffixesForMimeType(mimetype).first();
}
if (QFileInfo(filename).isRelative()) {
QDir baseDir(m_page->dirRequester->fileName());
if (m_page->shouldExportOnlyVideo->isChecked()) {
QString documentDir = QFileInfo(m_doc->url().toLocalFile()).absolutePath();
if (!documentDir.isEmpty()) {
baseDir = documentDir;
}
}
filename = baseDir.absoluteFilePath(filename);
}
return filename;
}
KisPropertiesConfigurationSP DlgAnimationRenderer::getSequenceConfiguration() const
{
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
cfg->setProperty("basename", m_page->txtBasename->text());
cfg->setProperty("last_document_path", m_doc->localFilePath());
cfg->setProperty("directory", fetchRenderingDirectory());
cfg->setProperty("first_frame", m_page->intStart->value());
cfg->setProperty("last_frame", m_page->intEnd->value());
cfg->setProperty("sequence_start", m_page->sequenceStart->value());
cfg->setProperty("mimetype", m_page->cmbMimetype->currentData().toString());
return cfg;
}
void DlgAnimationRenderer::setSequenceConfiguration(KisPropertiesConfigurationSP cfg)
{
m_page->txtBasename->setText(cfg->getString("basename", "frame"));
if (cfg->getString("last_document_path") != m_doc->localFilePath()) {
cfg->removeProperty("first_frame");
cfg->removeProperty("last_frame");
cfg->removeProperty("sequence_start");
}
m_page->dirRequester->setFileName(cfg->getString("directory", QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)));
m_page->intStart->setValue(cfg->getInt("first_frame", m_image->animationInterface()->playbackRange().start()));
m_page->intEnd->setValue(cfg->getInt("last_frame", m_image->animationInterface()->playbackRange().end()));
m_page->sequenceStart->setValue(cfg->getInt("sequence_start", m_image->animationInterface()->playbackRange().start()));
QString mimetype = cfg->getString("mimetype");
for (int i = 0; i < m_page->cmbMimetype->count(); ++i) {
if (m_page->cmbMimetype->itemData(i).toString() == mimetype) {
m_page->cmbMimetype->setCurrentIndex(i);
break;
}
}
}
KisPropertiesConfigurationSP DlgAnimationRenderer::getFrameExportConfiguration() const
{
if (m_frameExportConfigWidget) {
KisPropertiesConfigurationSP cfg = m_frameExportConfigWidget->configuration();
+ qDebug()<<Q_FUNC_INFO<<cfg;
cfg->setProperty("basename", m_page->txtBasename->text());
cfg->setProperty("directory", fetchRenderingDirectory());
cfg->setProperty("first_frame", m_page->intStart->value());
cfg->setProperty("last_frame", m_page->intEnd->value());
cfg->setProperty("sequence_start", m_page->sequenceStart->value());
cfg->setProperty("ffmpeg_path", m_page->ffmpegLocation->fileName());
return m_frameExportConfigWidget->configuration();
}
return 0;
}
KisPropertiesConfigurationSP DlgAnimationRenderer::getVideoConfiguration() const
{
// don't continue if we are only exporting image sequence
if (m_page->shouldExportOnlyImageSequence->isChecked()) {
return 0;
}
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
cfg->setProperty("filename", fetchRenderingFileName());
cfg->setProperty("first_frame", m_page->intStart->value());
cfg->setProperty("last_frame", m_page->intEnd->value());
cfg->setProperty("sequence_start", m_page->sequenceStart->value());
// delete image sequence if we are only exporting out video
cfg->setProperty("delete_sequence", m_page->shouldExportOnlyVideo->isChecked());
return cfg;
}
void DlgAnimationRenderer::setVideoConfiguration(KisPropertiesConfigurationSP /*cfg*/)
{
}
KisPropertiesConfigurationSP DlgAnimationRenderer::getEncoderConfiguration() const
{
// don't continue if we are only exporting image sequence
if (m_page->shouldExportOnlyImageSequence->isChecked()) {
return 0;
}
KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
if (m_encoderConfigWidget) {
cfg = m_encoderConfigWidget->configuration();
}
cfg->setProperty("mimetype", m_page->cmbRenderType->currentData().toString());
cfg->setProperty("directory", fetchRenderingDirectory());
cfg->setProperty("first_frame", m_page->intStart->value());
cfg->setProperty("last_frame", m_page->intEnd->value());
cfg->setProperty("framerate", m_page->intFramesPerSecond->value());
- cfg->setProperty("height", m_page->intHeight->value());
- cfg->setProperty("width", m_page->intWidth->value());
+ int height = m_page->intHeight->value();
+ if (height % 2) {
+ height += 1;
+ }
+ cfg->setProperty("height", height);
+ int width = m_page->intWidth->value();
+ if (width % 2) {
+ width += 1;
+ }
+ cfg->setProperty("width", width);
cfg->setProperty("sequence_start", m_page->sequenceStart->value());
cfg->setProperty("include_audio", m_page->chkIncludeAudio->isChecked());
return cfg;
}
void DlgAnimationRenderer::setEncoderConfiguration(KisPropertiesConfigurationSP cfg)
{
m_page->intHeight->setValue(cfg->getInt("height", int(m_image->height())));
m_page->intWidth->setValue(cfg->getInt("width", int(m_image->width())));
m_page->intFramesPerSecond->setValue(cfg->getInt("framerate", int(m_image->animationInterface()->framerate())));
if (m_encoderConfigWidget) {
m_encoderConfigWidget->setConfiguration(cfg);
}
}
QSharedPointer<KisImportExportFilter> DlgAnimationRenderer::encoderFilter() const
{
if (m_page->cmbRenderType->currentIndex() < m_renderFilters.size()) {
return m_renderFilters[m_page->cmbRenderType->currentIndex()];
}
return QSharedPointer<KisImportExportFilter>(0);
}
void DlgAnimationRenderer::selectRenderType(int index)
{
if (index >= m_renderFilters.size()) return;
QString mimetype = m_page->cmbRenderType->itemData(index).toString();
if (!m_page->videoFilename->fileName().isEmpty() && QFileInfo(m_page->videoFilename->fileName()).completeBaseName() != m_defaultFileName) {
m_defaultFileName = QFileInfo(m_page->videoFilename->fileName()).completeBaseName();
}
m_page->videoFilename->setMimeTypeFilters(QStringList() << mimetype, mimetype);
m_page->videoFilename->setFileName(m_defaultFileName + "." + KisMimeDatabase::suffixesForMimeType(mimetype).first());
+ createEncoderWidget(index);
}
void DlgAnimationRenderer::selectRenderOptions()
{
int index = m_page->cmbRenderType->currentIndex();
-
if (m_encoderConfigWidget) {
m_encoderConfigWidget->deleteLater();
m_encoderConfigWidget = 0;
}
if (index >= m_renderFilters.size()) return;
+ if (!m_encoderConfigWidget) {
+ createEncoderWidget(index);
+ }
QSharedPointer<KisImportExportFilter> filter = m_renderFilters[index];
QString mimetype = m_page->cmbRenderType->itemData(index).toString();
if (filter) {
- m_encoderConfigWidget = filter->createConfigurationWidget(0, KisDocument::nativeFormatMimeType(), mimetype.toLatin1());
if (m_encoderConfigWidget) {
- m_encoderConfigWidget->setConfiguration(filter->lastSavedConfiguration("", mimetype.toLatin1()));
KoDialog dlg(this);
dlg.setMainWidget(m_encoderConfigWidget);
dlg.setButtons(KoDialog::Ok | KoDialog::Cancel);
if (!dlg.exec()) {
- m_encoderConfigWidget->setConfiguration(filter->lastSavedConfiguration());
+ m_encoderConfigWidget->setConfiguration(filter->lastSavedConfiguration("", mimetype.toLatin1()));
} else {
KisConfig(false).setExportConfiguration(mimetype.toLatin1(), m_encoderConfigWidget->configuration());
}
dlg.setMainWidget(0);
m_encoderConfigWidget->hide();
m_encoderConfigWidget->setParent(0);
}
}
else {
m_encoderConfigWidget = 0;
}
}
+void DlgAnimationRenderer::createEncoderWidget(int index)
+{
+ if (m_encoderConfigWidget) {
+ m_encoderConfigWidget->deleteLater();
+ m_encoderConfigWidget = 0;
+ }
+ if (index >= m_renderFilters.size()) return;
+ QSharedPointer<KisImportExportFilter> filter = m_renderFilters[index];
+ QString mimetype = m_page->cmbRenderType->itemData(index).toString();
+ if (filter) {
+ m_encoderConfigWidget = filter->createConfigurationWidget(0, KisDocument::nativeFormatMimeType(), mimetype.toLatin1());
+ if (m_encoderConfigWidget) {
+ m_encoderConfigWidget->setConfiguration(filter->lastSavedConfiguration("", mimetype.toLatin1()));
+ }
+ }
+}
+
void DlgAnimationRenderer::sequenceMimeTypeSelected()
{
int index = m_page->cmbMimetype->currentIndex();
if (m_frameExportConfigWidget) {
m_frameExportConfigWidget->deleteLater();
m_frameExportConfigWidget = 0;
}
QString mimetype = m_page->cmbMimetype->itemData(index).toString();
QSharedPointer<KisImportExportFilter> filter(KisImportExportManager::filterForMimeType(mimetype, KisImportExportManager::Export));
if (filter) {
m_frameExportConfigWidget = filter->createConfigurationWidget(0, KisDocument::nativeFormatMimeType(), mimetype.toLatin1());
if (m_frameExportConfigWidget) {
KisPropertiesConfigurationSP config = filter->lastSavedConfiguration("", mimetype.toLatin1());
if (config) {
KisImportExportManager::fillStaticExportConfigurationProperties(config, m_image);
}
m_frameExportConfigWidget->setConfiguration(config);
KoDialog dlg(this);
dlg.setMainWidget(m_frameExportConfigWidget);
dlg.setButtons(KoDialog::Ok | KoDialog::Cancel);
if (!dlg.exec()) {
- config = filter->lastSavedConfiguration();
+ config = filter->lastSavedConfiguration("", mimetype.toLatin1());
if (config) {
KisImportExportManager::fillStaticExportConfigurationProperties(config, m_image);
}
- m_frameExportConfigWidget->setConfiguration(filter->lastSavedConfiguration());
+ m_frameExportConfigWidget->setConfiguration(filter->lastSavedConfiguration("", mimetype.toLatin1()));
}
m_frameExportConfigWidget->hide();
m_frameExportConfigWidget->setParent(0);
dlg.setMainWidget(0);
}
}
}
void DlgAnimationRenderer::ffmpegLocationChanged(const QString &s)
{
KisConfig cfg(false);
cfg.setCustomFFMpegPath(s);
}
void DlgAnimationRenderer::updateExportUIOptions() {
KisConfig cfg(true);
// read in what type to export to. Defaults to image sequence only
QString exportType = cfg.readEntry<QString>("AnimationRenderer/export_type", "ImageSequence");
if (exportType == "ImageSequence") {
m_page->shouldExportOnlyImageSequence->setChecked(true);
} else if (exportType == "Video") {
m_page->shouldExportOnlyVideo->setChecked(true);
} else {
m_page->shouldExportAll->setChecked(true); // export to both
}
}
void DlgAnimationRenderer::slotButtonClicked(int button)
{
if (button == KoDialog::Ok && !m_page->shouldExportOnlyImageSequence->isChecked()) {
QString ffmpeg = m_page->ffmpegLocation->fileName();
if (m_page->videoFilename->fileName().isEmpty()) {
QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("Please enter a file name to render to."));
return;
}
else if (ffmpeg.isEmpty()) {
QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("The location of FFmpeg is unknown. Please install FFmpeg first: Krita cannot render animations without FFmpeg. (<a href=\"https://www.ffmpeg.org\">www.ffmpeg.org</a>)"));
return;
}
else {
QFileInfo fi(ffmpeg);
if (!fi.exists()) {
QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("The location of FFmpeg is invalid. Please select the correct location of the FFmpeg executable on your system."));
return;
}
}
}
KoDialog::slotButtonClicked(button);
}
QString DlgAnimationRenderer::findFFMpeg()
{
QString result;
QStringList proposedPaths;
QString customPath = KisConfig(true).customFFMpegPath();
if (!customPath.isEmpty()) {
proposedPaths << customPath;
proposedPaths << customPath + QDir::separator() + "ffmpeg";
}
#ifndef Q_OS_WIN
proposedPaths << QDir::homePath() + "/bin/ffmpeg";
proposedPaths << "/usr/bin/ffmpeg";
proposedPaths << "/usr/local/bin/ffmpeg";
#endif
proposedPaths << KoResourcePaths::getApplicationRoot() +
QDir::separator() + "bin" + QDir::separator() + "ffmpeg";
Q_FOREACH (QString path, proposedPaths) {
if (path.isEmpty()) continue;
#ifdef Q_OS_WIN
path = QDir::toNativeSeparators(QDir::cleanPath(path));
if (path.endsWith(QDir::separator())) {
continue;
}
if (!path.endsWith(".exe")) {
if (!QFile::exists(path)) {
path += ".exe";
if (!QFile::exists(path)) {
continue;
}
}
}
#endif
QProcess testProcess;
testProcess.start(path, QStringList() << "-version");
if (testProcess.waitForStarted(1000)) {
testProcess.waitForFinished(1000);
}
const bool successfulStart =
testProcess.state() == QProcess::NotRunning &&
testProcess.error() == QProcess::UnknownError;
if (successfulStart) {
result = path;
break;
}
}
return result;
}
void DlgAnimationRenderer::slotExportTypeChanged()
{
KisConfig cfg(false);
bool willEncodeVideo =
m_page->shouldExportAll->isChecked() || m_page->shouldExportOnlyVideo->isChecked();
// if a video format needs to be outputted
if (willEncodeVideo) {
// videos always uses PNG for creating video, so disable the ability to change the format
m_page->cmbMimetype->setEnabled(false);
for (int i = 0; i < m_page->cmbMimetype->count(); ++i) {
if (m_page->cmbMimetype->itemData(i).toString() == "image/png") {
m_page->cmbMimetype->setCurrentIndex(i);
break;
}
}
}
m_page->intWidth->setVisible(willEncodeVideo);
m_page->intHeight->setVisible(willEncodeVideo);
m_page->intFramesPerSecond->setVisible(willEncodeVideo);
m_page->fpsLabel->setVisible(willEncodeVideo);
m_page->lblWidth->setVisible(willEncodeVideo);
m_page->lblHeight->setVisible(willEncodeVideo);
// if only exporting video
if (m_page->shouldExportOnlyVideo->isChecked()) {
m_page->cmbMimetype->setEnabled(false); // allow to change image format
m_page->imageSequenceOptionsGroup->setVisible(false);
m_page->videoOptionsGroup->setVisible(false); //shrinks the horizontal space temporarily to help resize() work
m_page->videoOptionsGroup->setVisible(true);
cfg.writeEntry<QString>("AnimationRenderer/export_type", "Video");
}
// if only an image sequence needs to be output
if (m_page->shouldExportOnlyImageSequence->isChecked()) {
m_page->cmbMimetype->setEnabled(true); // allow to change image format
m_page->videoOptionsGroup->setVisible(false);
m_page->imageSequenceOptionsGroup->setVisible(false);
m_page->imageSequenceOptionsGroup->setVisible(true);
cfg.writeEntry<QString>("AnimationRenderer/export_type", "ImageSequence");
}
// show all options
if (m_page->shouldExportAll->isChecked() ) {
m_page->imageSequenceOptionsGroup->setVisible(true);
m_page->videoOptionsGroup->setVisible(true);
cfg.writeEntry<QString>("AnimationRenderer/export_type", "VideoAndImageSequence");
}
// for the resize to work as expected, try to hide elements first before displaying other ones.
// if the widget gets bigger at any point, the resize will use that, even if elements are hidden later to make it smaller
resize(m_page->sizeHint());
}
void DlgAnimationRenderer::slotLockAspectRatioDimensionsWidth(int width)
{
Q_UNUSED(width);
float aspectRatio = (float)m_image->width() / (float)m_image->height();
// update height here
float newHeight = m_page->intWidth->value() / aspectRatio ;
m_page->intHeight->setValue(newHeight);
}
void DlgAnimationRenderer::slotLockAspectRatioDimensionsHeight(int height)
{
Q_UNUSED(height);
float aspectRatio = (float)m_image->width() / (float)m_image->height();
// update width here
float newWidth = aspectRatio * m_page->intHeight->value();
m_page->intWidth->setValue(newWidth);
}
diff --git a/plugins/extensions/animationrenderer/DlgAnimationRenderer.h b/plugins/extensions/animationrenderer/DlgAnimationRenderer.h
index 8336b94523..bbae137c3e 100644
--- a/plugins/extensions/animationrenderer/DlgAnimationRenderer.h
+++ b/plugins/extensions/animationrenderer/DlgAnimationRenderer.h
@@ -1,106 +1,115 @@
/*
* Copyright (c) 2016 Boudewijn Rempt <boud@valdyas.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef DLG_ANIMATIONRENDERERIMAGE
#define DLG_ANIMATIONRENDERERIMAGE
#include <KoDialog.h>
#include <kis_properties_configuration.h>
#include "ui_wdg_animationrenderer.h"
#include <QSharedPointer>
#include <kis_types.h>
class KisDocument;
class KisImportExportFilter;
class KisConfigWidget;
class QHBoxLayout;
class WdgAnimationRenderer : public QWidget, public Ui::WdgAnimaterionRenderer
{
Q_OBJECT
public:
WdgAnimationRenderer(QWidget *parent)
: QWidget(parent)
{
setupUi(this);
}
};
class DlgAnimationRenderer: public KoDialog
{
Q_OBJECT
public:
DlgAnimationRenderer(KisDocument *doc, QWidget *parent = 0);
~DlgAnimationRenderer() override;
KisPropertiesConfigurationSP getSequenceConfiguration() const;
void setSequenceConfiguration(KisPropertiesConfigurationSP cfg);
KisPropertiesConfigurationSP getFrameExportConfiguration() const;
KisPropertiesConfigurationSP getVideoConfiguration() const;
void setVideoConfiguration(KisPropertiesConfigurationSP cfg);
KisPropertiesConfigurationSP getEncoderConfiguration() const;
void setEncoderConfiguration(KisPropertiesConfigurationSP cfg);
QSharedPointer<KisImportExportFilter> encoderFilter() const;
// fires when the render animation action is called. makes sure the correct export type is selected for the UI
void updateExportUIOptions();
private Q_SLOTS:
void selectRenderType(int i);
void selectRenderOptions();
+ /**
+ * @brief createEncoderWidget
+ * creates a new settings widget for the filetype.
+ */
+ void createEncoderWidget(int index);
+ /**
+ * @brief sequenceMimeTypeSelected
+ * calls the dialog for the export widget.
+ */
void sequenceMimeTypeSelected();
void ffmpegLocationChanged(const QString&);
void slotLockAspectRatioDimensionsWidth(int width);
void slotLockAspectRatioDimensionsHeight(int height);
void slotExportTypeChanged();
protected Q_SLOTS:
void slotButtonClicked(int button) override;
private:
QString fetchRenderingDirectory() const;
QString fetchRenderingFileName() const;
private:
static QString findFFMpeg();
KisImageSP m_image;
KisDocument *m_doc;
WdgAnimationRenderer *m_page {0};
QList<QSharedPointer<KisImportExportFilter>> m_renderFilters;
KisConfigWidget *m_encoderConfigWidget {0};
KisConfigWidget *m_frameExportConfigWidget {0};
QString m_defaultFileName;
};
#endif // DLG_ANIMATIONRENDERERIMAGE
diff --git a/plugins/extensions/buginfo/dlg_buginfo.cpp b/plugins/extensions/buginfo/dlg_buginfo.cpp
index 69e7ffc47c..362c9e1253 100644
--- a/plugins/extensions/buginfo/dlg_buginfo.cpp
+++ b/plugins/extensions/buginfo/dlg_buginfo.cpp
@@ -1,96 +1,118 @@
/*
* Copyright (c) 2017 Boudewijn Rempt <boud@valdyas.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "dlg_buginfo.h"
#include <klocalizedstring.h>
#include <kis_debug.h>
#include <opengl/kis_opengl.h>
#include <KritaVersionWrapper.h>
#include <QSysInfo>
-
+#include <kis_image_config.h>
#include <QDesktopWidget>
#include <QClipboard>
+#include <QThread>
+#include <QFile>
+#include <QFileInfo>
+#include <QSettings>
+#include <QStandardPaths>
#include "kis_document_aware_spin_box_unit_manager.h"
DlgBugInfo::DlgBugInfo(QWidget *parent)
: KoDialog(parent)
{
setCaption(i18n("Please paste this information in your bug report"));
setButtons(User1 | Ok);
setButtonText(User1, i18n("Copy to clipboard"));
setDefaultButton(Ok);
m_page = new WdgBugInfo(this);
Q_CHECK_PTR(m_page);
setMainWidget(m_page);
QString info;
- // Krita version info
- info.append("Krita");
- info.append("\n Version: ").append(KritaVersionWrapper::versionString(true));
- info.append("\n\n");
-
- info.append("Qt");
- info.append("\n Version (compiled): ").append(QT_VERSION_STR);
- info.append("\n Version (loaded): ").append(qVersion());
- info.append("\n\n");
-
- // OS information
- info.append("OS Information");
- info.append("\n Build ABI: ").append(QSysInfo::buildAbi());
- info.append("\n Build CPU: ").append(QSysInfo::buildCpuArchitecture());
- info.append("\n CPU: ").append(QSysInfo::currentCpuArchitecture());
- info.append("\n Kernel Type: ").append(QSysInfo::kernelType());
- info.append("\n Kernel Version: ").append(QSysInfo::kernelVersion());
- info.append("\n Pretty Productname: ").append(QSysInfo::prettyProductName());
- info.append("\n Product Type: ").append(QSysInfo::productType());
- info.append("\n Product Version: ").append(QSysInfo::productVersion());
- info.append("\n");
-
- // OpenGL information
- info.append("\n").append(KisOpenGL::getDebugText());
-
- // Installation information
-
+ const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
+ QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat);
+
+ if (!kritarc.value("LogUsage", true).toBool() || !QFileInfo(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/krita.log").exists()) {
+
+ // NOTE: This is intentionally not translated!
+
+ // Krita version info
+ info.append("Krita");
+ info.append("\n Version: ").append(KritaVersionWrapper::versionString(true));
+ info.append("\n\n");
+
+ info.append("Qt");
+ info.append("\n Version (compiled): ").append(QT_VERSION_STR);
+ info.append("\n Version (loaded): ").append(qVersion());
+ info.append("\n\n");
+
+ // OS information
+ info.append("OS Information");
+ info.append("\n Build ABI: ").append(QSysInfo::buildAbi());
+ info.append("\n Build CPU: ").append(QSysInfo::buildCpuArchitecture());
+ info.append("\n CPU: ").append(QSysInfo::currentCpuArchitecture());
+ info.append("\n Kernel Type: ").append(QSysInfo::kernelType());
+ info.append("\n Kernel Version: ").append(QSysInfo::kernelVersion());
+ info.append("\n Pretty Productname: ").append(QSysInfo::prettyProductName());
+ info.append("\n Product Type: ").append(QSysInfo::productType());
+ info.append("\n Product Version: ").append(QSysInfo::productVersion());
+ info.append("\n\n");
+
+ // OpenGL information
+ info.append("\n").append(KisOpenGL::getDebugText());
+ info.append("\n\n");
+ // Hardware information
+ info.append("Hardware Information");
+ info.append(QString("\n Memory: %1").arg(KisImageConfig(true).totalRAM() / 1024)).append(" Gb");
+ info.append(QString("\n Cores: %1").arg(QThread::idealThreadCount()));
+ info.append("\n Swap: ").append(KisImageConfig(true).swapDir());
+ }
+ else {
+ QFile f(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/krita.log");
+ f.open(QFile::ReadOnly);
+ info = QString::fromUtf8(f.readAll());
+ f.close();
+ }
// calculate a default height for the widget
int wheight = m_page->sizeHint().height();
m_page->txtBugInfo->setText(info);
QFontMetrics fm = m_page->txtBugInfo->fontMetrics();
int target_height = fm.height() * info.split('\n').size() + wheight;
QDesktopWidget dw;
QRect screen_rect = dw.availableGeometry(dw.primaryScreen());
resize(m_page->size().width(), target_height > screen_rect.height() ? screen_rect.height() : target_height);
connect(this, &KoDialog::user1Clicked, this, [this](){
QGuiApplication::clipboard()->setText(m_page->txtBugInfo->toPlainText());
m_page->txtBugInfo->selectAll(); // feedback
});
}
DlgBugInfo::~DlgBugInfo()
{
delete m_page;
}
diff --git a/plugins/extensions/imagesplit/dlg_imagesplit.cpp b/plugins/extensions/imagesplit/dlg_imagesplit.cpp
index 10fef07819..ef428c7a07 100644
--- a/plugins/extensions/imagesplit/dlg_imagesplit.cpp
+++ b/plugins/extensions/imagesplit/dlg_imagesplit.cpp
@@ -1,111 +1,118 @@
/*
* dlg_imagesplit.cc - part of KimageShop^WKrayon^WKrita
*
* Copyright (c) 2009 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "dlg_imagesplit.h"
#include <klocalizedstring.h>
#include <kis_debug.h>
#include <KisViewManager.h>
#include <kis_image.h>
#include <kis_paint_device.h>
DlgImagesplit::DlgImagesplit(KisViewManager* view, const QString &suffix, QStringList listMimeFilter, int defaultMimeIndex)
: KoDialog(view->mainWindow())
{
-
m_page = new WdgImagesplit(this);
setCaption(i18n("Image Split"));
setButtons(Apply | Close);
setDefaultButton(Apply);
connect(this, SIGNAL(applyClicked()), this, SLOT(applyClicked()));
setMainWidget(m_page);
m_page->lineEdit->setText(suffix);
m_page->setMinimumWidth(200);
m_page->setMinimumHeight(160);
resize(m_page->sizeHint());
m_page->cmbFileType->clear();
m_page->cmbFileType->addItems(listMimeFilter);
m_page->cmbFileType->setCurrentIndex(defaultMimeIndex);
cmbIndex = defaultMimeIndex;
connect(m_page->chkAutoSave, SIGNAL(stateChanged(int)), SLOT(lineEditEnable()));
connect(m_page->cmbFileType, SIGNAL(activated(int)), this, SLOT(setMimeType(int)));
}
DlgImagesplit::~DlgImagesplit()
{
}
void DlgImagesplit::lineEditEnable()
{
if (m_page->chkAutoSave->isChecked()) {
m_page->lblSuffix->setEnabled(true);
m_page->lineEdit->setEnabled(true);
m_page->lblFileType->setEnabled(true);
m_page->cmbFileType->setEnabled(true);
}
else
{
m_page->lblSuffix->setEnabled(false);
m_page->lineEdit->setEnabled(false);
m_page->lblFileType->setEnabled(false);
m_page->cmbFileType->setEnabled(false);
}
}
bool DlgImagesplit::autoSave()
{
if (m_page->chkAutoSave->isChecked())
return true;
else
return false;
}
+bool DlgImagesplit::sortHorizontal()
+{
+ if (m_page->chkHorizontal->isChecked())
+ return true;
+ else
+ return false;
+}
+
int DlgImagesplit::horizontalLines()
{
return m_page->intHorizontalSplitLines->value();
}
int DlgImagesplit::verticalLines()
{
return m_page->intVerticalSplitLines->value();
}
QString DlgImagesplit::suffix()
{
return m_page->lineEdit->text();
}
void DlgImagesplit::setMimeType(int index)
{
cmbIndex = index;
}
void DlgImagesplit::applyClicked()
{
accept();
}
diff --git a/plugins/extensions/imagesplit/dlg_imagesplit.h b/plugins/extensions/imagesplit/dlg_imagesplit.h
index c59302654a..578297f9d8 100644
--- a/plugins/extensions/imagesplit/dlg_imagesplit.h
+++ b/plugins/extensions/imagesplit/dlg_imagesplit.h
@@ -1,58 +1,59 @@
/*
* dlg_imagesplit.h -- part of KimageShop^WKrayon^WKrita
*
* Copyright (c) 2009 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef DLG_IMAGESPLIT
#define DLG_IMAGESPLIT
#include <KoDialog.h>
#include <kis_types.h>
#include "wdg_imagesplit.h"
class KisViewManager;
/**
* This dialog allows the user to create a selection mask based
* on a (range of) colors.
*/
class DlgImagesplit: public KoDialog
{
Q_OBJECT
public:
DlgImagesplit(KisViewManager* view, const QString &suffix, QStringList listMimeType, int defaultMimeIndex);
~DlgImagesplit() override;
bool autoSave();
+ bool sortHorizontal();
int horizontalLines();
int verticalLines();
int cmbIndex;
QString suffix();
private Q_SLOTS:
void applyClicked();
void lineEditEnable();
void setMimeType(int index);
private:
WdgImagesplit* m_page;
};
#endif // DLG_IMAGESPLIT
diff --git a/plugins/extensions/imagesplit/imagesplit.cpp b/plugins/extensions/imagesplit/imagesplit.cpp
index 03acaee77b..87859c8fd5 100644
--- a/plugins/extensions/imagesplit/imagesplit.cpp
+++ b/plugins/extensions/imagesplit/imagesplit.cpp
@@ -1,206 +1,230 @@
/*
* imagesplit.cc -- Part of Krita
*
* Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org)
* Copyright (c) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
*
* 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 "imagesplit.h"
#include <QStringList>
#include <QDir>
#include <QStandardPaths>
#include <QMessageBox>
#include <klocalizedstring.h>
#include <kpluginfactory.h>
#include <KisImportExportManager.h>
#include <KoFileDialog.h>
#include <KisDocument.h>
#include <KisPart.h>
#include <kis_debug.h>
#include <kis_types.h>
#include <KisViewManager.h>
#include <kis_image.h>
#include <kis_action.h>
#include <kis_paint_layer.h>
#include <kis_painter.h>
#include <kis_paint_device.h>
#include <KisMimeDatabase.h>
#include "dlg_imagesplit.h"
K_PLUGIN_FACTORY_WITH_JSON(ImagesplitFactory, "kritaimagesplit.json", registerPlugin<Imagesplit>();)
Imagesplit::Imagesplit(QObject *parent, const QVariantList &)
: KisActionPlugin(parent)
{
KisAction *action = createAction("imagesplit");
connect(action, SIGNAL(triggered()), this, SLOT(slotImagesplit()));
}
Imagesplit::~Imagesplit()
{
}
bool Imagesplit::saveAsImage(const QRect &imgSize, const QString &mimeType, const QString &url)
{
KisImageSP image = viewManager()->image();
KisDocument *document = KisPart::instance()->createDocument();
- KisImageSP dst = new KisImage(document->createUndoStore(), imgSize.width(), imgSize.height(), image->colorSpace(), image->objectName());
- dst->setResolution(image->xRes(), image->yRes());
- document->setCurrentImage(dst);
-
- KisPaintLayer* paintLayer = new KisPaintLayer(dst, dst->nextLayerName(), 255);
- KisPainter gc(paintLayer->paintDevice());
- gc.bitBlt(QPoint(0, 0), image->projection(), imgSize);
-
- dst->addNode(paintLayer, KisNodeSP(0));
- dst->refreshGraph();
+ { // make sure dst is deleted before calling 'delete exportDocument',
+ // since KisDocument checks that its image is properly deref()'d.
+ KisImageSP dst = new KisImage(document->createUndoStore(), imgSize.width(), imgSize.height(), image->colorSpace(), image->objectName());
+ dst->setResolution(image->xRes(), image->yRes());
+ document->setCurrentImage(dst);
+
+ KisPaintLayer* paintLayer = new KisPaintLayer(dst, dst->nextLayerName(), 255);
+ KisPainter gc(paintLayer->paintDevice());
+ gc.bitBlt(QPoint(0, 0), image->projection(), imgSize);
+
+ dst->addNode(paintLayer, KisNodeSP(0));
+ dst->refreshGraph();
+ }
document->setFileBatchMode(true);
if (!document->exportDocumentSync(QUrl::fromLocalFile(url), mimeType.toLatin1())) {
if (document->errorMessage().isEmpty()) {
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not save\n%1", document->localFilePath()));
} else {
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not save %1\nReason: %2", document->localFilePath(), document->errorMessage()));
}
return false;
}
delete document;
return true;
}
void Imagesplit::slotImagesplit()
{
// Taking the title - url from caption function and removing file extension
QStringList strList = ((viewManager()->document())->caption()).split('.');
QString suffix = strList.at(0);
// Getting all mime types and converting them into names which are displayed at combo box
QStringList listMimeFilter = KisImportExportManager::supportedMimeTypes(KisImportExportManager::Export);
QString defaultMime = QString::fromLatin1(viewManager()->document()->mimeType());
+ if (defaultMime.isEmpty()) defaultMime = QString::fromLatin1(viewManager()->document()->nativeFormatMimeType());
int defaultMimeIndex = 0;
listMimeFilter.sort();
QStringList filteredMimeTypes;
QStringList listFileType;
int i = 0;
- Q_FOREACH (const QString & mimeType, listMimeFilter) {
+ Q_FOREACH (const QString &mimeType, listMimeFilter) {
listFileType.append(KisMimeDatabase::descriptionForMimeType(mimeType));
filteredMimeTypes.append(mimeType);
if (mimeType == defaultMime) {
defaultMimeIndex = i;
}
i++;
}
listMimeFilter = filteredMimeTypes;
Q_ASSERT(listMimeFilter.size() == listFileType.size());
DlgImagesplit *dlgImagesplit = new DlgImagesplit(viewManager(), suffix, listFileType, defaultMimeIndex);
dlgImagesplit->setObjectName("Imagesplit");
Q_CHECK_PTR(dlgImagesplit);
KisImageWSP image = viewManager()->image();
if (dlgImagesplit->exec() == QDialog::Accepted) {
int numHorizontalLines = dlgImagesplit->horizontalLines();
int numVerticalLines = dlgImagesplit->verticalLines();
int img_width = image->width() / (numVerticalLines + 1);
int img_height = image->height() / (numHorizontalLines + 1);
bool stop = false;
+
+ QString mimeType;
+ QString filepath;
+ QString homepath;
+ QString suffix;
+
if (dlgImagesplit->autoSave()) {
KoFileDialog dialog(viewManager()->mainWindow(), KoFileDialog::OpenDirectory, "OpenDocument");
dialog.setCaption(i18n("Save Image on Split"));
dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
QStringList mimeFilter = viewManager()->document()->importExportManager()->supportedMimeTypes(KisImportExportManager::Export);
QString defaultMime = QString::fromLatin1(viewManager()->document()->mimeType());
dialog.setMimeTypeFilters(mimeFilter, defaultMime);
QUrl directory = QUrl::fromUserInput(dialog.filename());
if (directory.isEmpty())
return;
- for (int i = 0, k = 1; i < (numVerticalLines + 1); i++) {
- for (int j = 0; j < (numHorizontalLines + 1); j++, k++) {
- QString mimeTypeSelected = listMimeFilter.at(dlgImagesplit->cmbIndex);
- QString homepath = directory.toLocalFile();
- QString suffix = KisMimeDatabase::suffixesForMimeType(mimeTypeSelected).first();
- qDebug() << "suffix" << suffix;
- if (suffix.startsWith("*.")) {
- suffix = suffix.remove(0, 1);
- }
- qDebug() << "\tsuffix" << suffix;
- if (!suffix.startsWith(".")) {
- suffix = suffix.prepend('.');
- }
- qDebug() << "\tsuffix" << suffix;
- QString fileName = dlgImagesplit->suffix() + '_' + QString::number(k) + suffix;
- QString url = homepath + '/' + fileName;
- if (!saveAsImage(QRect((i * img_width), (j * img_height), img_width, img_height), listMimeFilter.at(dlgImagesplit->cmbIndex), url)) {
- stop = true;
- break;
- }
- }
- if (stop) {
- break;
- }
+
+ mimeType = listMimeFilter.at(dlgImagesplit->cmbIndex);
+ homepath = directory.toLocalFile();
+ suffix = KisMimeDatabase::suffixesForMimeType(mimeType).first();
+ if (suffix.startsWith("*.")) {
+ suffix = suffix.remove(0, 1);
+ }
+ if (!suffix.startsWith(".")) {
+ suffix = suffix.prepend('.');
}
+ }
+
+ int outerLoop;
+ int innerLoop;
+
+ if (dlgImagesplit->sortHorizontal()) {
+ outerLoop = numHorizontalLines + 1;
+ innerLoop = numVerticalLines + 1;
}
else {
+ outerLoop = numVerticalLines + 1;
+ innerLoop = numHorizontalLines + 1;
+ }
+
+
+ for (int i = 0, k = 1; i < outerLoop; i++) {
+ for (int j = 0; j < innerLoop; j++, k++) {
+ int row;
+ int column;
+ if (dlgImagesplit->sortHorizontal()) {
+ row = i;
+ column = j;
+ }
+ else {
+ row = j;
+ column = i;
+ }
- for (int i = 0; i < (numVerticalLines + 1); i++) {
- for (int j = 0; j < (numHorizontalLines + 1); j++) {
+ if (dlgImagesplit->autoSave()) {
+ QString fileName = dlgImagesplit->suffix() + '_' + QString::number(k) + suffix;
+ filepath = homepath + '/' + fileName;
+ mimeType = listMimeFilter.at(dlgImagesplit->cmbIndex);
+ }
+ else {
KoFileDialog dialog(viewManager()->mainWindow(), KoFileDialog::SaveFile, "OpenDocument");
dialog.setCaption(i18n("Save Image on Split"));
dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
dialog.setMimeTypeFilters(listMimeFilter, defaultMime);
QUrl url = QUrl::fromUserInput(dialog.filename());
+ filepath = url.toLocalFile();
- QString mimefilter = KisMimeDatabase::mimeTypeForFile(url.toLocalFile(), false);
+ mimeType = KisMimeDatabase::mimeTypeForFile(url.toLocalFile(), false);
if (url.isEmpty())
return;
- if (!saveAsImage(QRect((i * img_width), (j * img_height), img_width, img_height), mimefilter, url.toLocalFile())) {
- stop = true;
- break;
- }
+
}
- if (stop) {
+ if (!saveAsImage(QRect((column * img_width), (row * img_height), img_width, img_height), mimeType, filepath)) {
+ stop = true;
break;
}
}
-
+ if (stop) {
+ break;
+ }
}
-
}
delete dlgImagesplit;
}
#include "imagesplit.moc"
diff --git a/plugins/extensions/imagesplit/wdg_imagesplit.cpp b/plugins/extensions/imagesplit/wdg_imagesplit.cpp
index dbe488fe19..6bcb5fbe8b 100644
--- a/plugins/extensions/imagesplit/wdg_imagesplit.cpp
+++ b/plugins/extensions/imagesplit/wdg_imagesplit.cpp
@@ -1,44 +1,45 @@
/*
* dlg_imagesplit.cc - part of KimageShop^WKrayon^WKrita
*
* Copyright (c) 2009 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
*
* 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 "wdg_imagesplit.h"
#include <QPainter>
#include <QStringList>
#include <QImage>
#include <QListWidgetItem>
#include <kis_debug.h>
#include "kis_config.h"
WdgImagesplit::WdgImagesplit(QWidget* parent)
: QWidget(parent)
{
setupUi(this);
KisConfig cfg(true);
intHorizontalSplitLines->setValue(cfg.horizontalSplitLines());
intVerticalSplitLines->setValue(cfg.verticalSplitLines());
+ chkHorizontal->setChecked(true);
chkAutoSave->setChecked(true);
}
diff --git a/plugins/extensions/imagesplit/wdg_imagesplit.ui b/plugins/extensions/imagesplit/wdg_imagesplit.ui
index 4fd7708036..fd61f5cd99 100644
--- a/plugins/extensions/imagesplit/wdg_imagesplit.ui
+++ b/plugins/extensions/imagesplit/wdg_imagesplit.ui
@@ -1,124 +1,156 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WdgImagesplit</class>
<widget class="QWidget" name="WdgImagesplit">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
- <width>236</width>
- <height>194</height>
+ <width>202</width>
+ <height>316</height>
</rect>
</property>
<property name="windowTitle">
<string>Image Size</string>
</property>
- <layout class="QGridLayout" name="gridLayout_2">
- <item row="0" column="0">
- <layout class="QGridLayout" name="gridLayout">
- <item row="0" column="0">
- <layout class="QFormLayout" name="formLayout">
- <item row="0" column="0">
- <widget class="QLabel" name="lblHorizontalSplitLines">
- <property name="text">
- <string>Horizontal Lines</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="KisIntParseSpinBox" name="intHorizontalSplitLines"/>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="lblVerticalSplitLines">
- <property name="text">
- <string>Vertical Lines</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="KisIntParseSpinBox" name="intVerticalSplitLines"/>
- </item>
- </layout>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="1" column="1">
+ <widget class="KisIntParseSpinBox" name="intHorizontalSplitLines"/>
</item>
- <item row="1" column="0">
+ <item row="3" column="1">
+ <widget class="KisIntParseSpinBox" name="intVerticalSplitLines"/>
+ </item>
+ <item row="4" column="0" colspan="2">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Sort Direction:</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QRadioButton" name="chkHorizontal">
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ </font>
+ </property>
+ <property name="text">
+ <string>Hori&amp;zontal</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="chkVertical">
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ </font>
+ </property>
+ <property name="text">
+ <string>&amp;Vertical</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="lblSuffix">
+ <property name="text">
+ <string>Prefix</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QLineEdit" name="lineEdit">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="0">
+ <widget class="QLabel" name="lblFileType">
+ <property name="text">
+ <string>File Type</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="1">
+ <widget class="QComboBox" name="cmbFileType">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="9" column="0" colspan="2">
<widget class="QCheckBox" name="chkAutoSave">
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Autosave on Split</string>
</property>
</widget>
</item>
- <item row="2" column="0">
- <layout class="QFormLayout" name="formLayout_2">
- <item row="0" column="0">
- <widget class="QLabel" name="lblSuffix">
- <property name="text">
- <string>Prefix</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QLineEdit" name="lineEdit">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="lblFileType">
- <property name="text">
- <string>File Type</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QComboBox" name="cmbFileType"/>
- </item>
- </layout>
+ <item row="1" column="0">
+ <widget class="QLabel" name="lblHorizontalSplitLines">
+ <property name="text">
+ <string>Horizontal Lines</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
</item>
<item row="3" column="0">
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
+ <widget class="QLabel" name="lblVerticalSplitLines">
+ <property name="text">
+ <string>Vertical Lines</string>
</property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>38</height>
- </size>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
- </spacer>
+ </widget>
</item>
</layout>
</item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>2</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KisIntParseSpinBox</class>
<extends>QSpinBox</extends>
<header>kis_int_parse_spin_box.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
diff --git a/plugins/extensions/pykrita/plugin/utilities.h b/plugins/extensions/pykrita/plugin/utilities.h
index a65d1ff520..2a71d22de7 100644
--- a/plugins/extensions/pykrita/plugin/utilities.h
+++ b/plugins/extensions/pykrita/plugin/utilities.h
@@ -1,249 +1,251 @@
// This file is part of PyKrita, Krita' Python scripting plugin.
//
// A couple of useful macros and functions used inside of pykrita_engine.cpp and pykrita_plugin.cpp.
//
// Copyright (C) 2006 Paul Giannaros <paul@giannaros.org>
// Copyright (C) 2012, 2013 Shaheed Haque <srhaque@theiet.org>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) version 3, or any
// later version accepted by the membership of KDE e.V. (or its
// successor approved by the membership of KDE e.V.), which shall
// act as a proxy defined in Section 6 of version 3 of the license.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef __PYKRITA_UTILITIES_H__
# define __PYKRITA_UTILITIES_H__
#include <cmath>
#include <Python.h>
#include <QString>
class KConfigBase;
/// Save us some ruddy time when printing out QStrings with UTF-8
# define PQ(x) x.toUtf8().constData()
class PythonPluginManager;
namespace PyKrita
{
enum InitResult {
INIT_UNINITIALIZED,
INIT_OK,
INIT_CANNOT_LOAD_PYTHON_LIBRARY,
INIT_CANNOT_SET_PYTHON_PATHS,
INIT_CANNOT_LOAD_PYKRITA_MODULE,
};
/**
* Initialize the Python environment and plugin manager.
* This should be called first before using the manager
* or the Python class.
*/
InitResult initialize();
/**
* Gets the instance of the plugin manager.
* Note: PyKrita::initialize() must be called
* before using this function.
*/
PythonPluginManager *pluginManager();
/**
* Cleanup after Python.
* Note: doing this as part of static/global destruction will not
* work. The call to Py_Finalize() would happen after the Python
* runtime has already been finalized, leading to a segfault.
*/
void finalize();
/**
* Instantiate this class on the stack to automatically get and release the
* GIL.
*
* Also, making all the utility functions members of this class means that in
* many cases the compiler tells us where the class in needed. In the remaining
* cases (i.e. bare calls to the Python C API), inspection is used to needed
* to add the requisite Python() object. To prevent this object being optimised
* away in these cases due to lack of use, all instances have the form of an
* assignment, e.g.:
*
* Python py = Python()
*
* This adds a little overhead, but this is a small price for consistency.
*/
class Python
{
public:
Python();
~Python();
/**
* Load the Python shared library. This does nothing on Windows.
*/
static bool libraryLoad();
/**
* Set the Python paths by calling Py_SetPath. This should be called before
* initialization to ensure the proper libraries get loaded.
*/
static bool setPath(const QStringList& scriptPaths);
/**
* Make sure the Python interpreter is initialized. Ideally should be only
* called once.
*/
static void ensureInitialized();
/**
* Finalize the Python interpreter. Not guaranteed to work.
*/
static void maybeFinalize();
/**
* Unload the Python shared library. This does nothing on Windows.
*/
static void libraryUnload();
/// Convert a QString to a Python unicode object.
static PyObject* unicode(const QString& string);
/// Convert a Python unicode object to a QString.
static QString unicode(PyObject* string);
/// Test if a Python object is compatible with a QString.
static bool isUnicode(PyObject* string);
/// Prepend a QString to a list as a Python unicode object
bool prependStringToList(PyObject* list, const QString& value);
/**
* Print and save (see @ref lastTraceback()) the current traceback in a
* form approximating what Python would print:
*
+ * @verbatim
* Traceback (most recent call last):
* File "/home/shahhaqu/.kde/share/apps/krita/pykrita/pluginmgr.py", line 13, in <module>
* import kdeui
* ImportError: No module named kdeui
* Could not import pluginmgr.
+ * @endverbatim
*/
void traceback(const QString& description);
/**
* Store the last traceback we handled using @ref traceback().
*/
QString lastTraceback(void) const;
/**
* Call the named module's named entry point.
*/
bool functionCall(const char* functionName, const char* moduleName = PYKRITA_ENGINE);
PyObject* functionCall(const char* functionName, const char* moduleName, PyObject* arguments);
/**
* Delete the item from the named module's dictionary.
*/
bool itemStringDel(const char* item, const char* moduleName = PYKRITA_ENGINE);
/**
* Get the item from the named module's dictionary.
*
* @return 0 or a borrowed reference to the item.
*/
PyObject* itemString(const char* item, const char* moduleName = PYKRITA_ENGINE);
/**
* Get the item from the given dictionary.
*
* @return 0 or a borrowed reference to the item.
*/
PyObject* itemString(const char* item, PyObject* dict);
/**
* Set the item in the named module's dictionary.
*/
bool itemStringSet(const char* item, PyObject* value, const char* moduleName = PYKRITA_ENGINE);
/**
* Get the Actions defined by a module. The returned object is
* [ { function, ( text, icon, shortcut, menu ) }... ] for each module
- * function decorated with @action.
+ * function decorated with @c action.
*
* @return 0 or a new reference to the result.
*/
PyObject* moduleActions(const char* moduleName);
/**
* Get the ConfigPages defined by a module. The returned object is
* [ { function, callable, ( name, fullName, icon ) }... ] for each module
- * function decorated with @configPage.
+ * function decorated with @c configPage.
*
* @return 0 or a new reference to the result.
*/
PyObject* moduleConfigPages(const char* moduleName);
/**
* Get the named module's dictionary.
*
* @return 0 or a borrowed reference to the dictionary.
*/
PyObject* moduleDict(const char* moduleName = PYKRITA_ENGINE);
/**
* Get the help text defined by a module.
*/
QString moduleHelp(const char* moduleName);
/**
* Import the named module.
*
* @return 0 or a borrowed reference to the module.
*/
PyObject* moduleImport(const char* moduleName);
/**
* Add a given path to to the front of \c PYTHONPATH
*
* @param path A string (path) to be added
* @return @c true on success, @c false otherwise.
*/
bool prependPythonPaths(const QString& path);
/**
* Add listed paths to to the front of \c PYTHONPATH
*
* @param paths A string list (paths) to be added
* @return @c true on success, @c false otherwise.
*/
bool prependPythonPaths(const QStringList& paths);
static const char* PYKRITA_ENGINE;
private:
/// @internal Helper function for @c prependPythonPaths overloads
bool prependPythonPaths(const QString&, PyObject*);
PyGILState_STATE m_state;
mutable QString m_traceback;
/**
* Run a handler function supplied by the krita module on another module.
*
* @return 0 or a new reference to the result.
*/
PyObject* kritaHandler(const char* moduleName, const char* handler);
};
} // namespace PyKrita
#endif // __PYKRITA_UTILITIES_H__
// krita: indent-width 4;
diff --git a/plugins/extensions/pykrita/sip/CMakeLists.txt b/plugins/extensions/pykrita/sip/CMakeLists.txt
index 754df9ba41..49ff5e1717 100644
--- a/plugins/extensions/pykrita/sip/CMakeLists.txt
+++ b/plugins/extensions/pykrita/sip/CMakeLists.txt
@@ -1,30 +1,30 @@
include(SIPMacros)
message( ${SIP_VERSION} " - The version of SIP found expressed as a 6 digit hex number suitable for comparison as a string.")
message( ${SIP_VERSION_STR} " - The version of SIP found as a human readable string.")
message( ${SIP_EXECUTABLE} " - Path and filename of the SIP command line executable.")
message( ${SIP_INCLUDE_DIR} " - Directory holding the SIP C++ header file.")
message( ${SIP_DEFAULT_SIP_DIR} " - default SIP dir" )
set(SIP_INCLUDES
${SIP_DEFAULT_SIP_DIR}
${PYQT5_SIP_DIR}
${PYQT_SIP_DIR_OVERRIDE}
./krita)
set(SIP_CONCAT_PARTS 1)
set(SIP_TAGS ALL WS_X11 ${PYQT5_VERSION_TAG})
-set(SIP_EXTRA_OPTIONS -g -x PyKDE_QVector ${PYQT5_SIP_NAME})
+set(SIP_EXTRA_OPTIONS -g -o -x PyKDE_QVector ${PYQT5_SIP_NAME})
set(PYTHON_SITE_PACKAGES_INSTALL_DIR ${LIB_INSTALL_DIR}/krita-python-libs)
file(GLOB PYKRITA_KRITA_sip_files ./krita/*.sip)
set(SIP_EXTRA_FILES_DEPEND ${PYKRITA_KRITA_sip_files})
add_sip_python_module(PyKrita.krita ./krita/kritamod.sip kritalibkis kritaui kritaimage kritalibbrush)
if (ENABLE_PYTHON_2)
# Add an init file to turn it into a valid py2 module.
# Otherwise PyKrita cannot be loaded.
install(FILES
./__init__.py
DESTINATION ${PYTHON_SITE_PACKAGES_INSTALL_DIR}/PyKrita)
endif (ENABLE_PYTHON_2)
diff --git a/plugins/extensions/pykrita/sip/krita/Node.sip b/plugins/extensions/pykrita/sip/krita/Node.sip
index d5d592216a..8a37503f9c 100644
--- a/plugins/extensions/pykrita/sip/krita/Node.sip
+++ b/plugins/extensions/pykrita/sip/krita/Node.sip
@@ -1,67 +1,68 @@
class Node : QObject
{
%TypeHeaderCode
#include "Node.h"
%End
Node(const Node & __0);
public:
virtual ~Node();
bool operator==(const Node &other) const;
bool operator!=(const Node &other) const;
public Q_SLOTS:
//QList<Shape *> shapes() const /Factory/;
Node *clone() const /Factory/;
bool alphaLocked() const;
void setAlphaLocked(bool value);
QString blendingMode() const;
void setBlendingMode(QString value);
QList<Channel *> channels() const /Factory/;
QList<Node *> childNodes() const /Factory/;
bool addChildNode(Node *child, Node *above);
bool removeChildNode(Node *child);
void setChildNodes(QList<Node *> nodes);
QString colorDepth() const;
bool animated() const;
void enableAnimation() const;
void setShowInTimeline(bool showInTimeline) const;
bool showInTimeline() const;
void setCollapsed(bool collapsed);
bool collapsed() const;
int colorLabel() const;
void setColorLabel(int value);
QString colorModel() const;
QString colorProfile() const;
bool setColorProfile(const QString &colorProfile);
bool setColorSpace(const QString &colorModel, const QString &colorDepth, const QString &colorProfile);
bool inheritAlpha() const;
void setInheritAlpha(bool value);
bool locked() const;
void setLocked(bool value);
QString name() const;
void setName(QString value);
int opacity() const;
void setOpacity(int value);
Node * parentNode() const /Factory/;
QString type() const;
QIcon icon() const;
bool visible() const;
+ bool hasKeyframeAtTime(int frameNumber);
void setVisible(bool value);
QByteArray pixelData(int x, int y, int w, int h) const;
QByteArray pixelDataAtTime(int x, int y, int w, int h, int time) const;
QByteArray projectionPixelData(int x, int y, int w, int h) const;
void setPixelData(QByteArray value, int x, int y, int w, int h);
QRect bounds() const;
void move(int x, int y);
QPoint position() const;
bool remove();
Node *duplicate() /Factory/;
- void save(const QString &filename, double xRes, double yRes);
+ void save(const QString &filename, double xRes, double yRes, const InfoObject & exportConfiguration);
Node *mergeDown() /Factory/;
- void scaleNode(const QPointF &origin, int width, int height, QString strategy);
+ void scaleNode(QPointF origin, int width, int height, QString strategy);
void rotateNode(double radians);
void cropNode(int x, int y, int w, int h);
void shearNode(double angleX, double angleY);
QImage thumbnail(int w, int h);
Q_SIGNALS:
private:
};
diff --git a/plugins/extensions/resourcemanager/resourcemanager.cpp b/plugins/extensions/resourcemanager/resourcemanager.cpp
index f4e9d676e3..7efb4bb064 100644
--- a/plugins/extensions/resourcemanager/resourcemanager.cpp
+++ b/plugins/extensions/resourcemanager/resourcemanager.cpp
@@ -1,335 +1,334 @@
/*
* resourcemanager.cc -- Part of Krita
*
* Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org)
*
* 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 "resourcemanager.h"
#include <QDir>
#include <QFileInfo>
#include <QTimer>
#include <QThread>
#include <QMessageBox>
#include <QGlobalStatic>
#include <QStandardPaths>
#include <klocalizedstring.h>
#include <KoResourcePaths.h>
#include <kpluginfactory.h>
#include <KoFileDialog.h>
#include <resources/KoResource.h>
#include <KoResourceServer.h>
#include <KoResourceServerProvider.h>
#include <kis_debug.h>
#include <kis_action.h>
#include <KisViewManager.h>
#include <KisResourceServerProvider.h>
#include <kis_workspace_resource.h>
#include <brushengine/kis_paintop_preset.h>
#include <kis_brush_server.h>
#include <kis_paintop_settings.h>
#include "dlg_bundle_manager.h"
#include "dlg_create_bundle.h"
#include <KisPaintopSettingsIds.h>
#include "krita_container_utils.h"
class ResourceManager::Private {
public:
Private()
{
brushServer = KisBrushServer::instance()->brushServer();
paintopServer = KisResourceServerProvider::instance()->paintOpPresetServer();
gradientServer = KoResourceServerProvider::instance()->gradientServer();
patternServer = KoResourceServerProvider::instance()->patternServer();
paletteServer = KoResourceServerProvider::instance()->paletteServer();
workspaceServer = KisResourceServerProvider::instance()->workspaceServer();
gamutMaskServer = KoResourceServerProvider::instance()->gamutMaskServer();
}
KisBrushResourceServer* brushServer;
KisPaintOpPresetResourceServer * paintopServer;
KoResourceServer<KoAbstractGradient>* gradientServer;
KoResourceServer<KoPattern> *patternServer;
KoResourceServer<KoColorSet>* paletteServer;
KoResourceServer<KisWorkspaceResource>* workspaceServer;
KoResourceServer<KoGamutMask>* gamutMaskServer;
};
K_PLUGIN_FACTORY_WITH_JSON(ResourceManagerFactory, "kritaresourcemanager.json", registerPlugin<ResourceManager>();)
ResourceManager::ResourceManager(QObject *parent, const QVariantList &)
: KisActionPlugin(parent)
, d(new Private())
{
KisAction *action = new KisAction(i18n("Import Bundles..."), this);
addAction("import_bundles", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotImportBundles()));
action = new KisAction(i18n("Import Brushes..."), this);
addAction("import_brushes", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotImportBrushes()));
action = new KisAction(i18n("Import Gradients..."), this);
addAction("import_gradients", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotImportGradients()));
action = new KisAction(i18n("Import Palettes..."), this);
addAction("import_palettes", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotImportPalettes()));
action = new KisAction(i18n("Import Patterns..."), this);
addAction("import_patterns", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotImportPatterns()));
action = new KisAction(i18n("Import Presets..."), this);
addAction("import_presets", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotImportPresets()));
action = new KisAction(i18n("Import Workspaces..."), this);
addAction("import_workspaces", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotImportWorkspaces()));
action = new KisAction(i18n("Create Resource Bundle..."), this);
addAction("create_bundle", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotCreateBundle()));
action = new KisAction(i18n("Manage Resources..."), this);
addAction("manage_bundles", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotManageBundles()));
}
ResourceManager::~ResourceManager()
{
}
void ResourceManager::slotCreateBundle()
{
DlgCreateBundle dlgCreateBundle;
if (dlgCreateBundle.exec() != QDialog::Accepted) {
return;
}
saveBundle(dlgCreateBundle);
}
KisResourceBundle *ResourceManager::saveBundle(const DlgCreateBundle &dlgCreateBundle)
{
QString bundlePath = dlgCreateBundle.saveLocation() + "/" + dlgCreateBundle.bundleName() + ".bundle";
KisResourceBundle *newBundle = new KisResourceBundle(bundlePath);
newBundle->addMeta("name", dlgCreateBundle.bundleName());
newBundle->addMeta("author", dlgCreateBundle.authorName());
newBundle->addMeta("email", dlgCreateBundle.email());
newBundle->addMeta("license", dlgCreateBundle.license());
newBundle->addMeta("website", dlgCreateBundle.website());
newBundle->addMeta("description", dlgCreateBundle.description());
newBundle->setThumbnail(dlgCreateBundle.previewImage());
QStringList res = dlgCreateBundle.selectedBrushes();
Q_FOREACH (const QString &r, res) {
KoResource *res = d->brushServer->resourceByFilename(r).data();
newBundle->addResource("kis_brushes", res->filename(), d->brushServer->assignedTagsList(res), res->md5());
}
res = dlgCreateBundle.selectedGradients();
Q_FOREACH (const QString &r, res) {
KoResource *res = d->gradientServer->resourceByFilename(r);
newBundle->addResource("ko_gradients", res->filename(), d->gradientServer->assignedTagsList(res), res->md5());
}
res = dlgCreateBundle.selectedPalettes();
Q_FOREACH (const QString &r, res) {
KoResource *res = d->paletteServer->resourceByFilename(r);
newBundle->addResource("ko_palettes", res->filename(), d->paletteServer->assignedTagsList(res), res->md5());
}
res = dlgCreateBundle.selectedPatterns();
Q_FOREACH (const QString &r, res) {
KoResource *res = d->patternServer->resourceByFilename(r);
newBundle->addResource("ko_patterns", res->filename(), d->patternServer->assignedTagsList(res), res->md5());
}
res = dlgCreateBundle.selectedPresets();
Q_FOREACH (const QString &r, res) {
KisPaintOpPresetSP preset = d->paintopServer->resourceByFilename(r);
KoResource *res = preset.data();
newBundle->addResource("kis_paintoppresets", res->filename(), d->paintopServer->assignedTagsList(res), res->md5());
KisPaintOpSettingsSP settings = preset->settings();
QStringList requiredFiles = settings->getStringList(KisPaintOpUtils::RequiredBrushFilesListTag);
requiredFiles << settings->getString(KisPaintOpUtils::RequiredBrushFileTag);
KritaUtils::makeContainerUnique(requiredFiles);
Q_FOREACH (const QString &brushFile, requiredFiles) {
KisBrush *brush = d->brushServer->resourceByFilename(brushFile).data();
if (brush) {
newBundle->addResource("kis_brushes", brushFile, d->brushServer->assignedTagsList(brush), brush->md5());
} else {
qWarning() << "There is no brush with name" << brushFile;
}
}
}
res = dlgCreateBundle.selectedWorkspaces();
Q_FOREACH (const QString &r, res) {
KoResource *res = d->workspaceServer->resourceByFilename(r);
newBundle->addResource("kis_workspaces", res->filename(), d->workspaceServer->assignedTagsList(res), res->md5());
}
res = dlgCreateBundle.selectedGamutMasks();
Q_FOREACH (const QString &r, res) {
KoResource *res = d->gamutMaskServer->resourceByFilename(r);
newBundle->addResource("ko_gamutmasks", res->filename(), d->gamutMaskServer->assignedTagsList(res), res->md5());
}
newBundle->addMeta("fileName", bundlePath);
newBundle->addMeta("created", QDate::currentDate().toString("dd/MM/yyyy"));
if (!newBundle->save()) {
QMessageBox::critical(viewManager()->mainWindow(), i18nc("@title:window", "Krita"), i18n("Could not create the new bundle."));
}
else {
newBundle->setValid(true);
if (QDir(KisResourceBundleServerProvider::instance()->resourceBundleServer()->saveLocation()) != QDir(QFileInfo(bundlePath).path())) {
newBundle->setFilename(KisResourceBundleServerProvider::instance()->resourceBundleServer()->saveLocation() + "/" + dlgCreateBundle.bundleName() + ".bundle");
}
if (KisResourceBundleServerProvider::instance()->resourceBundleServer()->resourceByName(newBundle->name())) {
KisResourceBundleServerProvider::instance()->resourceBundleServer()->removeResourceFromServer(
KisResourceBundleServerProvider::instance()->resourceBundleServer()->resourceByName(newBundle->name()));
}
KisResourceBundleServerProvider::instance()->resourceBundleServer()->addResource(newBundle, true);
newBundle->load();
}
return newBundle;
}
void ResourceManager::slotManageBundles()
{
DlgBundleManager* dlg = new DlgBundleManager(this, viewManager()->actionManager());
if (dlg->exec() != QDialog::Accepted) {
return;
}
}
QStringList ResourceManager::importResources(const QString &title, const QStringList &mimes) const
{
KoFileDialog dialog(viewManager()->mainWindow(), KoFileDialog::OpenFiles, "krita_resources");
dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation));
dialog.setCaption(title);
dialog.setMimeTypeFilters(mimes);
return dialog.filenames();
}
void ResourceManager::slotImportBrushes()
{
QStringList resources = importResources(i18n("Import Brushes"), QStringList() << "image/x-gimp-brush"
<< "image/x-gimp-x-gimp-brush-animated"
<< "image/x-adobe-brushlibrary"
<< "image/png"
<< "image/svg+xml");
Q_FOREACH (const QString &res, resources) {
d->brushServer->importResourceFile(res);
}
}
void ResourceManager::slotImportPresets()
{
QStringList resources = importResources(i18n("Import Presets"), QStringList() << "application/x-krita-paintoppreset");
Q_FOREACH (const QString &res, resources) {
d->paintopServer->importResourceFile(res);
}
}
void ResourceManager::slotImportGradients()
{
QStringList resources = importResources(i18n("Import Gradients"), QStringList() << "image/svg+xml"
- << "application/x-gimp-gradient"
- << "application/x-karbon-gradient");
+ << "application/x-gimp-gradient");
Q_FOREACH (const QString &res, resources) {
d->gradientServer->importResourceFile(res);
}
}
void ResourceManager::slotImportBundles()
{
QStringList resources = importResources(i18n("Import Bundles"), QStringList() << "application/x-krita-bundle");
Q_FOREACH (const QString &res, resources) {
KisResourceBundle *bundle = KisResourceBundleServerProvider::instance()->resourceBundleServer()->createResource(res);
bundle->load();
if (bundle->valid()) {
if (!bundle->install()) {
QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Could not install the resources for bundle %1.", res));
}
}
else {
QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Could not load bundle %1.", res));
}
QFileInfo fi(res);
QString newFilename = KisResourceBundleServerProvider::instance()->resourceBundleServer()->saveLocation() + fi.baseName() + bundle->defaultFileExtension();
QFileInfo fileInfo(newFilename);
int i = 1;
while (fileInfo.exists()) {
fileInfo.setFile(KisResourceBundleServerProvider::instance()->resourceBundleServer()->saveLocation() + fi.baseName() + QString("%1").arg(i) + bundle->defaultFileExtension());
i++;
}
bundle->setFilename(fileInfo.filePath());
QFile::copy(res, newFilename);
KisResourceBundleServerProvider::instance()->resourceBundleServer()->addResource(bundle, false);
}
}
void ResourceManager::slotImportPatterns()
{
QStringList resources = importResources(i18n("Import Patterns"), QStringList() << "image/png"
<< "image/svg+xml"
<< "application/x-gimp-pattern"
<< "image/jpeg"
<< "image/tiff"
<< "image/bmp"
<< "image/xpg");
Q_FOREACH (const QString &res, resources) {
d->patternServer->importResourceFile(res);
}
}
void ResourceManager::slotImportPalettes()
{
QStringList resources = importResources(i18n("Import Palettes"), QStringList() << "image/x-gimp-color-palette");
Q_FOREACH (const QString &res, resources) {
d->paletteServer->importResourceFile(res);
}
}
void ResourceManager::slotImportWorkspaces()
{
QStringList resources = importResources(i18n("Import Workspaces"), QStringList() << "application/x-krita-workspace");
Q_FOREACH (const QString &res, resources) {
d->workspaceServer->importResourceFile(res);
}
}
#include "resourcemanager.moc"
diff --git a/plugins/filters/colors/kis_color_to_alpha.cpp b/plugins/filters/colors/kis_color_to_alpha.cpp
index 8f1794b05f..b5fdafe079 100644
--- a/plugins/filters/colors/kis_color_to_alpha.cpp
+++ b/plugins/filters/colors/kis_color_to_alpha.cpp
@@ -1,189 +1,189 @@
/*
* This file is part of Krita
*
* Copyright (c) 2006 Cyrille Berger <cberger@cberger.net>
*
* 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_color_to_alpha.h"
#include <QCheckBox>
#include <QSpinBox>
#include <KoColorSpaceMaths.h>
+#include <KoConfig.h>
#include <KoUpdater.h>
#include "kis_progress_update_helper.h"
#include <kis_paint_device.h>
#include <kis_selection.h>
#include <filter/kis_filter_category_ids.h>
#include <filter/kis_filter_configuration.h>
#include <kis_processing_information.h>
#include "ui_wdgcolortoalphabase.h"
#include "kis_wdg_color_to_alpha.h"
#include <kis_iterator_ng.h>
#include <KisSequentialIteratorProgress.h>
KisFilterColorToAlpha::KisFilterColorToAlpha()
: KisFilter(id(), FiltersCategoryColorId, i18n("&Color to Alpha..."))
{
setSupportsPainting(true);
setSupportsAdjustmentLayers(true);
setSupportsLevelOfDetail(true);
setColorSpaceIndependence(FULLY_INDEPENDENT);
}
KisConfigWidget * KisFilterColorToAlpha::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP) const
{
return new KisWdgColorToAlpha(parent);
}
KisFilterConfigurationSP KisFilterColorToAlpha::factoryConfiguration() const
{
KisFilterConfigurationSP config = new KisFilterConfiguration("colortoalpha", 1);
config->setProperty("targetcolor", QColor(255, 255, 255));
config->setProperty("threshold", 100);
return config;
}
template<typename channel_type, typename composite_type>
inline void inverseOver(const int numChannels, const int *channelIndex,
channel_type *dst, const channel_type *baseColor,
qreal dstOpacity)
{
for (int i = 0; i < numChannels; i++) {
const int idx = channelIndex[i];
dst[idx] =
KoColorSpaceMaths<channel_type>::clamp(
(static_cast<composite_type>(dst[idx]) - baseColor[idx]) / dstOpacity + baseColor[idx]);
}
}
template<typename channel_type, typename composite_type>
void applyToIterator(const int numChannels, const int *channelIndex,
KisSequentialIteratorProgress &it, KoColor baseColor,
int threshold, const KoColorSpace *cs)
{
qreal thresholdF = threshold;
quint8 *baseColorData_uint8 = baseColor.data();
channel_type *baseColorData = reinterpret_cast<channel_type*>(baseColorData_uint8);
while (it.nextPixel()) {
channel_type *dst = reinterpret_cast<channel_type*>(it.rawData());
quint8 *dst_uint8 = it.rawData();
quint8 diff = cs->difference(baseColorData_uint8, dst_uint8);
qreal newOpacity = diff >= threshold ? 1.0 : diff / thresholdF;
if(newOpacity < cs->opacityF(dst_uint8)) {
cs->setOpacity(dst_uint8, newOpacity, 1);
}
inverseOver<channel_type, composite_type>(numChannels, channelIndex,
dst, baseColorData,
newOpacity);
}
}
void KisFilterColorToAlpha::processImpl(KisPaintDeviceSP device,
const QRect& rect,
const KisFilterConfigurationSP _config,
KoUpdater* progressUpdater
) const
{
Q_ASSERT(device != 0);
KisFilterConfigurationSP config = _config ? _config : new KisFilterConfiguration("colortoalpha", 1);
QVariant value;
QColor cTA = (config->getProperty("targetcolor", value)) ? value.value<QColor>() : QColor(255, 255, 255);
int threshold = (config->getProperty("threshold", value)) ? value.toInt() : 1;
const KoColorSpace * cs = device->colorSpace();
KisSequentialIteratorProgress it(device, rect, progressUpdater);
KoColor baseColor(cTA, cs);
QVector<int> channelIndex;
KoChannelInfo::enumChannelValueType valueType = KoChannelInfo::OTHER;
QList<KoChannelInfo*> channels = cs->channels();
for (int i = 0; i < channels.size(); i++) {
const KoChannelInfo *info = channels[i];
if (info->channelType() != KoChannelInfo::COLOR) continue;
KoChannelInfo::enumChannelValueType currentValueType =
info->channelValueType();
if (valueType != KoChannelInfo::OTHER &&
valueType != currentValueType) {
warnKrita << "Cannot apply a Color-to-Alpha filter to a heterogeneous colorspace";
return;
} else {
valueType = currentValueType;
}
channelIndex.append(i);
}
switch (valueType) {
case KoChannelInfo::UINT8:
applyToIterator<quint8, qint16>(channelIndex.size(), channelIndex.data(),
it, baseColor,
threshold, cs);
break;
case KoChannelInfo::UINT16:
applyToIterator<quint16, qint32>(channelIndex.size(), channelIndex.data(),
it, baseColor,
threshold, cs);
break;
case KoChannelInfo::UINT32:
applyToIterator<quint32, qint64>(channelIndex.size(), channelIndex.data(),
it, baseColor,
threshold, cs);
break;
case KoChannelInfo::FLOAT32:
applyToIterator<float, float>(channelIndex.size(), channelIndex.data(),
it, baseColor,
threshold, cs);
break;
case KoChannelInfo::FLOAT64:
applyToIterator<double, double>(channelIndex.size(), channelIndex.data(),
it, baseColor,
threshold, cs);
break;
case KoChannelInfo::FLOAT16:
-#include <KoConfig.h>
#ifdef HAVE_OPENEXR
#include <half.h>
applyToIterator<half, half>(channelIndex.size(), channelIndex.data(),
it, baseColor,
threshold, cs);
break;
#endif
case KoChannelInfo::INT8: /* !UNSUPPORTED! */
case KoChannelInfo::INT16: /* !UNSUPPORTED! */
case KoChannelInfo::OTHER:
warnKrita << "Color To Alpha: Unsupported channel type:" << valueType;
}
}
diff --git a/plugins/filters/colorsfilters/kis_multichannel_filter_base.cpp b/plugins/filters/colorsfilters/kis_multichannel_filter_base.cpp
index 45ebbbb90e..a5385239af 100644
--- a/plugins/filters/colorsfilters/kis_multichannel_filter_base.cpp
+++ b/plugins/filters/colorsfilters/kis_multichannel_filter_base.cpp
@@ -1,519 +1,521 @@
/*
* This file is part of Krita
*
* Copyright (c) 2018 Jouni Pentikainen <joupent@gmail.com>
*
* 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_multichannel_filter_base.h"
#include <Qt>
#include <QLayout>
#include <QPixmap>
#include <QPainter>
#include <QLabel>
#include <QComboBox>
#include <QDomDocument>
#include <QHBoxLayout>
#include "KoChannelInfo.h"
#include "KoBasicHistogramProducers.h"
#include "KoColorModelStandardIds.h"
#include "KoColorSpace.h"
#include "KoColorTransformation.h"
#include "KoCompositeColorTransformation.h"
#include "KoCompositeOp.h"
#include "KoID.h"
#include "kis_signals_blocker.h"
#include "kis_bookmarked_configuration_manager.h"
#include "kis_config_widget.h"
#include <filter/kis_filter_category_ids.h>
#include <filter/kis_filter_configuration.h>
#include <kis_selection.h>
#include <kis_paint_device.h>
#include <kis_processing_information.h>
#include "kis_histogram.h"
#include "kis_painter.h"
#include "widgets/kis_curve_widget.h"
KisMultiChannelFilter::KisMultiChannelFilter(const KoID& id, const QString &entry)
: KisColorTransformationFilter(id, FiltersCategoryAdjustId, entry)
{
setSupportsPainting(true);
setColorSpaceIndependence(TO_LAB16);
}
bool KisMultiChannelFilter::needsTransparentPixels(const KisFilterConfigurationSP config, const KoColorSpace *cs) const
{
Q_UNUSED(config);
return cs->colorModelId() == AlphaColorModelID;
}
QVector<VirtualChannelInfo> KisMultiChannelFilter::getVirtualChannels(const KoColorSpace *cs, int maxChannels)
{
const bool supportsLightness =
cs->colorModelId() != LABAColorModelID &&
cs->colorModelId() != GrayAColorModelID &&
cs->colorModelId() != GrayColorModelID &&
cs->colorModelId() != AlphaColorModelID;
const bool supportsHue = supportsLightness;
const bool supportSaturation = supportsLightness;
QVector<VirtualChannelInfo> vchannels;
QList<KoChannelInfo *> sortedChannels =
KoChannelInfo::displayOrderSorted(cs->channels());
if (supportsLightness) {
vchannels << VirtualChannelInfo(VirtualChannelInfo::ALL_COLORS, -1, 0, cs);
}
Q_FOREACH (KoChannelInfo *channel, sortedChannels) {
int pixelIndex = KoChannelInfo::displayPositionToChannelIndex(channel->displayPosition(), sortedChannels);
vchannels << VirtualChannelInfo(VirtualChannelInfo::REAL, pixelIndex, channel, cs);
}
if (supportsHue) {
vchannels << VirtualChannelInfo(VirtualChannelInfo::HUE, -1, 0, cs);
}
if (supportSaturation) {
vchannels << VirtualChannelInfo(VirtualChannelInfo::SATURATION, -1, 0, cs);
}
if (supportsLightness) {
vchannels << VirtualChannelInfo(VirtualChannelInfo::LIGHTNESS, -1, 0, cs);
}
if (maxChannels >= 0 && vchannels.size() > maxChannels) {
vchannels.resize(maxChannels);
}
return vchannels;
}
int KisMultiChannelFilter::findChannel(const QVector<VirtualChannelInfo> &virtualChannels,
const VirtualChannelInfo::Type &channelType)
{
for (int i = 0; i < virtualChannels.size(); i++) {
if (virtualChannels[i].type() == channelType) {
return i;
}
}
return -1;
}
KisMultiChannelFilterConfiguration::KisMultiChannelFilterConfiguration(int channelCount, const QString & name, qint32 version)
: KisColorTransformationConfiguration(name, version)
, m_channelCount(channelCount)
{
m_transfers.resize(m_channelCount);
}
KisMultiChannelFilterConfiguration::~KisMultiChannelFilterConfiguration()
{}
void KisMultiChannelFilterConfiguration::init()
{
m_curves.clear();
for (int i = 0; i < m_channelCount; ++i) {
m_curves.append(getDefaultCurve());
}
updateTransfers();
}
bool KisMultiChannelFilterConfiguration::isCompatible(const KisPaintDeviceSP dev) const
{
return (int)dev->compositionSourceColorSpace()->channelCount() == m_channelCount;
}
void KisMultiChannelFilterConfiguration::setCurves(QList<KisCubicCurve> &curves)
{
m_curves.clear();
m_curves = curves;
m_channelCount = curves.size();
updateTransfers();
}
void KisMultiChannelFilterConfiguration::updateTransfers()
{
m_transfers.resize(m_channelCount);
for (int i = 0; i < m_channelCount; i++) {
m_transfers[i] = m_curves[i].uint16Transfer();
}
}
const QVector<QVector<quint16> >&
KisMultiChannelFilterConfiguration::transfers() const
{
return m_transfers;
}
const QList<KisCubicCurve>&
KisMultiChannelFilterConfiguration::curves() const
{
return m_curves;
}
void KisMultiChannelFilterConfiguration::fromLegacyXML(const QDomElement& root)
{
fromXML(root);
}
void KisMultiChannelFilterConfiguration::fromXML(const QDomElement& root)
{
QList<KisCubicCurve> curves;
quint16 numTransfers = 0;
int version;
version = root.attribute("version").toInt();
QDomElement e = root.firstChild().toElement();
QString attributeName;
KisCubicCurve curve;
quint16 index;
while (!e.isNull()) {
if ((attributeName = e.attribute("name")) == "nTransfers") {
numTransfers = e.text().toUShort();
} else {
QRegExp rx("curve(\\d+)");
if (rx.indexIn(attributeName, 0) != -1) {
index = rx.cap(1).toUShort();
index = qMin(index, quint16(curves.count()));
if (!e.text().isEmpty()) {
curve.fromString(e.text());
}
curves.insert(index, curve);
}
}
e = e.nextSiblingElement();
}
//prepend empty curves for the brightness contrast filter.
if(getString("legacy") == "brightnesscontrast") {
if (getString("colorModel") == LABAColorModelID.id()) {
curves.append(KisCubicCurve());
curves.append(KisCubicCurve());
curves.append(KisCubicCurve());
} else {
int extraChannels = 5;
if (getString("colorModel") == CMYKAColorModelID.id()) {
extraChannels = 6;
} else if (getString("colorModel") == GrayAColorModelID.id()) {
extraChannels = 0;
}
for(int c = 0; c < extraChannels; c ++) {
curves.insert(0, KisCubicCurve());
}
}
}
if (!numTransfers)
return;
setVersion(version);
setCurves(curves);
}
/**
* Inherited from KisPropertiesConfiguration
*/
//void KisMultiChannelFilterConfiguration::fromXML(const QString& s)
void addParamNode(QDomDocument& doc,
QDomElement& root,
const QString &name,
const QString &value)
{
QDomText text = doc.createTextNode(value);
QDomElement t = doc.createElement("param");
t.setAttribute("name", name);
t.appendChild(text);
root.appendChild(t);
}
void KisMultiChannelFilterConfiguration::toXML(QDomDocument& doc, QDomElement& root) const
{
/**
+ * @code
* <params version=1>
* <param name="nTransfers">3</param>
* <param name="curve0">0,0;0.5,0.5;1,1;</param>
* <param name="curve1">0,0;1,1;</param>
* <param name="curve2">0,0;1,1;</param>
* </params>
+ * @endcode
*/
root.setAttribute("version", version());
QDomText text;
QDomElement t;
addParamNode(doc, root, "nTransfers", QString::number(m_channelCount));
KisCubicCurve curve;
QString paramName;
for (int i = 0; i < m_curves.size(); ++i) {
QString name = QLatin1String("curve") + QString::number(i);
QString value = m_curves[i].toString();
addParamNode(doc, root, name, value);
}
}
KisMultiChannelConfigWidget::KisMultiChannelConfigWidget(QWidget * parent, KisPaintDeviceSP dev, Qt::WindowFlags f)
: KisConfigWidget(parent, f)
, m_dev(dev)
, m_page(new WdgPerChannel(this))
{
Q_ASSERT(m_dev);
const KoColorSpace *targetColorSpace = dev->compositionSourceColorSpace();
m_virtualChannels = KisMultiChannelFilter::getVirtualChannels(targetColorSpace);
}
/**
* Initialize the dialog.
* Note: m_virtualChannels must be populated before calling this
*/
void KisMultiChannelConfigWidget::init() {
QHBoxLayout * layout = new QHBoxLayout(this);
Q_CHECK_PTR(layout);
layout->setContentsMargins(0,0,0,0);
layout->addWidget(m_page);
resetCurves();
const int virtualChannelCount = m_virtualChannels.size();
for (int i = 0; i < virtualChannelCount; i++) {
const VirtualChannelInfo &info = m_virtualChannels[i];
m_page->cmbChannel->addItem(info.name(), i);
}
connect(m_page->cmbChannel, SIGNAL(activated(int)), this, SLOT(slotChannelSelected(int)));
connect((QObject*)(m_page->chkLogarithmic), SIGNAL(toggled(bool)), this, SLOT(logHistView()));
connect((QObject*)(m_page->resetButton), SIGNAL(clicked()), this, SLOT(resetCurve()));
// create the horizontal and vertical gradient labels
m_page->hgradient->setPixmap(createGradient(Qt::Horizontal));
m_page->vgradient->setPixmap(createGradient(Qt::Vertical));
// init histogram calculator
const KoColorSpace *targetColorSpace = m_dev->compositionSourceColorSpace();
QList<QString> keys =
KoHistogramProducerFactoryRegistry::instance()->keysCompatibleWith(targetColorSpace);
if (keys.size() > 0) {
KoHistogramProducerFactory *hpf;
hpf = KoHistogramProducerFactoryRegistry::instance()->get(keys.at(0));
m_histogram = new KisHistogram(m_dev, m_dev->exactBounds(), hpf->generate(), LINEAR);
}
connect(m_page->curveWidget, SIGNAL(modified()), this, SIGNAL(sigConfigurationItemChanged()));
{
KisSignalsBlocker b(m_page->curveWidget);
m_page->curveWidget->setCurve(m_curves[0]);
setActiveChannel(0);
}
}
KisMultiChannelConfigWidget::~KisMultiChannelConfigWidget()
{
delete m_histogram;
}
void KisMultiChannelConfigWidget::resetCurves()
{
const KisPropertiesConfigurationSP &defaultConfiguration = getDefaultConfiguration();
const auto *defaults = dynamic_cast<const KisMultiChannelFilterConfiguration*>(defaultConfiguration.data());
KIS_SAFE_ASSERT_RECOVER_RETURN(defaults);
m_curves = defaults->curves();
const int virtualChannelCount = m_virtualChannels.size();
for (int i = 0; i < virtualChannelCount; i++) {
const VirtualChannelInfo &info = m_virtualChannels[i];
m_curves[i].setName(info.name());
}
}
void KisMultiChannelConfigWidget::setConfiguration(const KisPropertiesConfigurationSP config)
{
const KisMultiChannelFilterConfiguration * cfg = dynamic_cast<const KisMultiChannelFilterConfiguration *>(config.data());
if (!cfg) {
return;
}
if (cfg->curves().empty()) {
/**
* HACK ALERT: our configuration factory generates
* default configuration with nTransfers==0.
* Catching it here. Just set everything to defaults instead.
*/
const KisPropertiesConfigurationSP &defaultConfiguration = getDefaultConfiguration();
const auto *defaults = dynamic_cast<const KisMultiChannelFilterConfiguration*>(defaultConfiguration.data());
KIS_SAFE_ASSERT_RECOVER_RETURN(defaults);
if (!defaults->curves().isEmpty()) {
setConfiguration(defaultConfiguration);
return;
}
} else if (cfg->curves().size() > m_virtualChannels.size()) {
warnKrita << "WARNING: trying to load a curve with invalid number of channels!";
warnKrita << "WARNING: expected:" << m_virtualChannels.size();
warnKrita << "WARNING: got:" << cfg->curves().size();
return;
} else {
if (cfg->curves().size() < m_virtualChannels.size()) {
// The configuration does not cover all our channels.
// This happens when loading a document from an older version, which supported fewer channels.
// Reset to make sure the unspecified channels have their default values.
resetCurves();
}
for (int ch = 0; ch < cfg->curves().size(); ch++) {
m_curves[ch] = cfg->curves()[ch];
}
}
// HACK: we save the previous curve in setActiveChannel, so just copy it
m_page->curveWidget->setCurve(m_curves[m_activeVChannel]);
setActiveChannel(0);
}
inline QPixmap KisMultiChannelConfigWidget::createGradient(Qt::Orientation orient /*, int invert (not used yet) */)
{
int width;
int height;
int *i, inc, col;
int x = 0, y = 0;
if (orient == Qt::Horizontal) {
i = &x; inc = 1; col = 0;
width = 256; height = 1;
} else {
i = &y; inc = -1; col = 255;
width = 1; height = 256;
}
QPixmap gradientpix(width, height);
QPainter p(&gradientpix);
p.setPen(QPen(QColor(0, 0, 0), 1, Qt::SolidLine));
for (; *i < 256; (*i)++, col += inc) {
p.setPen(QColor(col, col, col));
p.drawPoint(x, y);
}
return gradientpix;
}
inline QPixmap KisMultiChannelConfigWidget::getHistogram()
{
int i;
int height = 256;
QPixmap pix(256, height);
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(m_histogram, pix);
bool logarithmic = m_page->chkLogarithmic->isChecked();
if (logarithmic)
m_histogram->setHistogramType(LOGARITHMIC);
else
m_histogram->setHistogramType(LINEAR);
QPalette appPalette = QApplication::palette();
pix.fill(QColor(appPalette.color(QPalette::Base)));
QPainter p(&pix);
p.setPen(QColor(appPalette.color(QPalette::Text)));
p.save();
p.setOpacity(0.2);
const VirtualChannelInfo &info = m_virtualChannels[m_activeVChannel];
if (info.type() == VirtualChannelInfo::REAL) {
m_histogram->setChannel(info.pixelIndex());
double highest = (double)m_histogram->calculations().getHighest();
qint32 bins = m_histogram->producer()->numberOfBins();
if (m_histogram->getHistogramType() == LINEAR) {
double factor = (double)height / highest;
for (i = 0; i < bins; ++i) {
p.drawLine(i, height, i, height - int(m_histogram->getValue(i) * factor));
}
} else {
double factor = (double)height / (double)log(highest);
for (i = 0; i < bins; ++i) {
p.drawLine(i, height, i, height - int(log((double)m_histogram->getValue(i)) * factor));
}
}
}
p.restore();
return pix;
}
void KisMultiChannelConfigWidget::slotChannelSelected(int index)
{
const int virtualChannel = m_page->cmbChannel->itemData(index).toInt();
setActiveChannel(virtualChannel);
}
void KisMultiChannelConfigWidget::setActiveChannel(int ch)
{
m_curves[m_activeVChannel] = m_page->curveWidget->curve();
m_activeVChannel = ch;
m_page->curveWidget->setCurve(m_curves[m_activeVChannel]);
m_page->curveWidget->setPixmap(getHistogram());
const int index = m_page->cmbChannel->findData(m_activeVChannel);
m_page->cmbChannel->setCurrentIndex(index);
updateChannelControls();
}
void KisMultiChannelConfigWidget::logHistView()
{
m_page->curveWidget->setPixmap(getHistogram());
}
void KisMultiChannelConfigWidget::resetCurve()
{
const KisPropertiesConfigurationSP &defaultConfiguration = getDefaultConfiguration();
const auto *defaults = dynamic_cast<const KisMultiChannelFilterConfiguration*>(defaultConfiguration.data());
KIS_SAFE_ASSERT_RECOVER_RETURN(defaults);
auto defaultCurves = defaults->curves();
KIS_SAFE_ASSERT_RECOVER_RETURN(defaultCurves.size() > m_activeVChannel);
m_page->curveWidget->setCurve(defaultCurves[m_activeVChannel]);
}
diff --git a/plugins/flake/textshape/dialogs/ParagraphBulletsNumbers.cpp b/plugins/flake/textshape/dialogs/ParagraphBulletsNumbers.cpp
index a16d57d513..e25c232ac4 100644
--- a/plugins/flake/textshape/dialogs/ParagraphBulletsNumbers.cpp
+++ b/plugins/flake/textshape/dialogs/ParagraphBulletsNumbers.cpp
@@ -1,387 +1,387 @@
/* This file is part of the KDE project
* Copyright (C) 2007, 2009-2010 Thomas Zander <zander@kde.org>
* Copyright (C) 2008 Girish Ramakrishnan <girish@forwardbias.in>
* Copyright (C) 2012 Gopalakrishna Bhat A <gopalakbhat@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "ParagraphBulletsNumbers.h"
#include <KoParagraphStyle.h>
#include <KoListLevelProperties.h>
#include <KoImageData.h>
#include <KoImageCollection.h>
#include <KoUnit.h>
#include <KoFileDialog.h>
#include <KoDialog.h>
#include <QDebug>
#include <QUrl>
#include <kcharselect.h>
ParagraphBulletsNumbers::ParagraphBulletsNumbers(QWidget *parent)
: QWidget(parent)
, m_alignmentMode(false)
, m_imageCollection(0)
, m_data(0)
, m_fontSize(0)
{
widget.setupUi(this);
Q_FOREACH (const Lists::ListStyleItem &item, Lists::genericListStyleItems()) {
addStyle(item);
}
addStyle(Lists::ListStyleItem(i18n("Custom Bullet"), KoListStyle::CustomCharItem));
m_blankCharIndex = addStyle(Lists::ListStyleItem(i18n("No Bullet"), KoListStyle::CustomCharItem));
Q_FOREACH (const Lists::ListStyleItem &item, Lists::otherListStyleItems()) {
addStyle(item);
}
widget.alignment->addItem(i18nc("Automatic horizontal alignment", "Auto"));
widget.alignment->addItem(i18nc("Text alignment", "Left"));
widget.alignment->addItem(i18nc("Text alignment", "Right"));
widget.alignment->addItem(i18nc("Text alignment", "Centered"));
widget.labelFollowedBy->addItem(i18nc("Tab follows the bullet or number", "Tab Stop"));
widget.labelFollowedBy->addItem(i18nc("Space", "Space"));
widget.labelFollowedBy->addItem(i18nc("None", "Nothing"));
widget.doubleSpinBox->setSingleStep(0.05);
widget.doubleSpinBox_2->setSingleStep(0.05);
widget.doubleSpinBox_3->setSingleStep(0.05);
connect(widget.labelFollowedBy, SIGNAL(currentIndexChanged(int)), this, SLOT(labelFollowedByIndexChanged(int)));
connect(widget.listTypes, SIGNAL(currentRowChanged(int)), this, SLOT(styleChanged(int)));
connect(widget.customCharacter, SIGNAL(clicked(bool)), this, SLOT(customCharButtonPressed()));
connect(widget.letterSynchronization, SIGNAL(toggled(bool)), widget.startValue, SLOT(setLetterSynchronization(bool)));
connect(widget.prefix, SIGNAL(textChanged(QString)), this, SLOT(recalcPreview()));
connect(widget.suffix, SIGNAL(textChanged(QString)), this, SLOT(recalcPreview()));
connect(widget.depth, SIGNAL(valueChanged(int)), this, SLOT(recalcPreview()));
connect(widget.levels, SIGNAL(valueChanged(int)), this, SLOT(recalcPreview()));
connect(widget.startValue, SIGNAL(valueChanged(int)), this, SLOT(recalcPreview()));
connect(widget.insertImage, SIGNAL(clicked()), this, SLOT(selectListImage()));
connect(widget.imageHeight, SIGNAL(valueChanged(double)), this, SLOT(recalcPreview()));
connect(widget.imageWidth, SIGNAL(valueChanged(double)), this, SLOT(recalcPreview()));
connect(widget.restartNumbering, SIGNAL(clicked()), this, SLOT(recalcPreview()));
}
int ParagraphBulletsNumbers::addStyle(const Lists::ListStyleItem &lsi)
{
m_mapping.insert(widget.listTypes->count(), lsi.style);
widget.listTypes->addItem(lsi.name);
return widget.listTypes->count() - 1;
}
void ParagraphBulletsNumbers::setDisplay(KoParagraphStyle *style, int level)
{
KoListStyle *listStyle = style->listStyle();
widget.listPropertiesPane->setEnabled(listStyle != 0);
widget.customCharacter->setText("-");
if (listStyle == 0) {
widget.listTypes->setCurrentRow(0);
return;
}
KoListLevelProperties llp = listStyle->levelProperties(level);
m_previousLevel = llp.level();
widget.prefix->setText(llp.listItemPrefix());
widget.suffix->setText(llp.listItemSuffix());
widget.letterSynchronization->setChecked(llp.letterSynchronization());
KoListStyle::Style s = llp.style();
Q_FOREACH (int row, m_mapping.keys()) {
if (m_mapping[row] == s) {
widget.listTypes->setCurrentRow(row);
break;
}
}
int align;
if (llp.alignment() == (Qt::AlignLeft | Qt::AlignAbsolute)) {
align = 1;
} else if (llp.alignment() == (Qt::AlignRight | Qt::AlignAbsolute)) {
align = 2;
} else if (llp.alignment() == Qt::AlignCenter) {
align = 3;
} else {
align = 0;
}
widget.alignment->setCurrentIndex(align);
widget.depth->setValue(llp.level());
widget.levels->setValue(llp.displayLevel());
widget.startValue->setValue(llp.startValue());
if (s == KoListStyle::CustomCharItem) {
widget.customCharacter->setText(llp.bulletCharacter());
}
if (s == KoListStyle::ImageItem) {
m_data = llp.bulletImage();
widget.imageHeight->setValue(llp.height());
widget.imageWidth->setValue(llp.width());
} else {
m_data = 0;
widget.imageHeight->setValue(0);
widget.imageWidth->setValue(0);
}
if (llp.alignmentMode() == false) { //for list-level-position-and-space-mode=label-width-and-position disable the following options
widget.label_8->setEnabled(false);
widget.label_9->setEnabled(false);
widget.label_10->setEnabled(false);
widget.label_11->setEnabled(false);
widget.labelFollowedBy->setEnabled(false);
widget.doubleSpinBox->setEnabled(false);
widget.doubleSpinBox_2->setEnabled(false);
widget.doubleSpinBox_3->setEnabled(false);
} else {
m_alignmentMode = true;
switch (llp.labelFollowedBy()) {
case KoListStyle::ListTab:
widget.doubleSpinBox->setEnabled(true);
widget.labelFollowedBy->setCurrentIndex(0);
widget.doubleSpinBox->setValue(KoUnit::toCentimeter(llp.tabStopPosition()));
break;
case KoListStyle::Space:
widget.doubleSpinBox->setEnabled(false);
widget.labelFollowedBy->setCurrentIndex(1);
break;
case KoListStyle::Nothing:
widget.doubleSpinBox->setEnabled(false);
widget.labelFollowedBy->setCurrentIndex(2);
break;
default:
Q_ASSERT(false);
}
widget.doubleSpinBox_2->setValue(KoUnit::toCentimeter(llp.margin()));
widget.doubleSpinBox_3->setValue(KoUnit::toCentimeter(llp.margin()) + KoUnit::toCentimeter(llp.textIndent()));
}
// *** features not in GUI;
// character style
// relative bullet size (percent)
// minimum label width
recalcPreview();
}
void ParagraphBulletsNumbers::save(KoParagraphStyle *savingStyle)
{
Q_ASSERT(savingStyle);
KoUnit unit(KoUnit::Centimeter);
const int currentRow = widget.listTypes->currentRow();
KoListStyle::Style style = m_mapping[currentRow];
if (style == KoListStyle::None) {
savingStyle->setListStyle(0);
return;
}
if (savingStyle->listStyle() == 0) {
KoListStyle *listStyle = new KoListStyle(savingStyle);
savingStyle->setListStyle(listStyle);
}
KoListStyle *listStyle = savingStyle->listStyle();
KoListLevelProperties llp = listStyle->levelProperties(widget.depth->value());
llp.setStyle(style);
llp.setLevel(widget.depth->value());
llp.setDisplayLevel(widget.levels->value());
llp.setStartValue(widget.startValue->value());
llp.setListItemPrefix(widget.prefix->text());
llp.setListItemSuffix(widget.suffix->text());
llp.setLetterSynchronization(widget.letterSynchronization->isVisible() && widget.letterSynchronization->isChecked());
if (m_alignmentMode == true) {
llp.setAlignmentMode(true);
switch (widget.labelFollowedBy->currentIndex()) {
case 0: llp.setLabelFollowedBy(KoListStyle::ListTab);
llp.setTabStopPosition(unit.fromUserValue(widget.doubleSpinBox->value()));
break;
case 1: llp.setLabelFollowedBy(KoListStyle::Space);
break;
case 2: llp.setLabelFollowedBy(KoListStyle::Nothing);
break;
default:
Q_ASSERT(false);
}
llp.setMargin(unit.fromUserValue(widget.doubleSpinBox_2->value()));
llp.setTextIndent(unit.fromUserValue(widget.doubleSpinBox_3->value()) - unit.fromUserValue(widget.doubleSpinBox_2->value()));
}
if (style == KoListStyle::ImageItem) {
if (m_data) {
llp.setBulletImage(m_data);
}
llp.setWidth(widget.imageWidth->value());
llp.setHeight(widget.imageHeight->value());
} else if (style == KoListStyle::CustomCharItem) {
llp.setBulletCharacter((currentRow == m_blankCharIndex) ? QChar() : widget.customCharacter->text().remove('&').at(0));
}
// it is important to not use 45 for CustomCharItem as it is also char based
else if (!KoListStyle::isNumberingStyle(style)) {
llp.setRelativeBulletSize(45); //for non-numbering bullets the default relative bullet size is 45%(The spec does not say it; we take it)
}
Qt::Alignment align;
switch (widget.alignment->currentIndex()) {
case 0: align = Qt::AlignLeft; break;
case 1: align = Qt::AlignLeft | Qt::AlignAbsolute; break;
case 2: align = Qt::AlignRight | Qt::AlignAbsolute; break;
case 3: align = Qt::AlignCenter; break;
default:
Q_ASSERT(false);
}
llp.setAlignment(align);
if (llp.level() != m_previousLevel) {
listStyle->removeLevelProperties(m_previousLevel);
}
listStyle->setLevelProperties(llp);
}
void ParagraphBulletsNumbers::styleChanged(int index)
{
KoListStyle::Style style = m_mapping[index];
bool showLetterSynchronization = false;
if (style == KoListStyle::ImageItem) {
widget.startValue->setValue(1);
widget.startValue->setEnabled(false);
widget.levels->setValue(1);
widget.levels->setEnabled(false);
widget.insertImage->setEnabled(true);
widget.imageHeight->setEnabled(true);
widget.imageWidth->setEnabled(true);
if (widget.imageHeight->value() == 0 && widget.imageWidth->value() == 0) {
widget.imageHeight->setValue(m_fontSize);
widget.imageWidth->setValue(m_fontSize);
}
} else if (!KoListStyle::isNumberingStyle(style)) {
widget.startValue->setCounterType(KoListStyle::DecimalItem);
widget.startValue->setValue(1);
widget.startValue->setEnabled(false);
widget.levels->setValue(1);
widget.levels->setEnabled(false);
widget.insertImage->setEnabled(false);
widget.imageHeight->setEnabled(false);
widget.imageWidth->setEnabled(false);
widget.imageHeight->setValue(0);
widget.imageWidth->setValue(0);
} else {
switch (style) {
case KoListStyle::AlphaLowerItem:
case KoListStyle::UpperAlphaItem:
showLetterSynchronization = true;
- // fall through
+ Q_FALLTHROUGH();
default:
widget.levels->setEnabled(true);
widget.startValue->setEnabled(true);
widget.startValue->setCounterType(style);
int value = widget.startValue->value();
widget.startValue->setValue(value + 1);
widget.startValue->setValue(value); // surely to trigger a change event.
widget.insertImage->setEnabled(false);
widget.imageHeight->setEnabled(false);
widget.imageWidth->setEnabled(false);
}
widget.imageHeight->setValue(0);
widget.imageWidth->setValue(0);
}
widget.customCharacter->setEnabled(style == KoListStyle::CustomCharItem && index != m_blankCharIndex);
widget.letterSynchronization->setVisible(showLetterSynchronization);
widget.listPropertiesPane->setEnabled(style != KoListStyle::None);
recalcPreview();
}
void ParagraphBulletsNumbers::customCharButtonPressed()
{
KoDialog *dialog = new KoDialog(this);
dialog->setModal(true);
dialog->setButtons(KoDialog::Ok | KoDialog::Cancel);
dialog->setDefaultButton(KoDialog::Ok);
KCharSelect *kcs = new KCharSelect(dialog, 0,
KCharSelect::SearchLine | KCharSelect::FontCombo | KCharSelect::BlockCombos
| KCharSelect::CharacterTable | KCharSelect::DetailBrowser);
dialog->setMainWidget(kcs);
if (dialog->exec() == KoDialog::Accepted) {
QChar character = kcs->currentChar();
widget.customCharacter->setText(character);
// also switch to the custom list style.
Q_FOREACH (int row, m_mapping.keys()) {
if (m_mapping[row] == KoListStyle::CustomCharItem) {
widget.listTypes->setCurrentRow(row);
break;
}
}
}
delete dialog;
recalcPreview();
}
void ParagraphBulletsNumbers::recalcPreview()
{
emit parStyleChanged();
}
void ParagraphBulletsNumbers::labelFollowedByIndexChanged(int index)
{
if (index == 1 || index == 2) {
widget.doubleSpinBox->setEnabled(false);
} else {
widget.doubleSpinBox->setEnabled(true);
}
emit parStyleChanged();
emit recalcPreview();
}
void ParagraphBulletsNumbers::setImageCollection(KoImageCollection *imageCollection)
{
m_imageCollection = imageCollection;
}
void ParagraphBulletsNumbers::selectListImage()
{
if (!m_imageCollection) {
return;
}
KoFileDialog dlg(0, KoFileDialog::OpenFile, "bullets");
dlg.setCaption(i18n("Select a list image"));
if (!dlg.filename().isEmpty()) {
QFile f(dlg.filename());
if (f.exists()) {
f.open(QIODevice::ReadOnly);
QByteArray ba = f.readAll();
f.close();
if (m_imageCollection) {
m_data = m_imageCollection->createImageData(ba);
}
emit recalcPreview();
}
}
}
void ParagraphBulletsNumbers::setFontSize(const KoCharacterStyle *style)
{
m_fontSize = style->fontPointSize();
}
diff --git a/plugins/flake/textshape/kotext/KoAnchorInlineObject.h b/plugins/flake/textshape/kotext/KoAnchorInlineObject.h
index 6627ed228c..e2a3bea8ff 100644
--- a/plugins/flake/textshape/kotext/KoAnchorInlineObject.h
+++ b/plugins/flake/textshape/kotext/KoAnchorInlineObject.h
@@ -1,99 +1,101 @@
/* This file is part of the KDE project
* Copyright (C) 2007, 2009 Thomas Zander <zander@kde.org>
* Copyright (C) 2011 Matus Hanzes <matus.hanzes@ixonos.com>
* Copyright (C) 2013 C. Boemann <cbo@boemann.dk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOANCHORINLINEOBJECT_H
#define KOANCHORINLINEOBJECT_H
#include "KoInlineObject.h"
#include "KoShapeAnchor.h"
#include "kritatext_export.h"
class KoAnchorInlineObjectPrivate;
/**
* This class connects KoShapeAnchor to an inline character in the text document.
*
* This class is used when the shape anchor is of type: as-char
*
* It has to be registered to the inlineobjectmanager and thus forms the connection between the text
* and the KoShapeAnchor and by extension the so called 'anchored-shape' (any kind of shape)
*
* The KoAnchorInlineObject is placed as a character in text. As such it will move and be
* editable like any other character, including deletion.
*
* Since this is a real character it will be positioned by the textlayout engine and anything that
* will change the position of the text will thus also change the KoAnchorInlineObject character.
*
* The anchored-shape can be repositioned on the canvas if the text is relayouted (for example after
* editing the text. This is dependent on how the text layout is implemented.
*
- * Steps to use a KoAnchorInlineObject are; <ol>
+ * Steps to use a KoAnchorInlineObject are
+ * <ol>
* <li> Create KoShapeAnchor *anchor = new KoShapeAnchor(shape);
* <li> Use anchor->loadOdf() to load additional attributes like the "text:anchor-type"
* <li> if type is as-char create KoAnchorInlineObject *anchorObj = new KoAnchorInlineObject(anchor);
+ * </ol>
*/
class KRITATEXT_EXPORT KoAnchorInlineObject : public KoInlineObject, public KoShapeAnchor::TextLocation
{
Q_OBJECT
public:
/**
* Constructor for an as-char anchor.
* @param parent the shapeanchor.
*/
explicit KoAnchorInlineObject(KoShapeAnchor *parent);
~KoAnchorInlineObject() override;
/// returns the parent anchor
KoShapeAnchor *anchor() const;
/// returns the cursor position in the document where this anchor is positioned.
int position() const override;
/// returns the document that this anchor is associated with.
const QTextDocument *document() const override;
/// reimplemented from KoInlineObject
void updatePosition(const QTextDocument *document,
int posInDocument, const QTextCharFormat &format) override;
/// reimplemented from KoInlineObject
void resize(const QTextDocument *document, QTextInlineObject &object,
int posInDocument, const QTextCharFormat &format, QPaintDevice *pd) override;
/// reimplemented from KoInlineObject
void paint(QPainter &painter, QPaintDevice *pd, const QTextDocument *document,
const QRectF &rect, const QTextInlineObject &object, int posInDocument, const QTextCharFormat &format) override;
qreal inlineObjectAscent() const;
qreal inlineObjectDescent() const;
/// reimplemented from KoInlineObject - should not do anything
bool loadOdf(const KoXmlElement &, KoShapeLoadingContext &) override;
/// reimplemented from KoInlineObject
void saveOdf(KoShapeSavingContext &context) override;
private:
Q_DECLARE_PRIVATE(KoAnchorInlineObject)
};
#endif
diff --git a/plugins/flake/textshape/kotext/KoAnchorTextRange.h b/plugins/flake/textshape/kotext/KoAnchorTextRange.h
index f7528884a4..35b441680c 100644
--- a/plugins/flake/textshape/kotext/KoAnchorTextRange.h
+++ b/plugins/flake/textshape/kotext/KoAnchorTextRange.h
@@ -1,86 +1,89 @@
/* This file is part of the KDE project
* Copyright (C) 2007, 2009 Thomas Zander <zander@kde.org>
* Copyright (C) 2011 Matus Hanzes <matus.hanzes@ixonos.com>
* Copyright (C) 2013 C. Boemann <cbo@boemann.dk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOANCHORTEXTRANGE_H
#define KOANCHORTEXTRANGE_H
#include "KoTextRange.h"
#include "KoShapeAnchor.h"
#include "kritatext_export.h"
class KoAnchorTextRangePrivate;
class QTextCursor;
/**
* This class connects KoShapeAnchor to a position in the text document.
*
* This class is used when the shape anchor is of type: char or paragraph
*
* It has to be registered to the textrange manager and thus forms the connection between the text
* and the KoShapeAnchor and by extension the so called 'anchored-shape' (any kind of shape)
*
* The KoAnchorTextRange is placed at a position in text. As with all KoTextRange it will change
* it's position in the text when the user edits text before it. The user is also able to delete it if
* deleting the text where it is positioned.
*
* The anchored-shape can be repositioned on the canvas if the text is relayouted (for example after
* editing the text. This is dependent on how the text layout is implemented.
*
- * Steps to use a KoAnchorTextRange are; <ol>
+ * Steps to use a KoAnchorTextRange are
+ * <ol>
* <li> Create KoShapeAnchor *anchor = new KoShapeAnchor(shape);
* <li> Use anchor->loadOdf() to load additional attributes like the "text:anchor-type"
* <li> if type is char or paragraph create KoAnchorTextRange *anchorRange = new KoAnchorTextRange(anchor);
+ * </ol>
*/
class KRITATEXT_EXPORT KoAnchorTextRange : public KoTextRange, public KoShapeAnchor::TextLocation
{
Q_OBJECT
public:
/**
* Constructor for a char or paragraph anchor.
- * @param parent the shapeanchor.
+ * @param parent the shape anchor.
+ * @param cursor the text cursor.
*/
KoAnchorTextRange(KoShapeAnchor *parent, const QTextCursor &cursor);
~KoAnchorTextRange() override;
/// returns the parent anchor
KoShapeAnchor *anchor() const;
/// reimplemented from KoShapeAnchor::TextLocation
const QTextDocument *document() const override;
/// reimplemented from KoShapeAnchor::TextLocation
int position() const override;
void updateContainerModel();
/// reimplemented from KoTextRange - should not do anything
bool loadOdf(const KoXmlElement &, KoShapeLoadingContext &) override;
/// reimplemented from KoTextRange
void saveOdf(KoShapeSavingContext &context, int position, KoTextRange::TagType tagType) const override;
private:
KoAnchorTextRangePrivate * const d_ptr;
Q_DECLARE_PRIVATE(KoAnchorTextRange)
};
#endif
diff --git a/plugins/flake/textshape/kotext/KoAnnotationManager.h b/plugins/flake/textshape/kotext/KoAnnotationManager.h
index accb285011..9d3df698a2 100644
--- a/plugins/flake/textshape/kotext/KoAnnotationManager.h
+++ b/plugins/flake/textshape/kotext/KoAnnotationManager.h
@@ -1,81 +1,81 @@
/* This file is part of the KDE project
* Copyright (C) 2007 Fredy Yanardi <fyanardi@gmail.com>
* Copyright (C) 2012 Inge Wallin <inge@lysator.liu.se>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOANNOTATIONMANAGER_H
#define KOANNOTATIONMANAGER_H
#include "kritatext_export.h"
#include <QObject>
#include <QList>
class KoAnnotation;
class KoAnnotationManagerPrivate;
/**
* A manager for all annotations in a document. Every annotation is identified by a unique name.
* Note that only SinglePosition and StartAnnotation annotations can be retrieved from this
* manager. An end annotation should be retrieved from it's parent (StartAnnotation) using
* KoAnnotation::endAnnotation()
* This class also maintains a list of annotation names so that it can be easily used to
* show all available annotation.
*/
class KRITATEXT_EXPORT KoAnnotationManager : public QObject
{
Q_OBJECT
public:
/// constructor
KoAnnotationManager();
~KoAnnotationManager() override;
/// @return an annotation with the specified name, or 0 if there is none
KoAnnotation *annotation(const QString &name) const;
/// @return a list of QString containing all annotation names
QList<QString> annotationNameList() const;
public Q_SLOTS:
/**
* Insert a new annotation to this manager. The name of the annotation
- * will be set to @param name, no matter what name has been set on
+ * will be set to @p name, no matter what name has been set on
* it.
* @param name the name of the annotation
* @param annotation the annotation object to insert
*/
void insert(const QString &name, KoAnnotation *annotation);
/**
* Remove an annotation from this manager.
* @param name the name of the annotation to remove
*/
void remove(const QString &name);
/**
* Rename an annotation
* @param oldName the old name of the annotation
* @param newName the new name of the annotation
*/
void rename(const QString &oldName, const QString &newName);
private:
KoAnnotationManagerPrivate * const d;
};
#endif
diff --git a/plugins/flake/textshape/kotext/KoBookmarkManager.h b/plugins/flake/textshape/kotext/KoBookmarkManager.h
index 2e85445f63..5ec4a2efde 100644
--- a/plugins/flake/textshape/kotext/KoBookmarkManager.h
+++ b/plugins/flake/textshape/kotext/KoBookmarkManager.h
@@ -1,82 +1,82 @@
/* This file is part of the KDE project
* Copyright (C) 2007 Fredy Yanardi <fyanardi@gmail.com>
* Copyright (C) 2012 C. Boemann <cbo@boemann.dk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOBOOKMARKMANAGER_H
#define KOBOOKMARKMANAGER_H
#include "kritatext_export.h"
#include <QObject>
#include <QList>
#include <QString>
class KoBookmark;
class KoBookmarkManagerPrivate;
/**
* A manager for all bookmarks in a document. Every bookmark is identified by a unique name.
* Note that only SinglePosition and StartBookmark bookmarks can be retrieved from this
* manager. An end bookmark should be retrieved from it's parent (StartBookmark) using
* KoBookmark::endBookmark()
* This class also maintains a list of bookmark names so that it can be easily used to
* show all available bookmark.
*/
class KRITATEXT_EXPORT KoBookmarkManager : public QObject
{
Q_OBJECT
public:
/// constructor
KoBookmarkManager();
~KoBookmarkManager() override;
/// @return a bookmark with the specified name, or 0 if there is none
KoBookmark *bookmark(const QString &name) const;
/// @return a list of QString containing all bookmark names
QList<QString> bookmarkNameList() const;
public Q_SLOTS:
/**
* Insert a new bookmark to this manager. The name of the bookmark
- * will be set to @param name, no matter what name has been set on
+ * will be set to @p name, no matter what name has been set on
* it.
* @param name the name of the bookmark
* @param bookmark the bookmark object to insert
*/
void insert(const QString &name, KoBookmark *bookmark);
/**
* Remove a bookmark from this manager.
* @param name the name of the bookmark to remove
*/
void remove(const QString &name);
/**
* Rename a bookmark
* @param oldName the old name of the bookmark
* @param newName the new name of the bookmark
*/
void rename(const QString &oldName, const QString &newName);
private:
KoBookmarkManagerPrivate * const d;
};
#endif
diff --git a/plugins/flake/textshape/kotext/KoDocumentRdfBase.h b/plugins/flake/textshape/kotext/KoDocumentRdfBase.h
index 96a737cc56..8549d44346 100644
--- a/plugins/flake/textshape/kotext/KoDocumentRdfBase.h
+++ b/plugins/flake/textshape/kotext/KoDocumentRdfBase.h
@@ -1,95 +1,95 @@
/* This file is part of the KDE project
Copyright (C) 2010 KO GmbH <ben.martin@kogmbh.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KO_DOCUMENT_Rdf_Base_H
#define KO_DOCUMENT_Rdf_Base_H
#include "kritatext_export.h"
#include <QObject>
#include <QMap>
#include <QString>
#include <QMetaType>
#include <QSharedPointer>
#include <KoDataCenterBase.h>
class KoDocumentResourceManager;
class QTextDocument;
class KoStore;
class KoXmlWriter;
///**
// * Dummy definition in case Soprano is not available.
// */
namespace Soprano
{
class Model;
}
/**
* A base class that provides the interface to many RDF features
* but will not do anything if Soprano support is not built.
* By having this "Base" class, code can call methods at points
- * where RDF handling is desired and can avoid #ifdef conditionals
+ * where RDF handling is desired and can avoid \#ifdef conditionals
* because the base class interface is here and will be valid, even
* if impotent when Soprano support is not built.
*/
class KRITATEXT_EXPORT KoDocumentRdfBase : public QObject, public KoDataCenterBase
{
Q_OBJECT
public:
explicit KoDocumentRdfBase(QObject *parent = 0);
~KoDocumentRdfBase() override;
/**
* Get the Soprano::Model that contains all the Rdf
* You do not own the model, do not delete it.
*/
#ifdef SHOULD_BUILD_RDF
virtual QSharedPointer<Soprano::Model> model() const;
#endif
virtual void linkToResourceManager(KoDocumentResourceManager *rm);
virtual void updateInlineRdfStatements(const QTextDocument *qdoc);
virtual void updateXmlIdReferences(const QMap<QString, QString> &m);
/**
* idrefList queries soprano after loading and creates a list of all rdfid's that
* where found in the manifest.rdf document. This list is used to make sure we do not
* create more inline rdf objects than necessary
* @return a list of xml-id's
*/
virtual QStringList idrefList() const;
virtual bool loadOasis(KoStore *store);
virtual bool saveOasis(KoStore *store, KoXmlWriter *manifestWriter);
// reimplemented in komain/rdf/KoDocumentRdf
bool completeLoading(KoStore *store) override;
bool completeSaving(KoStore *store, KoXmlWriter *manifestWriter, KoShapeSavingContext *context) override;
};
Q_DECLARE_METATYPE(KoDocumentRdfBase*)
#endif
diff --git a/plugins/flake/textshape/kotext/KoInlineObjectFactoryBase.h b/plugins/flake/textshape/kotext/KoInlineObjectFactoryBase.h
index 755d0ea726..0dd1339ac0 100644
--- a/plugins/flake/textshape/kotext/KoInlineObjectFactoryBase.h
+++ b/plugins/flake/textshape/kotext/KoInlineObjectFactoryBase.h
@@ -1,107 +1,107 @@
/* This file is part of the KDE project
* Copyright (C) 2006 Thomas Zander <zander@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOINLINEOBJECTFACTORY_H
#define KOINLINEOBJECTFACTORY_H
#include <QString>
#include "kritatext_export.h"
class KoInlineObject;
class InlineObjectFactoryPrivate;
class KoProperties;
/// A template used in the KoInlineObjectFactoryBase
struct KRITATEXT_EXPORT KoInlineObjectTemplate {
QString id; ///< The id of the inlineObject
QString name; ///< The name to be shown for this template
/**
* The properties which, when passed to the KoInlineObjectFactoryBase::createInlineObject() method
* result in the object this template represents.
*/
const KoProperties *properties;
};
/**
* A factory for inline text objects. There should be one for each plugin type to
* allow the creation of the inlineObject from that plugin.
* The factory additionally has information to allow showing a menu entry for user
* access to the object-type.
* @see KoInlineObjectRegistry
*/
class KRITATEXT_EXPORT KoInlineObjectFactoryBase
{
public:
/// The type of inlineObject this factory creates.
enum ObjectType {
TextVariable, ///< The factory creates KoVariable inherting objects.
Other = 0x100 ///< The factory creates objects that should not be shown in any menu
};
/**
* Create the new factory
- * @param parent the parent QObject for memory management usage.
* @param id a string that will be used internally for referencing the variable-type.
+ * @param type the object type for the new factory.
*/
KoInlineObjectFactoryBase(const QString &id, ObjectType type);
virtual ~KoInlineObjectFactoryBase();
/**
* Create a new instance of an inline object.
*/
virtual KoInlineObject *createInlineObject(const KoProperties *properties = 0) const = 0;
/**
* return the id for the variable this factory creates.
* @return the id for the variable this factory creates.
*/
QString id() const;
/**
* Returns the type of object this factory creates.
* The main purpose is to group plugins per type in, for example, a menu.
*/
ObjectType type() const;
/**
* Return all the templates this factory knows about.
* Each template shows a different way to create an object this factory is specialized in.
*/
QList<KoInlineObjectTemplate> templates() const;
QStringList odfElementNames() const;
QString odfNameSpace() const;
void setOdfElementNames(const QString &nameSpace, const QStringList &names);
protected:
/**
* Add a template with the properties of a specific type of object this factory can generate
* using the createInlineObject() method.
* @param params The new template this factory knows to produce
*/
void addTemplate(const KoInlineObjectTemplate &params);
private:
InlineObjectFactoryPrivate * const d;
};
#endif
diff --git a/plugins/flake/textshape/kotext/KoInlineTextObjectManager.h b/plugins/flake/textshape/kotext/KoInlineTextObjectManager.h
index 75500f0d24..64f1c83bb9 100644
--- a/plugins/flake/textshape/kotext/KoInlineTextObjectManager.h
+++ b/plugins/flake/textshape/kotext/KoInlineTextObjectManager.h
@@ -1,186 +1,186 @@
/* This file is part of the KDE project
* Copyright (C) 2006-2009 Thomas Zander <zander@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOINLINETEXTOBJECTMANAGER_H
#define KOINLINETEXTOBJECTMANAGER_H
#include "KoInlineObject.h"
#include "KoVariableManager.h"
#include "kritatext_export.h"
// Qt + kde
#include <QHash>
#include <QTextBlock>
class KoCanvasBase;
class KoTextLocator;
class KoInlineNote;
class KoInlineCite;
class QTextCharFormat;
class QAction;
/**
* A container to register all the inlineTextObjects with.
* Inserting an inline-object in a QTextDocument should be done via this manager which will
* insert a placeholder in the text and you should add the KoInlineTextObjectManager to the
* KoTextDocument.
*/
class KRITATEXT_EXPORT KoInlineTextObjectManager : public QObject
{
Q_OBJECT
public:
enum Properties {
InlineInstanceId = 577297549 // If you change this, don't forget to change KoCharacterStyle.h
};
/// Constructor
explicit KoInlineTextObjectManager(QObject *parent = 0);
~KoInlineTextObjectManager() override;
/**
* Retrieve a formerly added inline object based on the format.
* @param format the textCharFormat
*/
KoInlineObject *inlineTextObject(const QTextCharFormat &format) const;
/**
* Retrieve a formerly added inline object based on the cursor position.
* @param cursor the cursor which position is used. The anchor is ignored.
*/
KoInlineObject *inlineTextObject(const QTextCursor &cursor) const;
/**
* Retrieve a formerly added inline object based on the KoInlineObject::id() of the object.
* @param id the id assigned to the inline text object when it was added.
*/
KoInlineObject *inlineTextObject(int id) const;
QList<KoInlineObject*> inlineTextObjects() const;
/**
* Insert a new inline object into the manager as well as the document.
* This method will cause a placeholder to be inserted into the text at cursor position,
* possibly replacing a selection. The object will then be used as an inline
* character and painted at the specified location in the text.
* @param cursor the cursor which indicated the document and the position in that document
* where the inline object will be inserted.
* @param object the inline object to insert.
*/
void insertInlineObject(QTextCursor &cursor, KoInlineObject *object);
/**
* Add inline object into the manager.
*
* This methods add the inline object into the manager. This is useful if you have a command
* that removes and adds a inline object to the manager. If the object already was inserted before
* (the object id is already set) it keeps the old id, otherwise a new id will be generated.
*
* @param object the inline object to insert.
*/
void addInlineObject(KoInlineObject* object);
/**
* Remove an inline object from this manager. The object will also be removed from
* the bookmarkmanager if it is a bookmark. This is not done smart: you might end up
* with dangling start or end bookmarks.
* Should really only be called by KoTextEditor's delete commands
- * @param the object to be removed
+ * @param object the object to be removed
*/
void removeInlineObject(KoInlineObject *object);
/**
* Set a property that may have changed which will be forwarded to all registered textObjects.
* If the key has changed then all registered InlineObject instances that have stated to want
* updates will get called with the change.
* The property will be stored to allow it to be retrieved via the intProperty() and friends.
* @see KoInlineObject::propertyChangeListener()
*/
void setProperty(KoInlineObject::Property key, const QVariant &value);
/// retrieve a property
QVariant property(KoInlineObject::Property key) const;
/// retrieve an int property
int intProperty(KoInlineObject::Property key) const;
/// retrieve a bool property
bool boolProperty(KoInlineObject::Property key) const;
/// retrieve a string property
QString stringProperty(KoInlineObject::Property key) const;
/// remove a property from the store.
void removeProperty(KoInlineObject::Property key);
/**
* Return the variableManager.
*/
const KoVariableManager *variableManager() const;
/**
* Return the variableManager.
*/
KoVariableManager *variableManager();
/**
* Create a list of actions that can be used to plug into a menu, for example.
* This method internally uses KoInlineObjectRegistry::createInsertVariableActions() but extends
* the list with all registered variable-names.
* Each of these actions, when executed, will insert the relevant variable in the current text-position.
* The actions assume that the text tool is selected, if that's not the case then they will silently fail.
* @param host the canvas for which these actions are created. Note that the actions will get these
* actions as a parent (for memory management purposes) as well.
* @see KoVariableManager
*/
QList<QAction*> createInsertVariableActions(KoCanvasBase *host) const;
QList<KoTextLocator*> textLocators() const;
/**
* It returns a list of all end notes in the document
*/
QList<KoInlineNote*> endNotes() const;
QMap<QString, KoInlineCite*> citations(bool duplicatesEnabled = true) const;
QList<KoInlineCite*> citationsSortedByPosition(bool duplicatesEnabled = true,
QTextBlock block = QTextBlock()) const;
public Q_SLOTS:
void documentInformationUpdated(const QString &info, const QString &data);
Q_SIGNALS:
/**
* Emitted whenever a property is set and it turns out to be changed.
*/
void propertyChanged(int, const QVariant &variant);
private:
void insertObject(KoInlineObject *object);
QHash<int, KoInlineObject*> m_objects;
QHash<int, KoInlineObject*> m_deletedObjects;
QList<KoInlineObject*> m_listeners; // holds objects also in m_objects, but which want propertyChanges
int m_lastObjectId;
QHash<int, QVariant> m_properties;
KoVariableManager m_variableManager;
};
Q_DECLARE_METATYPE(KoInlineTextObjectManager*)
#endif
diff --git a/plugins/flake/textshape/kotext/KoSectionModel.h b/plugins/flake/textshape/kotext/KoSectionModel.h
index 700c03ca46..438622472b 100644
--- a/plugins/flake/textshape/kotext/KoSectionModel.h
+++ b/plugins/flake/textshape/kotext/KoSectionModel.h
@@ -1,142 +1,144 @@
#ifndef KOSECTIONMODEL_H
#define KOSECTIONMODEL_H
#include <QTextDocument>
#include <QAbstractItemModel>
#include <QVector>
#include <QSet>
#include <KoSection.h>
#include <KoSectionEnd.h>
/**
* Used to handle all the sections in the document
*
* Now there actually two levels of section handling:
* 1) Formatting Level: on this level we should be sure, that
* pointers to KoSection and KoSectionEnd in the KoParagraphStyles
* properties SectionEndings and SectionStartings are consistent.
* Handling on this level is provided on the level of text editing
* commands: DeleteCommand, NewSectionCommand
* We can't move it to another place, because we should know the
* semantics of operation to handle it right way.
* 2) Model(Tree) Level: on this level we should update KoSectionModel
* right way, so it in any moment represents the actual tree
* of sections. Tree is built easily:
* One section is son of another, if it is directly nested in it.
* As text editing commands have access to change Formatting Level,
* they are declared as friend classes of KoSectionModel to be able
* affect Model structure without changing something on Formatting
* Level. Also affected by RenameSectionCommand.
*
* Also we need to look at the consistency of some section properties:
*
* 1) Bounds. Those now are handled with QTextCursors that are placed
* on start and end of the section. In default state start cursor
* isn't moving if text inserted in its position, and end cursor
* moves. But in the case of initial document loading, it is necessary
* to make some end cursors stop moving, so we have:
* KoTextLoader -> calling -> KoSection::setKeepEndBound()
* KoTextLoader -> calling -> KoSectionModel::allowMovingEndBound()
* ^-- this needed to restore default behaviour after load
*
* 2) Level. Level means the depth of the section in tree. Root
* sections has 0 (zero) level. Now if you look at the possible
* text editing command affecting sections you may notice that
* level of section doesn't change in any case. Initial level
* is set in KoSection constructor as parent's level plus one.
* TODO: write about drag-n-drop here, when its implemented
*
* 3) Name. Each KoSection has a name that must be unique. We have
* two groups of KoSections in each moment of time: the first group
* consists of the sections that are present in document now,
* the second group consists of the sections that were deleted, but
* we still need them as they may be restored with "undo".
* This groups are stored in m_registeredSections and m_sectionNames.
*
* Sections are created through this newSection() and newSectionEnd()
* functions.
*
* This object is created for QTextDocument on the first query of it.
*/
class KRITATEXT_EXPORT KoSectionModel : public QAbstractItemModel
{
Q_OBJECT
public:
static const int PointerRole = Qt::UserRole;
explicit KoSectionModel(QTextDocument *doc);
~KoSectionModel() override;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &child) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
- /// Creates KoSection in position of @param cursor with some allowed name
+ /// Creates KoSection in position of @p cursor with some allowed name
KoSection *createSection(const QTextCursor &cursor, KoSection *parent);
- /// Creates KoSection in position of @param cursor with specified @param name
+ /// Creates KoSection in position of @p cursor with specified @p name
KoSection *createSection(const QTextCursor &cursor, KoSection *parent, const QString &name);
- /// Creates KoSectionEnd in pair for a @param section
+ /// Creates KoSectionEnd in pair for a @p section
KoSectionEnd *createSectionEnd(KoSection *section);
- /** Tries to set @param section name to @param name
+ /** Tries to set @p section name to @p name
* @return @c false if there is a section with such name
* and new name isn't accepted and @c true otherwise.
*/
bool setName(KoSection *section, const QString &name);
/**
* Returns pointer to the deepest KoSection that covers @p pos
* or 0 if there is no such section
*/
KoSection *sectionAtPosition(int pos);
/// Returns name for the new section.
QString possibleNewName();
/// Returns if this name is possible.
bool isValidNewName(const QString &name) const;
/// Setting all sections end bound cursor to move with text inserting.
void allowMovingEndBound();
- /// Finds index of @param child inside his parent.
+ /// Finds index of @p child inside his parent.
int findRowOfChild(KoSection *child) const;
private:
Q_DISABLE_COPY(KoSectionModel)
friend class DeleteCommand;
friend class NewSectionCommand;
/**
- * Inserts @param section to it's parent (should be
- * stored in @param section already) in position childIdx.
- * Affects only Model Level(@see KoSectionModel).
+ * Inserts @p section to it's parent (should be
+ * stored in @p section already) in position childIdx.
+ * Affects only Model Level
+ * @see KoSectionModel
*/
void insertToModel(KoSection* section, int childIdx);
/**
- * Deletes @param section from it's parent (should be
- * stored in @param section already).
- * Affects only Model Level(@see KoSectionModel).
+ * Deletes @p section from it's parent (should be
+ * stored in @p section already).
+ * Affects only Model Level
+ * @see KoSectionModel
*/
void deleteFromModel(KoSection *section);
QTextDocument *m_doc;
QSet<KoSection *> m_registeredSections; ///< stores pointer to sections that sometime was registered
QHash<QString, KoSection *> m_sectionNames; ///< stores name -> pointer reference, for sections that are visible in document now
QHash<KoSection *, QPersistentModelIndex> m_modelIndex;
QVector<KoSection *> m_rootSections;
};
Q_DECLARE_METATYPE(KoSectionModel *)
#endif //KOSECTIONMODEL_H
diff --git a/plugins/flake/textshape/kotext/KoText.h b/plugins/flake/textshape/kotext/KoText.h
index 2f89bbef61..f549b58f93 100644
--- a/plugins/flake/textshape/kotext/KoText.h
+++ b/plugins/flake/textshape/kotext/KoText.h
@@ -1,131 +1,131 @@
/* This file is part of the KDE project
* Copyright (C) 2006, 2010 Thomas Zander <zander@kde.org>
* Copyright (C) 2008 Girish Ramakrishnan <girish@forwardbias.in>
* Copyright (C) 2011 Pierre Ducroquet <pinaraf@pinaraf.info>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOTEXT_H
#define KOTEXT_H
#include "kritatext_export.h"
#include <KoDocumentResourceManager.h>
#include <styles/KoCharacterStyle.h>
#include <QChar>
#include <QMetaType>
#include <QTextOption>
class QStringList;
/**
* Generic namespace of the Calligra Text library for helper methods and data.
*/
namespace KoText
{
KRITATEXT_EXPORT QStringList underlineTypeList();
KRITATEXT_EXPORT QStringList underlineStyleList();
KRITATEXT_EXPORT Qt::Alignment alignmentFromString(const QString &align);
KRITATEXT_EXPORT QString alignmentToString(Qt::Alignment align);
KRITATEXT_EXPORT Qt::Alignment valignmentFromString(const QString &align);
KRITATEXT_EXPORT QString valignmentToString(Qt::Alignment align);
/// This enum contains values to be used as keys in KoCanvasResourceProvider
enum CanvasResource {
CurrentTextDocument = 382490375, ///< set by the text plugin whenever the document is changed
CurrentTextPosition = 183523, ///< used by the text plugin whenever the position is changed
CurrentTextAnchor = 341899485, ///< used by the text plugin whenever the anchor-position is changed
SelectedTextPosition = 21314576, ///< used by the text plugin whenever the alternative selection is changed
/// used by the text plugin whenever the alternative selection anchor-position is changed
SelectedTextAnchor = 3344189
};
/// For paragraphs each tab definition is represented by this struct.
struct KRITATEXT_EXPORT Tab {
Tab();
qreal position; ///< distance in ps-points from the edge of the text-shape
QTextOption::TabType type; ///< Determine which type is used.
QChar delimiter; ///< If type is DelimitorTab; tab until this char was found in the text.
KoCharacterStyle::LineType leaderType; // none/single/double
KoCharacterStyle::LineStyle leaderStyle; // solid/dotted/dash/...
KoCharacterStyle::LineWeight leaderWeight; // auto/bold/thin/length/percentage/...
qreal leaderWidth; // the width value if length/percentage
QColor leaderColor; ///< if color is valid, then use this instead of the (current) text color
QString leaderText; ///< character to print as the leader (filler of the tabbed space)
bool operator==(const Tab &tab) const;
};
/**
* Text resources per calligra-document.
* \sa KoDocumentResourceManager KoShapeController::resourceManager()
*/
enum DocumentResource {
ChangeTracker = KoDocumentResourceManager::KoTextStart + 1, ///< KoChangeTracker
InlineTextObjectManager, ///< The KoText inline-text-object manager. KoInlineTextObjectManager
TextRangeManager, ///< The KoText inline-text-object manager. KoInlineTextObjectManager
StyleManager, ///< The KoStyleManager
PageProvider, ///< The KoPageProvider
/** The KoDocumentRdf for the document,
this will be a KoDocumentRdfBase when Soprano support is not compiled in. */
DocumentRdf
};
enum KoTextFrameProperty {
SubFrameType = QTextFormat::UserProperty + 1
};
enum KoSubFrameType {
AuxillaryFrameType = 1,
NoteFrameType
};
/// Text in the objects will be positioned according to the direction.
enum Direction {
AutoDirection, ///< Take the direction from the text.
LeftRightTopBottom, ///< Text layout for most western languages
RightLeftTopBottom, ///< Text layout for languages like Hebrew
TopBottomRightLeft, ///< Vertical text layout.
TopBottomLeftRight, ///< Vertical text layout. ?
InheritDirection ///< Direction is unspecified and should come from the container
};
/// convert the string version of directions (as specified in XSL and ODF) to the Direction enum
KRITATEXT_EXPORT Direction directionFromString(const QString &direction);
/// convert the Direction enum to the string version of directions (as specified in XSL and ODF)
KRITATEXT_EXPORT QString directionToString(Direction direction);
/// There are several possible text breaks
enum KoTextBreakProperty {
NoBreak = 0, ///< No text break
ColumnBreak, ///< Column break
PageBreak ///< Page break
};
/// convert the string version of text break (as specified in ODF) to the KoTextBreakProperty enum
KRITATEXT_EXPORT KoTextBreakProperty textBreakFromString(const QString &textBreak);
/// convert the KoTextBreakProperty enum to the string version of text break (as specified in ODF)
KRITATEXT_EXPORT QString textBreakToString (KoTextBreakProperty textBreak);
-///@TODO: move to KoUnit ?
+///TODO: move to KoUnit ?
KRITATEXT_EXPORT QTextLength parseLength (const QString &length);
}
Q_DECLARE_METATYPE(KoText::Tab)
#endif
diff --git a/plugins/flake/textshape/kotext/KoTextDebug.h b/plugins/flake/textshape/kotext/KoTextDebug.h
index 063bc39056..5be3e0f03a 100644
--- a/plugins/flake/textshape/kotext/KoTextDebug.h
+++ b/plugins/flake/textshape/kotext/KoTextDebug.h
@@ -1,293 +1,293 @@
/* This file is part of the KDE project
* Copyright (C) 2008 Girish Ramakrishnan <girish@forwardbias.in>
* Copyright (C) 2009 Elvis Stansvik <elvstone@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOTEXTDEBUG_H
#define KOTEXTDEBUG_H
#include "kritatext_export.h"
class QTextDocument;
class QTextFrame;
class QTextBlock;
class QTextTable;
class QTextTableCell;
class QTextFragment;
class QTextCharFormat;
class QTextListFormat;
class QTextTableFormat;
class QTextTableCellFormat;
class QTextFrameFormat;
class QTextBlockFormat;
class QTextStream;
class KoParagraphStyle;
class KoCharacterStyle;
class KoTableStyle;
class KoTableCellStyle;
#include <QMap>
#include <QVariant>
/**
* @brief KoText debugging class.
*
* This class provides a set of public static functions for debugging the structure of
* QTextDocument text documents. The functions will dump the structure of the document
* along with any formats in a human-friendly pseudo-XML format.
*
* To most top level function is dumpDocument(), which can be used to dump an entire
* document. In addition to that, there's a set of functions for dumping certain
* parts of a document , such as dumpFrame(), dumpBlock() et.c.
*
* For example, the following code
*
* @code
* QTextDocument doc;
* QTextCursor cursor(&doc);
* cursor.insertText("Hello!\n");
* cursor.insertHtml("<b>World!</b>");
*
* QTextStream out(stdout);
* KoTextDebug::dumpDocument(&doc, out);
* @endcode
*
* will result in this output:
*
- * <pre>
+ * @verbatim
* <document defaultfont="Sans Serif,9,-1,5,50,0,0,0,0,0">
* <frame margin="4" top-margin="4" bottom-margin="4" left-margin="4" right-margin="4" border-style="Outset">
* <block type="char">
* <fragment type="char">
* |Hello!|
* </fragment>
* </block>
*
* <block type="char">
* <fragment type="char" font="weight 75">
* |World!|
* </fragment>
* </block>
* </frame>
* </document>
- * </pre>
+ * @endverbatim
*
* @sa dumpDocument(), dumpFrame(), dumpBlock()
*/
class KRITATEXT_EXPORT KoTextDebug
{
public:
/**
* Dump the structure of the specified document.
*
* @param document a pointer to the document that should be dumped.
* @param out output stream to dump to.
*/
static void dumpDocument(const QTextDocument *document, QTextStream &out);
/**
* Dump the structure of the specified frame.
*
* @sa frameAttributes()
*
* @param frame a pointer to the frame that should be dumped.
* @param out output stream to dump to.
*/
static void dumpFrame(const QTextFrame *frame, QTextStream &out);
/**
* Dump the structure of the specified block.
*
* @param block the block that should be dumped.
* @param out output stream to dump to.
*/
static void dumpBlock(const QTextBlock &block, QTextStream &out);
/**
* Dump the structure of the specified table.
*
* @sa tableAttributes()
*
- * @param a pointer to the table that should be dumped.
+ * @param table pointer to the table that should be dumped.
* @param out output stream to dump to.
*/
static void dumpTable(const QTextTable *table, QTextStream &out);
/**
* Dump the structure of the specified table cell.
*
* @sa tableCellAttributes()
*
* @param cell the cell that should be dumped.
* @param out output stream to dump to.
*/
static void dumpTableCell(const QTextTableCell &cell, QTextStream &out);
/**
* Dump the contents of the specified text fragment.
*
* @note { The fragment content will be enclosed in '|' characters. }
*
* @param fragment the fragment which content should be dumped.
* @param out output stream to dump to.
*/
static void dumpFragment(const QTextFragment &fragment, QTextStream &out);
/**
* Get the properties of the given text character format.
*
* The returned string will be formatted in XML-like attribute list format:
*
* <pre>"key=value key2=value2 ..."</pre>
*
* @param format the text character format from which properties should be fetched.
* @return the formatted attribute string.
*/
static QString textAttributes(const QTextCharFormat &format);
/**
* Get the properties of the given character style.
*
* The returned string will be formatted in XML-like attribute list format:
*
* <pre>"key=value key2=value2 ..."</pre>
*
* @param style the character style from which properties should be fetched.
* @return the formatted attribute string.
*/
static QString textAttributes(const KoCharacterStyle &style);
/**
* Get the properties of the given text block format.
*
* The returned string will be formatted in XML-like attribute list format:
*
* <pre>"key=value key2=value2 ..."</pre>
*
* @param format the text block format from which properties should be fetched.
* @return the formatted attribute string.
*/
static QString paraAttributes(const QTextBlockFormat &format);
/**
* Get the properties of the given paragraph style.
*
* The returned string will be formatted in XML-like attribute list format:
*
* <pre>"key=value key2=value2 ..."</pre>
*
* @param style the paragraph style from which properties should be fetched.
* @return the formatted attribute string.
*/
static QString paraAttributes(const KoParagraphStyle &style);
/**
* Get the properties of the given list format.
*
* The returned string will be formatted in XML-like attribute list format:
*
* <pre>"key=value key2=value2 ..."</pre>
*
* @param format the list format from which properties should be fetched.
* @return the formatted attribute string.
*/
static QString listAttributes(const QTextListFormat &format);
/**
* Get the properties of the given table style.
*
* The returned string will be formatted in XML-like attribute list format:
*
* <pre>"key=value key2=value2 ..."</pre>
*
* @param tableStyle the table style from which properties should be fetched.
* @return the formatted attribute string.
*/
static QString tableAttributes(const KoTableStyle &tableStyle);
/**
* Get the properties of the given table format.
*
* The returned string will be formatted in XML-like attribute list format:
*
* <pre>"key=value key2=value2 ..."</pre>
*
* @param tableFormat the table format from which properties should be fetched.
* @return the formatted attribute string.
*/
static QString tableAttributes(const QTextTableFormat &tableFormat);
/**
* Get the properties of the given table cell style.
*
* The returned string will be formatted in XML-like attribute list format:
*
* <pre>"key=value key2=value2 ..."</pre>
*
- * @param tableStyle the table cell style from which properties should be fetched.
+ * @param tableCellStyle the table cell style from which properties should be fetched.
* @return the formatted attribute string.
*/
static QString tableCellAttributes(const KoTableCellStyle &tableCellStyle);
/**
* Get the properties of the given table cell format.
*
* The returned string will be formatted in XML-like attribute list format:
*
* <pre>"key=value key2=value2 ..."</pre>
*
* @param tableCellFormat the table cell format from which properties should be fetched.
* @return the formatted attribute string.
*/
static QString tableCellAttributes(const QTextTableCellFormat &tableCellFormat);
/**
* Get the properties of the given text frame format.
*
* The returned string will be formatted in XML-like attribute list format:
*
* <pre>"key=value key2=value2 ..."</pre>
*
- * @param format the text frame format from which properties should be fetched.
+ * @param frameFormat the text frame format from which properties should be fetched.
* @return the formatted attribute string.
*/
static QString frameAttributes(const QTextFrameFormat &frameFormat);
/**
* Get the inline object properties of the object with the given text character format.
*
* The returned string will be formatted in XML-like attribute list format:
*
* <pre>"key=value key2=value2 ..."</pre>
*
- * @param format the character format of the object from which properties should be fetched.
+ * @param textFormat the character format of the object from which properties should be fetched.
* @return the formatted attribute string.
*/
static QString inlineObjectAttributes(const QTextCharFormat &textFormat);
private:
KoTextDebug();
KoTextDebug(const KoTextDebug&);
KoTextDebug operator=(const KoTextDebug&);
static const QTextDocument *document; /**< Pointer to the debugged document. */
static int depth; /**< Current indentation depth. */
static const int INDENT; /**< Indentation value. */
};
#endif /* KOTEXTDEBUG_H */
diff --git a/plugins/flake/textshape/kotext/KoTextEditor.h b/plugins/flake/textshape/kotext/KoTextEditor.h
index 33a4de7453..4708c735a6 100644
--- a/plugins/flake/textshape/kotext/KoTextEditor.h
+++ b/plugins/flake/textshape/kotext/KoTextEditor.h
@@ -1,561 +1,568 @@
/* This file is part of the KDE project
* Copyright (C) 2009 Pierre Stirnweiss <pstirnweiss@googlemail.com>
* Copyright (C) 2009 Thomas Zander <zander@kde.org>
* Copyright (C) 2011 Boudewijn Rempt <boud@valdyas.org>
* Copyright (C) 2011-2012 C. Boemann <cbo@boemann.dk>
* Copyright (C) 2014 Denis Kuplyakov <dener.kup@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOTEXTEDITOR_H
#define KOTEXTEDITOR_H
#include "kritatext_export.h"
#include <kundo2magicstring.h>
#include <KoGenChange.h>
#include <KoBorder.h>
#include <KoSection.h>
#include <QMetaType>
#include <QTextCursor>
#include <QTextFrame>
class KoListLevelProperties;
class KoCharacterStyle;
class KoInlineObject;
class KoParagraphStyle;
class KoInlineNote;
class KoInlineCite;
class KoBibliographyInfo;
class KoCanvasBase;
class KoTableOfContentsGeneratorInfo;
class KoShapeAnchor;
class KoShape;
class KoBookmark;
class KoAnnotation;
class KoTextRangeManager;
class KoTextVisitor;
class KUndo2Command;
class QTextBlock;
class QTextCharFormat;
class QTextBlockFormat;
class QTextDocument;
class QTextDocumentFragment;
class QString;
class QMimeData;
/**
* KoTextEditor is a wrapper around QTextCursor. It handles undo/redo and change
* tracking for all editing commands.
*/
class KRITATEXT_EXPORT KoTextEditor: public QObject
{
Q_OBJECT
public:
enum ChangeListFlag {
NoFlags = 0,
ModifyExistingList = 1,
MergeWithAdjacentList = 2,
MergeExactly = 4,
CreateNumberedParagraph = 8,
AutoListStyle = 16,
DontUnsetIfSame = 32 /// do not unset the current list style if it is already been set the same
};
Q_DECLARE_FLAGS(ChangeListFlags, ChangeListFlag)
explicit KoTextEditor(QTextDocument *document);
~KoTextEditor() override;
/**
* Retrieves the texteditor for the document of the first text shape in the current
* set of selected shapes on the given canvas.
*
* @param canvas the canvas we will check for a suitable selected shape.
* @returns a texteditor, or 0 if there is no shape active that has a QTextDocument as
* userdata
*/
static KoTextEditor *getTextEditorFromCanvas(KoCanvasBase *canvas);
public: // KoToolSelection overloads
/// returns true if the wrapped QTextCursor has a selection.
bool hasSelection() const;
/** returns true if the current cursor position is protected from editing
- * @param cached use cached value if available.
+ * @param useCached use cached value if available.
*/
bool isEditProtected(bool useCached = false) const;
public:
bool operator!=(const QTextCursor &other) const;
bool operator<(const QTextCursor &other) const;
bool operator<=(const QTextCursor &other) const;
bool operator==(const QTextCursor &other) const;
bool operator>(const QTextCursor &other) const;
bool operator>=(const QTextCursor &other) const;
const QTextCursor constCursor() const;
private:
// for the call to KoTextLoader::loadBody, which has a QTextCursor
friend class KoTextPaste;
// from KoTextEditor_p.h
friend class CharFormatVisitor;
// our commands can have access to us
friend class DeleteTableRowCommand;
friend class DeleteTableColumnCommand;
friend class InsertTableRowCommand;
friend class InsertTableColumnCommand;
friend class ChangeTrackedDeleteCommand;
friend class DeleteCommand;
friend class InsertInlineObjectCommand;
friend class InsertNoteCommand;
friend class ParagraphFormattingCommand;
friend class RenameSectionCommand;
friend class NewSectionCommand;
friend class SplitSectionsCommand;
// for unittests
friend class TestKoInlineTextObjectManager;
// temporary...
friend class TextShape;
friend class TextTool;
/**
* This should be used only as read-only cursor or within a KUndo2Command sub-class which
* will be added to the textEditor with addCommand. For examples of proper implementation of
* such undoCommands, see the TextShape commands.
*/
QTextCursor* cursor();
public Q_SLOTS:
- /// This adds the \ref command to the calligra undo stack.
+ /// This adds the \p command to the calligra undo stack.
///
/// From this point forward all text manipulation is placed in the qt text systems internal
- /// undostack while also adding representative subcommands to \ref command.
+ /// undostack while also adding representative subcommands to \p command.
///
- /// The \ref command is not redone as part of this process.
+ /// The \p command is not redone as part of this process.
///
- /// Note: Be aware that many KoTextEditor methods start their own commands thus terminating
- /// the recording of this \ref command. Only use QTextCursor manipulation (with all the issues
+ /// \note Be aware that many KoTextEditor methods start their own commands thus terminating
+ /// the recording of this \p command. Only use QTextCursor manipulation (with all the issues
/// that brings) or only use KoTextEditor methods that don't start their own command.
///
/// The recording is automatically terminated when another command is added, which as mentioned
/// can happen by executing some of the KoTextEditor methods.
void addCommand(KUndo2Command *command);
/// This instantly "redo" the command thus placing all the text manipulation the "redo" does
/// (should be implemented with a "first redo" pattern) in the qt text systems internal
- /// undostack while also adding representative subcommands to \ref command.
+ /// undostack while also adding representative subcommands to \p command.
///
- /// When \ref command is done "redoing" no further text manipulation is added as subcommands.
+ /// When \p command is done "redoing" no further text manipulation is added as subcommands.
///
- /// \ref command is not put on the calligra undo stack. That is the responsibility of the
- /// caller, or the caller can choose to quickly undo and then delete the \ref command.
+ /// \p command is not put on the calligra undo stack. That is the responsibility of the
+ /// caller, or the caller can choose to quickly undo and then delete the \p command.
void instantlyExecuteCommand(KUndo2Command *command);
void registerTrackedChange(QTextCursor &selection, KoGenChange::Type changeType, const KUndo2MagicString &title, QTextFormat &format, QTextFormat &prevFormat, bool applyToWholeBlock = false);
void bold(bool bold);
void italic(bool italic);
void underline(bool underline);
void strikeOut(bool strikeOut);
void setHorizontalTextAlignment(Qt::Alignment align);
void setVerticalTextAlignment(Qt::Alignment align);
void increaseIndent();
void decreaseIndent();
void increaseFontSize();
void decreaseFontSize();
void setFontFamily(const QString &font);
void setFontSize(qreal size);
void setTextColor(const QColor &color);
void setTextBackgroundColor(const QColor &color);
void setStyle(KoParagraphStyle *style);
void setStyle(KoCharacterStyle *style);
void mergeAutoStyle(const QTextCharFormat &deltaCharFormat);
void applyDirectFormatting(const QTextCharFormat &deltaCharFormat, const QTextBlockFormat &deltaBlockFormat, const KoListLevelProperties &llp);
/**
* Insert an inlineObject (such as a variable) at the current cursor position. Possibly replacing the selection.
* @param inliner the object to insert.
- * @param cmd a parent command for the commands created by this methods. If present, the commands
+ * @param parent a parent command for the commands created by this methods. If present, the commands
* will not be added to the document's undo stack automatically.
*/
void insertInlineObject(KoInlineObject *inliner, KUndo2Command *parent = 0);
/**
* update the position of all inline objects from the given start point to the given end point.
* @param start start position for updating. If 0, we update from the start of the document
* @param end end position for updating. If -1, we update to the end of the document
*/
void updateInlineObjectPosition(int start = 0, int end = -1);
/**
* Remove the KoShapeAnchor objects from the document.
*
* NOTE: Call this method only when the shapes belonging to the anchors have been deleted.
*/
void removeAnchors(const QList<KoShapeAnchor *> &anchors, KUndo2Command *parent);
/**
* Remove the KoAnnotation objects from the document.
*
* NOTE: Call this method only when the shapes belonging to the annotations have been deleted.
* This is not the way to delete annotations directly - instead delete the shape or
* delete the text containing the annotation
*/
void removeAnnotations(const QList<KoAnnotation *> &annotations, KUndo2Command *parent);
/**
* At the current cursor position, insert a marker that marks the next word as being part of the index.
* @returns returns the index marker when successful, or 0 if failed. Failure can be because there is no word
* at the cursor position or there already is an index marker available.
*/
KoInlineObject *insertIndexMarker();
/// add a bookmark on current cursor location or current selection
KoBookmark *addBookmark(const QString &name);
/// Add an annotation at the current cursor location or the current selection.
KoAnnotation *addAnnotation(KoShape *annotationShape);
KoTextRangeManager *textRangeManager() const;
/**
* Insert a frame break at the cursor position, moving the rest of the text to the next frame.
*/
void insertFrameBreak();
/**
* paste the given mimedata object at the current position
* @param canvas the canvas we used when placing the shape.
* @param mimeData: the mimedata containing text, html or odf
* @param pasteAsText: if true, paste without formatting
*/
void paste(KoCanvasBase *canvas, const QMimeData *mimeData, bool pasteAsText=false);
/**
* @param numberingEnabled when true, we will enable numbering for the current paragraph (block).
*/
void toggleListNumbering(bool numberingEnabled);
/**
* change the current block's list properties
*/
void setListProperties(const KoListLevelProperties &llp,
ChangeListFlags flags = ChangeListFlags(ModifyExistingList | MergeWithAdjacentList), KUndo2Command *parent = 0);
// -------------------------------------------------------------
// Wrapped QTextCursor methods
// -------------------------------------------------------------
int anchor() const;
bool atBlockEnd() const;
bool atBlockStart() const;
bool atEnd() const;
bool atStart() const;
QTextBlock block() const;
QTextCharFormat blockCharFormat() const;
QTextBlockFormat blockFormat() const;
int blockNumber() const;
QTextCharFormat charFormat() const;
void clearSelection();
int columnNumber() const;
void deleteChar();
void deletePreviousChar();
QTextDocument *document() const;
/// Same as Qt, only to be used inside KUndo2Commands
KUndo2Command *beginEditBlock(const KUndo2MagicString &title = KUndo2MagicString());
void endEditBlock();
/**
* Delete one character in the specified direction or a selection.
* Warning: From the outside this method should only be used with a parent command
* and only if there is a selection
* @param previous should be true if act like backspace
+ * @param parent the parent command used for stacking
*/
void deleteChar(bool previous, KUndo2Command *parent = 0);
bool hasComplexSelection() const;
/**
* Insert a table at the current cursor position.
* @param rows the number of rows in the created table.
* @param columns the number of columns in the created table.
*/
void insertTable(int rows, int columns);
/**
* Insert a table row above the current cursor position (if in a table).
*/
void insertTableRowAbove();
/**
* Insert a table row below the current cursor position (if in a table).
*/
void insertTableRowBelow();
/**
* Insert a table column to the left of the current cursor position (if in a table).
*/
void insertTableColumnLeft();
/**
* Insert a table column to the right of the current cursor position (if in a table).
*/
void insertTableColumnRight();
/**
* Delete a table column where the cursor is (if in a table).
*/
void deleteTableColumn();
/**
* Delete a table row where the cursor is (if in a table).
*/
void deleteTableRow();
/**
* Merge table cells (selected by the cursor).
*/
void mergeTableCells();
/**
* Split table cells (selected by the cursor) that were previously merged.
*/
void splitTableCells();
/**
* Sets the width of a table column.
* @param table is the table to be adjusted.
* @param column the column that is to be adjusted.
+ * @param width the new width of the column.
+ * @param parentCommand the parent command used for stacking.
*/
void adjustTableColumnWidth(QTextTable *table, int column, qreal width, KUndo2Command *parentCommand = 0);
/**
* Sets the height of a table row.
* @param table is the table to be adjusted.
* @param row the row that is to be adjusted.
+ * @param height the row height.
+ * @param parentCommand the parent command used for stacking.
*/
void adjustTableRowHeight(QTextTable *table, int row, qreal height, KUndo2Command *parentCommand = 0);
/**
* Changes the width of a table by adjusting the margins.
* @param table is the table to be adjusted.
* @param dLeft delta value for the left margin.
* @param dRight delta value for the right margin.
*/
void adjustTableWidth(QTextTable *table, qreal dLeft, qreal dRight);
/**
* Sets the border formatting of a side in a table cell.
* @param table is the table to be adjusted.
- * @param column the column coordinate of the cell that is to be adjusted.
* @param row the row coordinate of the cell that is to be adjusted.
+ * @param column the column coordinate of the cell that is to be adjusted.
+ * @param cellSide the side border of the cell.
+ * @param data the border data.
*/
void setTableBorderData(QTextTable *table, int row, int column, KoBorder::BorderSide cellSide,
const KoBorder::BorderData &data);
/**
* Insert a footnote at the current cursor position
* @return a pointer to the inserted footnote
*/
KoInlineNote *insertFootNote();
/**
* Insert an endnote at the current cursor position
* @return a pointer to the inserted endnote
*/
KoInlineNote *insertEndNote();
/**
* Insert a table of Contents at the current cursor position.
*/
void insertTableOfContents(KoTableOfContentsGeneratorInfo *info);
/**
* Configures various values of a ToC to the one passed in info
*/
void setTableOfContentsConfig(KoTableOfContentsGeneratorInfo *info, const QTextBlock &block);
void insertBibliography(KoBibliographyInfo *info);
KoInlineCite *insertCitation();
/**
* Inserts the supplied text at the current cursor position. If the second argument is
* supplied, a link is inserted at the current cursor position with the hRef as given
* by the user. To test whether the supplied link destination is a web url or a bookmark,
* a regular expression ( \\S+://\\S+ ) is used.
* @param text is the text to be inserted
* @param hRef if supplied is the Hypertext reference
*/
void insertText(const QString &text, const QString &hRef = QString());
void insertHtml(const QString &html);
void mergeBlockFormat( const QTextBlockFormat &modifier);
bool movePosition(QTextCursor::MoveOperation operation, QTextCursor::MoveMode mode = QTextCursor::MoveAnchor, int n = 1);
/**
* Inserts a new paragraph and warps it to new section
* Source:
* some|textP
* Result:
* someP
* [|textP]
*
* [] -- section bounds
* | -- cursor зщышешщт
* P -- paragraph sign
*/
void newSection();
/**
* Splits sections startings and inserts paragraph between them.
* Source: {sectionIdToInsertBefore == 1}
* [[[sometext...
* ^
* 012
* Result:
* [P
* [[sometext...
*
* [] -- section bounds
* P -- paragraph sign
*/
void splitSectionsStartings(int sectionIdToInsertBefore);
/**
* Splits section endings and insert paragraph between them.
* Source: {sectionIdToInsertAfter == 1}
* sometext]]]
* ^
* 012
* Result:
* sometext]]P
* P]
*
* [] -- section bounds
* P -- paragraph sign
*/
void splitSectionsEndings(int sectionIdToInsertAfter);
void renameSection(KoSection *section, const QString &newName);
void newLine();
bool isWithinSelection(int position) const;
int position() const;
void select(QTextCursor::SelectionType selection);
QString selectedText() const;
QTextDocumentFragment selection() const;
int selectionEnd() const;
int selectionStart() const;
void setBlockFormat(const QTextBlockFormat &format);
void setCharFormat(const QTextCharFormat &format);
void setPosition(int pos, QTextCursor::MoveMode mode = QTextCursor::MoveAnchor);
void setVisualNavigation(bool on);
bool visualNavigation() const;
const QTextFrame *currentFrame () const;
const QTextList *currentList () const;
const QTextTable *currentTable () const;
Q_SIGNALS:
void cursorPositionChanged();
void textFormatChanged();
void characterStyleApplied(KoCharacterStyle *style);
void paragraphStyleApplied(KoParagraphStyle *style);
protected:
void recursivelyVisitSelection(QTextFrame::iterator it, KoTextVisitor &visitor) const;
private:
Q_PRIVATE_SLOT(d, void documentCommandAdded())
class Private;
friend class Private;
Private* const d;
};
Q_DECLARE_METATYPE(KoTextEditor*)
Q_DECLARE_METATYPE(bool *)
Q_DECLARE_OPERATORS_FOR_FLAGS(KoTextEditor::ChangeListFlags)
#endif // KOTEXTEDITOR_H
diff --git a/plugins/flake/textshape/kotext/KoTextEditor_p.h b/plugins/flake/textshape/kotext/KoTextEditor_p.h
index d57122b9d2..18c3a53c1c 100644
--- a/plugins/flake/textshape/kotext/KoTextEditor_p.h
+++ b/plugins/flake/textshape/kotext/KoTextEditor_p.h
@@ -1,264 +1,264 @@
/* This file is part of the KDE project
* Copyright (C) 2009 Pierre Stirnweiss <pstirnweiss@googlemail.com>
* Copyright (C) 2009 Thomas Zander <zander@kde.org>
* Copyright (C) 2015 Soma Schliszka <soma.schliszka@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOTEXTEDITOR_P_H
#define KOTEXTEDITOR_P_H
#include "KoTextEditor.h"
#include "KoTextDocument.h"
#include "styles/KoParagraphStyle.h"
#include "styles/KoStyleManager.h"
#include "changetracker/KoChangeTracker.h"
#include <klocalizedstring.h>
#include <kundo2magicstring.h>
#include <QStack>
#include <QTextBlock>
#include <QTextDocument>
#include <QTextTableCell>
#include <QTimer>
class KUndo2Command;
class Q_DECL_HIDDEN KoTextEditor::Private
{
public:
enum State {
NoOp,
KeyPress,
Delete,
Format,
Custom
};
explicit Private(KoTextEditor *qq, QTextDocument *document);
~Private() {}
void documentCommandAdded();
void updateState(State newState, const KUndo2MagicString &title = KUndo2MagicString());
void newLine(KUndo2Command *parent);
void clearCharFormatProperty(int propertyId);
void emitTextFormatChanged();
KoTextEditor *q;
QTextCursor caret;
QTextDocument *document;
QStack<KUndo2Command*> commandStack;
bool addNewCommand;
bool dummyMacroAdded;
int customCommandCount;
KUndo2MagicString commandTitle;
State editorState;
bool editProtected;
bool editProtectionCached;
};
class KoTextVisitor
{
public:
/// The ObjectVisitingMode enum marks how was the visited object selected.
enum ObjectVisitingMode {
Partly, /// The visited object (table, cell, ...) is just @b partly selected. (Eg. just one cell is selected in the visited table)
Entirely, /// The visited object (table, cell, ...) is @b entirely selected.
};
explicit KoTextVisitor(KoTextEditor *editor)
: m_abortVisiting(false)
, m_editor(editor)
{
}
virtual ~KoTextVisitor() {}
// called whenever a visit was prevented by editprotection
virtual void nonVisit() {}
virtual void visitFragmentSelection(QTextCursor &)
{
}
/**
* This method allows to perform custom operation when the visitor reaches a QTextTable
* @param visitedTable pointer to the currently visited table object
* @param visitingMode flag, marks if the table is just partly visited or entirely
*/
virtual void visitTable(QTextTable *visitedTable, ObjectVisitingMode visitingMode)
{
Q_UNUSED(visitedTable);
Q_UNUSED(visitingMode);
}
/**
* This method allows to perform custom operation when the visitor reaches a QTextTableCell
- * @param visitedTable pointer to the currently visited cell object
+ * @param visitedCell pointer to the currently visited cell object
* @param visitingMode flag, marks if the cell is just partly visited or entirely
*/
virtual void visitTableCell(QTextTableCell *visitedCell, ObjectVisitingMode visitingMode)
{
Q_UNUSED(visitedCell);
Q_UNUSED(visitingMode);
}
// The default implementation calls visitFragmentSelection on each fragment.intersect.selection
virtual void visitBlock(QTextBlock &block, const QTextCursor &caret)
{
for (QTextBlock::iterator it = block.begin(); it != block.end(); ++it) {
QTextCursor fragmentSelection(caret);
fragmentSelection.setPosition(qMax(caret.selectionStart(), it.fragment().position()));
fragmentSelection.setPosition(qMin(caret.selectionEnd(), it.fragment().position() + it.fragment().length()), QTextCursor::KeepAnchor);
if (fragmentSelection.anchor() >= fragmentSelection.position()) {
continue;
}
visitFragmentSelection(fragmentSelection);
}
}
bool abortVisiting() { return m_abortVisiting;}
void setAbortVisiting(bool abort) {m_abortVisiting = abort;}
KoTextEditor * editor() const {return m_editor;}
private:
bool m_abortVisiting;
KoTextEditor *m_editor;
};
class BlockFormatVisitor
{
public:
BlockFormatVisitor() {}
virtual ~BlockFormatVisitor() {}
virtual void visit(QTextBlock &block) const = 0;
static void visitSelection(KoTextEditor *editor, const BlockFormatVisitor &visitor, const KUndo2MagicString &title = kundo2_i18n("Format"), bool resetProperties = false, bool registerChange = true) {
int start = qMin(editor->position(), editor->anchor());
int end = qMax(editor->position(), editor->anchor());
QTextBlock block = editor->block();
if (block.position() > start)
block = block.document()->findBlock(start);
// now loop over all blocks that the selection contains and alter the text fragments where applicable.
while (block.isValid() && block.position() <= end) {
QTextBlockFormat prevFormat = block.blockFormat();
if (resetProperties) {
if (KoTextDocument(editor->document()).styleManager()) {
KoParagraphStyle *old = KoTextDocument(editor->document()).styleManager()->paragraphStyle(block.blockFormat().intProperty(KoParagraphStyle::StyleId));
if (old)
old->unapplyStyle(block);
}
}
visitor.visit(block);
QTextCursor cursor(block);
QTextBlockFormat format = cursor.blockFormat();
if (registerChange)
editor->registerTrackedChange(cursor, KoGenChange::FormatChange, title, format, prevFormat, true);
block = block.next();
}
}
};
class CharFormatVisitor
{
public:
CharFormatVisitor() {}
virtual ~CharFormatVisitor() {}
virtual void visit(QTextCharFormat &format) const = 0;
static void visitSelection(KoTextEditor *editor, const CharFormatVisitor &visitor, const KUndo2MagicString &title = kundo2_i18n("Format"), bool registerChange = true) {
int start = qMin(editor->position(), editor->anchor());
int end = qMax(editor->position(), editor->anchor());
if (start == end) { // just set a new one.
QTextCharFormat format = editor->charFormat();
visitor.visit(format);
if (registerChange && KoTextDocument(editor->document()).changeTracker() && KoTextDocument(editor->document()).changeTracker()->recordChanges()) {
QTextCharFormat prevFormat(editor->charFormat());
int changeId = KoTextDocument(editor->document()).changeTracker()->getFormatChangeId(title, format, prevFormat, editor->charFormat().property( KoCharacterStyle::ChangeTrackerId ).toInt());
format.setProperty(KoCharacterStyle::ChangeTrackerId, changeId);
}
editor->cursor()->setCharFormat(format);
return;
}
QTextBlock block = editor->block();
if (block.position() > start)
block = block.document()->findBlock(start);
QList<QTextCursor> cursors;
QList<QTextCharFormat> formats;
// now loop over all blocks that the selection contains and alter the text fragments where applicable.
while (block.isValid() && block.position() < end) {
QTextBlock::iterator iter = block.begin();
while (! iter.atEnd()) {
QTextFragment fragment = iter.fragment();
if (fragment.position() > end)
break;
if (fragment.position() + fragment.length() <= start) {
++iter;
continue;
}
QTextCursor cursor(block);
cursor.setPosition(fragment.position() + 1);
QTextCharFormat format = cursor.charFormat(); // this gets the format one char after the position.
visitor.visit(format);
if (registerChange && KoTextDocument(editor->document()).changeTracker() && KoTextDocument(editor->document()).changeTracker()->recordChanges()) {
QTextCharFormat prevFormat(cursor.charFormat());
int changeId = KoTextDocument(editor->document()).changeTracker()->getFormatChangeId(title, format, prevFormat, cursor.charFormat().property( KoCharacterStyle::ChangeTrackerId ).toInt());
format.setProperty(KoCharacterStyle::ChangeTrackerId, changeId);
}
cursor.setPosition(qMax(start, fragment.position()));
int to = qMin(end, fragment.position() + fragment.length());
cursor.setPosition(to, QTextCursor::KeepAnchor);
cursors.append(cursor);
formats.append(format);
QTextCharFormat prevFormat(cursor.charFormat());
if (registerChange)
editor->registerTrackedChange(cursor,KoGenChange::FormatChange,title, format, prevFormat, false); //this will lead to every fragment having a different change until the change merging in registerTrackedChange checks also for formatChange or not?
++iter;
}
block = block.next();
}
QList<QTextCharFormat>::Iterator iter = formats.begin();
Q_FOREACH (QTextCursor cursor, cursors) {
cursor.setCharFormat(*iter);
++iter;
}
}
};
#endif //KOTEXTEDITOR_P_H
diff --git a/plugins/flake/textshape/kotext/KoTextEditor_undo.cpp b/plugins/flake/textshape/kotext/KoTextEditor_undo.cpp
index 11d6095347..d131318109 100644
--- a/plugins/flake/textshape/kotext/KoTextEditor_undo.cpp
+++ b/plugins/flake/textshape/kotext/KoTextEditor_undo.cpp
@@ -1,329 +1,329 @@
/* This file is part of the KDE project
* Copyright (C) 2009-2012 Pierre Stirnweiss <pstirnweiss@googlemail.com>
* Copyright (C) 2006-2010 Thomas Zander <zander@kde.org>
* Copyright (c) 2011 Boudewijn Rempt <boud@valdyas.org>
* Copyright (C) 2011-2012 C. Boemann <cbo@boemann.dk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoTextEditor.h"
#include "KoTextEditor_p.h"
#include "KoTextDocument.h"
#include <kundo2command.h>
#include <klocalizedstring.h>
#include <QTextDocument>
#include <QWeakPointer>
#include "TextDebug.h"
/** Calligra's undo/redo framework.
- The @class KoTextEditor undo/redo framework sits between the @class QTextDocument and the application's undo/redo stack.
+ The @c KoTextEditor undo/redo framework sits between the @c QTextDocument and the application's undo/redo stack.
- When the @class QTextDocument is changed by an editing action, it internally creates an undo/redo command. When doing so a signal (undoCommandAdded()) is emitted by the @class QTextDocument in order for applications to update their undo/redo stack accordingly.
- Each @class QTextDocument used in Calligra is handled by a specific @class KoTextEditor. It is responsible for on the one hand edit the @class QTextDocument, and on the other hand to listen for the QTextDocument's signal.
+ When the @c QTextDocument is changed by an editing action, it internally creates an undo/redo command. When doing so a signal (undoCommandAdded()) is emitted by the @c QTextDocument in order for applications to update their undo/redo stack accordingly.
+ Each @c QTextDocument used in Calligra is handled by a specific @c KoTextEditor. It is responsible for on the one hand edit the @c QTextDocument, and on the other hand to listen for the QTextDocument's signal.
- Calligra uses a @class KUndo2Stack as its application undo/redo stack. This stack is populated by @class KUndo2Command or sub-classes of it.
+ Calligra uses a @c KUndo2Stack as its application undo/redo stack. This stack is populated by @c KUndo2Command or sub-classes of it.
In order to limit the number of command sub-classes, KoTextEditor provides a framework which uses a generic command.
- The framework is based on a sort of state machine. The KoTextEditor can be in several different states (see @enum KoTextEditor::Private::State).
+ The framework is based on a sort of state machine. The KoTextEditor can be in several different states (see @ref KoTextEditor::Private::State ).
These are:
- NoOp: this states indicates that the KoTextEditor is not editing the QTextDocument.
- KeyPress: this state indicates that the user is typing text. All text typed in succession should correspond to one undo command. To be used when entering text outside of an insertTextCommand.
- Delete: this state indicates that the user is deleting characters. All deletions done in succession should correspond to one undo command. To be used for deleting outside a deleteCommand. Currently not in use, our deletion is done through a command because of inline objects.
- Format: this state indicates that we are formatting text. To be used when formatting outside of a command.
- Custom: this state indicates that the QTextDocument is changed through a KUndo2Command.
+ @c NoOp : this states indicates that the KoTextEditor is not editing the QTextDocument.
+ @c KeyPress : this state indicates that the user is typing text. All text typed in succession should correspond to one undo command. To be used when entering text outside of an insertTextCommand.
+ @c Delete : this state indicates that the user is deleting characters. All deletions done in succession should correspond to one undo command. To be used for deleting outside a deleteCommand. Currently not in use, our deletion is done through a command because of inline objects.
+ @c Format : this state indicates that we are formatting text. To be used when formatting outside of a command.
+ @c Custom : this state indicates that the QTextDocument is changed through a KUndo2Command.
KoTextEditor reacts differently when receiving the QTextDocument's signal, depending on its state.
In addition the framework allows to encapsulate modifications in a on-the-fly created custom command (\sa beginEditBlock() endEditBlock()).
Furthermore the framework allows to push complete KUndo2Commands.
See the documentation file for how to use this framework.
*/
/*
Important members:
commandStack: This stack holds the headCommands. These parent the generated UndoTextCommands. When undo or redo is called, they will in turn call UndoTextCommand::undo/redo.
editorState: Holds the state of the KoTextEditor. see above
commandTitle: Holds the title which is to be used when creating a headCommand.
addNewCommand: bool used to tell the framework to create a new headCommand and push it on the commandStack, when receiving an undoCommandAdded signal from QTextDocument.
customCommandCount: counter used to keep track of nested KUndo2Commands that are pushed on the KoTextEditor.
*/
// This slot is called when the KoTextEditor receives the signal undoCommandAdded() from QTextDocument. A generic UndoTextCommand is used to match the QTextDocument's internal undo command. This command calls QTextDocument::undo() or QTextDocument::redo() respectively, which triggers the undo redo actions on the document.
//In order to allow nesting commands, we maintain a stack of commands. The top command will be the parent of our auto generated UndoTextCommands.
//Depending on the case, we might create a command which will serve as head command. This is pushed to our commandStack and eventually to the application's stack.
void KoTextEditor::Private::documentCommandAdded()
{
class UndoTextCommand : public KUndo2Command
{
public:
UndoTextCommand(QTextDocument *document, KoTextEditor::Private *p, KUndo2Command *parent = 0)
: KUndo2Command(kundo2_i18n("Text"), parent),
m_document(document)
, m_p(p)
{}
void undo() override {
QTextDocument *doc = m_document.data();
if (doc == 0)
return;
doc->undo(KoTextDocument(doc).textEditor()->cursor());
m_p->emitTextFormatChanged();
}
void redo() override {
QTextDocument *doc = m_document.data();
if (doc == 0)
return;
doc->redo(KoTextDocument(doc).textEditor()->cursor());
m_p->emitTextFormatChanged();
}
QWeakPointer<QTextDocument> m_document;
KoTextEditor::Private *m_p;
};
debugText << "received a QTextDocument undoCommand signal";
debugText << "commandStack count: " << commandStack.count();
debugText << "addCommand: " << addNewCommand;
debugText << "editorState: " << editorState;
if (commandStack.isEmpty()) {
//We have an empty stack. We need a head command which is to be pushed onto our commandStack and on the application stack if there is one.
//This command will serve as a parent for the auto-generated UndoTextCommands.
debugText << "empty stack, will push a new headCommand on both commandStack and application's stack. title: " << commandTitle;
commandStack.push(new KUndo2Command(commandTitle));
if (KoTextDocument(document).undoStack()) {
KoTextDocument(document).undoStack()->push(commandStack.top());
}
addNewCommand = false;
debugText << "commandStack is now: " << commandStack.count();
}
else if (addNewCommand) {
//We have already a headCommand on the commandStack. However we want a new child headCommand (nested commands) on the commandStack for parenting further UndoTextCommands. This shouldn't be pushed on the application's stack because it is a child of the current commandStack's top.
debugText << "we have a headCommand on the commandStack but need a new child command. we will push it only on the commandStack: " << commandTitle;
commandStack.push(new KUndo2Command(commandTitle, commandStack.top()));
addNewCommand = false;
debugText << "commandStack count is now: " << commandStack.count();
}
else if ((editorState == KeyPress || editorState == Delete) && !commandStack.isEmpty() && commandStack.top()->childCount()) {
//QTextDocument emits a signal on the first key press (or delete) and "merges" the subsequent ones, until an incompatible one is done. In which case it re-emit a signal.
//Here we are in KeyPress (or Delete) state. The fact that the commandStack isn't empty and its top command has children means that we just received such a signal. We therefore need to pop the previous headCommand (which were either key press or delete) and create a new one to parent the UndoTextCommands. This command also need to be pushed on the application's stack.
debugText << "we are in subsequent keyPress/delete state and still received a signal. we need to create a new headCommand: " << commandTitle;
debugText << "so we pop the current one and push the new one on both the commandStack and the application's stack";
commandStack.pop();
commandStack.push(new KUndo2Command(commandTitle, !commandStack.isEmpty()?commandStack.top():0));
if (KoTextDocument(document).undoStack()) {
KoTextDocument(document).undoStack()->push(commandStack.top());
}
debugText << "commandStack count: " << commandStack.count();
}
//Now we can create our UndoTextCommand which is parented to the commandStack't top headCommand.
new UndoTextCommand(document, this, commandStack.top());
debugText << "done creating the dummy UndoTextCommand";
}
//This method is used to update the KoTextEditor state, which will condition how the QTextDocument::undoCommandAdded signal will get handled.
void KoTextEditor::Private::updateState(KoTextEditor::Private::State newState, const KUndo2MagicString &title)
{
debugText << "updateState from: " << editorState << " to: " << newState << " with: " << title;
debugText << "commandStack count: " << commandStack.count();
if (editorState == Custom && newState != NoOp) {
//We already are in a custom state (meaning that either a KUndo2Command was pushed on us, an on-the-fly macro command was started or we are executing a complex editing from within the KoTextEditor.
//In that state any update of the state different from NoOp is part of that "macro". However, updating the state means that we are now wanting to have a new command for parenting the UndoTextCommand generated after the signal
//from QTextDocument. This is to ensure that undo/redo actions are done in the proper order. Setting addNewCommand will ensure that we create such a child headCommand on the commandStack. This command will not be pushed on the application's stack.
debugText << "we are already in a custom state. a new state, which is not NoOp is part of the macro we are doing. we need however to create a new command on the commandStack to parent a signal induced UndoTextCommand";
addNewCommand = true;
if (!title.isEmpty())
commandTitle = title;
else
commandTitle = kundo2_i18n("Text");
debugText << "returning now. commandStack is not modified at this stage";
return;
}
if (newState == NoOp && !commandStack.isEmpty()) {
//Calling updateState to NoOp when the commandStack isn't empty means that the current headCommand on the commandStack is finished. Further UndoTextCommands do not belong to it. So we pop it.
//If after popping the headCommand we still have some commands on the commandStack means we have not finished with the highest "macro". In that case we need to stay in the "Custom" state.
//On the contrary, an empty commandStack means we have finished with the "macro". In that case, we set the editor to NoOp state. A signal from the QTextDocument should also generate a new headCommand.
debugText << "we are in a macro and update the state to NoOp. this means that the command on top of the commandStack is finished. we should pop it";
debugText << "commandStack count before: " << commandStack.count();
commandStack.pop();
debugText << "commandStack count after: " << commandStack.count();
if (commandStack.isEmpty()) {
debugText << "we have no more commands on the commandStack. the macro is complete. next signal induced command will need to be parented to a new headCommand. Also the editor should go to NoOp";
addNewCommand = true;
editorState = NoOp;
}
debugText << "returning now. commandStack count: " << commandStack.count();
return;
}
if (editorState != newState || commandTitle != title) {
//We are not in "Custom" state but either are moving to a new state (from editing to format,...) or the command type is the same, but not the command itself (like format:bold, format:italic). The later case is caracterised by a different command title.
//When we change command, we need to pop the current commandStack's top and ask for a new headCommand to be created.
debugText << "we are not in a custom state but change the command";
debugText << "commandStack count: " << commandStack.count();
if (!commandStack.isEmpty()) {
debugText << "the commandStack is not empty. however the command on it is not a macro. so we pop it and ask to recreate a new one: " << title;
commandStack.pop();
addNewCommand = true;
}
}
editorState = newState;
if (!title.isEmpty())
commandTitle = title;
else
commandTitle = kundo2_i18n("Text");
debugText << "returning now. commandStack count: " << commandStack.count();
}
/// This method is used to push a complete KUndo2Command on the KoTextEditor. This command will be pushed on the application's stack if needed. The logic allows to push several commands which are then going to be nested, provided these children are pushed from within the redo method of their parent.
void KoTextEditor::addCommand(KUndo2Command *command)
{
debugText << "we receive a command to add on the stack.";
debugText << "commandStack count: " << d->commandStack.count();
debugText << "customCommandCount counter: " << d->customCommandCount << " will increase";
//We increase the customCommandCount counter to inform the framework that we are having a further KUndo2Command and update the KoTextEditor's state to Custom.
//However, this update will request a new headCommand to be pushed on the commandStack. This is what we want for internal complex editions but not in this case. Indeed, it must be the KUndo2Command which will parent the UndoTextCommands. Therefore we set the addNewCommand back to false.
//If the commandStack is empty, we are the highest "macro" command and we should therefore push the KUndo2Command on the application's stack.
//On the contrary, if the commandStack is not empty, or the pushed command has a parent, it means that we are adding a nested KUndo2Command. In which case we just want to put it on the commandStack to parent UndoTextCommands. We need to call the redo method manually though.
++d->customCommandCount;
debugText << "we will now go to custom state";
d->updateState(KoTextEditor::Private::Custom, (!command->text().isEmpty())?command->text():kundo2_i18n("Text"));
debugText << "but will set the addCommand to false. we don't want a new headCommand";
d->addNewCommand = false;
debugText << "commandStack count is: " << d->commandStack.count();
if (d->commandStack.isEmpty()) {
debugText << "the commandStack is empty. this means we are the top most command";
d->commandStack.push(command);
debugText << "command pushed on the commandStack. count: " << d->commandStack.count();
KUndo2QStack *stack = KoTextDocument(d->document).undoStack();
if (stack && !command->hasParent()) {
debugText << "we have an application stack and the command is not a sub command of a non text command (which have been pushed outside kotext";
stack->push(command);
debugText << "so we pushed it on the application's' stack";
} else {
debugText << "we either have no application's stack, or our command is actually the child of a non kotext command";
command->redo();
debugText << "still called redo on it";
}
}
else {
debugText << "the commandStack is not empty, our command is actually nested in another kotext command. we don't push on the application stack but only on the commandStack";
d->commandStack.push(command);
debugText << "commandStack count after push: " << d->commandStack.count();
command->redo();
debugText << "called redo still";
}
//When we reach that point, the command has been executed. We first need to clean up all the automatically generated headCommand on our commandStack, which could potentially have been created during the editing. When we reach our pushed command, the commandStack is clean. We can then call a state update to NoOp and decrease the customCommandCount counter.
debugText << "the command has been executed. we need to clean up the commandStack of the auto generated headCommands";
debugText << "before cleaning. commandStack count: " << d->commandStack.count();
while (d->commandStack.top() != command) {
d->commandStack.pop();
}
debugText << "after cleaning. commandStack count: " << d->commandStack.count() << " will set NoOp";
d->updateState(KoTextEditor::Private::NoOp);
debugText << "after NoOp set. inCustomCounter: " << d->customCommandCount << " will decrease and return";
--d->customCommandCount;
}
/// DO NOT USE THIS. It stays here for compiling reasons. But it will severely break everything. Again: DO NOT USE THIS.
void KoTextEditor::instantlyExecuteCommand(KUndo2Command *command)
{
d->updateState(KoTextEditor::Private::Custom, (!command->text().isEmpty())?command->text():kundo2_i18n("Text"));
command->redo();
// instant replay done let's not keep it dangling
if (!command->hasParent()) {
d->updateState(KoTextEditor::Private::NoOp);
}
}
/// This method is used to start an on-the-fly macro command. Use KoTextEditor::endEditBlock to stop it.
/// ***
/// Important note:
/// ***
/// The framework does not allow to push a complete KUndo2Command (through KoTextEditor::addCommand) from within an EditBlock. Doing so will lead in the best case to several undo/redo commands on the application's stack instead of one, in the worst case to an out of sync application's stack.
/// ***
KUndo2Command *KoTextEditor::beginEditBlock(const KUndo2MagicString &title)
{
debugText << "beginEditBlock";
debugText << "commandStack count: " << d->commandStack.count();
debugText << "customCommandCount counter: " << d->customCommandCount;
if (!d->customCommandCount) {
// We are not in a custom macro command. So we first need to update the KoTextEditor's state to Custom. Additionally, if the commandStack is empty, we need to create a master headCommand for our macro and push it on the stack.
debugText << "we are not in a custom command. will update state to custom";
d->updateState(KoTextEditor::Private::Custom, title);
debugText << "commandStack count: " << d->commandStack.count();
if (d->commandStack.isEmpty()) {
debugText << "the commandStack is empty. we need a dummy headCommand both on the commandStack and on the application's stack";
KUndo2Command *command = new KUndo2Command(title);
d->commandStack.push(command);
++d->customCommandCount;
d->dummyMacroAdded = true; //This bool is used to tell endEditBlock that we have created a master headCommand.
KUndo2QStack *stack = KoTextDocument(d->document).undoStack();
if (stack) {
stack->push(command);
} else {
command->redo();
}
debugText << "done adding the headCommand. commandStack count: " << d->commandStack.count() << " inCommand counter: " << d->customCommandCount;
}
}
//QTextDocument sends the undoCommandAdded signal at the end of the QTextCursor edit block. Since we want our master headCommand to parent the signal induced UndoTextCommands, we should not call QTextCursor::beginEditBlock for the headCommand.
if (!(d->dummyMacroAdded && d->customCommandCount == 1)) {
debugText << "we did not add a dummy command, or we are further down nesting. call beginEditBlock on the caret to nest the QTextDoc changes";
//we don't call beginEditBlock for the first headCommand because we want the signals to be sent before we finished our command.
d->caret.beginEditBlock();
}
debugText << "will return top od commandStack";
return (d->commandStack.isEmpty())?0:d->commandStack.top();
}
void KoTextEditor::endEditBlock()
{
debugText << "endEditBlock";
//Only the self created master headCommand (see beginEditBlock) is left on the commandStack, we need to decrease the customCommandCount counter that we increased on creation.
//If we are not yet at this master headCommand, we can call QTextCursor::endEditBlock
if (d->dummyMacroAdded && d->customCommandCount == 1) {
debugText << "only the created dummy headCommand from beginEditBlock is left. we need to decrease further the nesting counter";
//we don't call caret.endEditBlock because we did not begin a block for the first headCommand
--d->customCommandCount;
d->dummyMacroAdded = false;
} else {
debugText << "we are not at our top dummy headCommand. call caret.endEditBlock";
d->caret.endEditBlock();
}
if (!d->customCommandCount) {
//We have now finished completely the macro, set the editor state to NoOp then.
debugText << "we have finished completely the macro, set the state to NoOp now. commandStack count: " << d->commandStack.count();
d->updateState(KoTextEditor::Private::NoOp);
debugText << "done setting the state. editorState: " << d->editorState << " commandStack count: " << d->commandStack.count();
}
}
diff --git a/plugins/flake/textshape/kotext/KoTextRdfCore.h b/plugins/flake/textshape/kotext/KoTextRdfCore.h
index d2295e4650..a0aecac055 100644
--- a/plugins/flake/textshape/kotext/KoTextRdfCore.h
+++ b/plugins/flake/textshape/kotext/KoTextRdfCore.h
@@ -1,149 +1,151 @@
/* This file is part of the KDE project
Copyright (C) 2010 KO GmbH <ben.martin@kogmbh.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KO_TEXT_RDF_CORE_H
#define KO_TEXT_RDF_CORE_H
#include "kritatext_export.h"
#include <QSharedPointer>
// this file can only be used by code that is built
// with soprano enabled.
#include <Soprano/Soprano>
class KoStore;
class KoXmlWriter;
/**
* @short Basic low level methods that are available to KoText objects
*
* Low level functionality such as streaming a Soprano::Model to and
* from an ODF container is provided here so that both KoDocumentRdf
* and other code in libs/kotext can share it.
*
* @author Ben Martin
* @see KoDocumentRdf
*/
namespace KoTextRdfCore
{
/**
* Save the RDF selected triples from model to the store with the
* given RDF/XML filename
*/
bool saveRdf( QSharedPointer<Soprano::Model> model, Soprano::StatementIterator triples,
KoStore *store, KoXmlWriter *manifestWriter, const QString &fileName);
/**
* Save the given RDF model to the manifest.rdf file. The idmap is used
* to maintain xml:id links from the model so they will be valid with
* the content.xml that generated the idmap.
*/
bool createAndSaveManifest(QSharedPointer<Soprano::Model> model,
const QMap<QString, QString> &idmap, KoStore *store, KoXmlWriter *manifestWriter);
/**
* Load the manifest.rdf file from the ODF container store
* into the model provided.
*/
bool loadManifest(KoStore *store, QSharedPointer<Soprano::Model> model);
/**
* For debugging, dump the model to debugText along with the
* given header message for identification
*/
void dumpModel(const QString &message, QSharedPointer<Soprano::Model> model);
/**
* Load an Rdf linked list of statements. See saveList() for the
* details. The return value of loadList() is the equivalent of
* dataBNodeList in saveList().
*
* @see saveList()
*/
QList<Soprano::Statement> KRITATEXT_EXPORT loadList(QSharedPointer<Soprano::Model> model, Soprano::Node ListHeadSubject);
/**
* Save an Rdf List of data nodes into the model. Rdf defines a
* linked list format in the
* http://www.w3.org/1999/02/22-rdf-syntax-ns URI namespace using
* first/rest to link the current element with the "rest" of the
* list. A scheme that will be familiar to many lisp programmers
* car/cdr. Unfortunately dealing with such lists directly is
* clumsy so this and loadList() let you store a list of data
* nodes and these methods create all the boilerplate Rdf triples
* to store/read a simple QList of nodes to Rdf properly. You
* supply the list header node ListHeadSubject which is normally
* the subject that you want the list associated with in Rdf. The
* other nodes used in the internal structure of the Rdf list are
* just random bnodes as shown below. If you have a previous,
* existing list then this method will remove those nodes first so
* that the Rdf model does not grow with disgarded list nodes over
* time.
*
* The old list nodes are removed if they exist, and a new list is
* created starting at ListHeadSubject, and linking all the nodes
* in dataBNodeList using the supplied rdf context. Use the
* loadList() method to get the list dataBNodeList back from the
* model again.
*
* The result will be like:
+ * @verbatim
* ListHeadSubject 22-rdf-syntax-ns#first dataBNodeList[0]
* ListHeadSubject 22-rdf-syntax-ns#rest bnodeA
* bnodeA 22-rdf-syntax-ns#first dataBNodeList[1]
* bnodeA 22-rdf-syntax-ns#rest bnodeB
* ...
* bnodeZ 22-rdf-syntax-ns#first dataBNodeList[N]
* bnodeZ 22-rdf-syntax-ns#rest nil
+ * @endverbatim
*
*/
void KRITATEXT_EXPORT saveList(QSharedPointer<Soprano::Model> model, Soprano::Node ListHeadSubject,
QList<Soprano::Node> &dataBNodeList, Soprano::Node context);
/**
* Using model->removeStatements() will fail if the statement does not
* exist in the model. This method is a bit sloppier in that it ignores
* attempts to remove statements twice, or ones that no longer exist
* in the model. This is handy for set based remove/add bulk updates
* because you don't have to ensure that a statement is added only once
* to the remove list.
*/
void KRITATEXT_EXPORT removeStatementsIfTheyExist( QSharedPointer<Soprano::Model> model,
const QList<Soprano::Statement> &removeList);
/**
* Given the Subj+Pred get the Object for the triple. If there are
* more than one object, a random one from the possible candidates is
* returned. This is mainly useful when you *know* there is only zero
* or one object.
*/
Soprano::Node KRITATEXT_EXPORT getObject(QSharedPointer<Soprano::Model> model, Soprano::Node s, Soprano::Node p);
QString KRITATEXT_EXPORT getProperty(QSharedPointer<Soprano::Model> m,
Soprano::Node subj,
Soprano::Node pred,
const QString &defval);
QString KRITATEXT_EXPORT optionalBindingAsString(Soprano::QueryResultIterator& it,
const QString &bindingName,
const QString &def = QString());
QByteArray KRITATEXT_EXPORT fileToByteArray(const QString &fileName);
}
#endif
diff --git a/plugins/flake/textshape/kotext/commands/ChangeListCommand.h b/plugins/flake/textshape/kotext/commands/ChangeListCommand.h
index 2f2cfaffe8..0aacc97fc4 100644
--- a/plugins/flake/textshape/kotext/commands/ChangeListCommand.h
+++ b/plugins/flake/textshape/kotext/commands/ChangeListCommand.h
@@ -1,106 +1,107 @@
/* This file is part of the KDE project
* Copyright (C) 2007 Thomas Zander <zander@kde.org>
* Copyright (C) 2008 Girish Ramakrishnan <girish@forwardbias.in>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef CHANGELISTCOMMAND
#define CHANGELISTCOMMAND
#include "KoTextCommandBase.h"
#include "KoListStyle.h"
#include "KoTextEditor.h"
#include "KoListLevelProperties.h"
#include <QTextBlock>
#include <QList>
#include <QHash>
class KoList;
/**
* This command is useful to alter the list-association of a single textBlock.
*/
class ChangeListCommand : public KoTextCommandBase
{
public:
- //FIXME: following comments seems to describe another function
/**
- * Change the list property of 'block'.
- * @param block the paragraph to change the list property of
- * @param style indicates which style to use.
+ * Change the list command.
+ * @param cursor text cursor properties.
+ * @param levelProperties level properties.
+ * @param flags the list flags.
* @param parent the parent undo command for macro functionality
*/
ChangeListCommand(const QTextCursor &cursor,
const KoListLevelProperties &levelProperties,
KoTextEditor::ChangeListFlags flags,
KUndo2Command *parent = 0);
/**
- * Change the list property of 'block'.
- * @param block the paragraph to change the list property of
- * @param style the style to apply
- * @param exact if true then the actual style 'style' should be set, if false we possibly merge with another similar style that is near the block
+ * Change the list command.
+ * @param cursor text cursor properties.
+ * @param style the style to apply.
+ * @param level the level in the list.
+ * @param flags the list flags.
* @param parent the parent undo command for macro functionality
*/
ChangeListCommand(const QTextCursor &cursor, KoListStyle *style, int level,
KoTextEditor::ChangeListFlags flags,
KUndo2Command *parent = 0);
~ChangeListCommand() override;
/// redo the command
void redo() override;
/// revert the actions done in redo
void undo() override;
/// reimplemented from KUndo2Command
int id() const override {
return 58450687;
}
/// reimplemented from KUndo2Command
bool mergeWith(const KUndo2Command *other) override;
private:
enum CommandAction {
CreateNew,
ModifyExisting,
ReparentList,
MergeList,
RemoveList
};
bool extractTextBlocks(const QTextCursor &cursor, int level, KoListStyle::Style newStyle = KoListStyle::None);
int detectLevel(const QTextBlock &block, int givenLevel);
void initList(KoListStyle *style);
bool formatsEqual(const KoListLevelProperties &llp, const QTextListFormat &format);
int m_flags;
bool m_first;
bool m_alignmentMode;
QList<QTextBlock> m_blocks;
QHash<int, KoListLevelProperties> m_formerProperties;
QHash<int, KoListLevelProperties> m_newProperties;
QHash<int, int> m_levels;
QHash<int, KoList*> m_list;
QHash<int, KoList*> m_oldList;
QHash<int, CommandAction> m_actions;
};
#endif
diff --git a/plugins/flake/textshape/kotext/opendocument/KoTextWriter.h b/plugins/flake/textshape/kotext/opendocument/KoTextWriter.h
index 250e4e1c20..5a9d668df6 100644
--- a/plugins/flake/textshape/kotext/opendocument/KoTextWriter.h
+++ b/plugins/flake/textshape/kotext/opendocument/KoTextWriter.h
@@ -1,86 +1,87 @@
/* This file is part of the KDE project
* Copyright (C) 2008 Girish Ramakrishnan <girish@forwardbias.in>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOTEXTWRITER_H
#define KOTEXTWRITER_H
#include "kritatext_export.h"
class KoShapeSavingContext;
class KoStyleManager;
class QTextDocument;
class QTextBlock;
class QTextBlockFormat;
class QTextCharFormat;
class QString;
class KoDocumentRdfBase;
/**
* KoTextWriter saves the text ODF of a shape
*/
class KRITATEXT_EXPORT KoTextWriter
{
public:
/**
* Constructor.
*
* @param context The context the KoTextWriter is called in
+ * @param rdfData The RDF data
*/
explicit KoTextWriter(KoShapeSavingContext &context, KoDocumentRdfBase *rdfData = 0);
/**
* Destructor.
*/
~KoTextWriter();
/// XXX: APIDOX!
static void saveOdf(KoShapeSavingContext &context, KoDocumentRdfBase *rdfData, QTextDocument *document, int from, int to);
/**
* Save a paragraph style used in a text block
*
* This checks if the style is a document style or a automatic style
* and saves it accordingly.
*
* @param block The block form which the style information are taken
* @param styleManager The used style manager
* @param context The saving context
*/
static QString saveParagraphStyle(const QTextBlock &block, KoStyleManager *styleManager, KoShapeSavingContext &context);
static QString saveParagraphStyle(const QTextBlockFormat &blockFormat, const QTextCharFormat &charFormat, KoStyleManager *styleManager, KoShapeSavingContext &context);
/**
* Writes the portion of document contained within 'from' and 'to'
*
* @param document The text document we are saving. There can be more than one
* text document in the office document, but we don't care
* @param from the start position in characters from which we save
* @param to the end position in characters up to which we save. If -1, we save to the end
*/
void write(const QTextDocument *document, int from, int to = -1);
private:
class Private;
Private* const d;
};
#endif
diff --git a/plugins/flake/textshape/kotext/styles/KoTableCellStyle.h b/plugins/flake/textshape/kotext/styles/KoTableCellStyle.h
index af96603da3..f8dadfd088 100644
--- a/plugins/flake/textshape/kotext/styles/KoTableCellStyle.h
+++ b/plugins/flake/textshape/kotext/styles/KoTableCellStyle.h
@@ -1,376 +1,376 @@
/* This file is part of the KDE project
* Copyright (C) 2006-2010 Thomas Zander <zander@kde.org>
* Copyright (C) 2008 Thorsten Zachmann <zachmann@kde.org>
* Copyright (C) 2008 Girish Ramakrishnan <girish@forwardbias.in>
* Copyright (C) 2009 KO GmbH <cbo@kogmbh.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOTABLECELLSTYLE_H
#define KOTABLECELLSTYLE_H
#include "KoText.h"
#include "kritatext_export.h"
#include <KoXmlReaderForward.h>
#include <KoBorder.h>
#include <KoShadowStyle.h>
#include <QColor>
#include <QObject>
struct Property;
class QTextTableCell;
class QRectF;
class KoStyleStack;
class KoGenStyle;
class KoParagraphStyle;
class KoShapeLoadingContext;
class KoShapeSavingContext;
class KoTableCellStylePrivate;
class QString;
class QVariant;
/**
* A container for all properties for the table cell style.
* Each tablecell in the main text either is based on a table cell style, or its not. Where
* it is based on a table cell style this is indecated that it has a property 'StyleId'
* with an integer as value. The integer value corresponds to the styleId() output of
* a specific KoTableCellStyle.
* @see KoStyleManager
*/
class KRITATEXT_EXPORT KoTableCellStyle : public QObject
{
Q_OBJECT
public:
enum CellProtectionFlag {
NoProtection,
HiddenAndProtected,
Protected,
FormulaHidden,
ProtectedAndFormulaHidden
};
enum CellTextDirection {
Default = 0,
LeftToRight,
TopToBottom
};
enum RotationAlignment {
RAlignNone,
RAlignBottom,
RAlignTop,
RAlignCenter
};
enum Property {
StyleId = QTextTableCellFormat::UserProperty + 7001,
ShrinkToFit, ///< Shrink the cell content to fit the size
Wrap, ///< Wrap the text within the cell
CellProtection, ///< The cell protection when the table is protected
PrintContent, ///< Should the content of this cell be printed
RepeatContent, ///< Display the cell content as many times as possible
DecimalPlaces, ///< Count the maximum number of decimal places to display
AlignFromType, ///< Should the alignment property be respected or should the alignment be based on the value type
RotationAngle, ///< Rotation angle of the cell content, in degrees
Direction, ///< The direction of the text in the cell. This is a CellTextDirection.
RotationAlign, ///< How the edge of the text is aligned after rotation. This is a RotationAlignment
TextWritingMode, ///< KoText::Direction, the direction for writing text in the cell
VerticalGlyphOrientation, ///< bool, specify whether this feature is enabled or not
CellBackgroundBrush, ///< the cell background brush, as QTextFormat::BackgroundBrush is used by paragraphs
VerticalAlignment, ///< the vertical alignment oinside the cell
MasterPageName, ///< Optional name of the master-page
InlineRdf, ///< Optional KoTextInlineRdf object
Borders, ///< KoBorder, the borders of this cell
Shadow, ///< KoShadowStyle, the shadow of this cell
CellIsProtected ///< boolean, if true, the cell is protected against edits
/// It's not really a property of KoTableCellStyle but defined here for convenience
,LastCellStyleProperty
};
/// Constructor
explicit KoTableCellStyle(QObject *parent = 0);
/// Creates a KoTableCellStyle with the given table cell format, and \a parent
explicit KoTableCellStyle(const QTextTableCellFormat &tableCellFormat, QObject *parent = 0);
KoTableCellStyle(const KoTableCellStyle &other);
KoTableCellStyle& operator=(const KoTableCellStyle &other);
/// Destructor
~KoTableCellStyle() override;
/// Creates a KoTableCellStyle that represents the formatting of \a block.
static KoTableCellStyle *fromTableCell(const QTextTableCell &table, QObject *parent = 0);
/// Creates a clean QTextCharFormat, but keeps all the table cell properties.
/// This is needed since block.charformat doubles as the QTextTableCellFormat
/// This method works even if \a charFormat is not a QTextTableCellFormat
static QTextCharFormat cleanCharFormat(const QTextCharFormat &charFormat);
/// creates a clone of this style with the specified parent
KoTableCellStyle *clone(QObject *parent = 0);
/**
- * Adjust the bounding rectangle \boundingRect according to the paddings and margins
+ * Adjust the bounding rectangle \p boundingRect according to the paddings and margins
* of this border data. The inverse of this function is boundingRect().
*
* \sa boundingRect()
*
- * @param the bounding rectangle.
+ * @param boundingRect the bounding rectangle.
* @return the adjusted rectangle.
*/
QRectF contentRect(const QRectF &boundingRect) const;
/**
* Get the bounding rect given a content rect, this is the inverse of contentRect().
*
* \sa contentRect()
*
* @param contentRect the content rectangle.
* @return the bounding rectangle.
*/
QRectF boundingRect(const QRectF &contentRect) const;
void setBackground(const QBrush &brush);
/// See similar named method on QTextBlockFormat
QBrush background() const;
/// See similar named method on QTextBlockFormat
void clearBackground();
/**
* Get the paragraph style for this cell style
*
* @return the paragraph style
*/
KoParagraphStyle *paragraphStyle() const;
bool shrinkToFit() const;
void setShrinkToFit(bool state);
bool repeatContent() const;
void setRepeatContent(bool state);
void setLeftPadding(qreal padding);
void setTopPadding(qreal padding);
void setRightPadding(qreal padding);
void setBottomPadding(qreal padding);
void setPadding(qreal padding);
qreal leftPadding() const;
qreal rightPadding() const;
qreal topPadding() const;
qreal bottomPadding() const;
void setAlignment(Qt::Alignment alignment);
Qt::Alignment alignment() const;
KoText::Direction textDirection() const;
void setTextDirection (KoText::Direction value);
void setWrap(bool state);
bool wrap() const;
CellProtectionFlag cellProtection() const;
void setCellProtection (CellProtectionFlag protection);
void setPrintContent(bool state);
bool printContent() const;
void setDecimalPlaces(int places);
int decimalPlaces() const;
void setAlignFromType(bool state);
bool alignFromType() const;
void setRotationAngle(qreal value);
qreal rotationAngle() const;
void setDirection(CellTextDirection direction);
CellTextDirection direction() const;
void setRotationAlignment(RotationAlignment align);
RotationAlignment rotationAlignment () const;
void setVerticalGlyphOrientation(bool state);
bool verticalGlyphOrientation() const;
void setBorders(const KoBorder &borders);
KoBorder borders() const;
void setShadow (const KoShadowStyle &shadow);
KoShadowStyle shadow() const;
/// set the parent style this one inherits its unset properties from.
void setParentStyle(KoTableCellStyle *parent);
/// return the parent style
KoTableCellStyle *parentStyle() const;
/// return the name of the style.
QString name() const;
/// set a user-visible name on the style.
void setName(const QString &name);
/// each style has a unique ID (non persistent) given out by the styleManager
int styleId() const;
/// each style has a unique ID (non persistent) given out by the styleManager
void setStyleId(int id);
/// return the optional name of the master-page or a QString() if this paragraph isn't attached to a master-page.
QString masterPageName() const;
/// Set the name of the master-page.
void setMasterPageName(const QString &name);
/// copy all the properties from the other style to this style, effectively duplicating it.
void copyProperties(const KoTableCellStyle *style);
/**
* Apply this style to a textTableCellFormat by copying all properties from this, and parent
* styles to the target textTableCellFormat. Note that the paragraph format will not be applied
* using this method, use the other method for that.
* No default values are applied.
*/
void applyStyle(QTextTableCellFormat &format) const;
void applyStyle(QTextTableCell &cell) const;
void remove(int key);
/// Compare the paragraph, character and list properties of this style with the other
bool operator==(const KoTableCellStyle &other) const;
void removeDuplicates(const KoTableCellStyle &other);
/**
* Load the style form the element
*
* @param context the odf loading context
* @param element the element containing the
*/
void loadOdf(const KoXmlElement *element, KoShapeLoadingContext &context);
void saveOdf(KoGenStyle &style, KoShapeSavingContext &context);
/**
* Returns true if this paragraph style has the property set.
* Note that this method does not delegate to the parent style.
* @param key the key as found in the Property enum
*/
bool hasProperty(int key) const;
/**
* Set a property with key to a certain value, overriding the value from the parent style.
* If the value set is equal to the value of the parent style, the key will be removed instead.
* @param key the Property to set.
* @param value the new value to set on this style.
* @see hasProperty(), value()
*/
void setProperty(int key, const QVariant &value);
/**
* Return the value of key as represented on this style, taking into account parent styles.
* You should consider using the direct accessors for individual properties instead.
* @param key the Property to request.
* @returns a QVariant which holds the property value.
*/
QVariant value(int key) const;
/**
* Set the properties of an edge.
*
* @param side defines which edge this is for.
* @param style the border style for this side.
* @param totalWidth the thickness of the border. Sum of outerwidth, spacing and innerwidth for double borders
* @param color the color of the border line(s).
*/
void setEdge(KoBorder::BorderSide side, KoBorder::BorderStyle style,
qreal totalWidth, const QColor &color);
/**
* Set the properties of a double border.
* Note: you need to set the edge first or that would overwrite these values.
*
* The values will not be set if the border doesn't have a double style
*
* @param side defines which edge this is for.
* @param space the amount of spacing between the outer border and the inner border in case of style being double
* @param innerWidth the thickness of the inner border line in case of style being double
*/
void setEdgeDoubleBorderValues(KoBorder::BorderSide side, qreal innerWidth, qreal space);
/**
* Check if the border data has any borders.
*
* @return true if there has been at least one border set.
*/
bool hasBorders() const;
qreal leftBorderWidth() const;
qreal rightBorderWidth() const;
qreal topBorderWidth() const;
qreal bottomBorderWidth() const;
qreal leftInnerBorderWidth() const;
qreal rightInnerBorderWidth() const;
qreal topInnerBorderWidth() const;
qreal bottomInnerBorderWidth() const;
qreal leftOuterBorderWidth() const;
qreal rightOuterBorderWidth() const;
qreal topOuterBorderWidth() const;
qreal bottomOuterBorderWidth() const;
KoBorder::BorderData getEdge(KoBorder::BorderSide side) const;
KoBorder::BorderStyle getBorderStyle(KoBorder::BorderSide side) const;
Q_SIGNALS:
void nameChanged(const QString &newName);
protected:
KoTableCellStylePrivate * const d_ptr;
private:
/**
* Load the style from the \a KoStyleStack style stack using the
* OpenDocument format.
*/
void loadOdfProperties(KoShapeLoadingContext &context, KoStyleStack &styleStack);
qreal propertyDouble(int key) const;
QPen propertyPen(int key) const;
int propertyInt(int key) const;
bool propertyBoolean(int key) const;
QColor propertyColor(int key) const;
/**
* Set the format properties from an Edge structure
*
* @param side defines which edge this is for.
* @param style the border style for this side.
* @param edge the Edge that hold the properties values
*/
void setEdge(KoBorder::BorderSide side, const KoBorder::BorderData &edge, KoBorder::BorderStyle style);
Q_DECLARE_PRIVATE(KoTableCellStyle)
};
Q_DECLARE_METATYPE(KoTableCellStyle *)
#endif
diff --git a/plugins/flake/textshape/textlayout/AnchorStrategy.h b/plugins/flake/textshape/textlayout/AnchorStrategy.h
index b1c139275b..597fada8d2 100644
--- a/plugins/flake/textshape/textlayout/AnchorStrategy.h
+++ b/plugins/flake/textshape/textlayout/AnchorStrategy.h
@@ -1,112 +1,112 @@
/* This file is part of the KDE project
* Copyright (C) 2011 Matus Hanzes <matus.hanzes@ixonos.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef ANCHORSTRATEGY_H_
#define ANCHORSTRATEGY_H_
#include "KoShapeAnchor.h"
#include <QRectF>
class KoTextShapeContainerModel;
class KoTextLayoutRootArea;
class AnchorStrategy : public KoShapeAnchor::PlacementStrategy
{
public:
AnchorStrategy(KoShapeAnchor *anchor, KoTextLayoutRootArea *rootArea);
~AnchorStrategy() override;
/**
* Moves the subject to it's right position.
*
* @return true if subject was moved to a new position (or if it couldn't be calculated yet)
*/
virtual bool moveSubject() = 0;
void detachFromModel() override;
/**
* Reparent the anchored shape under the rootArea's container this AnchorStrategy acts for
- .*
+ *
* If needed changes the parent KoShapeContainerModel and KoShapeContainer of the anchored shape.
* It is changed so the anchored shape is now under the rootArea
*/
void updateContainerModel() override;
/// get page rectangle coordinates to which this text anchor is anchored (needed for HPage)
QRectF pageRect() const;
/// set page rectangle coordinates to which this text anchor is anchored (needed for HPage)
void setPageRect(const QRectF &pageRect);
/// get content rectangle coordinates to which this text anchor is anchored (needed for
/// HPageContent)
QRectF pageContentRect() const;
/// set content rectangle coordinates to which this text anchor is anchored (needed for
/// HPageContent)
void setPageContentRect(const QRectF &marginRect);
/// get paragraph rectangle coordinates to which this text anchor is anchored (needed for
/// HParagraphContent, HParagraphStartMargin, HParagraphEndMargin, VParagraph)
QRectF paragraphRect() const;
/// set paragraph rectangle to which this text anchor is anchored (needed for HParagraphContent,
/// HParagraphStartMargin, HParagraphEndMargin, VParagraph)
void setParagraphRect(const QRectF &paragraphRect);
/// get paragraph rectangle coordinates to which this text anchor is anchored (needed for
/// HParagraphContent, HParagraphStartMargin, HParagraphEndMargin)
QRectF paragraphContentRect() const;
/// set paragraph rectangle to which this text anchor is anchored (needed for HParagraphContent,
/// HParagraphStartMargin, HParagraphEndMargin)
void setParagraphContentRect(const QRectF &paragraphContentRect);
/// get layout environment rectangle @see odf attribute style:flow-with-text
QRectF layoutEnvironmentRect() const;
/// set layout environment rect @see odf attribute style:flow-with-text
void setLayoutEnvironmentRect(const QRectF &layoutEnvironmentRect);
/// get number of page to which this text anchor is anchored (needed for HOutside, HInside,
/// HFromInside)
int pageNumber() const;
/// set number of page to which this text anchor is anchored (needed for HOutside, HInside,
/// HFromInside)
void setPageNumber(int pageNumber);
protected:
KoShapeAnchor * const m_anchor;
KoTextLayoutRootArea *m_rootArea;
private:
KoTextShapeContainerModel *m_model;
QRectF m_pageRect;
QRectF m_pageContentRect;
QRectF m_paragraphRect;
QRectF m_paragraphContentRect;
QRectF m_layoutEnvironmentRect;
int m_pageNumber;
};
#endif /* ANCHORSTRATEGY_H_ */
diff --git a/plugins/flake/textshape/textlayout/KoTextLayoutArea_paint.cpp b/plugins/flake/textshape/textlayout/KoTextLayoutArea_paint.cpp
index 567cd76b8c..ff90af68da 100644
--- a/plugins/flake/textshape/textlayout/KoTextLayoutArea_paint.cpp
+++ b/plugins/flake/textshape/textlayout/KoTextLayoutArea_paint.cpp
@@ -1,1200 +1,1200 @@
/* This file is part of the KDE project
* Copyright (C) 2006-2010 Thomas Zander <zander@kde.org>
* Copyright (C) 2008 Thorsten Zachmann <zachmann@kde.org>
* Copyright (C) 2008 Girish Ramakrishnan <girish@forwardbias.in>
* Copyright (C) 2008 Roopesh Chander <roop@forwardbias.in>
* Copyright (C) 2007-2008 Pierre Ducroquet <pinaraf@pinaraf.info>
* Copyright (C) 2009-2011 KO GmbH <cbo@kogmbh.com>
* Copyright (C) 2009-2012 C. Boemann <cbo@boemann.dk>
* Copyright (C) 2010 Nandita Suri <suri.nandita@gmail.com>
* Copyright (C) 2010 Ajay Pundhir <ajay.pratap@iiitb.net>
* Copyright (C) 2011 Lukáš Tvrdý <lukas.tvrdy@ixonos.com>
* Copyright (C) 2011 Gopalakrishna Bhat A <gopalakbhat@gmail.com>
* Copyright (C) 2011 Stuart Dickson <stuart@furkinfantasic.net>
* Copyright (C) 2014 Denis Kuplyakov <dener.kup@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoTextLayoutArea.h"
#include "KoTextLayoutEndNotesArea.h"
#include "KoTextLayoutTableArea.h"
#include "KoTextLayoutNoteArea.h"
#include "TableIterator.h"
#include "ListItemsHelper.h"
#include "RunAroundHelper.h"
#include "KoTextDocumentLayout.h"
#include "FrameIterator.h"
#include <KoParagraphStyle.h>
#include <KoCharacterStyle.h>
#include <KoListStyle.h>
#include <KoStyleManager.h>
#include <KoTextBlockData.h>
#include <KoTextBlockBorderData.h>
#include <KoTextBlockPaintStrategyBase.h>
#include <KoText.h>
#include <KoChangeTracker.h>
#include <KoChangeTrackerElement.h>
#include <KoImageData.h>
#include <TextLayoutDebug.h>
#include <KoSection.h>
#include <KoSectionEnd.h>
#include <KoSectionUtils.h>
#include <QPainter>
#include <QTextTable>
#include <QTextList>
#include <QFontMetrics>
#include <QTextFragment>
#include <QTextLayout>
#include <QTextCursor>
#include <QTime>
#include "kis_painting_tweaks.h"
extern int qt_defaultDpiY();
Q_DECLARE_METATYPE(QTextDocument *)
#define DropCapsAdditionalFormattingId 25602902
#include "KoTextLayoutArea_p.h"
void KoTextLayoutArea::paint(QPainter *painter, const KoTextDocumentLayout::PaintContext &context)
{
if (d->startOfArea == 0 || d->endOfArea == 0) // We have not been layouted yet
return;
/*
struct Timer {
QTime d->time;
Timer() { d->time.start(); }
~Timer() { warnTextLayout << "elapsed=" << d->time.elapsed(); }
};
Timer timer;
*/
painter->save();
painter->translate(0, d->verticalAlignOffset);
painter->setPen(context.textContext.palette.color(QPalette::Text)); // for text that has no color.
const QRegion clipRegion = KisPaintingTweaks::safeClipRegion(*painter); // fetch after painter->translate so the clipRegion is correct
KoTextBlockBorderData *lastBorder = 0;
QRectF lastBorderRect;
QTextFrame::iterator it = d->startOfArea->it;
QTextFrame::iterator stop = d->endOfArea->it;
if (!stop.atEnd()) {
if (!stop.currentBlock().isValid() || d->endOfArea->lineTextStart >= 0) {
// Last thing we show is a frame (table) or first part of a paragraph split in two
// The stop point should be the object after that
// However if stop is already atEnd we shouldn't increment further
++stop;
}
}
int tableAreaIndex = 0;
int blockIndex = 0;
int tocIndex = 0;
for (; it != stop && !it.atEnd(); ++it) {
QTextBlock block = it.currentBlock();
QTextTable *table = qobject_cast<QTextTable*>(it.currentFrame());
QTextFrame *subFrame = it.currentFrame();
QTextBlockFormat format = block.blockFormat();
if (!block.isValid()) {
if (lastBorder) { // draw previous block's border
lastBorder->paint(*painter, lastBorderRect);
lastBorder = 0;
}
}
if (table) {
if (tableAreaIndex >= d->tableAreas.size()) {
continue;
}
d->tableAreas[tableAreaIndex]->paint(painter, context);
++tableAreaIndex;
continue;
} else if (subFrame) {
if (subFrame->format().intProperty(KoText::SubFrameType) == KoText::AuxillaryFrameType) {
d->endNotesArea->paint(painter, context);
}
continue;
} else {
if (!block.isValid()) {
continue;
}
}
if (block.blockFormat().hasProperty(KoParagraphStyle::GeneratedDocument)) {
// Possibly paint the selection of the entire Table of Contents
// but since it's a secondary document we need to create a fake selection
QVariant data = block.blockFormat().property(KoParagraphStyle::GeneratedDocument);
QTextDocument *generatedDocument = data.value<QTextDocument *>();
KoTextDocumentLayout::PaintContext tocContext = context;
tocContext.textContext.selections = QVector<QAbstractTextDocumentLayout::Selection>();
bool pure = true;
Q_FOREACH (const QAbstractTextDocumentLayout::Selection &selection, context.textContext.selections) {
if (selection.cursor.selectionStart() <= block.position()
&& selection.cursor.selectionEnd() >= block.position()) {
painter->fillRect(d->generatedDocAreas[tocIndex]->boundingRect(), selection.format.background());
if (pure) {
tocContext.textContext.selections.append(QAbstractTextDocumentLayout::Selection());
tocContext.textContext.selections[0].cursor = QTextCursor(generatedDocument);
tocContext.textContext.selections[0].cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
tocContext.textContext.selections[0].format = selection.format;
pure = false;
}
}
}
d->generatedDocAreas[tocIndex]->paint(painter, tocContext);
++tocIndex;
continue;
}
QTextLayout *layout = block.layout();
KoTextBlockBorderData *border = 0;
if (blockIndex >= d->blockRects.count())
break;
QRectF br = d->blockRects[blockIndex];
++blockIndex;
if (!painter->hasClipping() || clipRegion.intersects(br.toRect())) {
KoTextBlockData blockData(block);
border = blockData.border();
KoTextBlockPaintStrategyBase *paintStrategy = blockData.paintStrategy();
KoTextBlockPaintStrategyBase dummyPaintStrategy;
if (paintStrategy == 0) {
paintStrategy = &dummyPaintStrategy;
}
if (!paintStrategy->isVisible()) {
if (lastBorder) { // draw previous block's border
lastBorder->paint(*painter, lastBorderRect);
lastBorder = 0;
}
continue; // this paragraph shouldn't be shown so just skip it
}
// Check and update border drawing code
if (lastBorder == 0) {
lastBorderRect = br;
} else if (lastBorder != border
|| lastBorderRect.width() != br.width()
|| lastBorderRect.x() != br.x()) {
lastBorder->paint(*painter, lastBorderRect);
lastBorderRect = br;
} else {
lastBorderRect = lastBorderRect.united(br);
}
lastBorder = border;
painter->save();
QBrush bg = paintStrategy->background(block.blockFormat().background());
if (bg != Qt::NoBrush ) {
painter->fillRect(br, bg);
} else {
bg = context.background;
}
paintStrategy->applyStrategy(painter);
painter->save();
drawListItem(painter, block);
painter->restore();
QVector<QTextLayout::FormatRange> selections;
if (context.showSelections) {
Q_FOREACH (const QAbstractTextDocumentLayout::Selection & selection, context.textContext.selections) {
QTextCursor cursor = selection.cursor;
int begin = cursor.position();
int end = cursor.anchor();
if (begin > end)
std::swap(begin, end);
if (end < block.position() || begin > block.position() + block.length())
continue; // selection does not intersect this block.
if (selection.cursor.hasComplexSelection()) {
continue; // selections of several table cells are covered by the within drawBorders above.
}
if (d->documentLayout->changeTracker()
&& !d->documentLayout->changeTracker()->displayChanges()
&& d->documentLayout->changeTracker()->containsInlineChanges(selection.format)
&& d->documentLayout->changeTracker()->elementById(selection.format.property(KoCharacterStyle::ChangeTrackerId).toInt())->isEnabled()
&& d->documentLayout->changeTracker()->elementById(selection.format.property(KoCharacterStyle::ChangeTrackerId).toInt())->getChangeType() == KoGenChange::DeleteChange) {
continue; // Deletions should not be shown.
}
QTextLayout::FormatRange fr;
fr.start = begin - block.position();
fr.length = end - begin;
fr.format = selection.format;
selections.append(fr);
}
}
// this is a workaround to fix text getting cut of when format ranges are used. There
// is a bug in Qt that can hit when text lines overlap each other. In case a format range
// is used for formatting it can clip the lines above/below as Qt creates a clip rect for
// the places it already painted for the format range which results in clippling. So use
// the format range always to paint the text.
QVector<QTextLayout::FormatRange> workaroundFormatRanges;
for (QTextBlock::iterator it = block.begin(); !(it.atEnd()); ++it) {
QTextFragment currentFragment = it.fragment();
if (currentFragment.isValid()) {
bool formatChanged = false;
QTextCharFormat format = currentFragment.charFormat();
int changeId = format.intProperty(KoCharacterStyle::ChangeTrackerId);
if (changeId && d->documentLayout->changeTracker() && d->documentLayout->changeTracker()->displayChanges()) {
KoChangeTrackerElement *changeElement = d->documentLayout->changeTracker()->elementById(changeId);
switch(changeElement->getChangeType()) {
case (KoGenChange::InsertChange):
format.setBackground(QBrush(d->documentLayout->changeTracker()->getInsertionBgColor()));
break;
case (KoGenChange::FormatChange):
format.setBackground(QBrush(d->documentLayout->changeTracker()->getFormatChangeBgColor()));
break;
case (KoGenChange::DeleteChange):
format.setBackground(QBrush(d->documentLayout->changeTracker()->getDeletionBgColor()));
break;
case (KoGenChange::UNKNOWN):
break;
}
formatChanged = true;
}
if (format.isAnchor()) {
if (!format.hasProperty(KoCharacterStyle::UnderlineStyle))
format.setFontUnderline(true);
if (!format.hasProperty(QTextFormat::ForegroundBrush))
format.setForeground(Qt::blue);
formatChanged = true;
}
if (format.boolProperty(KoCharacterStyle::UseWindowFontColor)) {
QBrush backbrush = bg;
if (format.background() != Qt::NoBrush) {
backbrush = format.background();
}
QBrush frontBrush;
frontBrush.setStyle(Qt::SolidPattern);
// use the same luma calculation and threshold as msoffice
// see http://social.msdn.microsoft.com/Forums/en-US/os_binaryfile/thread/a02a9a24-efb6-4ba0-a187-0e3d2704882b
int luma = ((5036060/2) * backbrush.color().red()
+ (9886846/2) * backbrush.color().green()
+ (1920103/2) * backbrush.color().blue()) >> 23;
if (luma > 60) {
frontBrush.setColor(QColor(Qt::black));
} else {
frontBrush.setColor(QColor(Qt::white));
}
format.setForeground(frontBrush);
formatChanged = true;
}
if (formatChanged) {
QTextLayout::FormatRange fr;
fr.start = currentFragment.position() - block.position();
fr.length = currentFragment.length();
if (!format.hasProperty(KoCharacterStyle::InlineInstanceId)) {
if (format.background().style() == Qt::NoBrush) {
format.setBackground(QBrush(QColor(0, 0, 0, 0)));
}
if (format.foreground().style() == Qt::NoBrush) {
format.setForeground(QBrush(QColor(0, 0, 0)));
}
}
fr.format = format;
// the prepend is done so the selections are at the end.
selections.prepend(fr);
}
else {
if (!format.hasProperty(KoCharacterStyle::InlineInstanceId)) {
QTextLayout::FormatRange fr;
fr.start = currentFragment.position() - block.position();
fr.length = currentFragment.length();
QTextCharFormat f;
if (format.background().style() == Qt::NoBrush) {
f.setBackground(QBrush(QColor(0, 0, 0, 0)));
}
else {
f.setBackground(format.background());
}
if (format.foreground().style() == Qt::NoBrush) {
f.setForeground(QBrush(QColor(0, 0, 0)));
}
else {
f.setForeground(format.foreground());
}
fr.format = f;
workaroundFormatRanges.append(fr);
}
}
}
}
if (!selections.isEmpty()) {
selections = workaroundFormatRanges + selections;
}
//We set clip because layout-draw doesn't clip text to it correctly after all
//and adjust to make sure we don't clip edges of glyphs. The clipping is
//important for paragraph split across two pages.
//20pt enlargement seems safe as pages is split by 50pt and this helps unwanted
//glyph cutting
painter->setClipRect(br.adjusted(-20,-20,20,20), Qt::IntersectClip);
layout->draw(painter, QPointF(0, 0), selections);
if (context.showSectionBounds) {
decorateParagraphSections(painter, block);
}
decorateParagraph(painter, block, context.showFormattingCharacters, context.showSpellChecking);
painter->restore();
} else {
if (lastBorder) {
lastBorder->paint(*painter, lastBorderRect);
lastBorder = 0;
}
}
}
if (lastBorder) {
lastBorder->paint(*painter, lastBorderRect);
}
painter->translate(0, -d->verticalAlignOffset);
painter->translate(0, bottom() - d->footNotesHeight);
Q_FOREACH (KoTextLayoutNoteArea *footerArea, d->footNoteAreas) {
footerArea->paint(painter, context);
painter->translate(0, footerArea->bottom() - footerArea->top());
}
painter->restore();
}
void KoTextLayoutArea::drawListItem(QPainter *painter, QTextBlock &block)
{
KoTextBlockData blockData(block);
QTextList *list = block.textList();
if (list && blockData.hasCounterData()) {
QTextListFormat listFormat = list->format();
if (! blockData.counterText().isEmpty()) {
QFont font(blockData.labelFormat().font(), d->documentLayout->paintDevice());
KoListStyle::Style listStyle = static_cast<KoListStyle::Style>(listFormat.style());
QString result = blockData.counterText();
QTextLayout layout(result, font, d->documentLayout->paintDevice());
QList<QTextLayout::FormatRange> layouts;
QTextLayout::FormatRange format;
format.start = 0;
format.length = blockData.counterText().length();
format.format = blockData.labelFormat();
layouts.append(format);
layout.setAdditionalFormats(layouts);
Qt::Alignment alignment = static_cast<Qt::Alignment>(listFormat.intProperty(KoListStyle::Alignment));
if (alignment == 0) {
alignment = Qt::AlignLeft | Qt::AlignAbsolute;
}
if (d->isRtl && (alignment & Qt::AlignAbsolute) == 0) {
if (alignment & Qt::AlignLeft) {
alignment = Qt::AlignRight;
} else if (alignment & Qt::AlignRight) {
alignment = Qt::AlignLeft;
}
}
alignment |= Qt::AlignAbsolute;
QTextOption option(alignment);
option.setTextDirection(block.layout()->textOption().textDirection());
/*
if (option.textDirection() == Qt::RightToLeft || blockData.counterText().isRightToLeft()) {
option.setAlignment(Qt::AlignRight);
}
*/
layout.setTextOption(option);
layout.beginLayout();
QTextLine line = layout.createLine();
line.setLineWidth(blockData.counterWidth());
layout.endLayout();
QPointF counterPosition = blockData.counterPosition();
if (block.layout()->lineCount() > 0) {
// if there is text, then baseline align the counter.
QTextLine firstParagLine = block.layout()->lineAt(0);
if (KoListStyle::isNumberingStyle(listStyle)) {
//if numbered list baseline align
counterPosition += QPointF(0, firstParagLine.ascent() - layout.lineAt(0).ascent());
} else {
//for unnumbered list center align
counterPosition += QPointF(0, (firstParagLine.height() - layout.lineAt(0).height())/2.0);
}
}
layout.draw(painter, counterPosition);
//decorate the list label iff it is a numbered list
if (KoListStyle::isNumberingStyle(listStyle)) {
painter->save();
decorateListLabel(painter, blockData, layout.lineAt(0), block);
painter->restore();
}
}
KoListStyle::Style listStyle = static_cast<KoListStyle::Style>(listFormat.style());
if (listStyle == KoListStyle::ImageItem) {
QFontMetricsF fm(blockData.labelFormat().font(), d->documentLayout->paintDevice());
qreal x = qMax(qreal(1), blockData.counterPosition().x());
qreal width = qMax(listFormat.doubleProperty(KoListStyle::Width), (qreal)1.0);
qreal height = qMax(listFormat.doubleProperty(KoListStyle::Height), (qreal)1.0);
qreal y = blockData.counterPosition().y() + fm.ascent() - fm.xHeight()/2 - height/2; // centered
KoImageData *idata = listFormat.property(KoListStyle::BulletImage).value<KoImageData *>();
if (idata) {
painter->drawPixmap(x, y, width, height, idata->pixmap());
}
}
}
}
void KoTextLayoutArea::decorateListLabel(QPainter *painter, const KoTextBlockData &blockData, const QTextLine &listLabelLine, const QTextBlock &listItem)
{
const QTextCharFormat listLabelCharFormat = blockData.labelFormat();
painter->setFont(listLabelCharFormat.font());
int startOfFragmentInBlock = 0;
Q_ASSERT_X(listLabelLine.isValid(), __FUNCTION__, QString("Invalid list label").toLocal8Bit());
if (!listLabelLine.isValid()) {
return;
}
int fragmentToLineOffset = 0;
qreal x1 = blockData.counterPosition().x();
qreal x2 = listItem.layout()->lineAt(0).x();
if (x2 != x1) {
drawStrikeOuts(painter, listLabelCharFormat, blockData.counterText(), listItem.layout()->lineAt(0), x1, x2, startOfFragmentInBlock, fragmentToLineOffset);
drawOverlines(painter, listLabelCharFormat, blockData.counterText(), listItem.layout()->lineAt(0), x1, x2, startOfFragmentInBlock, fragmentToLineOffset);
drawUnderlines(painter, listLabelCharFormat, blockData.counterText(), listItem.layout()->lineAt(0), x1, x2, startOfFragmentInBlock, fragmentToLineOffset);
}
}
/**
* Draw a line. Typically meant to underline text or similar.
* @param painter the painter to paint on.
- * @painter color the pen color to for the decoratoin line
+ * @param color the pen color to for the decoration line
* @param type The type
* @param style the type of line to draw.
* @param width The thickness of the line, in pixels (the painter will be prescaled to points coordinate system).
* @param x1 we are always drawing horizontal lines, this is the start point.
* @param x2 we are always drawing horizontal lines, this is the end point.
* @param y the y-offset to paint on.
*/
static void drawDecorationLine(QPainter *painter, const QColor &color, KoCharacterStyle::LineType type, KoCharacterStyle::LineStyle style, qreal width, const qreal x1, const qreal x2, const qreal y)
{
QPen penBackup = painter->pen();
QPen pen = painter->pen();
pen.setColor(color);
pen.setWidthF(width);
if (style == KoCharacterStyle::WaveLine) {
// Ok, try the waves :)
pen.setStyle(Qt::SolidLine);
painter->setPen(pen);
qreal x = x1;
const qreal halfWaveWidth = 0.5 * width;
const qreal halfWaveLength = 2 * width;
const int startAngle = 0 * 16;
const int middleAngle = 180 * 16;
const int endAngle = 180 * 16;
while (x < x2) {
QRectF rectangle1(x, y, halfWaveLength, 2*halfWaveWidth);
if (type == KoCharacterStyle::DoubleLine) {
painter->translate(0, -pen.width());
painter->drawArc(rectangle1, startAngle, middleAngle);
painter->translate(0, 2*pen.width());
painter->drawArc(rectangle1, startAngle, middleAngle);
painter->translate(0, -pen.width());
} else {
painter->drawArc(rectangle1, startAngle, middleAngle);
}
if (x + halfWaveLength > x2)
break;
QRectF rectangle2(x + halfWaveLength, y, halfWaveLength, 2*halfWaveWidth);
if (type == KoCharacterStyle::DoubleLine) {
painter->translate(0, -pen.width());
painter->drawArc(rectangle2, middleAngle, endAngle);
painter->translate(0, 2*pen.width());
painter->drawArc(rectangle2, middleAngle, endAngle);
painter->translate(0, -pen.width());
} else {
painter->drawArc(rectangle2, middleAngle, endAngle);
}
x = x + 2 * halfWaveLength;
}
} else {
if (style == KoCharacterStyle::LongDashLine) {
QVector<qreal> dashes;
dashes << 12 << 2;
pen.setDashPattern(dashes);
} else {
pen.setStyle((Qt::PenStyle)style);
}
painter->setPen(pen);
if (type == KoCharacterStyle::DoubleLine) {
painter->translate(0, -pen.width());
painter->drawLine(QPointF(x1, y), QPointF(x2, y));
painter->translate(0, 2*pen.width());
painter->drawLine(QPointF(x1, y), QPointF(x2, y));
painter->translate(0, -pen.width());
} else {
painter->drawLine(QPointF(x1, y), QPointF(x2, y));
}
}
painter->setPen(penBackup);
}
static void drawDecorationText(QPainter *painter, const QTextLine &line, const QColor &color, const QString& decorText, qreal x1, qreal x2)
{
qreal y = line.position().y();
QPen oldPen = painter->pen();
painter->setPen(QPen(color));
do {
QRectF br;
painter->drawText(QRectF(QPointF(x1, y), QPointF(x2, y + line.height())), Qt::AlignLeft | Qt::AlignVCenter, decorText, &br);
x1 = br.right();
} while (x1 <= x2);
painter->setPen(oldPen);
}
static void drawDecorationWords(QPainter *painter, const QTextLine &line, const QString &text, const QColor &color, KoCharacterStyle::LineType type, KoCharacterStyle::LineStyle style, const QString& decorText, qreal width, const qreal y, const int fragmentToLineOffset, const int startOfFragmentInBlock)
{
qreal wordBeginX = -1;
int j = line.textStart()+fragmentToLineOffset;
while (j < line.textLength() + line.textStart() && j-startOfFragmentInBlock<text.size()) {
if (text[j-startOfFragmentInBlock].isSpace()) {
if (wordBeginX != -1) {
if (decorText.isEmpty())
drawDecorationLine(painter, color, type, style, width, wordBeginX, line.cursorToX(j), y);
else
drawDecorationText(painter, line, color, decorText, wordBeginX, line.cursorToX(j));
}
wordBeginX = -1;
} else if (wordBeginX == -1) {
wordBeginX = line.cursorToX(j);
}
++j;
}
if (wordBeginX != -1) {
if (decorText.isEmpty())
drawDecorationLine(painter, color, type, style, width, wordBeginX, line.cursorToX(j), y);
else
drawDecorationText(painter, line, color, decorText, wordBeginX, line.cursorToX(j));
}
}
static qreal computeWidth(KoCharacterStyle::LineWeight weight, qreal width, const QFont& font)
{
switch (weight) {
case KoCharacterStyle::AutoLineWeight:
case KoCharacterStyle::NormalLineWeight:
case KoCharacterStyle::MediumLineWeight:
case KoCharacterStyle::DashLineWeight:
return QFontMetricsF(font).lineWidth();
case KoCharacterStyle::BoldLineWeight:
case KoCharacterStyle::ThickLineWeight:
return QFontMetricsF(font).lineWidth() * 1.5;
case KoCharacterStyle::ThinLineWeight:
return QFontMetricsF(font).lineWidth() * 0.7;
case KoCharacterStyle::PercentLineWeight:
return QFontInfo(font).pointSizeF() * width / 100;
case KoCharacterStyle::LengthLineWeight:
return width;
}
Q_ASSERT(0); // illegal weight passed
return 0;
}
void KoTextLayoutArea::decorateParagraphSections(QPainter *painter, QTextBlock &block)
{
QTextLayout *layout = block.layout();
QTextBlockFormat bf = block.blockFormat();
QPen penBackup = painter->pen();
QPen pen = painter->pen();
pen.setWidth(1);
pen.setColor(Qt::gray);
painter->setPen(pen);
qreal xl = layout->boundingRect().left();
qreal xr = qMax(layout->boundingRect().right(), layout->boundingRect().left() + width());
qreal yu = layout->boundingRect().top();
qreal yd = layout->boundingRect().bottom();
qreal bracketSize = painter->fontMetrics().height() / 2;
const qreal levelShift = 3;
QList<KoSection *> openList = KoSectionUtils::sectionStartings(bf);
for (int i = 0; i < openList.size(); i++) {
int sectionLevel = openList[i]->level();
if (i == 0) {
painter->drawLine(xl + sectionLevel * levelShift, yu,
xr - sectionLevel * levelShift, yu);
}
painter->drawLine(xl + sectionLevel * levelShift, yu,
xl + sectionLevel * levelShift, yu + bracketSize);
painter->drawLine(xr - sectionLevel * levelShift, yu,
xr - sectionLevel * levelShift, yu + bracketSize);
}
QList<KoSectionEnd *> closeList = KoSectionUtils::sectionEndings(bf);
for (int i = 0; i < closeList.size(); i++) {
int sectionLevel = closeList[i]->correspondingSection()->level();
if (i == closeList.count() - 1) {
painter->drawLine(xl + sectionLevel * levelShift, yd,
xr - sectionLevel * levelShift, yd);
}
painter->drawLine(xl + sectionLevel * levelShift, yd,
xl + sectionLevel * levelShift, yd - bracketSize);
painter->drawLine(xr - sectionLevel * levelShift, yd,
xr - sectionLevel * levelShift, yd - bracketSize);
}
painter->setPen(penBackup);
}
void KoTextLayoutArea::decorateParagraph(QPainter *painter, QTextBlock &block, bool showFormattingCharacters, bool showSpellChecking)
{
QTextLayout *layout = block.layout();
QTextBlockFormat bf = block.blockFormat();
QVariantList tabList = bf.property(KoParagraphStyle::TabPositions).toList();
QFont oldFont = painter->font();
QTextBlock::iterator it;
int startOfBlock = -1;
int currentTabStop = 0;
// qDebug() << "\n-------------------"
// << "\nGoing to decorate block\n"
// << block.text()
// << "\n-------------------";
// loop over text fragments in this paragraph and draw the underline and line through.
for (it = block.begin(); !it.atEnd(); ++it) {
QTextFragment currentFragment = it.fragment();
if (currentFragment.isValid()) {
// qDebug() << "\tGoing to layout fragment:" << currentFragment.text();
QTextCharFormat fmt = currentFragment.charFormat();
painter->setFont(fmt.font());
// a block doesn't have a real start position, so use our own counter. Initialize
// it with the position of the first text fragment in the block.
if (startOfBlock == -1) {
startOfBlock = currentFragment.position(); // start of this block w.r.t. the document
}
// the start of our fragment in the block is the absolute position of the fragment
// in the document minus the start of the block in the document.
int startOfFragmentInBlock = currentFragment.position() - startOfBlock;
// a fragment can span multiple lines, but we paint the decorations per line.
int firstLine = layout->lineForTextPosition(currentFragment.position() - startOfBlock).lineNumber();
int lastLine = layout->lineForTextPosition(currentFragment.position() + currentFragment.length()
- startOfBlock).lineNumber();
// qDebug() << "\tfirst line:" << firstLine << "last line:" << lastLine;
for (int i = firstLine ; i <= lastLine ; ++i) {
QTextLine line = layout->lineAt(i);
// qDebug() << "\n\t\tcurrent line:" << i
// << "\n\t\tline length:" << line.textLength() << "width:"<< line.width() << "natural width" << line.naturalTextWidth()
// << "\n\t\tvalid:" << layout->isValidCursorPosition(currentFragment.position() - startOfBlock)
// << "\n\t\tcurrentFragment.position:" << currentFragment.position()
// << "\n\t\tstartOfBlock:" << startOfBlock
// << "\n\t\tstartOfFragmentInBlock:" << startOfFragmentInBlock;
if (layout->isValidCursorPosition(currentFragment.position() - startOfBlock)) {
// the start position for painting the decoration is the position of the fragment
// inside, but after the first line, the decoration always starts at the beginning
// of the line. See bug: 264471
int p1 = startOfFragmentInBlock;
if (i > firstLine) {
p1 = line.textStart();
}
// qDebug() << "\n\t\tblock.text.length:" << block.text().length() << "p1" << p1;
if (block.text().length() > p1 && block.text().at(p1) != QChar::ObjectReplacementCharacter) {
Q_ASSERT_X(line.isValid(), __FUNCTION__, QString("Invalid line=%1 first=%2 last=%3").arg(i).arg(firstLine).arg(lastLine).toLocal8Bit()); // see bug 278682
if (!line.isValid())
continue;
// end position: note that x2 can be smaller than x1 when we are handling RTL
int p2 = startOfFragmentInBlock + currentFragment.length();
int lineEndWithoutPreedit = line.textStart() + line.textLength();
if (block.layout()->preeditAreaPosition() >= block.position() + line.textStart() &&
block.layout()->preeditAreaPosition() <= block.position() + line.textStart() + line.textLength()) {
lineEndWithoutPreedit -= block.layout()->preeditAreaText().length();
}
while (lineEndWithoutPreedit > line.textStart() && block.text().at(lineEndWithoutPreedit - 1) == ' ') {
--lineEndWithoutPreedit;
}
if (lineEndWithoutPreedit < p2) { //line caps
p2 = lineEndWithoutPreedit;
}
int fragmentToLineOffset = qMax(startOfFragmentInBlock - line.textStart(), 0);
qreal x1 = line.cursorToX(p1);
qreal x2 = line.cursorToX(p2);
//qDebug() << "\n\t\t\tp1:" << p1 << "x1:" << x1
// << "\n\t\t\tp2:" << p2 << "x2:" << x2
// << "\n\t\t\tlineEndWithoutPreedit" << lineEndWithoutPreedit;
if (x1 != x2) {
drawStrikeOuts(painter, fmt, currentFragment.text(), line, x1, x2, startOfFragmentInBlock, fragmentToLineOffset);
drawOverlines(painter, fmt, currentFragment.text(), line, x1, x2, startOfFragmentInBlock, fragmentToLineOffset);
drawUnderlines(painter, fmt, currentFragment.text(), line, x1, x2, startOfFragmentInBlock, fragmentToLineOffset);
}
decorateTabsAndFormatting(painter, currentFragment, line, startOfFragmentInBlock, tabList, currentTabStop, showFormattingCharacters);
// underline preedit strings
if (layout->preeditAreaPosition() > -1) {
int start = block.layout()->preeditAreaPosition();
int end = block.layout()->preeditAreaPosition() + block.layout()->preeditAreaText().length();
QTextCharFormat underline;
underline.setFontUnderline(true);
underline.setUnderlineStyle(QTextCharFormat::DashUnderline);
//qDebug() << "underline style" << underline.underlineStyle();
//qDebug() << "line start: " << block.position() << line.textStart();
qreal z1 = 0;
qreal z2 = 0;
// preedit start in this line, end in this line
if ( start >= block.position() + line.textStart() &&
end <= block.position() + line.textStart() + line.textLength() ) {
z1 = line.cursorToX(start);
z2 = line.cursorToX(end);
}
// preedit start in this line, end after this line
if ( start >= block.position() + line.textStart() &&
end > block.position() + line.textStart() + line.textLength() ) {
z1 = line.cursorToX(start);
z2 = line.cursorToX(block.position() + line.textStart() + line.textLength());
}
// preedit start before this line, end in this line
if ( start < block.position() + line.textStart() &&
end <= block.position() + line.textStart() + line.textLength() ) {
z1 = line.cursorToX(block.position() + line.textStart());
z2 = line.cursorToX(end);
}
// preedit start before this line, end after this line
if ( start < block.position() + line.textStart() &&
end > block.position() + line.textStart() + line.textLength() ) {
z1 = line.cursorToX(block.position() + line.textStart());
z2 = line.cursorToX(block.position() + line.textStart() + line.textLength());
}
if (z2 > z1) {
//qDebug() << "z1: " << z1 << "z2: " << z2;
KoCharacterStyle::LineStyle fontUnderLineStyle = KoCharacterStyle::DashLine;
KoCharacterStyle::LineType fontUnderLineType = KoCharacterStyle::SingleLine;
QTextCharFormat::VerticalAlignment valign = fmt.verticalAlignment();
QFont font(fmt.font());
if (valign == QTextCharFormat::AlignSubScript
|| valign == QTextCharFormat::AlignSuperScript)
font.setPointSize(font.pointSize() * 2 / 3);
QFontMetricsF metrics(font, d->documentLayout->paintDevice());
qreal y = line.position().y();
if (valign == QTextCharFormat::AlignSubScript)
y += line.height() - metrics.descent() + metrics.underlinePos();
else if (valign == QTextCharFormat::AlignSuperScript)
y += metrics.ascent() + metrics.underlinePos();
else
y += line.ascent() + metrics.underlinePos();
QColor color = fmt.foreground().color();
qreal width = computeWidth( // line thickness
(KoCharacterStyle::LineWeight) underline.intProperty(KoCharacterStyle::UnderlineWeight),
underline.doubleProperty(KoCharacterStyle::UnderlineWidth),
font);
if (valign == QTextCharFormat::AlignSubScript
|| valign == QTextCharFormat::AlignSuperScript) // adjust size.
width = width * 2 / 3;
drawDecorationLine(painter, color, fontUnderLineType, fontUnderLineStyle, width, z1, z2, y);
}
}
}
}
}
}
}
if (showFormattingCharacters) {
QTextLine line = layout->lineForTextPosition(block.length()-1);
qreal y = line.position().y() + line.ascent();
qreal x = line.cursorToX(block.length()-1);
painter->drawText(QPointF(x, y), QChar((ushort)0x00B6));
}
if (showSpellChecking) {
// Finally let's paint our own spelling markings
// TODO Should we make this optional at this point (right on/off handled by the plugin)
// also we might want to provide alternative ways of drawing it
KoTextBlockData blockData(block);
QPen penBackup = painter->pen();
QPen pen;
pen.setColor(QColor(Qt::red));
pen.setWidthF(1.5);
QVector<qreal> pattern;
pattern << 1 << 2;
pen.setDashPattern(pattern);
painter->setPen(pen);
QList<KoTextBlockData::MarkupRange>::Iterator markIt = blockData.markupsBegin(KoTextBlockData::Misspell);
QList<KoTextBlockData::MarkupRange>::Iterator markEnd = blockData.markupsEnd(KoTextBlockData::Misspell);
for (int i = 0 ; i < layout->lineCount(); ++i) {
if (markIt == markEnd) {
break;
}
QTextLine line = layout->lineAt(i);
// the y position is placed half way between baseline and descent of the line
// this is fast and sufficient
qreal y = line.position().y() + line.ascent() + 0.5 * line.descent();
// first handle all those marking ranges that end on this line
while (markIt != markEnd && markIt->lastChar < line.textStart() + line.textLength()
&& line.textStart() + line.textLength() <= block.length()) {
if (!blockData.isMarkupsLayoutValid(KoTextBlockData::Misspell)) {
if (markIt->firstChar > line.textStart()) {
markIt->startX = line.cursorToX(markIt->firstChar);
}
markIt->endX = line.cursorToX(qMin(markIt->lastChar, block.length()));
}
qreal x1 = (markIt->firstChar > line.textStart()) ? markIt->startX : line.cursorToX(0);
painter->drawLine(QPointF(x1, y), QPointF(markIt->endX, y));
++markIt;
}
// there may be a markup range on this line that extends to the next line
if (markIt != markEnd && markIt->firstChar < line.textStart() + line.textLength()
&& line.textStart() + line.textLength() <= block.length()) {
if (!blockData.isMarkupsLayoutValid(KoTextBlockData::Misspell)) {
if (markIt->firstChar > line.textStart()) {
markIt->startX = line.cursorToX(markIt->firstChar);
}
}
qreal x1 = (markIt->firstChar > line.textStart()) ? markIt->startX : line.cursorToX(0);
painter->drawLine(QPointF(x1, y), QPointF(line.cursorToX(line.textStart() + line.textLength()), y));
// since it extends to next line we don't increment the iterator
}
}
blockData.setMarkupsLayoutValidity(KoTextBlockData::Misspell, true);
painter->setPen(penBackup);
}
painter->setFont(oldFont);
}
void KoTextLayoutArea::drawStrikeOuts(QPainter *painter, const QTextCharFormat &currentCharFormat, const QString &text, const QTextLine &line, qreal x1, qreal x2, const int startOfFragmentInBlock, const int fragmentToLineOffset) const
{
KoCharacterStyle::LineStyle strikeOutStyle = (KoCharacterStyle::LineStyle)
currentCharFormat.intProperty(KoCharacterStyle::StrikeOutStyle);
KoCharacterStyle::LineType strikeOutType = (KoCharacterStyle::LineType)
currentCharFormat.intProperty(KoCharacterStyle::StrikeOutType);
if ((strikeOutStyle != KoCharacterStyle::NoLineStyle) &&
(strikeOutType != KoCharacterStyle::NoLineType)) {
QTextCharFormat::VerticalAlignment valign = currentCharFormat.verticalAlignment();
QFont font(currentCharFormat.font());
if (valign == QTextCharFormat::AlignSubScript
|| valign == QTextCharFormat::AlignSuperScript)
font.setPointSize(qRound(font.pointSize() * 2 / 3.));
QFontMetricsF metrics(font, d->documentLayout->paintDevice());
qreal y = line.position().y();
if (valign == QTextCharFormat::AlignSubScript)
y += line.height() - metrics.descent() - metrics.strikeOutPos();
else if (valign == QTextCharFormat::AlignSuperScript)
y += metrics.ascent() - metrics.strikeOutPos();
else
y += line.ascent() - metrics.strikeOutPos();
QColor color = currentCharFormat.colorProperty(KoCharacterStyle::StrikeOutColor);
if (!color.isValid())
color = currentCharFormat.foreground().color();
KoCharacterStyle::LineMode strikeOutMode =
(KoCharacterStyle::LineMode) currentCharFormat.intProperty(KoCharacterStyle::StrikeOutMode);
QString strikeOutText = currentCharFormat.stringProperty(KoCharacterStyle::StrikeOutText);
qreal width = 0; // line thickness
if (strikeOutText.isEmpty()) {
width = computeWidth(
(KoCharacterStyle::LineWeight) currentCharFormat.intProperty(KoCharacterStyle::StrikeOutWeight),
currentCharFormat.doubleProperty(KoCharacterStyle::StrikeOutWidth),
font);
}
if (valign == QTextCharFormat::AlignSubScript
|| valign == QTextCharFormat::AlignSuperScript) // adjust size.
width = width * 2 / 3;
if (strikeOutMode == KoCharacterStyle::SkipWhiteSpaceLineMode) {
drawDecorationWords(painter, line, text, color, strikeOutType,
strikeOutStyle, strikeOutText, width, y, fragmentToLineOffset,
startOfFragmentInBlock);
} else {
if (strikeOutText.isEmpty())
drawDecorationLine(painter, color, strikeOutType, strikeOutStyle, width, x1, x2, y);
else
drawDecorationText(painter, line, color, strikeOutText, x1, x2);
}
}
}
void KoTextLayoutArea::drawOverlines(QPainter *painter, const QTextCharFormat &currentCharFormat, const QString &text, const QTextLine &line, qreal x1, qreal x2, const int startOfFragmentInBlock, const int fragmentToLineOffset) const
{
KoCharacterStyle::LineStyle fontOverLineStyle = (KoCharacterStyle::LineStyle) currentCharFormat.intProperty(KoCharacterStyle::OverlineStyle);
KoCharacterStyle::LineType fontOverLineType = (KoCharacterStyle::LineType) currentCharFormat.intProperty(KoCharacterStyle::OverlineType);
if ((fontOverLineStyle != KoCharacterStyle::NoLineStyle) &&
(fontOverLineType != KoCharacterStyle::NoLineType)) {
QTextCharFormat::VerticalAlignment valign = currentCharFormat.verticalAlignment();
QFont font(currentCharFormat.font());
if (valign == QTextCharFormat::AlignSubScript
|| valign == QTextCharFormat::AlignSuperScript)
font.setPointSize(font.pointSize() * 2 / 3);
QFontMetricsF metrics(font, d->documentLayout->paintDevice());
qreal y = line.position().y();
if (valign == QTextCharFormat::AlignSubScript)
y += line.height() - metrics.descent() - metrics.overlinePos();
else if (valign == QTextCharFormat::AlignSuperScript)
y += metrics.ascent() - metrics.overlinePos();
else
y += line.ascent() - metrics.overlinePos();
QColor color = currentCharFormat.colorProperty(KoCharacterStyle::OverlineColor);
if (!color.isValid())
color = currentCharFormat.foreground().color();
KoCharacterStyle::LineMode overlineMode =
(KoCharacterStyle::LineMode) currentCharFormat.intProperty(KoCharacterStyle::OverlineMode);
qreal width = computeWidth( // line thickness
(KoCharacterStyle::LineWeight) currentCharFormat.intProperty(KoCharacterStyle::OverlineWeight),
currentCharFormat.doubleProperty(KoCharacterStyle::OverlineWidth),
font);
if (valign == QTextCharFormat::AlignSubScript
|| valign == QTextCharFormat::AlignSuperScript) // adjust size.
width = width * 2 / 3;
if (overlineMode == KoCharacterStyle::SkipWhiteSpaceLineMode) {
drawDecorationWords(painter, line, text, color, fontOverLineType,
fontOverLineStyle, QString(), width, y, fragmentToLineOffset, startOfFragmentInBlock);
} else {
drawDecorationLine(painter, color, fontOverLineType, fontOverLineStyle, width, x1, x2, y);
}
}
}
void KoTextLayoutArea::drawUnderlines(QPainter *painter, const QTextCharFormat &currentCharFormat,const QString &text, const QTextLine &line, qreal x1, qreal x2, const int startOfFragmentInBlock, const int fragmentToLineOffset) const
{
KoCharacterStyle::LineStyle fontUnderLineStyle = (KoCharacterStyle::LineStyle) currentCharFormat.intProperty(KoCharacterStyle::UnderlineStyle);
KoCharacterStyle::LineType fontUnderLineType = (KoCharacterStyle::LineType) currentCharFormat.intProperty(KoCharacterStyle::UnderlineType);
if ((fontUnderLineStyle != KoCharacterStyle::NoLineStyle) &&
(fontUnderLineType != KoCharacterStyle::NoLineType)) {
QTextCharFormat::VerticalAlignment valign = currentCharFormat.verticalAlignment();
QFont font(currentCharFormat.font());
if (valign == QTextCharFormat::AlignSubScript
|| valign == QTextCharFormat::AlignSuperScript)
font.setPointSize(font.pointSize() * 2 / 3);
QFontMetricsF metrics(font, d->documentLayout->paintDevice());
qreal y = line.position().y();
if (valign == QTextCharFormat::AlignSubScript)
y += line.height() - metrics.descent() + metrics.underlinePos();
else if (valign == QTextCharFormat::AlignSuperScript)
y += metrics.ascent() + metrics.underlinePos();
else
y += line.ascent() + metrics.underlinePos();
QColor color = currentCharFormat.underlineColor();
if (!color.isValid())
color = currentCharFormat.foreground().color();
KoCharacterStyle::LineMode underlineMode =
(KoCharacterStyle::LineMode) currentCharFormat.intProperty(KoCharacterStyle::UnderlineMode);
qreal width = computeWidth( // line thickness
(KoCharacterStyle::LineWeight) currentCharFormat.intProperty(KoCharacterStyle::UnderlineWeight),
currentCharFormat.doubleProperty(KoCharacterStyle::UnderlineWidth),
font);
if (valign == QTextCharFormat::AlignSubScript
|| valign == QTextCharFormat::AlignSuperScript) // adjust size.
width = width * 2 / 3;
if (underlineMode == KoCharacterStyle::SkipWhiteSpaceLineMode) {
drawDecorationWords(painter, line, text, color, fontUnderLineType,
fontUnderLineStyle, QString(), width, y, fragmentToLineOffset, startOfFragmentInBlock);
} else {
drawDecorationLine(painter, color, fontUnderLineType, fontUnderLineStyle, width, x1, x2, y);
}
}
}
// Decorate any tabs ('\t's) in 'currentFragment' and laid out in 'line'.
int KoTextLayoutArea::decorateTabsAndFormatting(QPainter *painter, const QTextFragment& currentFragment, const QTextLine &line, const int startOfFragmentInBlock, const QVariantList& tabList, int currentTabStop, bool showFormattingCharacters)
{
// If a line in the layout represent multiple text fragments, this function will
// be called multiple times on the same line, with different fragments.
// Likewise, if a fragment spans two lines, then this function will be called twice
// on the same fragment, once for each line.
QString fragText = currentFragment.text();
QFontMetricsF fm(currentFragment.charFormat().font(), d->documentLayout->paintDevice());
qreal tabStyleLineMargin = fm.averageCharWidth() / 4; // leave some margin for the tab decoration line
// currentFragment.position() : start of this fragment w.r.t. the document
// startOfFragmentInBlock : start of this fragment w.r.t. the block
// line.textStart() : start of this line w.r.t. the block
int searchForCharFrom; // search for \t from this point onwards in fragText
int searchForCharTill; // search for \t till this point in fragText
if (line.textStart() >= startOfFragmentInBlock) { // fragment starts at or before the start of line
// we are concerned with only that part of the fragment displayed in this line
searchForCharFrom = line.textStart() - startOfFragmentInBlock;
// It's a new line. So we should look at the first tab-stop properties for the next \t.
currentTabStop = 0;
} else { // fragment starts in the middle of the line
searchForCharFrom = 0;
}
if (line.textStart() + line.textLength() > startOfFragmentInBlock + currentFragment.length()) {
// fragment ends before the end of line. need to see only till the end of the fragment.
searchForCharTill = currentFragment.length();
} else {
// line ends before the fragment ends. need to see only till the end of this line.
// but then, we need to convert the end of line to an index into fragText
searchForCharTill = line.textLength() + line.textStart() - startOfFragmentInBlock;
}
for (int i = searchForCharFrom ; i < searchForCharTill; i++) {
if (currentTabStop >= tabList.size() && !showFormattingCharacters) // no more decorations
break;
if (fragText[i] == '\t') {
qreal x1(0.0);
qreal x2(0.0);
if (showFormattingCharacters) {
x1 = line.cursorToX(startOfFragmentInBlock + i);
x2 = line.cursorToX(startOfFragmentInBlock + i + 1);
qreal y = line.position().y() + line.ascent() - fm.xHeight()/2.0;
qreal arrowDim = fm.xHeight()/2.0;
QPen penBackup = painter->pen();
QPen pen = painter->pen();
pen.setWidthF(fm.ascent()/10.0);
pen.setStyle(Qt::SolidLine);
painter->setPen(pen);
painter->drawLine(QPointF(x1, y), QPointF(x2, y));
painter->drawLine(QPointF(x2 - arrowDim, y - arrowDim), QPointF(x2, y));
painter->drawLine(QPointF(x2 - arrowDim, y + arrowDim), QPointF(x2, y));
painter->setPen(penBackup);
}
if (currentTabStop < tabList.size()) { // still tabsstops worth examining
if (!showFormattingCharacters) {
// only then was it not calculated
x1 = line.cursorToX(startOfFragmentInBlock + i);
}
// find a tab-stop decoration for this tab position
// for eg., if there's a tab-stop at 1in, but the text before \t already spans 1.2in,
// we should look at the next tab-stop
KoText::Tab tab;
do {
tab = qvariant_cast<KoText::Tab>(tabList[currentTabStop]);
currentTabStop++;
// comparing with x1 should work for all of left/right/center/char tabs
} while (tab.position <= x1 && currentTabStop < tabList.size());
if (tab.position > x1) {
if (!showFormattingCharacters) {
// only then was it not calculated
x2 = line.cursorToX(startOfFragmentInBlock + i + 1);
}
qreal tabStyleLeftLineMargin = tabStyleLineMargin;
qreal tabStyleRightLineMargin = tabStyleLineMargin;
// no margin if its adjacent char is also a tab
if (i > searchForCharFrom && fragText[i-1] == '\t')
tabStyleLeftLineMargin = 0;
if (i < (searchForCharTill - 1) && fragText[i+1] == '\t')
tabStyleRightLineMargin = 0;
qreal y = line.position().y() + line.ascent() - 1;
x1 += tabStyleLeftLineMargin;
x2 -= tabStyleRightLineMargin;
QColor tabDecorColor = currentFragment.charFormat().foreground().color();
if (tab.leaderColor.isValid())
tabDecorColor = tab.leaderColor;
qreal width = computeWidth(tab.leaderWeight, tab.leaderWidth, painter->font());
if (x1 < x2) {
if (tab.leaderText.isEmpty()) {
drawDecorationLine(painter, tabDecorColor, tab.leaderType, tab.leaderStyle, width, x1, x2, y);
} else {
drawDecorationText(painter, line, tabDecorColor, tab.leaderText, x1, x2);
}
}
}
}
} else if (showFormattingCharacters) {
if (fragText[i] == ' ' || fragText[i] == QChar::Nbsp) {
qreal x = line.cursorToX(startOfFragmentInBlock + i);
qreal y = line.position().y() + line.ascent();
painter->drawText(QPointF(x, y), QChar((ushort)0xb7));
} else if (fragText[i] == QChar::LineSeparator){
qreal x = line.cursorToX(startOfFragmentInBlock + i);
qreal y = line.position().y() + line.ascent();
painter->drawText(QPointF(x, y), QChar((ushort)0x21B5));
}
}
}
return currentTabStop;
}
diff --git a/plugins/flake/textshape/textlayout/KoTextLayoutCellHelper.h b/plugins/flake/textshape/textlayout/KoTextLayoutCellHelper.h
index 3b344c8219..839ab3cf31 100644
--- a/plugins/flake/textshape/textlayout/KoTextLayoutCellHelper.h
+++ b/plugins/flake/textshape/textlayout/KoTextLayoutCellHelper.h
@@ -1,138 +1,140 @@
/* This file is part of the KDE project
* Copyright (C) 2006-2010 Thomas Zander <zander@kde.org>
* Copyright (C) 2008 Thorsten Zachmann <zachmann@kde.org>
* Copyright (C) 2008 Roopesh Chander <roop@forwardbias.in>
* Copyright (C) 2008 Girish Ramakrishnan <girish@forwardbias.in>
* Copyright (C) 2009 KO GmbH <cbo@kogmbh.com>
* Copyright (C) 2011 Pierre Ducroquet <pinaraf@pinaraf.info>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOTEXTLAYOUTCELLHELPER_H
#define KOTEXTLAYOUTCELLHELPER_H
#include "kritatextlayout_export.h"
#include <KoBorder.h>
#include <QObject>
class KoTableCellStyle;
class QPainter;
class KRITATEXTLAYOUT_EXPORT KoTextLayoutCellHelper : public QObject
{
Q_OBJECT
public:
explicit KoTextLayoutCellHelper(const KoTableCellStyle &cellStyle, QObject *parent = 0);
/// draws a horizontal wave line
void drawHorizontalWave(KoBorder::BorderStyle style, QPainter &painter, qreal x, qreal w, qreal t) const;
/// draws a vertical wave line
void drawVerticalWave(KoBorder::BorderStyle style, QPainter &painter, qreal y, qreal h, qreal t) const;
/**
* Paint the borders.
*
- * @painter the painter to draw with.
- * @bounds the bounding rectangle to draw.
- * @blanks a painterpath where blank borders should be added to.
+ * @param painter the painter to draw with.
+ * @param bounds the bounding rectangle to draw.
+ * @param blanks a painterpath where blank borders should be added to.
*/
void paintBorders(QPainter &painter, const QRectF &bounds, QVector<QLineF> *blanks) const;
/**
* Paint the diagonal borders.
*
- * @painter the painter to draw with.
- * @bounds the bounding rectangle to draw.
+ * @param painter the painter to draw with.
+ * @param bounds the bounding rectangle to draw.
*/
void paintDiagonalBorders(QPainter &painter, const QRectF &bounds) const;
/**
* Paint the top border.
*
- * @painter the painter to draw with.
- * @x the x position.
- * @y the y position.
- * @w the width.
- * @blanks a painterpath where blank borders should be added to.
+ * @param painter the painter to draw with.
+ * @param x the x position.
+ * @param y the y position.
+ * @param w the width.
+ * @param blanks a painterpath where blank borders should be added to.
*/
void drawTopHorizontalBorder(QPainter &painter, qreal x, qreal y, qreal w, QVector<QLineF> *blanks = 0) const;
/**
* Paint the border that is shared.
* It only draws the thickest and it always draws it below the y position.
*
- * @painter the painter to draw with.
- * @x the x position.
- * @y the y position.
- * @w the width.
- * @blanks a painterpath where blank borders should be added to.
+ * @param painter the painter to draw with.
+ * @param styleBelow the table cell style.
+ * @param x the x position.
+ * @param y the y position.
+ * @param w the width.
+ * @param blanks a painterpath where blank borders should be added to.
*/
void drawSharedHorizontalBorder(QPainter &painter, const KoTableCellStyle &styleBelow, qreal x, qreal y, qreal w, QVector<QLineF> *blanks = 0) const;
/**
* Paint the bottom border.
*
- * @painter the painter to draw with.
- * @x the x position.
- * @y the y position.
- * @w the width.
- * @blanks a painterpath where blank borders should be added to.
+ * @param painter the painter to draw with.
+ * @param x the x position.
+ * @param y the y position.
+ * @param w the width.
+ * @param blanks a painterpath where blank borders should be added to.
*/
void drawBottomHorizontalBorder(QPainter &painter, qreal x, qreal y, qreal w, QVector<QLineF> *blanks = 0) const;
/**
* Paint the leftmost border.
*
- * @painter the painter to draw with.
- * @x the x position.
- * @y the y position.
- * @h the height.
- * @blanks a painterpath where blank borders should be added to.
+ * @param painter the painter to draw with.
+ * @param x the x position.
+ * @param y the y position.
+ * @param h the height.
+ * @param blanks a painterpath where blank borders should be added to.
*/
void drawLeftmostVerticalBorder(QPainter &painter, qreal x, qreal y, qreal h, QVector<QLineF> *blanks = 0) const;
/**
* Paint the border that is shared.
- * It only draws the thickest and it always draws it below the y position.
+ * It only draws the thickest and it always draws it to the right of the x position.
*
- * @painter the painter to draw with.
- * @x the x position.
- * @y the y position.
- * @h the height.
- * @blanks a painterpath where blank borders should be added to.
+ * @param painter the painter to draw with.
+ * @param styleRight the table cell style.
+ * @param x the x position.
+ * @param y the y position.
+ * @param h the height.
+ * @param blanks a painterpath where blank borders should be added to.
*/
void drawSharedVerticalBorder(QPainter &painter, const KoTableCellStyle &styleRight, qreal x, qreal y, qreal h, QVector<QLineF> *blanks = 0) const;
/**
* Paint the rightmost border.
*
- * @painter the painter to draw with.
- * @x the x position.
- * @y the y position.
- * @h the height.
- * @blanks a painterpath where blank borders should be added to.
+ * @param painter the painter to draw with.
+ * @param x the x position.
+ * @param y the y position.
+ * @param h the height.
+ * @param blanks a painterpath where blank borders should be added to.
*/
void drawRightmostVerticalBorder(QPainter &painter, qreal x, qreal y, qreal h, QVector<QLineF> *blanks = 0) const;
private:
const KoTableCellStyle &m_cellStyle;
};
#endif // KOTEXTLAYOUTCELLHELPER_H
diff --git a/plugins/flake/textshape/textlayout/KoTextShapeData.h b/plugins/flake/textshape/textlayout/KoTextShapeData.h
index ffdbca958d..35edead9f5 100644
--- a/plugins/flake/textshape/textlayout/KoTextShapeData.h
+++ b/plugins/flake/textshape/textlayout/KoTextShapeData.h
@@ -1,147 +1,145 @@
/* This file is part of the KDE project
* Copyright (C) 2006, 2009-2010 Thomas Zander <zander@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOTEXTSHAPEDATA_H
#define KOTEXTSHAPEDATA_H
#include "KoText.h"
#include "kritatextlayout_export.h"
#include <KoTextShapeDataBase.h>
#include <KoXmlReaderForward.h>
class QTextDocument;
class KoShapeLoadingContext;
class KoShapeSavingContext;
class KoTextShapeDataPrivate;
class KoDocumentRdfBase;
class KoTextLayoutRootArea;
/**
* The data store that is held by each TextShape instance.
* This is a separate object to allow Words proper to use this class' API and
* access the internals of the text shape.
*
* This class holds a QTextDocument pointer and is built so multiple shapes (and thus
* multiple instances of this shape data) can share one QTextDocument by providing a
* different view on (a different part of) the QTextDocument.
*/
class KRITATEXTLAYOUT_EXPORT KoTextShapeData : public KoTextShapeDataBase
{
Q_OBJECT
public:
/// constructor
KoTextShapeData();
~KoTextShapeData() override;
KoShapeUserData* clone() const override;
/**
* Replace the QTextDocument this shape will render.
* @param document the new document. If there was an old document owned, it will be deleted.
- * @param transferOwnership if true then the document will be considered the responsibility
- * of this data and the doc will be deleted when this shapeData dies.
*/
void setDocument(QTextDocument *document);
/**
* return the amount of points into the document (y) this shape will display.
*/
qreal documentOffset() const;
/// mark shape as dirty triggering a re-layout of its text.
void setDirty();
/// return if the shape is marked dirty and its text content needs to be relayout
bool isDirty() const;
/// Set the rootArea that is associated to the textshape
void setRootArea(KoTextLayoutRootArea *rootArea);
/// the rootArea that is associated to the textshape
KoTextLayoutRootArea *rootArea();
void setLeftPadding(qreal padding);
qreal leftPadding() const;
void setTopPadding(qreal padding);
qreal topPadding() const;
void setRightPadding(qreal padding);
qreal rightPadding() const;
void setBottomPadding(qreal padding);
qreal bottomPadding() const;
void setPadding(qreal padding);
/**
* Load the TextShape from ODF.
*
* @see the @a TextShape::loadOdf() method which calls this method.
* @see the @a KoTextLoader::loadBody() method which got called by this method
* to load the ODF.
*/
bool loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context, KoDocumentRdfBase *rdfData, KoShape *shape = 0);
/**
* Load the TextShape from ODF.
* Overloaded method provided for your convenience.
*/
bool loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context) override {
return loadOdf(element, context, 0);
}
/**
* Store the TextShape data as ODF.
* @see TextShape::saveOdf()
*/
void saveOdf(KoShapeSavingContext &context, KoDocumentRdfBase *rdfData, int from = 0, int to = -1) const;
/**
* Store the TextShape data as ODF.
* Overloaded method provided for your convenience.
*/
void saveOdf(KoShapeSavingContext &context, int from = 0, int to = -1) const override {
saveOdf(context, 0, from, to);
}
// reimplemented
void loadStyle(const KoXmlElement &element, KoShapeLoadingContext &context) override;
// reimplemented
void saveStyle(KoGenStyle &style, KoShapeSavingContext &context) const override;
/**
* Set the page direction.
* The page direction will determine behavior on the insertion of new text and those
* new paragraphs default direction.
*/
void setPageDirection(KoText::Direction direction);
/**
* Return the direction set on the page.
* The page direction will determine behavior on the insertion of new text and those
* new paragraphs default direction.
*/
KoText::Direction pageDirection() const;
private:
KoTextShapeData(KoTextShapeDataPrivate *dd);
private:
Q_DECLARE_PRIVATE(KoTextShapeData)
};
#endif
diff --git a/plugins/generators/pattern/kis_wdg_pattern.cpp b/plugins/generators/pattern/kis_wdg_pattern.cpp
index 2f16ae2548..9397f9ca22 100644
--- a/plugins/generators/pattern/kis_wdg_pattern.cpp
+++ b/plugins/generators/pattern/kis_wdg_pattern.cpp
@@ -1,70 +1,71 @@
/*
* This file is part of Krita
*
* Copyright (c) 2006 Cyrille Berger <cberger@cberger.net>
*
* 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_wdg_pattern.h"
#include <QLayout>
#include <QLabel>
#include <KoColor.h>
#include <KoResourceServer.h>
#include <resources/KoPattern.h>
#include <KoResourceServerProvider.h>
#include <filter/kis_filter_configuration.h>
#include <kis_pattern_chooser.h>
#include "ui_wdgpatternoptions.h"
KisWdgPattern::KisWdgPattern(QWidget* parent)
: KisConfigWidget(parent)
{
m_widget = new Ui_WdgPatternOptions();
m_widget->setupUi(this);
m_widget->lblPattern->setVisible(false);
m_widget->lblColor->setVisible(false);
m_widget->bnColor->setVisible(false);
+ connect(m_widget->patternChooser, SIGNAL(resourceSelected(KoResource*)), this, SIGNAL(sigConfigurationUpdated()));
}
KisWdgPattern::~KisWdgPattern()
{
delete m_widget;
}
void KisWdgPattern::setConfiguration(const KisPropertiesConfigurationSP config)
{
KoResourceServer<KoPattern> *rserver = KoResourceServerProvider::instance()->patternServer();
KoPattern *pattern = rserver->resourceByName(config->getString("pattern", "Grid01.pat"));
if (pattern) {
widget()->patternChooser->setCurrentPattern(pattern);
}
}
KisPropertiesConfigurationSP KisWdgPattern::configuration() const
{
KisFilterConfigurationSP config = new KisFilterConfiguration("pattern", 1);
QVariant v;
v.setValue(widget()->patternChooser->currentResource()->name());
config->setProperty("pattern", v);
return config;
}
diff --git a/plugins/generators/solid/kis_wdg_color.cpp b/plugins/generators/solid/kis_wdg_color.cpp
index b2bd6e9a88..f3ce030119 100644
--- a/plugins/generators/solid/kis_wdg_color.cpp
+++ b/plugins/generators/solid/kis_wdg_color.cpp
@@ -1,63 +1,64 @@
/*
* This file is part of Krita
*
* Copyright (c) 2006 Cyrille Berger <cberger@cberger.net>
*
* 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_wdg_color.h"
#include <QLayout>
#include <KoColor.h>
#include <filter/kis_filter_configuration.h>
#include "ui_wdgcoloroptions.h"
KisWdgColor::KisWdgColor(QWidget* parent, const KoColorSpace *cs)
: KisConfigWidget(parent)
{
m_widget = new Ui_WdgColorOptions();
m_widget->setupUi(this);
m_cs = cs;
+ connect(m_widget->bnColor, SIGNAL(changed(const KoColor&)), this, SIGNAL(sigConfigurationUpdated()));
}
KisWdgColor::~KisWdgColor()
{
delete m_widget;
}
void KisWdgColor::setConfiguration(const KisPropertiesConfigurationSP config)
{
QVariant value;
KoColor c =config->getColor("color");
c.convertTo(m_cs);
widget()->bnColor->setColor(c);
}
KisPropertiesConfigurationSP KisWdgColor::configuration() const
{
KisFilterConfigurationSP config = new KisFilterConfiguration("color", 1);
KoColor c;
c.fromKoColor(this->widget()->bnColor->color());
QVariant v;
v.setValue(c);
config->setProperty("color", v);
return config;
}
diff --git a/plugins/impex/csv/csv_loader.cpp b/plugins/impex/csv/csv_loader.cpp
index c47e7026e3..6feedd40bc 100644
--- a/plugins/impex/csv/csv_loader.cpp
+++ b/plugins/impex/csv/csv_loader.cpp
@@ -1,489 +1,489 @@
/*
* Copyright (c) 2016 Laszlo Fazekas <mneko@freemail.hu>
*
* 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 "csv_loader.h"
#include <QDebug>
#include <QApplication>
#include <QFile>
#include <QVector>
#include <QIODevice>
#include <QStatusBar>
#include <QFileInfo>
#include <KisPart.h>
#include <KisView.h>
#include <KisDocument.h>
#include <KoColorSpace.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorModelStandardIds.h>
#include <kis_debug.h>
#include <kis_image.h>
#include <kis_paint_layer.h>
#include <kis_raster_keyframe_channel.h>
#include <kis_image_animation_interface.h>
#include <kis_time_range.h>
#include "csv_read_line.h"
#include "csv_layer_record.h"
CSVLoader::CSVLoader(KisDocument *doc, bool batchMode)
: m_image(0)
, m_doc(doc)
, m_batchMode(batchMode)
, m_stop(false)
{
}
CSVLoader::~CSVLoader()
{
}
KisImageBuilder_Result CSVLoader::decode(QIODevice *io, const QString &filename)
{
QString field;
int idx;
int frame = 0;
QString projName;
int width = 0;
int height = 0;
int frameCount = 1;
float framerate = 24.0;
float pixelRatio = 1.0;
int projNameIdx = -1;
int widthIdx = -1;
int heightIdx = -1;
int frameCountIdx = -1;
int framerateIdx = -1;
int pixelRatioIdx = -1;
QVector<CSVLayerRecord*> layers;
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
idx = filename.lastIndexOf(QRegExp("[\\/]"));
QString base = (idx == -1) ? QString() : filename.left(idx + 1); //include separator
QString path = filename;
if (path.right(4).toUpper() == ".CSV")
path = path.left(path.size() - 4);
//according to the QT docs, the slash is a universal directory separator
path.append(".frames/");
KisImageBuilder_Result retval = KisImageBuilder_RESULT_OK;
dbgFile << "pos:" << io->pos();
CSVReadLine readLine;
QScopedPointer<KisDocument> importDoc(KisPart::instance()->createDocument());
importDoc->setInfiniteAutoSaveInterval();
importDoc->setFileBatchMode(true);
KisView *setView(0);
if (!m_batchMode) {
// TODO: use other systems of progress reporting (KisViewManager::createUnthreadedUpdater()
// //show the statusbar message even if no view
// Q_FOREACH (KisView* view, KisPart::instance()->views()) {
// if (view && view->document() == m_doc) {
// setView = view;
// break;
// }
// }
// if (!setView) {
// QStatusBar *sb = KisPart::instance()->currentMainwindow()->statusBar();
// if (sb) {
// sb->showMessage(i18n("Loading CSV file..."));
// }
// } else {
// emit m_doc->statusBarMessage(i18n("Loading CSV file..."));
// }
// emit m_doc->sigProgress(0);
// connect(m_doc, SIGNAL(sigProgressCanceled()), this, SLOT(cancel()));
}
int step = 0;
do {
qApp->processEvents();
if (m_stop) {
retval = KisImageBuilder_RESULT_CANCEL;
break;
}
if ((idx = readLine.nextLine(io)) <= 0) {
if ((idx < 0) ||(step < 5))
retval = KisImageBuilder_RESULT_FAILURE;
break;
}
field = readLine.nextField(); //first field of the line
if (field.isNull()) continue; //empty row
switch (step) {
case 0 : //skip first row
step = 1;
break;
case 1 : //scene header names
step = 2;
for (idx = 0; !field.isNull(); idx++) {
if (field == "Project Name") {
projNameIdx = idx;
} else if (field == "Width") {
widthIdx = idx;
} else if (field == "Height") {
heightIdx = idx;
} else if (field == "Frame Count") {
frameCountIdx = idx;
} else if (field == "Frame Rate") {
framerateIdx = idx;
} else if (field == "Pixel Aspect Ratio") {
pixelRatioIdx = idx;
}
field= readLine.nextField();
}
break;
case 2 : //scene header values
step= 3;
for (idx= 0; !field.isNull(); idx++) {
if (idx == projNameIdx) {
projName = field;
} else if (idx == widthIdx) {
width = field.toInt();
} else if (idx == heightIdx) {
height = field.toInt();
} else if (idx == frameCountIdx) {
frameCount = field.toInt();
if (frameCount < 1) frameCount= 1;
} else if (idx == framerateIdx) {
framerate = field.toFloat();
} else if (idx == pixelRatioIdx) {
pixelRatio = field.toFloat();
}
field= readLine.nextField();
}
if ((width < 1) || (height < 1)) {
retval = KisImageBuilder_RESULT_FAILURE;
break;
}
retval = createNewImage(width, height, pixelRatio, projName.isNull() ? filename : projName);
break;
case 3 : //create level headers
if (field[0] != '#') break;
for (; !(field = readLine.nextField()).isNull(); ) {
CSVLayerRecord* layerRecord = new CSVLayerRecord();
layers.append(layerRecord);
}
readLine.rewind();
field = readLine.nextField();
step = 4;
- /* Falls through */
+ Q_FALLTHROUGH();
case 4 : //level header
if (field == "#Layers") {
//layer name
for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++)
layers.at(idx)->name = field;
break;
}
if (field == "#Density") {
//layer opacity
for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++)
layers.at(idx)->density = field.toFloat();
break;
}
if (field == "#Blending") {
//layer blending mode
for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++)
layers.at(idx)->blending = field;
break;
}
if (field == "#Visible") {
//layer visibility
for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++)
layers.at(idx)->visible = field.toInt();
break;
}
if (field == "#Folder") {
//CSV 1.1 folder location
for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++)
layers.at(idx)->path = validPath(field, base);
break;
}
if ((field.size() < 2) || (field[0] != '#') || !field[1].isDigit()) break;
step = 5;
- /* Falls through */
+ Q_FALLTHROUGH();
case 5 : //frames
if ((field.size() < 2) || (field[0] != '#') || !field[1].isDigit()) break;
for (idx = 0; !(field = readLine.nextField()).isNull() && (idx < layers.size()); idx++) {
CSVLayerRecord* layer = layers.at(idx);
if (layer->last != field) {
if (!m_batchMode) {
//emit m_doc->sigProgress((frame * layers.size() + idx) * 100 /
// (frameCount * layers.size()));
}
retval = setLayer(layer, importDoc.data(), path);
layer->last = field;
layer->frame = frame;
}
}
frame++;
break;
}
} while (retval == KisImageBuilder_RESULT_OK);
//finish the layers
if (retval == KisImageBuilder_RESULT_OK) {
if (m_image) {
KisImageAnimationInterface *animation = m_image->animationInterface();
if (frame > frameCount)
frameCount = frame;
animation->setFullClipRange(KisTimeRange::fromTime(0,frameCount - 1));
animation->setFramerate((int)framerate);
}
for (idx = 0; idx < layers.size(); idx++) {
CSVLayerRecord* layer = layers.at(idx);
//empty layers without any pictures are dropped
if ((layer->frame > 0) || !layer->last.isEmpty()) {
retval = setLayer(layer, importDoc.data(), path);
if (retval != KisImageBuilder_RESULT_OK)
break;
}
}
}
if (m_image) {
//insert the existing layers by the right order
for (idx = layers.size() - 1; idx >= 0; idx--) {
CSVLayerRecord* layer = layers.at(idx);
if (layer->layer) {
m_image->addNode(layer->layer, m_image->root());
}
}
m_image->unlock();
}
qDeleteAll(layers);
io->close();
if (!m_batchMode) {
// disconnect(m_doc, SIGNAL(sigProgressCanceled()), this, SLOT(cancel()));
// emit m_doc->sigProgress(100);
if (!setView) {
QStatusBar *sb = KisPart::instance()->currentMainwindow()->statusBar();
if (sb) {
sb->clearMessage();
}
} else {
emit m_doc->clearStatusBarMessage();
}
}
QApplication::restoreOverrideCursor();
return retval;
}
QString CSVLoader::convertBlending(const QString &blending)
{
if (blending == "Color") return COMPOSITE_OVER;
if (blending == "Behind") return COMPOSITE_BEHIND;
if (blending == "Erase") return COMPOSITE_ERASE;
// "Shade"
if (blending == "Light") return COMPOSITE_LINEAR_LIGHT;
if (blending == "Colorize") return COMPOSITE_COLORIZE;
if (blending == "Hue") return COMPOSITE_HUE;
if (blending == "Add") return COMPOSITE_ADD;
if (blending == "Sub") return COMPOSITE_INVERSE_SUBTRACT;
if (blending == "Multiply") return COMPOSITE_MULT;
if (blending == "Screen") return COMPOSITE_SCREEN;
// "Replace"
// "Substitute"
if (blending == "Difference") return COMPOSITE_DIFF;
if (blending == "Divide") return COMPOSITE_DIVIDE;
if (blending == "Overlay") return COMPOSITE_OVERLAY;
if (blending == "Light2") return COMPOSITE_DODGE;
if (blending == "Shade2") return COMPOSITE_BURN;
if (blending == "HardLight") return COMPOSITE_HARD_LIGHT;
if (blending == "SoftLight") return COMPOSITE_SOFT_LIGHT_PHOTOSHOP;
if (blending == "GrainExtract") return COMPOSITE_GRAIN_EXTRACT;
if (blending == "GrainMerge") return COMPOSITE_GRAIN_MERGE;
if (blending == "Sub2") return COMPOSITE_SUBTRACT;
if (blending == "Darken") return COMPOSITE_DARKEN;
if (blending == "Lighten") return COMPOSITE_LIGHTEN;
if (blending == "Saturation") return COMPOSITE_SATURATION;
return COMPOSITE_OVER;
}
QString CSVLoader::validPath(const QString &path,const QString &base)
{
//replace Windows directory separators with the universal /
QString tryPath= QString(path).replace(QString("\\"), QString("/"));
int i = tryPath.lastIndexOf("/");
if (i == (tryPath.size() - 1))
tryPath= tryPath.left(i); //remove the ending separator if exists
if (QFileInfo(tryPath).isDir())
return tryPath.append("/");
QString scan(tryPath);
i = -1;
while ((i= (scan.lastIndexOf("/",i) - 1)) > 0) {
//avoid testing if the next level will be the default xxxx.layers folder
if ((i >= 6) && (scan.mid(i - 6, 7) == ".layers")) continue;
tryPath= QString(base).append(scan.mid(i + 2)); //base already ending with a /
if (QFileInfo(tryPath).isDir())
return tryPath.append("/");
}
return QString(); //NULL string
}
KisImageBuilder_Result CSVLoader::setLayer(CSVLayerRecord* layer, KisDocument *importDoc, const QString &path)
{
bool result = true;
if (layer->channel == 0) {
//create a new document layer
float opacity = layer->density;
if (opacity > 1.0)
opacity = 1.0;
else if (opacity < 0.0)
opacity = 0.0;
const KoColorSpace* cs = m_image->colorSpace();
const QString layerName = (layer->name).isEmpty() ? m_image->nextLayerName() : layer->name;
KisPaintLayer* paintLayer = new KisPaintLayer(m_image, layerName,
(quint8)(opacity * OPACITY_OPAQUE_U8), cs);
paintLayer->setCompositeOpId(convertBlending(layer->blending));
paintLayer->setVisible(layer->visible);
paintLayer->enableAnimation();
layer->layer = paintLayer;
layer->channel = qobject_cast<KisRasterKeyframeChannel*>
(paintLayer->getKeyframeChannel(KisKeyframeChannel::Content.id(), true));
}
if (!layer->last.isEmpty()) {
//png image
QString filename = layer->path.isNull() ? path : layer->path;
filename.append(layer->last);
result = importDoc->openUrl(QUrl::fromLocalFile(filename),
KisDocument::DontAddToRecent);
if (result)
layer->channel->importFrame(layer->frame, importDoc->image()->projection(), 0);
} else {
//blank
layer->channel->addKeyframe(layer->frame);
}
return (result) ? KisImageBuilder_RESULT_OK : KisImageBuilder_RESULT_FAILURE;
}
KisImageBuilder_Result CSVLoader::createNewImage(int width, int height, float ratio, const QString &name)
{
//the CSV is RGBA 8bits, sRGB
if (!m_image) {
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace(
RGBAColorModelID.id(), Integer8BitsColorDepthID.id(), 0);
if (cs) m_image = new KisImage(m_doc->createUndoStore(), width, height, cs, name);
if (!m_image) return KisImageBuilder_RESULT_FAILURE;
m_image->setResolution(ratio, 1.0);
m_image->lock();
}
return KisImageBuilder_RESULT_OK;
}
KisImageBuilder_Result CSVLoader::buildAnimation(QIODevice *io, const QString &filename)
{
return decode(io, filename);
}
KisImageSP CSVLoader::image()
{
return m_image;
}
void CSVLoader::cancel()
{
m_stop = true;
}
diff --git a/plugins/impex/exr/exr_converter.cc b/plugins/impex/exr/exr_converter.cc
index b0dc356df1..b440105e83 100644
--- a/plugins/impex/exr/exr_converter.cc
+++ b/plugins/impex/exr/exr_converter.cc
@@ -1,1354 +1,1368 @@
/*
* Copyright (c) 2005 Adrian Page <adrian@pagenet.plus.com>
* Copyright (c) 2010 Cyrille Berger <cberger@cberger.net>
*
* 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 "exr_converter.h"
#include <half.h>
#include <ImfAttribute.h>
#include <ImfChannelList.h>
#include <ImfInputFile.h>
#include <ImfOutputFile.h>
#include <ImfStringAttribute.h>
#include "exr_extra_tags.h"
#include <QApplication>
#include <QMessageBox>
#include <QDomDocument>
#include <QThread>
#include <QFileInfo>
#include <KoColorSpaceRegistry.h>
#include <KoCompositeOpRegistry.h>
#include <KoColorSpaceTraits.h>
#include <KoColorModelStandardIds.h>
#include <KoColor.h>
#include <KisDocument.h>
#include <kis_group_layer.h>
#include <kis_image.h>
#include <kis_paint_device.h>
#include <kis_paint_layer.h>
#include <kis_transaction.h>
#include "kis_iterator_ng.h"
#include <kis_exr_layers_sorter.h>
#include <kis_meta_data_entry.h>
#include <kis_meta_data_schema.h>
#include <kis_meta_data_schema_registry.h>
#include <kis_meta_data_store.h>
#include <kis_meta_data_value.h>
#include "kis_kra_savexml_visitor.h"
// Do not translate!
#define HDR_LAYER "HDR Layer"
template<typename _T_>
struct Rgba {
_T_ r;
_T_ g;
_T_ b;
_T_ a;
};
struct ExrGroupLayerInfo;
struct ExrLayerInfoBase {
ExrLayerInfoBase() : colorSpace(0), parent(0) {
}
const KoColorSpace* colorSpace;
QString name;
const ExrGroupLayerInfo* parent;
};
struct ExrGroupLayerInfo : public ExrLayerInfoBase {
ExrGroupLayerInfo() : groupLayer(0) {}
KisGroupLayerSP groupLayer;
};
enum ImageType {
IT_UNKNOWN,
IT_FLOAT16,
IT_FLOAT32,
IT_UNSUPPORTED
};
struct ExrPaintLayerInfo : public ExrLayerInfoBase {
ExrPaintLayerInfo()
: imageType(IT_UNKNOWN)
{
}
ImageType imageType;
QMap< QString, QString> channelMap; ///< first is either R, G, B or A second is the EXR channel name
struct Remap {
Remap(const QString& _original, const QString& _current) : original(_original), current(_current) {
}
QString original;
QString current;
};
QList< Remap > remappedChannels; ///< this is used to store in the metadata the mapping between exr channel name, and channels used in Krita
void updateImageType(ImageType channelType);
};
void ExrPaintLayerInfo::updateImageType(ImageType channelType)
{
if (imageType == IT_UNKNOWN) {
imageType = channelType;
}
else if (imageType != channelType) {
imageType = IT_UNSUPPORTED;
}
}
struct ExrPaintLayerSaveInfo {
QString name; ///< name of the layer with a "." at the end (ie "group1.group2.layer1.")
KisPaintLayerSP layer;
QList<QString> channels;
Imf::PixelType pixelType;
};
struct EXRConverter::Private {
Private()
: doc(0)
, alphaWasModified(false)
, showNotifications(false)
{}
KisImageSP image;
KisDocument *doc;
bool alphaWasModified;
bool showNotifications;
QString errorMessage;
template <class WrapperType>
void unmultiplyAlpha(typename WrapperType::pixel_type *pixel);
template<typename _T_>
void decodeData4(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype);
template<typename _T_>
void decodeData1(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype);
QDomDocument loadExtraLayersInfo(const Imf::Header &header);
bool checkExtraLayersInfoConsistent(const QDomDocument &doc, std::set<std::string> exrLayerNames);
void makeLayerNamesUnique(QList<ExrPaintLayerSaveInfo>& informationObjects);
void recBuildPaintLayerSaveInfo(QList<ExrPaintLayerSaveInfo>& informationObjects, const QString& name, KisGroupLayerSP parent);
void reportLayersNotSaved(const QSet<KisNodeSP> &layersNotSaved);
QString fetchExtraLayersInfo(QList<ExrPaintLayerSaveInfo>& informationObjects);
};
EXRConverter::EXRConverter(KisDocument *doc, bool showNotifications)
: d(new Private)
{
d->doc = doc;
d->showNotifications = showNotifications;
// Set thread count for IlmImf library
Imf::setGlobalThreadCount(QThread::idealThreadCount());
dbgFile << "EXR Threadcount was set to: " << QThread::idealThreadCount();
}
EXRConverter::~EXRConverter()
{
}
ImageType imfTypeToKisType(Imf::PixelType type)
{
switch (type) {
case Imf::UINT:
case Imf::NUM_PIXELTYPES:
return IT_UNSUPPORTED;
case Imf::HALF:
return IT_FLOAT16;
case Imf::FLOAT:
return IT_FLOAT32;
default:
qFatal("Out of bound enum");
return IT_UNKNOWN;
}
}
const KoColorSpace* kisTypeToColorSpace(QString model, ImageType imageType)
{
switch (imageType) {
case IT_FLOAT16:
return KoColorSpaceRegistry::instance()->colorSpace(model, Float16BitsColorDepthID.id(), "");
case IT_FLOAT32:
return KoColorSpaceRegistry::instance()->colorSpace(model, Float32BitsColorDepthID.id(), "");
case IT_UNKNOWN:
case IT_UNSUPPORTED:
return 0;
default:
qFatal("Out of bound enum");
return 0;
}
}
template <typename T>
static inline T alphaEpsilon()
{
return static_cast<T>(HALF_EPSILON);
}
template <typename T>
static inline T alphaNoiseThreshold()
{
return static_cast<T>(0.01); // 1%
}
+static inline bool qFuzzyCompare(half p1, half p2)
+{
+ return std::abs(p1 - p2) < float(HALF_EPSILON);
+}
+
+static inline bool qFuzzyIsNull(half h)
+{
+ return std::abs(h) < float(HALF_EPSILON);
+}
+
template <typename T>
struct RgbPixelWrapper
{
typedef T channel_type;
typedef Rgba<T> pixel_type;
RgbPixelWrapper(Rgba<T> &_pixel) : pixel(_pixel) {}
inline T alpha() const {
return pixel.a;
}
inline bool checkMultipliedColorsConsistent() const {
- return !(pixel.a < alphaEpsilon<T>() &&
- (pixel.r > 0.0 ||
- pixel.g > 0.0 ||
- pixel.b > 0.0));
+ return !(std::abs(pixel.a) < alphaEpsilon<T>() &&
+ (!qFuzzyIsNull(pixel.r) ||
+ !qFuzzyIsNull(pixel.g) ||
+ !qFuzzyIsNull(pixel.b)));
}
inline bool checkUnmultipliedColorsConsistent(const Rgba<T> &mult) const {
- const T alpha = pixel.a;
+ const T alpha = std::abs(pixel.a);
- return abs(alpha) >= alphaNoiseThreshold<T>() ||
- (pixel.r * alpha == mult.r &&
- pixel.g * alpha == mult.g &&
- pixel.b * alpha == mult.b);
+ return alpha >= alphaNoiseThreshold<T>() ||
+ (qFuzzyCompare(T(pixel.r * alpha), mult.r) &&
+ qFuzzyCompare(T(pixel.g * alpha), mult.g) &&
+ qFuzzyCompare(T(pixel.b * alpha), mult.b));
}
- inline void setUnmultiplied(const Rgba<T> &mult, qreal newAlpha) {
- pixel.r = mult.r / newAlpha;
- pixel.g = mult.g / newAlpha;
- pixel.b = mult.b / newAlpha;
+ inline void setUnmultiplied(const Rgba<T> &mult, T newAlpha) {
+ const T absoluteAlpha = std::abs(newAlpha);
+
+ pixel.r = mult.r / absoluteAlpha;
+ pixel.g = mult.g / absoluteAlpha;
+ pixel.b = mult.b / absoluteAlpha;
pixel.a = newAlpha;
}
Rgba<T> &pixel;
};
template <typename T>
struct GrayPixelWrapper
{
typedef T channel_type;
typedef typename KoGrayTraits<T>::Pixel pixel_type;
GrayPixelWrapper(pixel_type &_pixel) : pixel(_pixel) {}
inline T alpha() const {
return pixel.alpha;
}
inline bool checkMultipliedColorsConsistent() const {
- return !(pixel.alpha < alphaEpsilon<T>() &&
- pixel.gray > 0.0);
+ return !(std::abs(pixel.alpha) < alphaEpsilon<T>() &&
+ !qFuzzyIsNull(pixel.gray));
}
inline bool checkUnmultipliedColorsConsistent(const pixel_type &mult) const {
- const T alpha = pixel.alpha;
+ const T alpha = std::abs(pixel.alpha);
return alpha >= alphaNoiseThreshold<T>() ||
- pixel.gray * alpha == mult.gray;
+ qFuzzyCompare(T(pixel.gray * alpha), mult.gray);
}
- inline void setUnmultiplied(const pixel_type &mult, qreal newAlpha) {
- pixel.gray = mult.gray / newAlpha;
+ inline void setUnmultiplied(const pixel_type &mult, T newAlpha) {
+ const T absoluteAlpha = std::abs(newAlpha);
+
+ pixel.gray = mult.gray / absoluteAlpha;
pixel.alpha = newAlpha;
}
pixel_type &pixel;
};
template <class WrapperType>
void EXRConverter::Private::unmultiplyAlpha(typename WrapperType::pixel_type *pixel)
{
typedef typename WrapperType::pixel_type pixel_type;
typedef typename WrapperType::channel_type channel_type;
WrapperType srcPixel(*pixel);
if (!srcPixel.checkMultipliedColorsConsistent()) {
channel_type newAlpha = srcPixel.alpha();
pixel_type __dstPixelData;
WrapperType dstPixel(__dstPixelData);
/**
* Division by a tiny alpha may result in an overflow of half
* value. That is why we use safe iterational approach.
*/
while (1) {
dstPixel.setUnmultiplied(srcPixel.pixel, newAlpha);
if (dstPixel.checkUnmultipliedColorsConsistent(srcPixel.pixel)) {
break;
}
newAlpha += alphaEpsilon<channel_type>();
alphaWasModified = true;
}
*pixel = dstPixel.pixel;
} else if (srcPixel.alpha() > 0.0) {
srcPixel.setUnmultiplied(srcPixel.pixel, srcPixel.alpha());
}
}
template <typename T, typename Pixel, int size, int alphaPos>
void multiplyAlpha(Pixel *pixel)
{
if (alphaPos >= 0) {
T alpha = pixel->data[alphaPos];
if (alpha > 0.0) {
for (int i = 0; i < size; ++i) {
if (i != alphaPos) {
pixel->data[i] *= alpha;
}
}
pixel->data[alphaPos] = alpha;
}
}
}
template<typename _T_>
void EXRConverter::Private::decodeData4(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype)
{
typedef Rgba<_T_> Rgba;
QVector<Rgba> pixels(width * height);
bool hasAlpha = info.channelMap.contains("A");
Imf::FrameBuffer frameBuffer;
Rgba* frameBufferData = (pixels.data()) - xstart - ystart * width;
frameBuffer.insert(info.channelMap["R"].toLatin1().constData(),
Imf::Slice(ptype, (char *) &frameBufferData->r,
sizeof(Rgba) * 1,
sizeof(Rgba) * width));
frameBuffer.insert(info.channelMap["G"].toLatin1().constData(),
Imf::Slice(ptype, (char *) &frameBufferData->g,
sizeof(Rgba) * 1,
sizeof(Rgba) * width));
frameBuffer.insert(info.channelMap["B"].toLatin1().constData(),
Imf::Slice(ptype, (char *) &frameBufferData->b,
sizeof(Rgba) * 1,
sizeof(Rgba) * width));
if (hasAlpha) {
frameBuffer.insert(info.channelMap["A"].toLatin1().constData(),
Imf::Slice(ptype, (char *) &frameBufferData->a,
sizeof(Rgba) * 1,
sizeof(Rgba) * width));
}
file.setFrameBuffer(frameBuffer);
file.readPixels(ystart, height + ystart - 1);
Rgba *rgba = pixels.data();
QRect paintRegion(xstart, ystart, width, height);
KisSequentialIterator it(layer->paintDevice(), paintRegion);
while (it.nextPixel()) {
if (hasAlpha) {
unmultiplyAlpha<RgbPixelWrapper<_T_> >(rgba);
}
typename KoRgbTraits<_T_>::Pixel* dst = reinterpret_cast<typename KoRgbTraits<_T_>::Pixel*>(it.rawData());
dst->red = rgba->r;
dst->green = rgba->g;
dst->blue = rgba->b;
if (hasAlpha) {
dst->alpha = rgba->a;
} else {
dst->alpha = 1.0;
}
++rgba;
}
}
template<typename _T_>
void EXRConverter::Private::decodeData1(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype)
{
typedef typename GrayPixelWrapper<_T_>::channel_type channel_type;
typedef typename GrayPixelWrapper<_T_>::pixel_type pixel_type;
KIS_ASSERT_RECOVER_RETURN(
layer->paintDevice()->colorSpace()->colorModelId() == GrayAColorModelID);
QVector<pixel_type> pixels(width * height);
Q_ASSERT(info.channelMap.contains("G"));
dbgFile << "G -> " << info.channelMap["G"];
bool hasAlpha = info.channelMap.contains("A");
dbgFile << "Has Alpha:" << hasAlpha;
Imf::FrameBuffer frameBuffer;
pixel_type* frameBufferData = (pixels.data()) - xstart - ystart * width;
frameBuffer.insert(info.channelMap["G"].toLatin1().constData(),
Imf::Slice(ptype, (char *) &frameBufferData->gray,
sizeof(pixel_type) * 1,
sizeof(pixel_type) * width));
if (hasAlpha) {
frameBuffer.insert(info.channelMap["A"].toLatin1().constData(),
Imf::Slice(ptype, (char *) &frameBufferData->alpha,
sizeof(pixel_type) * 1,
sizeof(pixel_type) * width));
}
file.setFrameBuffer(frameBuffer);
file.readPixels(ystart, height + ystart - 1);
pixel_type *srcPtr = pixels.data();
QRect paintRegion(xstart, ystart, width, height);
KisSequentialIterator it(layer->paintDevice(), paintRegion);
do {
if (hasAlpha) {
unmultiplyAlpha<GrayPixelWrapper<_T_> >(srcPtr);
}
pixel_type* dstPtr = reinterpret_cast<pixel_type*>(it.rawData());
dstPtr->gray = srcPtr->gray;
dstPtr->alpha = hasAlpha ? srcPtr->alpha : channel_type(1.0);
++srcPtr;
} while (it.nextPixel());
}
bool recCheckGroup(const ExrGroupLayerInfo& group, QStringList list, int idx1, int idx2)
{
if (idx1 > idx2) return true;
if (group.name == list[idx2]) {
return recCheckGroup(*group.parent, list, idx1, idx2 - 1);
}
return false;
}
ExrGroupLayerInfo* searchGroup(QList<ExrGroupLayerInfo>* groups, QStringList list, int idx1, int idx2)
{
if (idx1 > idx2) {
return 0;
}
// Look for the group
for (int i = 0; i < groups->size(); ++i) {
if (recCheckGroup(groups->at(i), list, idx1, idx2)) {
return &(*groups)[i];
}
}
// Create the group
ExrGroupLayerInfo info;
info.name = list.at(idx2);
info.parent = searchGroup(groups, list, idx1, idx2 - 1);
groups->append(info);
return &groups->last();
}
QDomDocument EXRConverter::Private::loadExtraLayersInfo(const Imf::Header &header)
{
const Imf::StringAttribute *layersInfoAttribute =
header.findTypedAttribute<Imf::StringAttribute>(EXR_KRITA_LAYERS);
if (!layersInfoAttribute) return QDomDocument();
QString layersInfoString = QString::fromUtf8(layersInfoAttribute->value().c_str());
QDomDocument doc;
doc.setContent(layersInfoString);
return doc;
}
bool EXRConverter::Private::checkExtraLayersInfoConsistent(const QDomDocument &doc, std::set<std::string> exrLayerNames)
{
std::set<std::string> extraInfoLayers;
QDomElement root = doc.documentElement();
KIS_ASSERT_RECOVER(!root.isNull() && root.hasChildNodes()) { return false; };
QDomElement el = root.firstChildElement();
while(!el.isNull()) {
KIS_ASSERT_RECOVER(el.hasAttribute(EXR_NAME)) { return false; };
QString layerName = el.attribute(EXR_NAME).toUtf8();
if (layerName != QString(HDR_LAYER)) {
extraInfoLayers.insert(el.attribute(EXR_NAME).toUtf8().constData());
}
el = el.nextSiblingElement();
}
bool result = (extraInfoLayers == exrLayerNames);
if (!result) {
dbgKrita << "WARINING: Krita EXR extra layers info is inconsistent!";
dbgKrita << ppVar(extraInfoLayers.size()) << ppVar(exrLayerNames.size());
std::set<std::string>::const_iterator it1 = extraInfoLayers.begin();
std::set<std::string>::const_iterator it2 = exrLayerNames.begin();
std::set<std::string>::const_iterator end1 = extraInfoLayers.end();
for (; it1 != end1; ++it1, ++it2) {
dbgKrita << it1->c_str() << it2->c_str();
}
}
return result;
}
KisImageBuilder_Result EXRConverter::decode(const QString &filename)
{
Imf::InputFile file(QFile::encodeName(filename));
Imath::Box2i dw = file.header().dataWindow();
Imath::Box2i displayWindow = file.header().displayWindow();
int width = dw.max.x - dw.min.x + 1;
int height = dw.max.y - dw.min.y + 1;
int dx = dw.min.x;
int dy = dw.min.y;
// Display the attributes of a file
for (Imf::Header::ConstIterator it = file.header().begin();
it != file.header().end(); ++it) {
dbgFile << "Attribute: " << it.name() << " type: " << it.attribute().typeName();
}
// fetch Krita's extra layer info, which might have been stored previously
QDomDocument extraLayersInfo = d->loadExtraLayersInfo(file.header());
// Construct the list of LayerInfo
QList<ExrPaintLayerInfo> informationObjects;
QList<ExrGroupLayerInfo> groups;
ImageType imageType = IT_UNKNOWN;
const Imf::ChannelList &channels = file.header().channels();
std::set<std::string> layerNames;
channels.layers(layerNames);
if (!extraLayersInfo.isNull() &&
!d->checkExtraLayersInfoConsistent(extraLayersInfo, layerNames)) {
// it is inconsistent anyway
extraLayersInfo = QDomDocument();
}
// Check if there are A, R, G, B channels
dbgFile << "Checking for ARGB channels, they can occur in single-layer _or_ multi-layer images:";
ExrPaintLayerInfo info;
bool topLevelRGBFound = false;
info.name = HDR_LAYER;
QStringList topLevelChannelNames = QStringList() << "A" << "R" << "G" << "B"
<< ".A" << ".R" << ".G" << ".B"
<< "A." << "R." << "G." << "B."
<< "A." << "R." << "G." << "B."
<< ".alpha" << ".red" << ".green" << ".blue";
for (Imf::ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {
const Imf::Channel &channel = i.channel();
dbgFile << "Channel name = " << i.name() << " type = " << channel.type;
QString qname = i.name();
if (topLevelChannelNames.contains(qname)) {
topLevelRGBFound = true;
dbgFile << "Found top-level channel" << qname;
info.channelMap[qname] = qname;
info.updateImageType(imfTypeToKisType(channel.type));
}
// Channel names that don't contain a "." or that contain a
// "." only at the beginning or at the end are not considered
// to be part of any layer.
else if (!qname.contains('.')
|| !qname.mid(1).contains('.')
|| !qname.left(qname.size() - 1).contains('.')) {
warnFile << "Found a top-level channel that is not part of the rendered image" << qname << ". Krita will not load this channel.";
}
}
if (topLevelRGBFound) {
dbgFile << "Toplevel layer" << info.name << ":Image type:" << imageType << "Layer type" << info.imageType;
informationObjects.push_back(info);
imageType = info.imageType;
}
dbgFile << "Extra layers:" << layerNames.size();
for (std::set<std::string>::const_iterator i = layerNames.begin();i != layerNames.end(); ++i) {
info = ExrPaintLayerInfo();
dbgFile << "layer name = " << i->c_str();
info.name = i->c_str();
Imf::ChannelList::ConstIterator layerBegin, layerEnd;
channels.channelsInLayer(*i, layerBegin, layerEnd);
for (Imf::ChannelList::ConstIterator j = layerBegin;
j != layerEnd; ++j) {
const Imf::Channel &channel = j.channel();
info.updateImageType(imfTypeToKisType(channel.type));
QString qname = j.name();
QStringList list = qname.split('.');
QString layersuffix = list.last();
dbgFile << "\tchannel " << j.name() << "suffix" << layersuffix << " type = " << channel.type;
// Nuke writes the channels for sublayers as .red instead of .R, so convert those.
// See https://bugs.kde.org/show_bug.cgi?id=393771
if (topLevelChannelNames.contains("." + layersuffix)) {
layersuffix = layersuffix.at(0).toUpper();
}
dbgFile << "\t\tsuffix" << layersuffix;
if (list.size() > 1) {
info.name = list[list.size()-2];
info.parent = searchGroup(&groups, list, 0, list.size() - 3);
}
info.channelMap[layersuffix] = qname;
}
if (info.imageType != IT_UNKNOWN && info.imageType != IT_UNSUPPORTED) {
informationObjects.push_back(info);
if (imageType < info.imageType) {
imageType = info.imageType;
}
}
}
dbgFile << "File has" << informationObjects.size() << "layer(s)";
// Set the colorspaces
for (int i = 0; i < informationObjects.size(); ++i) {
ExrPaintLayerInfo& info = informationObjects[i];
QString modelId;
if (info.channelMap.size() == 1) {
modelId = GrayAColorModelID.id();
QString key = info.channelMap.begin().key();
if (key != "G") {
info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(key, "G"));
QString channel = info.channelMap.begin().value();
info.channelMap.clear();
info.channelMap["G"] = channel;
}
}
else if (info.channelMap.size() == 2) {
modelId = GrayAColorModelID.id();
QMap<QString,QString>::const_iterator it = info.channelMap.constBegin();
QMap<QString,QString>::const_iterator end = info.channelMap.constEnd();
QString failingChannelKey;
for (; it != end; ++it) {
if (it.key() != "G" && it.key() != "A") {
failingChannelKey = it.key();
break;
}
}
info.remappedChannels.push_back(
ExrPaintLayerInfo::Remap(failingChannelKey, "G"));
QString failingChannelValue = info.channelMap[failingChannelKey];
info.channelMap.remove(failingChannelKey);
info.channelMap["G"] = failingChannelValue;
}
else if (info.channelMap.size() == 3 || info.channelMap.size() == 4) {
if (info.channelMap.contains("R") && info.channelMap.contains("G") && info.channelMap.contains("B")) {
modelId = RGBAColorModelID.id();
}
else if (info.channelMap.contains("X") && info.channelMap.contains("Y") && info.channelMap.contains("Z")) {
modelId = XYZAColorModelID.id();
QMap<QString, QString> newChannelMap;
if (info.channelMap.contains("W")) {
newChannelMap["A"] = info.channelMap["W"];
info.remappedChannels.push_back(ExrPaintLayerInfo::Remap("W", "A"));
info.remappedChannels.push_back(ExrPaintLayerInfo::Remap("X", "X"));
info.remappedChannels.push_back(ExrPaintLayerInfo::Remap("Y", "Y"));
info.remappedChannels.push_back(ExrPaintLayerInfo::Remap("Z", "Z"));
} else if (info.channelMap.contains("A")) {
newChannelMap["A"] = info.channelMap["A"];
}
// The decode function expect R, G, B in the channel map
newChannelMap["B"] = info.channelMap["X"];
newChannelMap["G"] = info.channelMap["Y"];
newChannelMap["R"] = info.channelMap["Z"];
info.channelMap = newChannelMap;
}
else {
modelId = RGBAColorModelID.id();
QMap<QString, QString> newChannelMap;
QMap<QString, QString>::iterator it = info.channelMap.begin();
newChannelMap["R"] = it.value();
info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(it.key(), "R"));
++it;
newChannelMap["G"] = it.value();
info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(it.key(), "G"));
++it;
newChannelMap["B"] = it.value();
info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(it.key(), "B"));
if (info.channelMap.size() == 4) {
++it;
newChannelMap["A"] = it.value();
info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(it.key(), "A"));
}
info.channelMap = newChannelMap;
}
}
else {
dbgFile << info.name << "has" << info.channelMap.size() << "channels, and we don't know what to do.";
}
if (!modelId.isEmpty()) {
info.colorSpace = kisTypeToColorSpace(modelId, info.imageType);
}
}
// Get colorspace
dbgFile << "Image type = " << imageType;
const KoColorSpace* colorSpace = kisTypeToColorSpace(RGBAColorModelID.id(), imageType);
if (!colorSpace) return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
dbgFile << "Colorspace: " << colorSpace->name();
// Set the colorspace on all groups
for (int i = 0; i < groups.size(); ++i) {
ExrGroupLayerInfo& info = groups[i];
info.colorSpace = colorSpace;
}
// Create the image
// Make sure the created image is the same size as the displayWindow since
// the dataWindow can be cropped in some cases.
int displayWidth = displayWindow.max.x - displayWindow.min.x + 1;
int displayHeight = displayWindow.max.y - displayWindow.min.y + 1;
d->image = new KisImage(d->doc->createUndoStore(), displayWidth, displayHeight, colorSpace, "");
if (!d->image) {
return KisImageBuilder_RESULT_FAILURE;
}
/**
* EXR semi-transparent images are expected to be rendered on
* black to ensure correctness of the light model
*/
d->image->setDefaultProjectionColor(KoColor(Qt::black, colorSpace));
// Create group layers
for (int i = 0; i < groups.size(); ++i) {
ExrGroupLayerInfo& info = groups[i];
Q_ASSERT(info.parent == 0 || info.parent->groupLayer);
KisGroupLayerSP groupLayerParent = (info.parent) ? info.parent->groupLayer : d->image->rootLayer();
info.groupLayer = new KisGroupLayer(d->image, info.name, OPACITY_OPAQUE_U8);
d->image->addNode(info.groupLayer, groupLayerParent);
}
// Load the layers
for (int i = informationObjects.size() - 1; i >= 0; --i) {
ExrPaintLayerInfo& info = informationObjects[i];
if (info.colorSpace) {
dbgFile << "Decoding " << info.name << " with " << info.channelMap.size() << " channels, and color space " << info.colorSpace->id();
KisPaintLayerSP layer = new KisPaintLayer(d->image, info.name, OPACITY_OPAQUE_U8, info.colorSpace);
layer->setCompositeOpId(COMPOSITE_OVER);
if (!layer) {
return KisImageBuilder_RESULT_FAILURE;
}
switch (info.channelMap.size()) {
case 1:
case 2:
// Decode the data
switch (info.imageType) {
case IT_FLOAT16:
d->decodeData1<half>(file, info, layer, width, dx, dy, height, Imf::HALF);
break;
case IT_FLOAT32:
d->decodeData1<float>(file, info, layer, width, dx, dy, height, Imf::FLOAT);
break;
case IT_UNKNOWN:
case IT_UNSUPPORTED:
qFatal("Impossible error");
}
break;
case 3:
case 4:
// Decode the data
switch (info.imageType) {
case IT_FLOAT16:
d->decodeData4<half>(file, info, layer, width, dx, dy, height, Imf::HALF);
break;
case IT_FLOAT32:
d->decodeData4<float>(file, info, layer, width, dx, dy, height, Imf::FLOAT);
break;
case IT_UNKNOWN:
case IT_UNSUPPORTED:
qFatal("Impossible error");
}
break;
default:
qFatal("Invalid number of channels: %i", info.channelMap.size());
}
// Check if should set the channels
if (!info.remappedChannels.isEmpty()) {
QList<KisMetaData::Value> values;
Q_FOREACH (const ExrPaintLayerInfo::Remap& remap, info.remappedChannels) {
QMap<QString, KisMetaData::Value> map;
map["original"] = KisMetaData::Value(remap.original);
map["current"] = KisMetaData::Value(remap.current);
values.append(map);
}
layer->metaData()->addEntry(KisMetaData::Entry(KisMetaData::SchemaRegistry::instance()->create("http://krita.org/exrchannels/1.0/" , "exrchannels"), "channelsmap", values));
}
// Add the layer
KisGroupLayerSP groupLayerParent = (info.parent) ? info.parent->groupLayer : d->image->rootLayer();
d->image->addNode(layer, groupLayerParent);
} else {
dbgFile << "No decoding " << info.name << " with " << info.channelMap.size() << " channels, and lack of a color space";
}
}
// Set projectionColor to opaque
d->image->setDefaultProjectionColor(KoColor(Qt::transparent, colorSpace));
// After reading the image, notify the user about changed alpha.
if (d->alphaWasModified) {
QString msg =
i18nc("@info",
"The image contains pixels with zero alpha channel and non-zero "
"color channels. Krita has modified those pixels to have "
"at least some alpha. The initial values will <i>not</i> "
"be reverted on saving the image back."
"<br/><br/>"
"This will hardly make any visual difference just keep it in mind.");
if (d->showNotifications) {
QMessageBox::information(0, i18nc("@title:window", "EXR image has been modified"), msg);
} else {
warnKrita << "WARNING:" << msg;
}
}
if (!extraLayersInfo.isNull()) {
KisExrLayersSorter sorter(extraLayersInfo, d->image);
}
return KisImageBuilder_RESULT_OK;
}
KisImageBuilder_Result EXRConverter::buildImage(const QString &filename)
{
return decode(filename);
}
KisImageSP EXRConverter::image()
{
return d->image;
}
QString EXRConverter::errorMessage() const
{
return d->errorMessage;
}
template<typename _T_, int size>
struct ExrPixel_ {
_T_ data[size];
};
class Encoder
{
public:
virtual ~Encoder() {}
virtual void prepareFrameBuffer(Imf::FrameBuffer*, int line) = 0;
virtual void encodeData(int line) = 0;
};
template<typename _T_, int size, int alphaPos>
class EncoderImpl : public Encoder
{
public:
EncoderImpl(Imf::OutputFile* _file, const ExrPaintLayerSaveInfo* _info, int width) : file(_file), info(_info), pixels(width), m_width(width) {}
~EncoderImpl() override {}
void prepareFrameBuffer(Imf::FrameBuffer*, int line) override;
void encodeData(int line) override;
private:
typedef ExrPixel_<_T_, size> ExrPixel;
Imf::OutputFile* file;
const ExrPaintLayerSaveInfo* info;
QVector<ExrPixel> pixels;
int m_width;
};
template<typename _T_, int size, int alphaPos>
void EncoderImpl<_T_, size, alphaPos>::prepareFrameBuffer(Imf::FrameBuffer* frameBuffer, int line)
{
int xstart = 0;
int ystart = 0;
ExrPixel* frameBufferData = (pixels.data()) - xstart - (ystart + line) * m_width;
for (int k = 0; k < size; ++k) {
frameBuffer->insert(info->channels[k].toUtf8(),
Imf::Slice(info->pixelType, (char *) &frameBufferData->data[k],
sizeof(ExrPixel) * 1,
sizeof(ExrPixel) * m_width));
}
}
template<typename _T_, int size, int alphaPos>
void EncoderImpl<_T_, size, alphaPos>::encodeData(int line)
{
ExrPixel *rgba = pixels.data();
KisHLineIteratorSP it = info->layer->paintDevice()->createHLineIteratorNG(0, line, m_width);
do {
const _T_* dst = reinterpret_cast < const _T_* >(it->oldRawData());
for (int i = 0; i < size; ++i) {
rgba->data[i] = dst[i];
}
if (alphaPos != -1) {
multiplyAlpha<_T_, ExrPixel, size, alphaPos>(rgba);
}
++rgba;
} while (it->nextPixel());
}
Encoder* encoder(Imf::OutputFile& file, const ExrPaintLayerSaveInfo& info, int width)
{
dbgFile << "Create encoder for" << info.layer->name() << info.channels << info.layer->colorSpace()->channelCount();
switch (info.layer->colorSpace()->channelCount()) {
case 1: {
if (info.layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
Q_ASSERT(info.pixelType == Imf::HALF);
return new EncoderImpl < half, 1, -1 > (&file, &info, width);
} else if (info.layer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) {
Q_ASSERT(info.pixelType == Imf::FLOAT);
return new EncoderImpl < float, 1, -1 > (&file, &info, width);
}
break;
}
case 2: {
if (info.layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
Q_ASSERT(info.pixelType == Imf::HALF);
return new EncoderImpl<half, 2, 1>(&file, &info, width);
} else if (info.layer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) {
Q_ASSERT(info.pixelType == Imf::FLOAT);
return new EncoderImpl<float, 2, 1>(&file, &info, width);
}
break;
}
case 4: {
if (info.layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
Q_ASSERT(info.pixelType == Imf::HALF);
return new EncoderImpl<half, 4, 3>(&file, &info, width);
} else if (info.layer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) {
Q_ASSERT(info.pixelType == Imf::FLOAT);
return new EncoderImpl<float, 4, 3>(&file, &info, width);
}
break;
}
default:
qFatal("Impossible error");
}
return 0;
}
void encodeData(Imf::OutputFile& file, const QList<ExrPaintLayerSaveInfo>& informationObjects, int width, int height)
{
QList<Encoder*> encoders;
Q_FOREACH (const ExrPaintLayerSaveInfo& info, informationObjects) {
encoders.push_back(encoder(file, info, width));
}
for (int y = 0; y < height; ++y) {
Imf::FrameBuffer frameBuffer;
Q_FOREACH (Encoder* encoder, encoders) {
encoder->prepareFrameBuffer(&frameBuffer, y);
}
file.setFrameBuffer(frameBuffer);
Q_FOREACH (Encoder* encoder, encoders) {
encoder->encodeData(y);
}
file.writePixels(1);
}
qDeleteAll(encoders);
}
KisImageBuilder_Result EXRConverter::buildFile(const QString &filename, KisPaintLayerSP layer)
{
if (!layer)
return KisImageBuilder_RESULT_INVALID_ARG;
KisImageSP image = layer->image();
if (!image)
return KisImageBuilder_RESULT_EMPTY;
// Make the header
qint32 height = image->height();
qint32 width = image->width();
Imf::Header header(width, height);
Imf::PixelType pixelType = Imf::NUM_PIXELTYPES;
if (layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
pixelType = Imf::HALF;
}
else if(layer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) {
pixelType = Imf::FLOAT;
}
else {
const KoColorSpace *cs = 0;
if (layer->colorSpace()->colorModelId() == GrayAColorModelID) {
cs = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Float16BitsColorDepthID.id());
}
else {
cs = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float16BitsColorDepthID.id());
}
image->convertImageColorSpace(cs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
pixelType = Imf::HALF;
}
header.channels().insert("R", Imf::Channel(pixelType));
header.channels().insert("G", Imf::Channel(pixelType));
header.channels().insert("B", Imf::Channel(pixelType));
header.channels().insert("A", Imf::Channel(pixelType));
ExrPaintLayerSaveInfo info;
info.layer = layer;
info.channels.push_back("R");
info.channels.push_back("G");
info.channels.push_back("B");
info.channels.push_back("A");
info.pixelType = pixelType;
// Open file for writing
Imf::OutputFile file(QFile::encodeName(filename), header);
QList<ExrPaintLayerSaveInfo> informationObjects;
informationObjects.push_back(info);
encodeData(file, informationObjects, width, height);
return KisImageBuilder_RESULT_OK;
}
QString remap(const QMap<QString, QString>& current2original, const QString& current)
{
if (current2original.contains(current)) {
return current2original[current];
}
return current;
}
void EXRConverter::Private::makeLayerNamesUnique(QList<ExrPaintLayerSaveInfo>& informationObjects)
{
typedef QMultiMap<QString, QList<ExrPaintLayerSaveInfo>::iterator> NamesMap;
NamesMap namesMap;
{
QList<ExrPaintLayerSaveInfo>::iterator it = informationObjects.begin();
QList<ExrPaintLayerSaveInfo>::iterator end = informationObjects.end();
for (; it != end; ++it) {
namesMap.insert(it->name, it);
}
}
Q_FOREACH (const QString &key, namesMap.keys()) {
if (namesMap.count(key) > 1) {
KIS_ASSERT_RECOVER(key.endsWith(".")) { continue; }
QString strippedName = key.left(key.size() - 1); // trim the ending dot
int nameCounter = 0;
NamesMap::iterator it = namesMap.find(key);
NamesMap::iterator end = namesMap.end();
for (; it != end; ++it) {
QString newName =
QString("%1_%2.")
.arg(strippedName)
.arg(nameCounter++);
it.value()->name = newName;
QList<QString>::iterator channelsIt = it.value()->channels.begin();
QList<QString>::iterator channelsEnd = it.value()->channels.end();
for (; channelsIt != channelsEnd; ++channelsIt) {
channelsIt->replace(key, newName);
}
}
}
}
}
void EXRConverter::Private::recBuildPaintLayerSaveInfo(QList<ExrPaintLayerSaveInfo>& informationObjects, const QString& name, KisGroupLayerSP parent)
{
QSet<KisNodeSP> layersNotSaved;
for (uint i = 0; i < parent->childCount(); ++i) {
KisNodeSP node = parent->at(i);
if (KisPaintLayerSP paintLayer = dynamic_cast<KisPaintLayer*>(node.data())) {
QMap<QString, QString> current2original;
if (paintLayer->metaData()->containsEntry(KisMetaData::SchemaRegistry::instance()->create("http://krita.org/exrchannels/1.0/" , "exrchannels"), "channelsmap")) {
const KisMetaData::Entry& entry = paintLayer->metaData()->getEntry(KisMetaData::SchemaRegistry::instance()->create("http://krita.org/exrchannels/1.0/" , "exrchannels"), "channelsmap");
QList< KisMetaData::Value> values = entry.value().asArray();
Q_FOREACH (const KisMetaData::Value& value, values) {
QMap<QString, KisMetaData::Value> map = value.asStructure();
if (map.contains("original") && map.contains("current")) {
current2original[map["current"].toString()] = map["original"].toString();
}
}
}
ExrPaintLayerSaveInfo info;
info.name = name + paintLayer->name() + '.';
info.layer = paintLayer;
if (info.name == QString(HDR_LAYER) + ".") {
info.channels.push_back("R");
info.channels.push_back("G");
info.channels.push_back("B");
info.channels.push_back("A");
}
else {
if (paintLayer->colorSpace()->colorModelId() == RGBAColorModelID) {
info.channels.push_back(info.name + remap(current2original, "R"));
info.channels.push_back(info.name + remap(current2original, "G"));
info.channels.push_back(info.name + remap(current2original, "B"));
info.channels.push_back(info.name + remap(current2original, "A"));
}
else if (paintLayer->colorSpace()->colorModelId() == GrayAColorModelID) {
info.channels.push_back(info.name + remap(current2original, "G"));
info.channels.push_back(info.name + remap(current2original, "A"));
}
else if (paintLayer->colorSpace()->colorModelId() == GrayColorModelID) {
info.channels.push_back(info.name + remap(current2original, "G"));
}
else if (paintLayer->colorSpace()->colorModelId() == XYZAColorModelID) {
info.channels.push_back(info.name + remap(current2original, "X"));
info.channels.push_back(info.name + remap(current2original, "Y"));
info.channels.push_back(info.name + remap(current2original, "Z"));
info.channels.push_back(info.name + remap(current2original, "A"));
}
}
if (paintLayer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
info.pixelType = Imf::HALF;
}
else if (paintLayer->colorSpace()->colorDepthId() == Float32BitsColorDepthID) {
info.pixelType = Imf::FLOAT;
}
else {
info.pixelType = Imf::NUM_PIXELTYPES;
}
if (info.pixelType < Imf::NUM_PIXELTYPES) {
dbgFile << "Going to save layer" << info.name;
informationObjects.push_back(info);
}
else {
warnFile << "Will not save layer" << info.name;
layersNotSaved << node;
}
}
else if (KisGroupLayerSP groupLayer = dynamic_cast<KisGroupLayer*>(node.data())) {
recBuildPaintLayerSaveInfo(informationObjects, name + groupLayer->name() + '.', groupLayer);
}
else {
/**
* The EXR can store paint and group layers only. The rest will
* go to /dev/null :(
*/
layersNotSaved.insert(node);
}
}
if (!layersNotSaved.isEmpty()) {
reportLayersNotSaved(layersNotSaved);
}
}
void EXRConverter::Private::reportLayersNotSaved(const QSet<KisNodeSP> &layersNotSaved)
{
QString layersList;
QTextStream textStream(&layersList);
textStream.setCodec("UTF-8");
Q_FOREACH (KisNodeSP node, layersNotSaved) {
textStream << "<li>" << i18nc("@item:unsupported-node-message", "%1 (type: \"%2\")", node->name(), node->metaObject()->className()) << "</li>";
}
QString msg =
i18nc("@info",
"<p>The following layers have a type that is not supported by EXR format:</p>"
"<r><ul>%1</ul></p>"
"<p><warning>these layers have <b>not</b> been saved to the final EXR file</warning></p>", layersList);
errorMessage = msg;
}
QString EXRConverter::Private::fetchExtraLayersInfo(QList<ExrPaintLayerSaveInfo>& informationObjects)
{
KIS_ASSERT_RECOVER_NOOP(!informationObjects.isEmpty());
if (informationObjects.size() == 1 && informationObjects[0].name == QString(HDR_LAYER) + ".") {
return QString();
}
QDomDocument doc("krita-extra-layers-info");
doc.appendChild(doc.createElement("root"));
QDomElement rootElement = doc.documentElement();
for (int i = 0; i < informationObjects.size(); i++) {
ExrPaintLayerSaveInfo &info = informationObjects[i];
quint32 unused;
KisSaveXmlVisitor visitor(doc, rootElement, unused, QString(), false);
QDomElement el = visitor.savePaintLayerAttributes(info.layer.data(), doc);
// cut the ending '.'
QString strippedName = info.name.left(info.name.size() - 1);
el.setAttribute(EXR_NAME, strippedName);
rootElement.appendChild(el);
}
return doc.toString();
}
KisImageBuilder_Result EXRConverter::buildFile(const QString &filename, KisGroupLayerSP layer, bool flatten)
{
if (!layer)
return KisImageBuilder_RESULT_INVALID_ARG;
KisImageSP image = layer->image();
if (!image)
return KisImageBuilder_RESULT_EMPTY;
qint32 height = image->height();
qint32 width = image->width();
Imf::Header header(width, height);
if (image->colorSpace()->colorDepthId() != Float16BitsColorDepthID && image->colorSpace()->colorDepthId() != Float32BitsColorDepthID) {
const KoColorSpace *cs = 0;
if (layer->colorSpace()->colorModelId() == GrayAColorModelID) {
cs = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Float16BitsColorDepthID.id());
}
else {
cs = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float16BitsColorDepthID.id());
}
image->convertImageColorSpace(cs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
}
if (flatten) {
image->waitForDone(); // This is to make sure we have a full image to project.
KisPaintDeviceSP pd = new KisPaintDevice(*image->projection());
KisPaintLayerSP l = new KisPaintLayer(image, "projection", OPACITY_OPAQUE_U8, pd);
return buildFile(filename, l);
}
else {
QList<ExrPaintLayerSaveInfo> informationObjects;
d->recBuildPaintLayerSaveInfo(informationObjects, "", layer);
if(informationObjects.isEmpty()) {
return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
}
d->makeLayerNamesUnique(informationObjects);
QByteArray extraLayersInfo = d->fetchExtraLayersInfo(informationObjects).toUtf8();
if (!extraLayersInfo.isNull()) {
header.insert(EXR_KRITA_LAYERS, Imf::StringAttribute(extraLayersInfo.constData()));
}
dbgFile << informationObjects.size() << " layers to save";
Q_FOREACH (const ExrPaintLayerSaveInfo& info, informationObjects) {
if (info.pixelType < Imf::NUM_PIXELTYPES) {
Q_FOREACH (const QString& channel, info.channels) {
dbgFile << channel << " " << info.pixelType;
header.channels().insert(channel.toUtf8().data(), Imf::Channel(info.pixelType));
}
}
}
// Open file for writing
Imf::OutputFile file(QFile::encodeName(filename), header);
encodeData(file, informationObjects, width, height);
return KisImageBuilder_RESULT_OK;
}
}
void EXRConverter::cancel()
{
warnKrita << "WARNING: Cancelling of an EXR loading is not supported!";
}
diff --git a/plugins/impex/exr/krita_exr.desktop b/plugins/impex/exr/krita_exr.desktop
index ed2565b3eb..c21e965c6b 100644
--- a/plugins/impex/exr/krita_exr.desktop
+++ b/plugins/impex/exr/krita_exr.desktop
@@ -1,124 +1,124 @@
[Desktop Entry]
Categories=Qt;KDE;Office;Graphics;
Exec=krita %F
GenericName=Application for Drawing and Handling of Images
GenericName[ar]=تطبيق لرسم الصور والتعامل معها
GenericName[bg]=Приложение за рисуване и обработка на изображения
GenericName[bs]=Aplikacija za crtanje i upravljanje slikom
GenericName[ca]=Aplicació per a dibuix i modificació d'imatges
GenericName[ca@valencia]=Aplicació per a dibuix i modificació d'imatges
GenericName[da]=Tegne- og billedbehandlingsprogram
GenericName[de]=Programm zum Zeichnen und Bearbeiten von Bildern
GenericName[el]=Εφαρμογή για επεξεργασία και χειρισμό εικόνων
GenericName[en_GB]=Application for Drawing and Handling of Images
GenericName[eo]=Aplikaĵo por Desegnado kaj Mastrumado de Bildoj
GenericName[es]=Aplicación para dibujo y manipulación de imágenes
GenericName[et]=Joonistamise ja pilditöötluse rakendus
GenericName[eu]=Irudiak marrazteko eta manipulatzeko aplikazioa
GenericName[fa]=کاربرد برای ترسیم و به کار بردن تصاویر
GenericName[fi]=Ohjelma kuvien piirtämiseen ja käsittelyyn
GenericName[fr]=Application pour dessiner et manipuler des images
GenericName[fy]=Aplikaasje om ôfbyldings mei te tekenjen en te bewurkjen
GenericName[ga]=Feidhmchlár le haghaidh Líníochta agus Láimhseála Íomhánna
GenericName[gl]=Aplicativo de debuxo e edición de imaxes
GenericName[he]=יישום לצביעה וניהול תמונות
GenericName[hi]=छवियों को ड्रा करने तथा उन्हें प्रबन्धित करने का अनुप्रयोग
GenericName[hne]=फोटू मन ल ड्रा करे अउ ओ मन ल प्रबन्धित करे के अनुपरयोग
GenericName[hu]=Rajzoló és képkezelő
GenericName[is]=Teikni og myndvinnsluforrit
GenericName[it]=Applicazione di disegno e gestione di immagini
GenericName[ja]=描画と画像操作のためのアプリケーション
GenericName[kk]=Кескінді салу және өңдеу бағдарламасы
GenericName[ko]=그림 그리기 및 처리 프로그램
GenericName[lv]=Programma zīmēšanai un attēlu apstrādei
GenericName[nb]=Program for tegning og bildehåndtering
GenericName[nds]=Programm för't Teken un Bildhanteren
GenericName[ne]=रेखाचित्र बनाउन र छविको ह्यान्डल गर्नका लागि अनुप्रयोग
GenericName[nl]=Toepassing om afbeeldingen te tekenen en te bewerken
GenericName[pl]=Program do rysowania i obróbki obrazów
GenericName[pt]=Aplicação de Desenho e Manipulação de Imagens
GenericName[pt_BR]=Aplicativo de desenho e manipulação de imagens
GenericName[ru]=Приложение для рисования и редактирования изображений
GenericName[sk]=Aplikácia na kresnenie a manilupáciu s obrázkami
GenericName[sl]=Program za risanje in rokovanje s slikami
GenericName[sv]=Program för att rita och hantera bilder
GenericName[ta]=பிம்பங்களை கையாளுதல் மற்றும் வரைதலுக்கான பயன்னாடு
GenericName[tr]=Çizim ve Resim İşleme Uygulaması
GenericName[uk]=Програма для малювання і обробки зображень
GenericName[uz]=Rasm chizish dasturi
GenericName[uz@cyrillic]=Расм чизиш дастури
GenericName[wa]=Programe po dessiner et apougnî des imådjes
GenericName[x-test]=xxApplication for Drawing and Handling of Imagesxx
-GenericName[zh_CN]=绘制和处理图像的应用程序
+GenericName[zh_CN]=用于绘制和处理图像的应用程序
GenericName[zh_TW]=繪圖與影像處理的應用程式
Icon=calligrakrita
MimeType=image/exr;
Name=Krita
Name[af]=Krita
Name[ar]=كريتا
Name[bg]=Krita
Name[br]=Krita
Name[bs]=Krita
Name[ca]=Krita
Name[ca@valencia]=Krita
Name[cs]=Krita
Name[cy]=Krita
Name[da]=Krita
Name[de]=Krita
Name[el]=Krita
Name[en_GB]=Krita
Name[eo]=Krita
Name[es]=Krita
Name[et]=Krita
Name[eu]=Krita
Name[fi]=Krita
Name[fr]=Krita
Name[fy]=Krita
Name[ga]=Krita
Name[gl]=Krita
Name[he]=Krita
Name[hi]=केरिता
Name[hne]=केरिता
Name[hr]=Krita
Name[hu]=Krita
Name[ia]=Krita
Name[is]=Krita
Name[it]=Krita
Name[ja]=Krita
Name[kk]=Krita
Name[ko]=Krita
Name[lt]=Krita
Name[lv]=Krita
Name[mr]=क्रिटा
Name[ms]=Krita
Name[nb]=Krita
Name[nds]=Krita
Name[ne]=क्रिता
Name[nl]=Krita
Name[pl]=Krita
Name[pt]=Krita
Name[pt_BR]=Krita
Name[ro]=Krita
Name[ru]=Krita
Name[se]=Krita
Name[sk]=Krita
Name[sl]=Krita
Name[sv]=Krita
Name[ta]=கிரிட்டா
Name[tg]=Krita
Name[tr]=Krita
Name[ug]=Krita
Name[uk]=Krita
Name[uz]=Krita
Name[uz@cyrillic]=Krita
Name[wa]=Krita
Name[xh]=Krita
Name[x-test]=xxKritaxx
Name[zh_CN]=Krita
Name[zh_TW]=Krita
StartupNotify=true
Terminal=false
Type=Application
X-KDE-SubstituteUID=false
X-KDE-Username=
NoDisplay=true
diff --git a/plugins/impex/gif/qgiflibhandler.cpp b/plugins/impex/gif/qgiflibhandler.cpp
index 6cfb109cf5..b3b367f6fe 100644
--- a/plugins/impex/gif/qgiflibhandler.cpp
+++ b/plugins/impex/gif/qgiflibhandler.cpp
@@ -1,362 +1,362 @@
/*
* Copyright (C) 2009 Shawn T. Rutledge (shawn.t.rutledge@gmail.com)
* Copyright (c) 2018 Boudewijn Rempt <boud@valdyas.org>
*
* 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
*
*/
#include "qgiflibhandler.h"
#include <QDebug>
#include <QVariant>
#include <gif_lib.h>
#include <string.h> // memset
#include <QPainter>
extern int _GifError;
static const int InterlacedOffset[] = { 0, 4, 2, 1 }; /* The way Interlaced image should */
static const int InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */
int doOutput(GifFileType* gif, const GifByteType * data, int i)
{
QIODevice* out = (QIODevice*)gif->UserData;
// qDebug("given %d bytes to write; device is writeable? %d", i, out->isWritable());
return out->write((const char*)data, i);
}
int doInput(GifFileType* gif, GifByteType* data, int i)
{
QIODevice* in = (QIODevice*)gif->UserData;
return in->read((char*)data, i);
}
QGIFLibHandler::QGIFLibHandler()
: QImageIOHandler()
{
}
bool QGIFLibHandler::canRead () const
{
if (canRead(device())) {
setFormat("gif");
return true;
}
return false;
}
bool QGIFLibHandler::read ( QImage * image )
{
// The contents of this function are based on gif2rgb.c, from the giflib source.
// qDebug("QGIFLibHandler::read into image with size %d x %d", image->size().width(), image->size().height());
int err;
GifFileType* gifFile = DGifOpen(device(), doInput, &err);
if (!gifFile) {
qWarning() << "Received error code" << err;
return false;
}
// qDebug("dimensions %d x %d", gifFile->SWidth, gifFile->SHeight);
*image = QImage(gifFile->SWidth, gifFile->SHeight, QImage::Format_Indexed8);
GifRecordType recordType;
ColorMapObject* ColorMap;
int i, row, imageNum = 0, topRow, leftCol, width, height;
int transColor = -1;
do
{
DGifGetRecordType(gifFile, &recordType);
switch (recordType)
{
case IMAGE_DESC_RECORD_TYPE:
if (DGifGetImageDesc(gifFile) == GIF_ERROR)
{
qWarning("QGIFLibHandler::read: error %d", gifFile->Error);
return false;
}
topRow = gifFile->Image.Top; /* Image Position relative to Screen. */
leftCol = gifFile->Image.Left;
width = gifFile->Image.Width;
height = gifFile->Image.Height;
- qDebug("Image %d at (%d, %d) [%dx%d]", ++imageNum, leftCol, topRow, width, height);
+ //qDebug("Image %d at (%d, %d) [%dx%d]", ++imageNum, leftCol, topRow, width, height);
if (gifFile->Image.Left + width > gifFile->SWidth ||
gifFile->Image.Top + height > gifFile->SHeight)
{
qWarning("Image %d is not confined to screen dimension, aborted.", imageNum);
return false;
}
// Pre-fill with background color
// qDebug("background color is at index %d", gifFile->SBackGroundColor);
image->fill(gifFile->SBackGroundColor);
// Now read the image data
if (gifFile->Image.Interlace)
{
/* Need to perform 4 passes on the images: */
for (i = 0; i < 4; i++)
for (row = topRow + InterlacedOffset[i]; row < topRow + height;
row += InterlacedJumps[i])
{
if (DGifGetLine(gifFile, image->scanLine(row), width) == GIF_ERROR)
{
qWarning("QGIFLibHandler::read: error %d", gifFile->Error);
return false;
}
// else
// qDebug("got row %d: %d %d %d %d %d %d %d %d ...", row,
// image->scanLine(row)[0], image->scanLine(row)[1], image->scanLine(row)[2], image->scanLine(row)[3],
// image->scanLine(row)[4], image->scanLine(row)[5], image->scanLine(row)[6], image->scanLine(row)[7]);
}
}
else
{
for (row = 0; row < height; row++)
{
if (DGifGetLine(gifFile, image->scanLine(row), width) == GIF_ERROR)
{
qWarning("QGIFLibHandler::read: error %d", gifFile->Error);
return false;
}
// else
// qDebug("got row %d: %d %d %d %d %d %d %d %d ...", row,
// image->scanLine(row)[0], image->scanLine(row)[1], image->scanLine(row)[2], image->scanLine(row)[3],
// image->scanLine(row)[4], image->scanLine(row)[5], image->scanLine(row)[6], image->scanLine(row)[7]);
}
}
break;
case EXTENSION_RECORD_TYPE:
{
int extCode;
GifByteType* extData;
/* Skip any extension blocks in file: */
if (DGifGetExtension(gifFile, &extCode, &extData) == GIF_ERROR)
{
qWarning("QGIFLibHandler::read: error %d", gifFile->Error);
return false;
}
while (extData != NULL)
{
int len = extData[0];
switch (extCode)
{
case GRAPHICS_EXT_FUNC_CODE: // Graphics control extension
// qDebug("graphics control: %x %x %x %x %x", extData[0], extData[1], extData[2], extData[3], extData[4]);
// Should be block size, packed fields, delay time,
// transparent color, block terminator
// see doc/gif89.txt in libgif source package
// If the trans bit is set in packed fields,
// then set the trans color to the one given
if (extData[1] & 0x01)
{
transColor = extData[3];
// qDebug("transparent color is at index %d", transColor);
/// @todo is it correct to override default fill color?
// image->fill(transColor);
}
break;
case COMMENT_EXT_FUNC_CODE:
{
QByteArray comment((char*)(extData + 1), len);
// qDebug("comment of len %d: \"%s\"", len, comment.constData());
image->setText("Description", comment);
}
break;
case PLAINTEXT_EXT_FUNC_CODE:
break;
}
if (DGifGetExtensionNext(gifFile, &extData) == GIF_ERROR)
{
qWarning("QGIFLibHandler::read: error %d", gifFile->Error);
return false;
}
}
}
break;
case TERMINATE_RECORD_TYPE:
break;
default:
break;
}
}
while (recordType != TERMINATE_RECORD_TYPE);
// BackGround = gifFile->SBackGroundColor;
ColorMap = (gifFile->Image.ColorMap
? gifFile->Image.ColorMap
: gifFile->SColorMap);
if (!ColorMap)
{
qWarning("QGIFLibHandler::read: Image does not have a colormap");
return false;
}
int ccount = ColorMap->ColorCount;
image->setColorCount(ccount);
for (i = 0; i < ccount; ++i)
{
GifColorType gifColor = ColorMap->Colors[i];
QRgb color = gifColor.Blue | (gifColor.Green << 8) | (gifColor.Red << 16);
// If this is not the transparent color,
// set the alpha to opaque.
if (i != transColor)
color |= 0xff << 24;
// qDebug("color %d: 0x%X", i, color);
image->setColor(i, color);
}
return true;
}
bool QGIFLibHandler::canRead(QIODevice *device)
{
if (!device) {
qWarning("QGIFLibHandler::canRead() called with no device");
return false;
}
char head[6];
if (device->peek(head, sizeof(head)) == sizeof(head))
return qstrncmp(head, "GIF87a", 6) == 0
|| qstrncmp(head, "GIF89a", 6) == 0;
return false;
}
bool QGIFLibHandler::write ( const QImage & image )
{
QImage toWrite(image);
/// @todo how to specify dithering method
if (toWrite.colorCount() == 0 || toWrite.colorCount() > 256)
toWrite = image.convertToFormat(QImage::Format_Indexed8);
QVector<QRgb> colorTable = toWrite.colorTable();
ColorMapObject cmap;
// colorCount must be a power of 2
int colorCount = 1 << GifBitSize(toWrite.colorCount());
cmap.ColorCount = colorCount;
cmap.BitsPerPixel = 8; /// @todo based on colorCount (or not? we did ask for Format_Indexed8, so the data is always 8-bit, right?)
GifColorType* colorValues = (GifColorType*)malloc(cmap.ColorCount * sizeof(GifColorType));
cmap.Colors = colorValues;
int c = 0;
for(; c < toWrite.colorCount(); ++c)
{
// qDebug("color %d has %02X%02X%02X", c, qRed(colorTable[c]), qGreen(colorTable[c]), qBlue(colorTable[c]));
colorValues[c].Red = qRed(colorTable[c]);
colorValues[c].Green = qGreen(colorTable[c]);
colorValues[c].Blue = qBlue(colorTable[c]);
}
// In case we had an actual number of colors that's not a power of 2,
// fill the rest with something (black perhaps).
for (; c < colorCount; ++c)
{
colorValues[c].Red = 0;
colorValues[c].Green = 0;
colorValues[c].Blue = 0;
}
/// @todo transparent GIFs (use alpha?)
/// @todo write to m_device
int err;
GifFileType *gif = EGifOpen(device(), doOutput, &err);
/// @todo how to specify which version, or decide based on features in use
// Because of this call, libgif is not re-entrant
EGifSetGifVersion(gif, true);
/// @todo how to specify background
if (EGifPutScreenDesc(gif, toWrite.width(), toWrite.height(), colorCount, 0, &cmap) == GIF_ERROR) {
qWarning("EGifPutScreenDesc returned error %d", gif->Error);
}
QVariant descText = option(QImageIOHandler::Description);
if (descText.type() == QVariant::String)
{
QString comment = descText.toString();
// Will be something like "Description: actual text" or just
// ": actual text", so remove everything leading up to and
// including the first colon and the space following it.
int idx = comment.indexOf(": ");
if (idx >= 0)
comment.remove(0, idx + 2);
// qDebug() << "comment:" << comment;
if (!comment.isEmpty())
EGifPutComment(gif, comment.toUtf8().constData());
}
// else
// qDebug("description is of qvariant type %d", descText.type());
/// @todo foreach of multiple images in an animation...
if (EGifPutImageDesc(gif, 0, 0, toWrite.width(), toWrite.height(), 0, &cmap) == GIF_ERROR)
qWarning("EGifPutImageDesc returned error %d", gif->Error);
int lc = toWrite.height();
int llen = toWrite.bytesPerLine();
// qDebug("will write %d lines, %d bytes each", lc, llen);
for (int l = 0; l < lc; ++l)
{
uchar* line = toWrite.scanLine(l);
if (EGifPutLine(gif, (GifPixelType*)line, llen) == GIF_ERROR)
{
int i = gif->Error;
qWarning("EGifPutLine returned error %d", i);
}
}
EGifCloseFile(gif, &err);
return true;
}
bool QGIFLibHandler::supportsOption ( ImageOption option ) const
{
// qDebug("supportsOption %d", option);
switch (option)
{
// These are relevant only for reading
case QImageIOHandler::ImageFormat:
case QImageIOHandler::Size:
// This is relevant for both reading and writing
case QImageIOHandler::Description:
return true;
break;
default:
return false;
}
}
void QGIFLibHandler::setOption ( ImageOption option, const QVariant & value )
{
// qDebug("setOption given option %d, variant of type %d", option, value.type());
if (option == QImageIOHandler::Description)
m_description = value.toString();
}
QVariant QGIFLibHandler::option( ImageOption option ) const
{
switch (option)
{
case QImageIOHandler::ImageFormat:
return QVariant(); /// @todo
break;
case QImageIOHandler::Size:
return QVariant(); /// @todo
break;
case QImageIOHandler::Description:
return QVariant(m_description);
break;
default:
return QVariant();
}
}
diff --git a/plugins/impex/heif/krita_heif.desktop b/plugins/impex/heif/krita_heif.desktop
index 6da0d4b9e4..52b988375b 100644
--- a/plugins/impex/heif/krita_heif.desktop
+++ b/plugins/impex/heif/krita_heif.desktop
@@ -1,124 +1,124 @@
[Desktop Entry]
Categories=Qt;KDE;Office;Graphics;
Exec=krita %F
GenericName=Application for Drawing and Handling of Images
GenericName[ar]=تطبيق لرسم الصور والتعامل معها
GenericName[bg]=Приложение за рисуване и обработка на изображения
GenericName[bs]=Aplikacija za crtanje i upravljanje slikom
GenericName[ca]=Aplicació per a dibuix i modificació d'imatges
GenericName[ca@valencia]=Aplicació per a dibuix i modificació d'imatges
GenericName[da]=Tegne- og billedbehandlingsprogram
GenericName[de]=Programm zum Zeichnen und Bearbeiten von Bildern
GenericName[el]=Εφαρμογή για επεξεργασία και χειρισμό εικόνων
GenericName[en_GB]=Application for Drawing and Handling of Images
GenericName[eo]=Aplikaĵo por Desegnado kaj Mastrumado de Bildoj
GenericName[es]=Aplicación para dibujo y manipulación de imágenes
GenericName[et]=Joonistamise ja pilditöötluse rakendus
GenericName[eu]=Irudiak marrazteko eta manipulatzeko aplikazioa
GenericName[fa]=کاربرد برای ترسیم و به کار بردن تصاویر
GenericName[fi]=Ohjelma kuvien piirtämiseen ja käsittelyyn
GenericName[fr]=Application pour dessiner et manipuler des images
GenericName[fy]=Aplikaasje om ôfbyldings mei te tekenjen en te bewurkjen
GenericName[ga]=Feidhmchlár le haghaidh Líníochta agus Láimhseála Íomhánna
GenericName[gl]=Aplicativo de debuxo e edición de imaxes
GenericName[he]=יישום לצביעה וניהול תמונות
GenericName[hi]=छवियों को ड्रा करने तथा उन्हें प्रबन्धित करने का अनुप्रयोग
GenericName[hne]=फोटू मन ल ड्रा करे अउ ओ मन ल प्रबन्धित करे के अनुपरयोग
GenericName[hu]=Rajzoló és képkezelő
GenericName[is]=Teikni og myndvinnsluforrit
GenericName[it]=Applicazione di disegno e gestione di immagini
GenericName[ja]=描画と画像操作のためのアプリケーション
GenericName[kk]=Кескінді салу және өңдеу бағдарламасы
GenericName[ko]=그림 그리기 및 처리 프로그램
GenericName[lv]=Programma zīmēšanai un attēlu apstrādei
GenericName[nb]=Program for tegning og bildehåndtering
GenericName[nds]=Programm för't Teken un Bildhanteren
GenericName[ne]=रेखाचित्र बनाउन र छविको ह्यान्डल गर्नका लागि अनुप्रयोग
GenericName[nl]=Toepassing om afbeeldingen te tekenen en te bewerken
GenericName[pl]=Program do rysowania i obróbki obrazów
GenericName[pt]=Aplicação de Desenho e Manipulação de Imagens
GenericName[pt_BR]=Aplicativo de desenho e manipulação de imagens
GenericName[ru]=Приложение для рисования и редактирования изображений
GenericName[sk]=Aplikácia na kresnenie a manilupáciu s obrázkami
GenericName[sl]=Program za risanje in rokovanje s slikami
GenericName[sv]=Program för att rita och hantera bilder
GenericName[ta]=பிம்பங்களை கையாளுதல் மற்றும் வரைதலுக்கான பயன்னாடு
GenericName[tr]=Çizim ve Resim İşleme Uygulaması
GenericName[uk]=Програма для малювання і обробки зображень
GenericName[uz]=Rasm chizish dasturi
GenericName[uz@cyrillic]=Расм чизиш дастури
GenericName[wa]=Programe po dessiner et apougnî des imådjes
GenericName[x-test]=xxApplication for Drawing and Handling of Imagesxx
-GenericName[zh_CN]=绘制和处理图像的应用程序
+GenericName[zh_CN]=用于绘制和处理图像的应用程序
GenericName[zh_TW]=繪圖與影像處理的應用程式
Icon=calligrakrita
MimeType=image/heic;
Name=Krita
Name[af]=Krita
Name[ar]=كريتا
Name[bg]=Krita
Name[br]=Krita
Name[bs]=Krita
Name[ca]=Krita
Name[ca@valencia]=Krita
Name[cs]=Krita
Name[cy]=Krita
Name[da]=Krita
Name[de]=Krita
Name[el]=Krita
Name[en_GB]=Krita
Name[eo]=Krita
Name[es]=Krita
Name[et]=Krita
Name[eu]=Krita
Name[fi]=Krita
Name[fr]=Krita
Name[fy]=Krita
Name[ga]=Krita
Name[gl]=Krita
Name[he]=Krita
Name[hi]=केरिता
Name[hne]=केरिता
Name[hr]=Krita
Name[hu]=Krita
Name[ia]=Krita
Name[is]=Krita
Name[it]=Krita
Name[ja]=Krita
Name[kk]=Krita
Name[ko]=Krita
Name[lt]=Krita
Name[lv]=Krita
Name[mr]=क्रिटा
Name[ms]=Krita
Name[nb]=Krita
Name[nds]=Krita
Name[ne]=क्रिता
Name[nl]=Krita
Name[pl]=Krita
Name[pt]=Krita
Name[pt_BR]=Krita
Name[ro]=Krita
Name[ru]=Krita
Name[se]=Krita
Name[sk]=Krita
Name[sl]=Krita
Name[sv]=Krita
Name[ta]=கிரிட்டா
Name[tg]=Krita
Name[tr]=Krita
Name[ug]=Krita
Name[uk]=Krita
Name[uz]=Krita
Name[uz@cyrillic]=Krita
Name[wa]=Krita
Name[xh]=Krita
Name[x-test]=xxKritaxx
Name[zh_CN]=Krita
Name[zh_TW]=Krita
StartupNotify=true
Terminal=false
Type=Application
X-KDE-SubstituteUID=false
X-KDE-Username=
NoDisplay=true
diff --git a/plugins/impex/jpeg/CMakeLists.txt b/plugins/impex/jpeg/CMakeLists.txt
index 347e46dbe3..50edb05c96 100644
--- a/plugins/impex/jpeg/CMakeLists.txt
+++ b/plugins/impex/jpeg/CMakeLists.txt
@@ -1,44 +1,43 @@
add_subdirectory(tests)
set(ICCJPEG_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/lcms")
include_directories(
${ICCJPEG_SOURCE_DIR}
- ${EXIV2_INCLUDE_DIR}
)
include_directories(SYSTEM
${LCMS2_INCLUDE_DIR}
)
set(libkritaconverter_LIB_SRCS
kis_jpeg_converter.cc
kis_jpeg_source.cpp
kis_jpeg_destination.cpp
${ICCJPEG_SOURCE_DIR}/iccjpeg.c
)
set(kritajpegimport_SOURCES
kis_jpeg_import.cc
${libkritaconverter_LIB_SRCS}
)
add_library(kritajpegimport MODULE ${kritajpegimport_SOURCES})
-target_link_libraries(kritajpegimport kritaui ${JPEG_LIBRARIES} ${LCMS2_LIBRARIES} ${EXIV2_LIBRARIES} )
+target_link_libraries(kritajpegimport kritaui ${JPEG_LIBRARIES} ${LCMS2_LIBRARIES} LibExiv2::LibExiv2 )
install(TARGETS kritajpegimport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
set(kritajpegexport_SOURCES
kis_jpeg_export.cc
${libkritaconverter_LIB_SRCS}
)
ki18n_wrap_ui(kritajpegexport_SOURCES kis_wdg_options_jpeg.ui )
add_library(kritajpegexport MODULE ${kritajpegexport_SOURCES})
-target_link_libraries(kritajpegexport kritaui kritaimpex ${JPEG_LIBRARIES} ${LCMS2_LIBRARIES} ${EXIV2_LIBRARIES} )
+target_link_libraries(kritajpegexport kritaui kritaimpex ${JPEG_LIBRARIES} ${LCMS2_LIBRARIES} LibExiv2::LibExiv2 )
install(TARGETS kritajpegexport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
install( PROGRAMS krita_jpeg.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
diff --git a/plugins/impex/kra/kra_export.cpp b/plugins/impex/kra/kra_export.cpp
index f135675413..69a1a6984b 100644
--- a/plugins/impex/kra/kra_export.cpp
+++ b/plugins/impex/kra/kra_export.cpp
@@ -1,83 +1,83 @@
/*
* Copyright (C) 2016 Boudewijn Rempt <boud@valdyas.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kra_export.h"
#include <QCheckBox>
#include <QSlider>
#include <kpluginfactory.h>
#include <QFileInfo>
#include <QApplication>
#include <KisImportExportManager.h>
#include <KoColorModelStandardIds.h>
#include <KoColorSpace.h>
#include <KisExportCheckRegistry.h>
#include <KisDocument.h>
#include <kis_image.h>
#include <kis_node.h>
#include <kis_group_layer.h>
#include <kis_paint_layer.h>
#include <kis_shape_layer.h>
#include <KoProperties.h>
#include "kra_converter.h"
class KisExternalLayer;
K_PLUGIN_FACTORY_WITH_JSON(ExportFactory, "krita_kra_export.json", registerPlugin<KraExport>();)
KraExport::KraExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
{
}
KraExport::~KraExport()
{
}
KisImportExportFilter::ConversionStatus KraExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/)
{
KisImageSP image = document->savingImage();
KIS_ASSERT_RECOVER_RETURN_VALUE(image, CreationError);
KraConverter kraConverter(document);
KisImageBuilder_Result res = kraConverter.buildFile(io, filename());
if (res == KisImageBuilder_RESULT_OK) {
- dbgFile << "success !";
+ dbgFile << "KraExport::convert success !";
return KisImportExportFilter::OK;
}
- dbgFile << " Result =" << res;
+ dbgFile << "KraExport::convert result =" << res;
return KisImportExportFilter::InternalError;
}
void KraExport::initializeCapabilities()
{
// Kra supports everything, by definition
KisExportCheckFactory *factory = 0;
Q_FOREACH(const QString &id, KisExportCheckRegistry::instance()->keys()) {
factory = KisExportCheckRegistry::instance()->get(id);
addCapability(factory->create(KisExportCheckBase::SUPPORTED));
}
}
#include <kra_export.moc>
diff --git a/plugins/impex/libkra/kis_kra_load_visitor.cpp b/plugins/impex/libkra/kis_kra_load_visitor.cpp
index 90af54d710..1e3e8494d1 100644
--- a/plugins/impex/libkra/kis_kra_load_visitor.cpp
+++ b/plugins/impex/libkra/kis_kra_load_visitor.cpp
@@ -1,753 +1,753 @@
/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2005 C. Boemann <cbo@boemann.dk>
*
* 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_kra_load_visitor.h"
#include "kis_kra_tags.h"
#include "flake/kis_shape_layer.h"
#include "flake/KisReferenceImagesLayer.h"
#include "KisReferenceImage.h"
#include <KisImportExportManager.h>
#include <QRect>
#include <QBuffer>
#include <QByteArray>
#include <QMessageBox>
#include <KoColorSpaceRegistry.h>
#include <KoColorProfile.h>
#include <KoFileDialog.h>
#include <KoStore.h>
#include <KoColorSpace.h>
#include <KoShapeControllerBase.h>
// kritaimage
#include <kis_meta_data_io_backend.h>
#include <kis_meta_data_store.h>
#include <kis_types.h>
#include <kis_node_visitor.h>
#include <kis_image.h>
#include <kis_selection.h>
#include <kis_layer.h>
#include <kis_paint_layer.h>
#include <kis_group_layer.h>
#include <kis_adjustment_layer.h>
#include <filter/kis_filter_configuration.h>
#include <kis_datamanager.h>
#include <generator/kis_generator_layer.h>
#include <kis_pixel_selection.h>
#include <kis_clone_layer.h>
#include <kis_filter_mask.h>
#include <kis_transform_mask.h>
#include <kis_transform_mask_params_interface.h>
#include "kis_transform_mask_params_factory_registry.h"
#include <kis_transparency_mask.h>
#include <kis_selection_mask.h>
#include <lazybrush/kis_colorize_mask.h>
#include <lazybrush/kis_lazy_fill_tools.h>
#include "kis_shape_selection.h"
#include "kis_colorize_dom_utils.h"
#include "kis_dom_utils.h"
#include "kis_raster_keyframe_channel.h"
#include "kis_paint_device_frames_interface.h"
using namespace KRA;
QString expandEncodedDirectory(const QString& _intern)
{
+
QString intern = _intern;
QString result;
int pos;
while ((pos = intern.indexOf('/')) != -1) {
if (QChar(intern.at(0)).isDigit())
result += "part";
result += intern.left(pos + 1); // copy numbers (or "pictures") + "/"
intern = intern.mid(pos + 1); // remove the dir we just processed
}
if (!intern.isEmpty() && QChar(intern.at(0)).isDigit())
result += "part";
result += intern;
+
+
return result;
}
KisKraLoadVisitor::KisKraLoadVisitor(KisImageSP image,
KoStore *store,
KoShapeControllerBase *shapeController,
QMap<KisNode *, QString> &layerFilenames,
QMap<KisNode *, QString> &keyframeFilenames,
const QString & name,
int syntaxVersion)
: KisNodeVisitor()
, m_image(image)
, m_store(store)
, m_external(false)
, m_layerFilenames(layerFilenames)
, m_keyframeFilenames(keyframeFilenames)
, m_name(name)
, m_shapeController(shapeController)
{
m_store->pushDirectory();
- if (m_name.startsWith("/")) {
- m_name.remove(0, 1);
- }
+
if (!m_store->enterDirectory(m_name)) {
QStringList directories = m_store->directoryList();
dbgKrita << directories;
if (directories.size() > 0) {
dbgFile << "Could not locate the directory, maybe some encoding issue? Grab the first directory, that'll be the image one." << m_name << directories;
m_name = directories.first();
}
else {
dbgFile << "Could not enter directory" << m_name << ", probably an old-style file with 'part' added.";
m_name = expandEncodedDirectory(m_name);
}
}
else {
m_store->popDirectory();
}
m_syntaxVersion = syntaxVersion;
}
void KisKraLoadVisitor::setExternalUri(const QString &uri)
{
m_external = true;
m_uri = uri;
}
bool KisKraLoadVisitor::visit(KisExternalLayer * layer)
{
bool result = false;
if (auto *referencesLayer = dynamic_cast<KisReferenceImagesLayer*>(layer)) {
Q_FOREACH(KoShape *shape, referencesLayer->shapes()) {
auto *reference = dynamic_cast<KisReferenceImage*>(shape);
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(reference, false);
while (!reference->loadImage(m_store)) {
if (reference->embed()) {
m_errorMessages << i18n("Could not load embedded reference image %1 ", reference->internalFile());
break;
} else {
QString msg = i18nc(
"@info",
"A reference image linked to an external file could not be loaded.\n\n"
"Path: %1\n\n"
"Do you want to select another location?", reference->filename());
int locateManually = QMessageBox::warning(0, i18nc("@title:window", "File not found"), msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
QString url;
if (locateManually == QMessageBox::Yes) {
KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument");
dialog.setMimeTypeFilters(KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import));
url = dialog.filename();
}
if (url.isEmpty()) {
break;
} else {
reference->setFilename(url);
}
}
}
}
} else if (KisShapeLayer *shapeLayer = dynamic_cast<KisShapeLayer*>(layer)) {
if (!loadMetaData(layer)) {
return false;
}
m_store->pushDirectory();
m_store->enterDirectory(getLocation(layer, DOT_SHAPE_LAYER)) ;
result = shapeLayer->loadLayer(m_store);
m_store->popDirectory();
}
result = visitAll(layer) && result;
return result;
}
bool KisKraLoadVisitor::visit(KisPaintLayer *layer)
{
loadNodeKeyframes(layer);
- dbgFile << "Visit: " << layer->name() << " colorSpace: " << layer->colorSpace()->id();
if (!loadPaintDevice(layer->paintDevice(), getLocation(layer))) {
return false;
}
if (!loadProfile(layer->paintDevice(), getLocation(layer, DOT_ICC))) {
return false;
}
if (!loadMetaData(layer)) {
return false;
}
if (m_syntaxVersion == 1) {
// Check whether there is a file with a .mask extension in the
// layer directory, if so, it's an old-style transparency mask
// that should be converted.
QString location = getLocation(layer, ".mask");
if (m_store->open(location)) {
KisSelectionSP selection = KisSelectionSP(new KisSelection());
KisPixelSelectionSP pixelSelection = selection->pixelSelection();
if (!pixelSelection->read(m_store->device())) {
pixelSelection->disconnect();
} else {
KisTransparencyMask* mask = new KisTransparencyMask();
mask->setSelection(selection);
m_image->addNode(mask, layer, layer->firstChild());
}
m_store->close();
}
}
bool result = visitAll(layer);
return result;
}
bool KisKraLoadVisitor::visit(KisGroupLayer *layer)
{
if (*layer->colorSpace() != *m_image->colorSpace()) {
layer->resetCache(m_image->colorSpace());
}
if (!loadMetaData(layer)) {
return false;
}
bool result = visitAll(layer);
return result;
}
bool KisKraLoadVisitor::visit(KisAdjustmentLayer* layer)
{
loadNodeKeyframes(layer);
// Adjustmentlayers are tricky: there's the 1.x style and the 2.x
// style, which has selections with selection components
bool result = true;
if (m_syntaxVersion == 1) {
KisSelectionSP selection = new KisSelection();
KisPixelSelectionSP pixelSelection = selection->pixelSelection();
result = loadPaintDevice(pixelSelection, getLocation(layer, ".selection"));
layer->setInternalSelection(selection);
} else if (m_syntaxVersion == 2) {
result = loadSelection(getLocation(layer), layer->internalSelection());
} else {
// We use the default, empty selection
}
if (!loadMetaData(layer)) {
return false;
}
loadFilterConfiguration(layer->filter().data(), getLocation(layer, DOT_FILTERCONFIG));
result = visitAll(layer);
return result;
}
bool KisKraLoadVisitor::visit(KisGeneratorLayer *layer)
{
if (!loadMetaData(layer)) {
return false;
}
bool result = true;
loadNodeKeyframes(layer);
result = loadSelection(getLocation(layer), layer->internalSelection());
// HACK ALERT: we set the same filter again to ensure the layer
// is correctly updated
KisFilterConfigurationSP filter = layer->filter();
result = loadFilterConfiguration(filter.data(), getLocation(layer, DOT_FILTERCONFIG));
layer->setFilter(filter);
result = visitAll(layer);
return result;
}
bool KisKraLoadVisitor::visit(KisCloneLayer *layer)
{
if (!loadMetaData(layer)) {
return false;
}
// the layer might have already been lazily initialized
// from the mask loading code
if (layer->copyFrom()) {
return true;
}
KisNodeSP srcNode = layer->copyFromInfo().findNode(m_image->rootLayer());
KisLayerSP srcLayer = qobject_cast<KisLayer*>(srcNode.data());
Q_ASSERT(srcLayer);
layer->setCopyFrom(srcLayer);
// Clone layers have no data except for their masks
bool result = visitAll(layer);
return result;
}
void KisKraLoadVisitor::initSelectionForMask(KisMask *mask)
{
KisLayer *cloneLayer = dynamic_cast<KisCloneLayer*>(mask->parent().data());
if (cloneLayer) {
// the clone layers should be initialized out of order
// and lazily, because their original() is still not
// initialized
cloneLayer->accept(*this);
}
KisLayer *parentLayer = qobject_cast<KisLayer*>(mask->parent().data());
// the KisKraLoader must have already set the parent for us
Q_ASSERT(parentLayer);
mask->initSelection(parentLayer);
}
bool KisKraLoadVisitor::visit(KisFilterMask *mask)
{
initSelectionForMask(mask);
loadNodeKeyframes(mask);
bool result = true;
result = loadSelection(getLocation(mask), mask->selection());
result = loadFilterConfiguration(mask->filter().data(), getLocation(mask, DOT_FILTERCONFIG));
return result;
}
bool KisKraLoadVisitor::visit(KisTransformMask *mask)
{
QString location = getLocation(mask, DOT_TRANSFORMCONFIG);
if (m_store->hasFile(location)) {
QByteArray data;
m_store->open(location);
data = m_store->read(m_store->size());
m_store->close();
if (!data.isEmpty()) {
QDomDocument doc;
doc.setContent(data);
QDomElement rootElement = doc.documentElement();
QDomElement main;
if (!KisDomUtils::findOnlyElement(rootElement, "main", &main/*, &m_errorMessages*/)) {
return false;
}
QString id = main.attribute("id", "not-valid");
if (id == "not-valid") {
m_errorMessages << i18n("Could not load \"id\" of the transform mask");
return false;
}
QDomElement data;
if (!KisDomUtils::findOnlyElement(rootElement, "data", &data, &m_errorMessages)) {
return false;
}
KisTransformMaskParamsInterfaceSP params =
KisTransformMaskParamsFactoryRegistry::instance()->createParams(id, data);
if (!params) {
m_errorMessages << i18n("Could not create transform mask params");
return false;
}
mask->setTransformParams(params);
loadNodeKeyframes(mask);
params->clearChangedFlag();
return true;
}
}
return false;
}
bool KisKraLoadVisitor::visit(KisTransparencyMask *mask)
{
initSelectionForMask(mask);
loadNodeKeyframes(mask);
return loadSelection(getLocation(mask), mask->selection());
}
bool KisKraLoadVisitor::visit(KisSelectionMask *mask)
{
initSelectionForMask(mask);
return loadSelection(getLocation(mask), mask->selection());
}
bool KisKraLoadVisitor::visit(KisColorizeMask *mask)
{
m_store->pushDirectory();
QString location = getLocation(mask, DOT_COLORIZE_MASK);
m_store->enterDirectory(location) ;
QByteArray data;
if (!m_store->extractFile("content.xml", data))
return false;
QDomDocument doc;
if (!doc.setContent(data))
return false;
QVector<KisLazyFillTools::KeyStroke> strokes;
if (!KisDomUtils::loadValue(doc.documentElement(), COLORIZE_KEYSTROKES_SECTION, &strokes, mask->colorSpace()))
return false;
int i = 0;
Q_FOREACH (const KisLazyFillTools::KeyStroke &stroke, strokes) {
const QString fileName = QString("%1_%2").arg(COLORIZE_KEYSTROKE).arg(i++);
loadPaintDevice(stroke.dev, fileName);
}
mask->setKeyStrokesDirect(QList<KisLazyFillTools::KeyStroke>::fromVector(strokes));
loadPaintDevice(mask->coloringProjection(), COLORIZE_COLORING_DEVICE);
mask->resetCache();
m_store->popDirectory();
return true;
}
QStringList KisKraLoadVisitor::errorMessages() const
{
return m_errorMessages;
}
QStringList KisKraLoadVisitor::warningMessages() const
{
return m_warningMessages;
}
struct SimpleDevicePolicy
{
bool read(KisPaintDeviceSP dev, QIODevice *stream) {
return dev->read(stream);
}
void setDefaultPixel(KisPaintDeviceSP dev, const KoColor &defaultPixel) const {
return dev->setDefaultPixel(defaultPixel);
}
};
struct FramedDevicePolicy
{
FramedDevicePolicy(int frameId)
: m_frameId(frameId) {}
bool read(KisPaintDeviceSP dev, QIODevice *stream) {
return dev->framesInterface()->readFrame(stream, m_frameId);
}
void setDefaultPixel(KisPaintDeviceSP dev, const KoColor &defaultPixel) const {
return dev->framesInterface()->setFrameDefaultPixel(defaultPixel, m_frameId);
}
int m_frameId;
};
bool KisKraLoadVisitor::loadPaintDevice(KisPaintDeviceSP device, const QString& location)
{
// Layer data
KisPaintDeviceFramesInterface *frameInterface = device->framesInterface();
QList<int> frames;
if (frameInterface) {
frames = device->framesInterface()->frames();
}
if (!frameInterface || frames.count() <= 1) {
return loadPaintDeviceFrame(device, location, SimpleDevicePolicy());
} else {
KisRasterKeyframeChannel *keyframeChannel = device->keyframeChannel();
for (int i = 0; i < frames.count(); i++) {
int id = frames[i];
if (keyframeChannel->frameFilename(id).isEmpty()) {
m_warningMessages << i18n("Could not find keyframe pixel data for frame %1 in %2.", id, location);
}
else {
Q_ASSERT(!keyframeChannel->frameFilename(id).isEmpty());
QString frameFilename = getLocation(keyframeChannel->frameFilename(id));
Q_ASSERT(!frameFilename.isEmpty());
if (!loadPaintDeviceFrame(device, frameFilename, FramedDevicePolicy(id))) {
m_warningMessages << i18n("Could not load keyframe pixel data for frame %1 in %2.", id, location);
}
}
}
}
return true;
}
template<class DevicePolicy>
bool KisKraLoadVisitor::loadPaintDeviceFrame(KisPaintDeviceSP device, const QString &location, DevicePolicy policy)
{
{
const int pixelSize = device->colorSpace()->pixelSize();
KoColor color(Qt::transparent, device->colorSpace());
if (m_store->open(location + ".defaultpixel")) {
if (m_store->size() == pixelSize) {
m_store->read((char*)color.data(), pixelSize);
}
m_store->close();
}
policy.setDefaultPixel(device, color);
}
if (m_store->open(location)) {
if (!policy.read(device, m_store->device())) {
m_warningMessages << i18n("Could not read pixel data: %1.", location);
device->disconnect();
m_store->close();
return true;
}
m_store->close();
} else {
m_warningMessages << i18n("Could not load pixel data: %1.", location);
return true;
}
return true;
}
bool KisKraLoadVisitor::loadProfile(KisPaintDeviceSP device, const QString& location)
{
if (m_store->hasFile(location)) {
m_store->open(location);
QByteArray data; data.resize(m_store->size());
dbgFile << "Data to load: " << m_store->size() << " from " << location << " with color space " << device->colorSpace()->id();
int read = m_store->read(data.data(), m_store->size());
dbgFile << "Profile size: " << data.size() << " " << m_store->atEnd() << " " << m_store->device()->bytesAvailable() << " " << read;
m_store->close();
// Create a colorspace with the embedded profile
const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(device->colorSpace()->colorModelId().id(), device->colorSpace()->colorDepthId().id(), data);
if (device->setProfile(profile)) {
return true;
}
}
m_warningMessages << i18n("Could not load profile: %1.", location);
return true;
}
bool KisKraLoadVisitor::loadFilterConfiguration(KisFilterConfigurationSP kfc, const QString& location)
{
if (m_store->hasFile(location)) {
QByteArray data;
m_store->open(location);
data = m_store->read(m_store->size());
m_store->close();
if (!data.isEmpty()) {
QDomDocument doc;
doc.setContent(data);
QDomElement e = doc.documentElement();
if (e.tagName() == "filterconfig") {
kfc->fromLegacyXML(e);
} else {
kfc->fromXML(e);
}
loadDeprecatedFilter(kfc);
return true;
}
}
m_warningMessages << i18n("Could not filter configuration %1.", location);
return true;
}
bool KisKraLoadVisitor::loadMetaData(KisNode* node)
{
KisLayer* layer = qobject_cast<KisLayer*>(node);
if (!layer) return true;
KisMetaData::IOBackend* backend = KisMetaData::IOBackendRegistry::instance()->get("xmp");
if (!backend || !backend->supportLoading()) {
if (backend)
dbgFile << "Backend " << backend->id() << " does not support loading.";
else
dbgFile << "Could not load the XMP backend at all";
return true;
}
QString location = getLocation(node, QString(".") + backend->id() + DOT_METADATA);
dbgFile << "going to load " << backend->id() << ", " << backend->name() << " from " << location;
if (m_store->hasFile(location)) {
QByteArray data;
m_store->open(location);
data = m_store->read(m_store->size());
m_store->close();
QBuffer buffer(&data);
if (!backend->loadFrom(layer->metaData(), &buffer)) {
m_warningMessages << i18n("Could not load metadata for layer %1.", layer->name());
}
}
return true;
}
bool KisKraLoadVisitor::loadSelection(const QString& location, KisSelectionSP dstSelection)
{
// by default the selection is expected to be fully transparent
{
KisPixelSelectionSP pixelSelection = dstSelection->pixelSelection();
KoColor transparent(Qt::transparent, pixelSelection->colorSpace());
pixelSelection->setDefaultPixel(transparent);
}
// Pixel selection
bool result = true;
QString pixelSelectionLocation = location + DOT_PIXEL_SELECTION;
if (m_store->hasFile(pixelSelectionLocation)) {
KisPixelSelectionSP pixelSelection = dstSelection->pixelSelection();
result = loadPaintDevice(pixelSelection, pixelSelectionLocation);
if (!result) {
m_warningMessages << i18n("Could not load raster selection %1.", location);
}
pixelSelection->invalidateOutlineCache();
}
// Shape selection
QString shapeSelectionLocation = location + DOT_SHAPE_SELECTION;
if (m_store->hasFile(shapeSelectionLocation + "/content.svg") ||
m_store->hasFile(shapeSelectionLocation + "/content.xml")) {
m_store->pushDirectory();
m_store->enterDirectory(shapeSelectionLocation) ;
KisShapeSelection* shapeSelection = new KisShapeSelection(m_shapeController, m_image, dstSelection);
dstSelection->setShapeSelection(shapeSelection);
result = shapeSelection->loadSelection(m_store);
m_store->popDirectory();
if (!result) {
m_warningMessages << i18n("Could not load vector selection %1.", location);
}
}
return true;
}
QString KisKraLoadVisitor::getLocation(KisNode* node, const QString& suffix)
{
return getLocation(m_layerFilenames[node], suffix);
}
QString KisKraLoadVisitor::getLocation(const QString &filename, const QString& suffix)
{
QString location = m_external ? QString() : m_uri;
location += m_name + LAYER_PATH + filename + suffix;
return location;
}
void KisKraLoadVisitor::loadNodeKeyframes(KisNode *node)
{
if (!m_keyframeFilenames.contains(node)) return;
node->enableAnimation();
const QString &location = getLocation(m_keyframeFilenames[node]);
if (!m_store->open(location)) {
m_errorMessages << i18n("Could not load keyframes from %1.", location);
return;
}
QString errorMsg;
int errorLine;
int errorColumn;
QDomDocument dom;
bool ok = dom.setContent(m_store->device(), &errorMsg, &errorLine, &errorColumn);
m_store->close();
if (!ok) {
m_errorMessages << i18n("parsing error in the keyframe file %1 at line %2, column %3\nError message: %4", location, errorLine, errorColumn, i18n(errorMsg.toUtf8()));
return;
}
QDomElement root = dom.firstChildElement();
for (QDomElement child = root.firstChildElement(); !child.isNull(); child = child.nextSiblingElement()) {
if (child.nodeName().toUpper() == "CHANNEL") {
QString id = child.attribute("name");
KisKeyframeChannel *channel = node->getKeyframeChannel(id, true);
if (!channel) {
m_warningMessages << i18n("unknown keyframe channel type: %1 in %2", id, location);
continue;
}
channel->loadXML(child);
}
}
}
void KisKraLoadVisitor::loadDeprecatedFilter(KisFilterConfigurationSP cfg)
{
if (cfg->getString("legacy") == "left edge detections") {
cfg->setProperty("horizRadius", 1);
cfg->setProperty("vertRadius", 1);
cfg->setProperty("type", "prewitt");
cfg->setProperty("output", "yFall");
cfg->setProperty("lockAspect", true);
cfg->setProperty("transparency", false);
} else if (cfg->getString("legacy") == "right edge detections") {
cfg->setProperty("horizRadius", 1);
cfg->setProperty("vertRadius", 1);
cfg->setProperty("type", "prewitt");
cfg->setProperty("output", "yGrowth");
cfg->setProperty("lockAspect", true);
cfg->setProperty("transparency", false);
} else if (cfg->getString("legacy") == "top edge detections") {
cfg->setProperty("horizRadius", 1);
cfg->setProperty("vertRadius", 1);
cfg->setProperty("type", "prewitt");
cfg->setProperty("output", "xGrowth");
cfg->setProperty("lockAspect", true);
cfg->setProperty("transparency", false);
} else if (cfg->getString("legacy") == "bottom edge detections") {
cfg->setProperty("horizRadius", 1);
cfg->setProperty("vertRadius", 1);
cfg->setProperty("type", "prewitt");
cfg->setProperty("output", "xFall");
cfg->setProperty("lockAspect", true);
cfg->setProperty("transparency", false);
}
}
diff --git a/plugins/impex/libkra/kis_kra_loader.cpp b/plugins/impex/libkra/kis_kra_loader.cpp
index 514eff3c7a..dd86b6ba8b 100644
--- a/plugins/impex/libkra/kis_kra_loader.cpp
+++ b/plugins/impex/libkra/kis_kra_loader.cpp
@@ -1,1247 +1,1247 @@
/* This file is part of the KDE project
* Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2007
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_kra_loader.h"
#include <QApplication>
#include <QStringList>
#include <QMessageBox>
#include <QUrl>
#include <QBuffer>
#include <KoStore.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorSpaceEngine.h>
#include <KoColorProfile.h>
#include <KoDocumentInfo.h>
#include <KoFileDialog.h>
#include <KisImportExportManager.h>
#include <KoXmlReader.h>
#include <KoStoreDevice.h>
#include <KoResourceServerProvider.h>
#include <filter/kis_filter.h>
#include <filter/kis_filter_registry.h>
#include <generator/kis_generator.h>
#include <generator/kis_generator_layer.h>
#include <generator/kis_generator_registry.h>
#include <kis_adjustment_layer.h>
#include <kis_annotation.h>
#include <kis_base_node.h>
#include <kis_clone_layer.h>
#include <kis_debug.h>
#include <kis_assert.h>
#include <kis_external_layer_iface.h>
#include <kis_filter_mask.h>
#include <kis_transform_mask.h>
#include "lazybrush/kis_colorize_mask.h"
#include <kis_group_layer.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <kis_name_server.h>
#include <kis_paint_layer.h>
#include <kis_selection.h>
#include <kis_selection_mask.h>
#include <kis_shape_layer.h>
#include <kis_transparency_mask.h>
#include <kis_layer_composition.h>
#include <kis_file_layer.h>
#include <kis_psd_layer_style.h>
#include <kis_psd_layer_style_resource.h>
#include "KisResourceServerProvider.h"
#include "kis_keyframe_channel.h"
#include <kis_filter_configuration.h>
#include "KisReferenceImagesLayer.h"
#include "KisReferenceImage.h"
#include <KoColorSet.h>
#include "KisDocument.h"
#include "kis_config.h"
#include "kis_kra_tags.h"
#include "kis_kra_utils.h"
#include "kis_kra_load_visitor.h"
#include "kis_dom_utils.h"
#include "kis_image_animation_interface.h"
#include "kis_time_range.h"
#include "kis_grid_config.h"
#include "kis_guides_config.h"
#include "kis_image_config.h"
#include "KisProofingConfiguration.h"
#include "kis_layer_properties_icons.h"
#include "kis_node_view_color_scheme.h"
/*
Color model id comparison through the ages:
2.4 2.5 2.6 ideal
ALPHA ALPHA ALPHA ALPHAU8
CMYK CMYK CMYK CMYKAU8
CMYKAF32 CMYKAF32
CMYKA16 CMYKAU16 CMYKAU16
GRAYA GRAYA GRAYA GRAYAU8
GrayF32 GRAYAF32 GRAYAF32
GRAYA16 GRAYAU16 GRAYAU16
LABA LABA LABA LABAU16
LABAF32 LABAF32
LABAU8 LABAU8
RGBA RGBA RGBA RGBAU8
RGBA16 RGBA16 RGBA16 RGBAU16
RgbAF32 RGBAF32 RGBAF32
RgbAF16 RgbAF16 RGBAF16
XYZA16 XYZA16 XYZA16 XYZAU16
XYZA8 XYZA8 XYZAU8
XyzAF16 XyzAF16 XYZAF16
XyzAF32 XYZAF32 XYZAF32
YCbCrA YCBCRA8 YCBCRA8 YCBCRAU8
YCbCrAU16 YCBCRAU16 YCBCRAU16
YCBCRF32 YCBCRF32
*/
using namespace KRA;
struct KisKraLoader::Private
{
public:
KisDocument* document;
QString imageName; // used to be stored in the image, is now in the documentInfo block
QString imageComment; // used to be stored in the image, is now in the documentInfo block
QMap<KisNode*, QString> layerFilenames; // temp storage during loading
int syntaxVersion; // version of the fileformat we are loading
vKisNodeSP selectedNodes; // the nodes that were active when saving the document.
QMap<QString, QString> assistantsFilenames;
QList<KisPaintingAssistantSP> assistants;
QMap<KisNode*, QString> keyframeFilenames;
QVector<QString> paletteFilenames;
QStringList errorMessages;
QStringList warningMessages;
};
void convertColorSpaceNames(QString &colorspacename, QString &profileProductName) {
if (colorspacename == "Grayscale + Alpha") {
colorspacename = "GRAYA";
profileProductName.clear();
}
else if (colorspacename == "RgbAF32") {
colorspacename = "RGBAF32";
profileProductName.clear();
}
else if (colorspacename == "RgbAF16") {
colorspacename = "RGBAF16";
profileProductName.clear();
}
else if (colorspacename == "CMYKA16") {
colorspacename = "CMYKAU16";
}
else if (colorspacename == "GrayF32") {
colorspacename = "GRAYAF32";
profileProductName.clear();
}
else if (colorspacename == "GRAYA16") {
colorspacename = "GRAYAU16";
}
else if (colorspacename == "XyzAF16") {
colorspacename = "XYZAF16";
profileProductName.clear();
}
else if (colorspacename == "XyzAF32") {
colorspacename = "XYZAF32";
profileProductName.clear();
}
else if (colorspacename == "YCbCrA") {
colorspacename = "YCBCRA8";
}
else if (colorspacename == "YCbCrAU16") {
colorspacename = "YCBCRAU16";
}
}
KisKraLoader::KisKraLoader(KisDocument * document, int syntaxVersion)
: m_d(new Private())
{
m_d->document = document;
m_d->syntaxVersion = syntaxVersion;
}
KisKraLoader::~KisKraLoader()
{
delete m_d;
}
KisImageSP KisKraLoader::loadXML(const KoXmlElement& element)
{
QString attr;
KisImageSP image = 0;
- QString name;
qint32 width;
qint32 height;
QString profileProductName;
double xres;
double yres;
QString colorspacename;
const KoColorSpace * cs;
if ((attr = element.attribute(MIME)) == NATIVE_MIMETYPE) {
if ((m_d->imageName = element.attribute(NAME)).isNull()) {
m_d->errorMessages << i18n("Image does not have a name.");
return KisImageSP(0);
}
+
if ((attr = element.attribute(WIDTH)).isNull()) {
m_d->errorMessages << i18n("Image does not specify a width.");
return KisImageSP(0);
}
width = KisDomUtils::toInt(attr);
if ((attr = element.attribute(HEIGHT)).isNull()) {
m_d->errorMessages << i18n("Image does not specify a height.");
return KisImageSP(0);
}
height = KisDomUtils::toInt(attr);
m_d->imageComment = element.attribute(DESCRIPTION);
xres = 100.0 / 72.0;
if (!(attr = element.attribute(X_RESOLUTION)).isNull()) {
qreal value = KisDomUtils::toDouble(attr);
if (value > 1.0) {
xres = value / 72.0;
}
}
yres = 100.0 / 72.0;
if (!(attr = element.attribute(Y_RESOLUTION)).isNull()) {
qreal value = KisDomUtils::toDouble(attr);
if (value > 1.0) {
yres = value / 72.0;
}
}
if ((colorspacename = element.attribute(COLORSPACE_NAME)).isNull()) {
// An old file: take a reasonable default.
// Krita didn't support anything else in those
// days anyway.
colorspacename = "RGBA";
}
profileProductName = element.attribute(PROFILE);
// A hack for an old colorspacename
convertColorSpaceNames(colorspacename, profileProductName);
QString colorspaceModel = KoColorSpaceRegistry::instance()->colorSpaceColorModelId(colorspacename).id();
QString colorspaceDepth = KoColorSpaceRegistry::instance()->colorSpaceColorDepthId(colorspacename).id();
if (profileProductName.isNull()) {
// no mention of profile so get default profile";
cs = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, "");
} else {
cs = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, profileProductName);
}
if (cs == 0) {
// try once more without the profile
cs = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, "");
if (cs == 0) {
m_d->errorMessages << i18n("Image specifies an unsupported color model: %1.", colorspacename);
return KisImageSP(0);
}
}
KisProofingConfigurationSP proofingConfig = KisImageConfig(true).defaultProofingconfiguration();
if (!(attr = element.attribute(PROOFINGPROFILENAME)).isNull()) {
proofingConfig->proofingProfile = attr;
}
if (!(attr = element.attribute(PROOFINGMODEL)).isNull()) {
proofingConfig->proofingModel = attr;
}
if (!(attr = element.attribute(PROOFINGDEPTH)).isNull()) {
proofingConfig->proofingDepth = attr;
}
if (!(attr = element.attribute(PROOFINGINTENT)).isNull()) {
proofingConfig->intent = (KoColorConversionTransformation::Intent) KisDomUtils::toInt(attr);
}
if (!(attr = element.attribute(PROOFINGADAPTATIONSTATE)).isNull()) {
proofingConfig->adaptationState = KisDomUtils::toDouble(attr);
}
if (m_d->document) {
- image = new KisImage(m_d->document->createUndoStore(), width, height, cs, name);
+ image = new KisImage(m_d->document->createUndoStore(), width, height, cs, m_d->imageName);
}
else {
- image = new KisImage(0, width, height, cs, name);
+ image = new KisImage(0, width, height, cs, m_d->imageName);
}
image->setResolution(xres, yres);
loadNodes(element, image, const_cast<KisGroupLayer*>(image->rootLayer().data()));
KoXmlNode child;
for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) {
KoXmlElement e = child.toElement();
if(e.tagName() == CANVASPROJECTIONCOLOR) {
if (e.hasAttribute(COLORBYTEDATA)) {
QByteArray colorData = QByteArray::fromBase64(e.attribute(COLORBYTEDATA).toLatin1());
KoColor color((const quint8*)colorData.data(), image->colorSpace());
image->setDefaultProjectionColor(color);
}
}
if(e.tagName() == GLOBALASSISTANTSCOLOR) {
if (e.hasAttribute(SIMPLECOLORDATA)) {
QString colorData = e.attribute(SIMPLECOLORDATA);
m_d->document->setAssistantsGlobalColor(KisDomUtils::qStringToQColor(colorData));
}
}
if(e.tagName()== PROOFINGWARNINGCOLOR) {
QDomDocument dom;
KoXml::asQDomElement(dom, e);
QDomElement eq = dom.firstChildElement();
proofingConfig->warningColor = KoColor::fromXML(eq.firstChildElement(), Integer8BitsColorDepthID.id());
}
if (e.tagName().toLower() == "animation") {
loadAnimationMetadata(e, image);
}
}
image->setProofingConfiguration(proofingConfig);
for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) {
KoXmlElement e = child.toElement();
if(e.tagName() == "compositions") {
loadCompositions(e, image);
}
}
}
KoXmlNode child;
for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) {
KoXmlElement e = child.toElement();
if (e.tagName() == "grid") {
loadGrid(e);
} else if (e.tagName() == "guides") {
loadGuides(e);
} else if (e.tagName() == "assistants") {
loadAssistantsList(e);
} else if (e.tagName() == "audio") {
loadAudio(e, image);
}
}
// reading palettes from XML
for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) {
QDomElement e = child.toElement();
if (e.tagName() == PALETTES) {
for (QDomElement paletteElement = e.lastChildElement();
!paletteElement.isNull();
paletteElement = paletteElement.previousSiblingElement()) {
QString paletteName = paletteElement.attribute("filename");
m_d->paletteFilenames.append(paletteName);
}
break;
}
}
return image;
}
void KisKraLoader::loadBinaryData(KoStore * store, KisImageSP image, const QString & uri, bool external)
{
// icc profile: if present, this overrides the profile product name loaded in loadXML.
QString location = external ? QString() : uri;
location += m_d->imageName + ICC_PATH;
if (store->hasFile(location)) {
if (store->open(location)) {
QByteArray data; data.resize(store->size());
bool res = (store->read(data.data(), store->size()) > -1);
store->close();
if (res) {
const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(image->colorSpace()->colorModelId().id(), image->colorSpace()->colorDepthId().id(), data);
if (profile && profile->valid()) {
res = image->assignImageProfile(profile);
}
if (!res) {
const QString defaultProfileId = KoColorSpaceRegistry::instance()->defaultProfileForColorSpace(image->colorSpace()->id());
profile = KoColorSpaceRegistry::instance()->profileByName(defaultProfileId);
Q_ASSERT(profile && profile->valid());
image->assignImageProfile(profile);
}
}
}
}
//load the embed proofing profile, it only needs to be loaded into Krita, not assigned.
location = external ? QString() : uri;
location += m_d->imageName + ICC_PROOFING_PATH;
if (store->hasFile(location)) {
if (store->open(location)) {
QByteArray proofingData;
proofingData.resize(store->size());
bool proofingProfileRes = (store->read(proofingData.data(), store->size())>-1);
store->close();
KisProofingConfigurationSP proofingConfig = image->proofingConfiguration();
if (!proofingConfig) {
proofingConfig = KisImageConfig(true).defaultProofingconfiguration();
}
if (proofingProfileRes) {
const KoColorProfile *proofingProfile = KoColorSpaceRegistry::instance()->createColorProfile(proofingConfig->proofingModel, proofingConfig->proofingDepth, proofingData);
if (proofingProfile->valid()){
KoColorSpaceRegistry::instance()->addProfile(proofingProfile);
}
}
}
}
// Load the layers data: if there is a profile associated with a layer it will be set now.
KisKraLoadVisitor visitor(image, store, m_d->document->shapeController(), m_d->layerFilenames, m_d->keyframeFilenames, m_d->imageName, m_d->syntaxVersion);
if (external) {
visitor.setExternalUri(uri);
}
image->rootLayer()->accept(visitor);
if (!visitor.errorMessages().isEmpty()) {
m_d->errorMessages.append(visitor.errorMessages());
}
if (!visitor.warningMessages().isEmpty()) {
m_d->warningMessages.append(visitor.warningMessages());
}
// annotations
// exif
location = external ? QString() : uri;
location += m_d->imageName + EXIF_PATH;
if (store->hasFile(location)) {
QByteArray data;
store->open(location);
data = store->read(store->size());
store->close();
image->addAnnotation(KisAnnotationSP(new KisAnnotation("exif", "", data)));
}
// layer styles
location = external ? QString() : uri;
location += m_d->imageName + LAYER_STYLES_PATH;
if (store->hasFile(location)) {
KisPSDLayerStyleCollectionResource *collection =
new KisPSDLayerStyleCollectionResource("Embedded Styles.asl");
collection->setName(i18nc("Auto-generated layer style collection name for embedded styles (collection)", "<%1> (embedded)", m_d->imageName));
KIS_ASSERT_RECOVER_NOOP(!collection->valid());
store->open(location);
{
KoStoreDevice device(store);
device.open(QIODevice::ReadOnly);
/**
* ASL loading code cannot work with non-sequential IO devices,
* so convert the device beforehand!
*/
QByteArray buf = device.readAll();
QBuffer raDevice(&buf);
raDevice.open(QIODevice::ReadOnly);
collection->loadFromDevice(&raDevice);
}
store->close();
if (collection->valid()) {
KoResourceServer<KisPSDLayerStyleCollectionResource> *server = KisResourceServerProvider::instance()->layerStyleCollectionServer();
server->addResource(collection, false);
collection->assignAllLayerStyles(image->root());
} else {
warnKrita << "WARNING: Couldn't load layer styles library from .kra!";
delete collection;
}
}
if (m_d->document && m_d->document->documentInfo()->aboutInfo("title").isNull())
m_d->document->documentInfo()->setAboutInfo("title", m_d->imageName);
if (m_d->document && m_d->document->documentInfo()->aboutInfo("comment").isNull())
m_d->document->documentInfo()->setAboutInfo("comment", m_d->imageComment);
loadAssistants(store, uri, external);
}
void KisKraLoader::loadPalettes(KoStore *store, KisDocument *doc)
{
QList<KoColorSet*> list;
Q_FOREACH (const QString &filename, m_d->paletteFilenames) {
KoColorSet *newPalette = new KoColorSet(filename);
store->open(m_d->imageName + PALETTE_PATH + filename);
QByteArray data = store->read(store->size());
newPalette->fromByteArray(data);
newPalette->setIsGlobal(false);
newPalette->setIsEditable(true);
store->close();
list.append(newPalette);
}
doc->setPaletteList(list);
}
vKisNodeSP KisKraLoader::selectedNodes() const
{
return m_d->selectedNodes;
}
QList<KisPaintingAssistantSP> KisKraLoader::assistants() const
{
return m_d->assistants;
}
QStringList KisKraLoader::errorMessages() const
{
return m_d->errorMessages;
}
QStringList KisKraLoader::warningMessages() const
{
return m_d->warningMessages;
}
void KisKraLoader::loadAssistants(KoStore *store, const QString &uri, bool external)
{
QString file_path;
QString location;
QMap<int ,KisPaintingAssistantHandleSP> handleMap;
KisPaintingAssistant* assistant = 0;
const QColor globalColor = m_d->document->assistantsGlobalColor();
QMap<QString,QString>::const_iterator loadedAssistant = m_d->assistantsFilenames.constBegin();
while (loadedAssistant != m_d->assistantsFilenames.constEnd()){
const KisPaintingAssistantFactory* factory = KisPaintingAssistantFactoryRegistry::instance()->get(loadedAssistant.value());
if (factory) {
assistant = factory->createPaintingAssistant();
location = external ? QString() : uri;
location += m_d->imageName + ASSISTANTS_PATH;
file_path = location + loadedAssistant.key();
assistant->loadXml(store, handleMap, file_path);
assistant->setAssistantGlobalColorCache(globalColor);
//If an assistant has too few handles than it should according to it's own setup, just don't load it//
if (assistant->handles().size()==assistant->numHandles()){
m_d->assistants.append(toQShared(assistant));
}
}
loadedAssistant++;
}
}
void KisKraLoader::loadAnimationMetadata(const KoXmlElement &element, KisImageSP image)
{
QDomDocument qDom;
KoXml::asQDomElement(qDom, element);
QDomElement qElement = qDom.firstChildElement();
float framerate;
KisTimeRange range;
int currentTime;
KisImageAnimationInterface *animation = image->animationInterface();
if (KisDomUtils::loadValue(qElement, "framerate", &framerate)) {
animation->setFramerate(framerate);
}
if (KisDomUtils::loadValue(qElement, "range", &range)) {
animation->setFullClipRange(range);
}
if (KisDomUtils::loadValue(qElement, "currentTime", &currentTime)) {
animation->switchCurrentTimeAsync(currentTime);
}
}
KisNodeSP KisKraLoader::loadNodes(const KoXmlElement& element, KisImageSP image, KisNodeSP parent)
{
KoXmlNode node = element.firstChild();
KoXmlNode child;
if (!node.isNull()) {
if (node.isElement()) {
if (node.nodeName().toUpper() == LAYERS.toUpper() || node.nodeName().toUpper() == MASKS.toUpper()) {
for (child = node.lastChild(); !child.isNull(); child = child.previousSibling()) {
KisNodeSP node = loadNode(child.toElement(), image);
if (node) {
image->nextLayerName(); // Make sure the nameserver is current with the number of nodes.
image->addNode(node, parent);
if (node->inherits("KisLayer") && KoXml::childNodesCount(child) > 0) {
loadNodes(child.toElement(), image, node);
}
}
}
}
}
}
return parent;
}
KisNodeSP KisKraLoader::loadNode(const KoXmlElement& element, KisImageSP image)
{
// Nota bene: If you add new properties to layers, you should
// ALWAYS define a default value in case the property is not
// present in the layer definition: this helps a LOT with backward
// compatibility.
QString name = element.attribute(NAME, "No Name");
QUuid id = QUuid(element.attribute(UUID, QUuid().toString()));
qint32 x = element.attribute(X, "0").toInt();
qint32 y = element.attribute(Y, "0").toInt();
qint32 opacity = element.attribute(OPACITY, QString::number(OPACITY_OPAQUE_U8)).toInt();
if (opacity < OPACITY_TRANSPARENT_U8) opacity = OPACITY_TRANSPARENT_U8;
if (opacity > OPACITY_OPAQUE_U8) opacity = OPACITY_OPAQUE_U8;
const KoColorSpace* colorSpace = 0;
if ((element.attribute(COLORSPACE_NAME)).isNull()) {
dbgFile << "No attribute color space for layer: " << name;
colorSpace = image->colorSpace();
}
else {
QString colorspacename = element.attribute(COLORSPACE_NAME);
QString profileProductName;
convertColorSpaceNames(colorspacename, profileProductName);
QString colorspaceModel = KoColorSpaceRegistry::instance()->colorSpaceColorModelId(colorspacename).id();
QString colorspaceDepth = KoColorSpaceRegistry::instance()->colorSpaceColorDepthId(colorspacename).id();
dbgFile << "Searching color space: " << colorspacename << colorspaceModel << colorspaceDepth << " for layer: " << name;
// use default profile - it will be replaced later in completeLoading
colorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, "");
dbgFile << "found colorspace" << colorSpace;
if (!colorSpace) {
m_d->warningMessages << i18n("Layer %1 specifies an unsupported color model: %2.", name, colorspacename);
return 0;
}
}
const bool visible = element.attribute(VISIBLE, "1") == "0" ? false : true;
const bool locked = element.attribute(LOCKED, "0") == "0" ? false : true;
const bool collapsed = element.attribute(COLLAPSED, "0") == "0" ? false : true;
int colorLabelIndex = element.attribute(COLOR_LABEL, "0").toInt();
QVector<QColor> labels = KisNodeViewColorScheme::instance()->allColorLabels();
if (colorLabelIndex >= labels.size()) {
colorLabelIndex = labels.size() - 1;
}
// Now find out the layer type and do specific handling
QString nodeType;
if (m_d->syntaxVersion == 1) {
nodeType = element.attribute("layertype");
if (nodeType.isEmpty()) {
nodeType = PAINT_LAYER;
}
}
else {
nodeType = element.attribute(NODE_TYPE);
}
if (nodeType.isEmpty()) {
m_d->warningMessages << i18n("Layer %1 has an unsupported type.", name);
return 0;
}
KisNodeSP node = 0;
if (nodeType == PAINT_LAYER)
node = loadPaintLayer(element, image, name, colorSpace, opacity);
else if (nodeType == GROUP_LAYER)
node = loadGroupLayer(element, image, name, colorSpace, opacity);
else if (nodeType == ADJUSTMENT_LAYER)
node = loadAdjustmentLayer(element, image, name, colorSpace, opacity);
else if (nodeType == SHAPE_LAYER)
node = loadShapeLayer(element, image, name, colorSpace, opacity);
else if (nodeType == GENERATOR_LAYER)
node = loadGeneratorLayer(element, image, name, colorSpace, opacity);
else if (nodeType == CLONE_LAYER)
node = loadCloneLayer(element, image, name, colorSpace, opacity);
else if (nodeType == FILTER_MASK)
node = loadFilterMask(element);
else if (nodeType == TRANSFORM_MASK)
node = loadTransformMask(element);
else if (nodeType == TRANSPARENCY_MASK)
node = loadTransparencyMask(element);
else if (nodeType == SELECTION_MASK)
node = loadSelectionMask(image, element);
else if (nodeType == COLORIZE_MASK)
node = loadColorizeMask(image, element, colorSpace);
else if (nodeType == FILE_LAYER)
node = loadFileLayer(element, image, name, opacity);
else if (nodeType == REFERENCE_IMAGES_LAYER)
node = loadReferenceImagesLayer(element, image);
else {
m_d->warningMessages << i18n("Layer %1 has an unsupported type: %2.", name, nodeType);
return 0;
}
// Loading the node went wrong. Return empty node and leave to
// upstream to complain to the user
if (!node) {
m_d->warningMessages << i18n("Failure loading layer %1 of type: %2.", name, nodeType);
return 0;
}
node->setVisible(visible, true);
node->setUserLocked(locked);
node->setCollapsed(collapsed);
node->setColorLabelIndex(colorLabelIndex);
node->setX(x);
node->setY(y);
node->setName(name);
if (! id.isNull()) // if no uuid in file, new one has been generated already
node->setUuid(id);
if (node->inherits("KisLayer") || node->inherits("KisColorizeMask")) {
QString compositeOpName = element.attribute(COMPOSITE_OP, "normal");
node->setCompositeOpId(compositeOpName);
}
if (node->inherits("KisLayer")) {
KisLayer* layer = qobject_cast<KisLayer*>(node.data());
QBitArray channelFlags = stringToFlags(element.attribute(CHANNEL_FLAGS, ""), colorSpace->channelCount());
layer->setChannelFlags(channelFlags);
if (element.hasAttribute(LAYER_STYLE_UUID)) {
QString uuidString = element.attribute(LAYER_STYLE_UUID);
QUuid uuid(uuidString);
if (!uuid.isNull()) {
KisPSDLayerStyleSP dumbLayerStyle(new KisPSDLayerStyle());
dumbLayerStyle->setUuid(uuid);
layer->setLayerStyle(dumbLayerStyle);
} else {
warnKrita << "WARNING: Layer style for layer" << layer->name() << "contains invalid UUID" << uuidString;
}
}
}
if (node->inherits("KisGroupLayer")) {
if (element.hasAttribute(PASS_THROUGH_MODE)) {
bool value = element.attribute(PASS_THROUGH_MODE, "0") != "0";
KisGroupLayer *group = qobject_cast<KisGroupLayer*>(node.data());
group->setPassThroughMode(value);
}
}
const bool timelineEnabled = element.attribute(VISIBLE_IN_TIMELINE, "0") == "0" ? false : true;
node->setUseInTimeline(timelineEnabled);
if (node->inherits("KisPaintLayer")) {
- KisPaintLayer* layer = qobject_cast<KisPaintLayer*>(node.data());
- QBitArray channelLockFlags = stringToFlags(element.attribute(CHANNEL_LOCK_FLAGS, ""), colorSpace->channelCount());
+ KisPaintLayer* layer = qobject_cast<KisPaintLayer*>(node.data());
+ QBitArray channelLockFlags = stringToFlags(element.attribute(CHANNEL_LOCK_FLAGS, ""), colorSpace->channelCount());
layer->setChannelLockFlags(channelLockFlags);
bool onionEnabled = element.attribute(ONION_SKIN_ENABLED, "0") == "0" ? false : true;
layer->setOnionSkinEnabled(onionEnabled);
}
if (element.attribute(FILE_NAME).isNull()) {
m_d->layerFilenames[node.data()] = name;
}
else {
m_d->layerFilenames[node.data()] = element.attribute(FILE_NAME);
}
if (element.hasAttribute("selected") && element.attribute("selected") == "true") {
m_d->selectedNodes.append(node);
}
if (element.hasAttribute(KEYFRAME_FILE)) {
m_d->keyframeFilenames.insert(node.data(), element.attribute(KEYFRAME_FILE));
}
return node;
}
KisNodeSP KisKraLoader::loadPaintLayer(const KoXmlElement& element, KisImageSP image,
const QString& name, const KoColorSpace* cs, quint32 opacity)
{
Q_UNUSED(element);
KisPaintLayer* layer;
layer = new KisPaintLayer(image, name, opacity, cs);
Q_CHECK_PTR(layer);
return layer;
}
KisNodeSP KisKraLoader::loadFileLayer(const KoXmlElement& element, KisImageSP image, const QString& name, quint32 opacity)
{
QString filename = element.attribute("source", QString());
if (filename.isNull()) return 0;
bool scale = (element.attribute("scale", "true") == "true");
int scalingMethod = element.attribute("scalingmethod", "-1").toInt();
if (scalingMethod < 0) {
if (scale) {
scalingMethod = KisFileLayer::ToImagePPI;
}
else {
scalingMethod = KisFileLayer::None;
}
}
QString documentPath;
if (m_d->document) {
documentPath = m_d->document->url().toLocalFile();
}
QFileInfo info(documentPath);
QString basePath = info.absolutePath();
QString fullPath = QDir(basePath).filePath(QDir::cleanPath(filename));
if (!QFileInfo(fullPath).exists()) {
qApp->setOverrideCursor(Qt::ArrowCursor);
QString msg = i18nc(
"@info",
"The file associated to a file layer with the name \"%1\" is not found.\n\n"
"Expected path:\n"
"%2\n\n"
"Do you want to locate it manually?", name, fullPath);
int result = QMessageBox::warning(0, i18nc("@title:window", "File not found"), msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (result == QMessageBox::Yes) {
KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument");
dialog.setMimeTypeFilters(KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import));
dialog.setDefaultDir(basePath);
QString url = dialog.filename();
if (!QFileInfo(basePath).exists()) {
filename = url;
} else {
QDir d(basePath);
filename = d.relativeFilePath(url);
}
}
qApp->restoreOverrideCursor();
}
KisLayer *layer = new KisFileLayer(image, basePath, filename, (KisFileLayer::ScalingMethod)scalingMethod, name, opacity);
Q_CHECK_PTR(layer);
return layer;
}
KisNodeSP KisKraLoader::loadGroupLayer(const KoXmlElement& element, KisImageSP image,
const QString& name, const KoColorSpace* cs, quint32 opacity)
{
Q_UNUSED(element);
Q_UNUSED(cs);
QString attr;
KisGroupLayer* layer;
layer = new KisGroupLayer(image, name, opacity);
Q_CHECK_PTR(layer);
return layer;
}
KisNodeSP KisKraLoader::loadAdjustmentLayer(const KoXmlElement& element, KisImageSP image,
const QString& name, const KoColorSpace* cs, quint32 opacity)
{
// XXX: do something with filterversion?
Q_UNUSED(cs);
QString attr;
KisAdjustmentLayer* layer;
QString filtername;
QString legacy = filtername;
if ((filtername = element.attribute(FILTER_NAME)).isNull()) {
// XXX: Invalid adjustmentlayer! We should warn about it!
warnFile << "No filter in adjustment layer";
return 0;
}
//get deprecated filters.
if (filtername=="brightnesscontrast") {
legacy = filtername;
filtername = "perchannel";
}
if (filtername=="left edge detections"
|| filtername=="right edge detections"
|| filtername=="top edge detections"
|| filtername=="bottom edge detections") {
legacy = filtername;
filtername = "edge detection";
}
KisFilterSP f = KisFilterRegistry::instance()->value(filtername);
if (!f) {
warnFile << "No filter for filtername" << filtername << "";
return 0; // XXX: We don't have this filter. We should warn about it!
}
KisFilterConfigurationSP kfc = f->defaultConfiguration();
kfc->setProperty("legacy", legacy);
if (legacy=="brightnesscontrast") {
kfc->setProperty("colorModel", cs->colorModelId().id());
}
// We'll load the configuration and the selection later.
layer = new KisAdjustmentLayer(image, name, kfc, 0);
Q_CHECK_PTR(layer);
layer->setOpacity(opacity);
return layer;
}
KisNodeSP KisKraLoader::loadShapeLayer(const KoXmlElement& element, KisImageSP image,
const QString& name, const KoColorSpace* cs, quint32 opacity)
{
Q_UNUSED(element);
Q_UNUSED(cs);
QString attr;
KoShapeControllerBase * shapeController = 0;
if (m_d->document) {
shapeController = m_d->document->shapeController();
}
KisShapeLayer* layer = new KisShapeLayer(shapeController, image, name, opacity);
Q_CHECK_PTR(layer);
return layer;
}
KisNodeSP KisKraLoader::loadGeneratorLayer(const KoXmlElement& element, KisImageSP image,
const QString& name, const KoColorSpace* cs, quint32 opacity)
{
Q_UNUSED(cs);
// XXX: do something with generator version?
KisGeneratorLayer* layer;
QString generatorname = element.attribute(GENERATOR_NAME);
if (generatorname.isNull()) {
// XXX: Invalid generator layer! We should warn about it!
warnFile << "No generator in generator layer";
return 0;
}
KisGeneratorSP generator = KisGeneratorRegistry::instance()->value(generatorname);
if (!generator) {
warnFile << "No generator for generatorname" << generatorname << "";
return 0; // XXX: We don't have this generator. We should warn about it!
}
KisFilterConfigurationSP kgc = generator->defaultConfiguration();
// We'll load the configuration and the selection later.
layer = new KisGeneratorLayer(image, name, kgc, 0);
Q_CHECK_PTR(layer);
layer->setOpacity(opacity);
return layer;
}
KisNodeSP KisKraLoader::loadCloneLayer(const KoXmlElement& element, KisImageSP image,
const QString& name, const KoColorSpace* cs, quint32 opacity)
{
Q_UNUSED(cs);
KisCloneLayerSP layer = new KisCloneLayer(0, image, name, opacity);
KisNodeUuidInfo info;
if (! (element.attribute(CLONE_FROM_UUID)).isNull()) {
info = KisNodeUuidInfo(QUuid(element.attribute(CLONE_FROM_UUID)));
} else {
if ((element.attribute(CLONE_FROM)).isNull()) {
return 0;
} else {
info = KisNodeUuidInfo(element.attribute(CLONE_FROM));
}
}
layer->setCopyFromInfo(info);
if ((element.attribute(CLONE_TYPE)).isNull()) {
return 0;
} else {
layer->setCopyType((CopyLayerType) element.attribute(CLONE_TYPE).toInt());
}
return layer;
}
KisNodeSP KisKraLoader::loadFilterMask(const KoXmlElement& element)
{
QString attr;
KisFilterMask* mask;
QString filtername;
// XXX: should we check the version?
if ((filtername = element.attribute(FILTER_NAME)).isNull()) {
// XXX: Invalid filter layer! We should warn about it!
warnFile << "No filter in filter layer";
return 0;
}
KisFilterSP f = KisFilterRegistry::instance()->value(filtername);
if (!f) {
warnFile << "No filter for filtername" << filtername << "";
return 0; // XXX: We don't have this filter. We should warn about it!
}
KisFilterConfigurationSP kfc = f->defaultConfiguration();
// We'll load the configuration and the selection later.
mask = new KisFilterMask();
mask->setFilter(kfc);
Q_CHECK_PTR(mask);
return mask;
}
KisNodeSP KisKraLoader::loadTransformMask(const KoXmlElement& element)
{
Q_UNUSED(element);
KisTransformMask* mask;
/**
* We'll load the transform configuration later on a stage
* of binary data loading
*/
mask = new KisTransformMask();
Q_CHECK_PTR(mask);
return mask;
}
KisNodeSP KisKraLoader::loadTransparencyMask(const KoXmlElement& element)
{
Q_UNUSED(element);
KisTransparencyMask* mask = new KisTransparencyMask();
Q_CHECK_PTR(mask);
return mask;
}
KisNodeSP KisKraLoader::loadSelectionMask(KisImageSP image, const KoXmlElement& element)
{
KisSelectionMaskSP mask = new KisSelectionMask(image);
bool active = element.attribute(ACTIVE, "1") == "0" ? false : true;
mask->setActive(active);
Q_CHECK_PTR(mask);
return mask;
}
KisNodeSP KisKraLoader::loadColorizeMask(KisImageSP image, const KoXmlElement& element, const KoColorSpace *colorSpace)
{
KisColorizeMaskSP mask = new KisColorizeMask();
const bool editKeystrokes = element.attribute(COLORIZE_EDIT_KEYSTROKES, "1") == "0" ? false : true;
const bool showColoring = element.attribute(COLORIZE_SHOW_COLORING, "1") == "0" ? false : true;
KisLayerPropertiesIcons::setNodeProperty(mask, KisLayerPropertiesIcons::colorizeEditKeyStrokes, editKeystrokes, image);
KisLayerPropertiesIcons::setNodeProperty(mask, KisLayerPropertiesIcons::colorizeShowColoring, showColoring, image);
const bool useEdgeDetection = KisDomUtils::toInt(element.attribute(COLORIZE_USE_EDGE_DETECTION, "0"));
const qreal edgeDetectionSize = KisDomUtils::toDouble(element.attribute(COLORIZE_EDGE_DETECTION_SIZE, "4"));
const qreal radius = KisDomUtils::toDouble(element.attribute(COLORIZE_FUZZY_RADIUS, "0"));
const int cleanUp = KisDomUtils::toInt(element.attribute(COLORIZE_CLEANUP, "0"));
const bool limitToDevice = KisDomUtils::toInt(element.attribute(COLORIZE_LIMIT_TO_DEVICE, "0"));
mask->setUseEdgeDetection(useEdgeDetection);
mask->setEdgeDetectionSize(edgeDetectionSize);
mask->setFuzzyRadius(radius);
mask->setCleanUpAmount(qreal(cleanUp) / 100.0);
mask->setLimitToDeviceBounds(limitToDevice);
delete mask->setColorSpace(colorSpace);
mask->setImage(image);
return mask;
}
void KisKraLoader::loadCompositions(const KoXmlElement& elem, KisImageSP image)
{
KoXmlNode child;
for (child = elem.firstChild(); !child.isNull(); child = child.nextSibling()) {
KoXmlElement e = child.toElement();
QString name = e.attribute("name");
bool exportEnabled = e.attribute("exportEnabled", "1") == "0" ? false : true;
KisLayerCompositionSP composition(new KisLayerComposition(image, name));
composition->setExportEnabled(exportEnabled);
KoXmlNode value;
for (value = child.lastChild(); !value.isNull(); value = value.previousSibling()) {
KoXmlElement e = value.toElement();
QUuid uuid(e.attribute("uuid"));
bool visible = e.attribute("visible", "1") == "0" ? false : true;
composition->setVisible(uuid, visible);
bool collapsed = e.attribute("collapsed", "1") == "0" ? false : true;
composition->setCollapsed(uuid, collapsed);
}
image->addComposition(composition);
}
}
void KisKraLoader::loadAssistantsList(const KoXmlElement &elem)
{
KoXmlNode child;
int count = 0;
for (child = elem.firstChild(); !child.isNull(); child = child.nextSibling()) {
KoXmlElement e = child.toElement();
QString type = e.attribute("type");
QString file_name = e.attribute("filename");
m_d->assistantsFilenames.insert(file_name,type);
count++;
}
}
void KisKraLoader::loadGrid(const KoXmlElement& elem)
{
QDomDocument dom;
KoXml::asQDomElement(dom, elem);
QDomElement domElement = dom.firstChildElement();
KisGridConfig config;
config.loadDynamicDataFromXml(domElement);
config.loadStaticData();
m_d->document->setGridConfig(config);
}
void KisKraLoader::loadGuides(const KoXmlElement& elem)
{
QDomDocument dom;
KoXml::asQDomElement(dom, elem);
QDomElement domElement = dom.firstChildElement();
KisGuidesConfig guides;
guides.loadFromXml(domElement);
m_d->document->setGuidesConfig(guides);
}
void KisKraLoader::loadAudio(const KoXmlElement& elem, KisImageSP image)
{
QDomDocument dom;
KoXml::asQDomElement(dom, elem);
QDomElement qElement = dom.firstChildElement();
QString fileName;
if (KisDomUtils::loadValue(qElement, "masterChannelPath", &fileName)) {
fileName = QDir::toNativeSeparators(fileName);
QDir baseDirectory = QFileInfo(m_d->document->localFilePath()).absoluteDir();
fileName = baseDirectory.absoluteFilePath(fileName);
QFileInfo info(fileName);
if (!info.exists()) {
qApp->setOverrideCursor(Qt::ArrowCursor);
QString msg = i18nc(
"@info",
"Audio channel file \"%1\" doesn't exist!\n\n"
"Expected path:\n"
"%2\n\n"
"Do you want to locate it manually?", info.fileName(), info.absoluteFilePath());
int result = QMessageBox::warning(0, i18nc("@title:window", "File not found"), msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (result == QMessageBox::Yes) {
info.setFile(KisImportExportManager::askForAudioFileName(info.absolutePath(), 0));
}
qApp->restoreOverrideCursor();
}
if (info.exists()) {
image->animationInterface()->setAudioChannelFileName(info.absoluteFilePath());
}
}
bool audioMuted = false;
if (KisDomUtils::loadValue(qElement, "audioMuted", &audioMuted)) {
image->animationInterface()->setAudioMuted(audioMuted);
}
qreal audioVolume = 0.5;
if (KisDomUtils::loadValue(qElement, "audioVolume", &audioVolume)) {
image->animationInterface()->setAudioVolume(audioVolume);
}
}
KisNodeSP KisKraLoader::loadReferenceImagesLayer(const KoXmlElement &elem, KisImageSP image)
{
KisSharedPtr<KisReferenceImagesLayer> layer =
new KisReferenceImagesLayer(m_d->document->shapeController(), image);
m_d->document->setReferenceImagesLayer(layer, false);
for (QDomElement child = elem.firstChildElement(); !child.isNull(); child = child.nextSiblingElement()) {
if (child.nodeName().toLower() == "referenceimage") {
auto* reference = KisReferenceImage::fromXml(child);
layer->addShape(reference);
}
}
return layer;
}
diff --git a/plugins/impex/tga/tga.h b/plugins/impex/tga/tga.h
index 0c239071d6..ae52f0d0bb 100644
--- a/plugins/impex/tga/tga.h
+++ b/plugins/impex/tga/tga.h
@@ -1,114 +1,114 @@
/* This file is part of the KDE project
Copyright (C) 2003 Dominik Seichter <domseichter@web.de>
Copyright (C) 2004 Ignacio Castaño <castano@ludicon.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the Lesser 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 code supports:
* reading:
* uncompressed and run length encoded indexed, grey and color tga files.
* image types 1, 2, 3, 9, 10 and 11.
* only RGB color maps with no more than 256 colors.
* pixel formats 8, 15, 24 and 32.
* writing:
* uncompressed true color tga files
*/
#ifndef TGA_H
#define TGA_H
#include <QDataStream>
#include <QImage>
#include <QColor>
// Header format of saved files.
const uchar targaMagic[12] = { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
enum TGAType {
TGA_TYPE_INDEXED = 1,
TGA_TYPE_RGB = 2,
TGA_TYPE_GREY = 3,
TGA_TYPE_RLE_INDEXED = 9,
TGA_TYPE_RLE_RGB = 10,
TGA_TYPE_RLE_GREY = 11
};
#define TGA_INTERLEAVE_MASK 0xc0
#define TGA_INTERLEAVE_NONE 0x00
#define TGA_INTERLEAVE_2WAY 0x40
#define TGA_INTERLEAVE_4WAY 0x80
#define TGA_ORIGIN_MASK 0x30
#define TGA_ORIGIN_LEFT 0x00
#define TGA_ORIGIN_RIGHT 0x10
#define TGA_ORIGIN_LOWER 0x00
#define TGA_ORIGIN_UPPER 0x20
/** Tga Header. */
struct TgaHeader {
uchar id_length;
uchar colormap_type;
uchar image_type;
ushort colormap_index;
ushort colormap_length;
uchar colormap_size;
ushort x_origin;
ushort y_origin;
ushort width;
ushort height;
uchar pixel_size;
uchar flags;
enum { SIZE = 18 }; // const static int SIZE = 18;
};
struct Color555 {
ushort b : 5;
ushort g : 5;
ushort r : 5;
};
struct TgaHeaderInfo {
bool rle;
bool pal;
bool rgb;
bool grey;
TgaHeaderInfo(const TgaHeader & tga) : rle(false), pal(false), rgb(false), grey(false) {
switch (tga.image_type) {
case TGA_TYPE_RLE_INDEXED:
rle = true;
- /* Falls through */
+ Q_FALLTHROUGH();
case TGA_TYPE_INDEXED:
pal = true;
break;
case TGA_TYPE_RLE_RGB:
rle = true;
- /* Falls through */
+ Q_FALLTHROUGH();
case TGA_TYPE_RGB:
rgb = true;
break;
case TGA_TYPE_RLE_GREY:
rle = true;
- /* Falls through */
+ Q_FALLTHROUGH();
case TGA_TYPE_GREY:
grey = true;
break;
default:
// Error, unknown image type.
break;
}
}
};
#endif // TGA_H
diff --git a/plugins/paintops/libpaintop/KisMaskingBrushOption.cpp b/plugins/paintops/libpaintop/KisMaskingBrushOption.cpp
index a55bb84983..c180d5962c 100644
--- a/plugins/paintops/libpaintop/KisMaskingBrushOption.cpp
+++ b/plugins/paintops/libpaintop/KisMaskingBrushOption.cpp
@@ -1,129 +1,129 @@
/*
* Copyright (c) 2017 Dmitry Kazakov <dimula73@gmail.com>
*
* 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 "KisMaskingBrushOption.h"
#include "kis_brush_chooser.h"
#include "kis_brush_selection_widget.h"
#include <QWidget>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QComboBox>
#include <QDomDocument>
#include "kis_brush.h"
#include "kis_image.h"
#include "kis_brush_option.h"
#include "KisMaskingBrushOptionProperties.h"
#include <strokes/KisMaskingBrushCompositeOpFactory.h>
#include <KoCompositeOpRegistry.h>
struct KisMaskingBrushOption::Private
{
Private()
: ui(new QWidget())
{
- QVBoxLayout *l = new QVBoxLayout(ui.data());
+ QVBoxLayout *l = new QVBoxLayout();
- QHBoxLayout *compositeOpLayout = new QHBoxLayout(ui.data());
+ QHBoxLayout *compositeOpLayout = new QHBoxLayout();
compositeSelector = new QComboBox(ui.data());
const QStringList supportedComposites = KisMaskingBrushCompositeOpFactory::supportedCompositeOpIds();
Q_FOREACH (const QString &id, supportedComposites) {
const QString name = KoCompositeOpRegistry::instance().getKoID(id).name();
compositeSelector->addItem(name, id);
}
compositeSelector->setCurrentIndex(0);
compositeOpLayout->addWidget(new QLabel(i18n("Blending Mode:")), 0);
compositeOpLayout->addWidget(compositeSelector, 1);
l->addLayout(compositeOpLayout, 0);
brushChooser = new KisBrushSelectionWidget(ui.data());
l->addWidget(brushChooser, 1);
ui->setLayout(l);
}
QScopedPointer<QWidget> ui;
KisBrushSelectionWidget *brushChooser = 0;
QComboBox *compositeSelector = 0;
MasterBrushSizeAdapter masterBrushSizeAdapter;
};
KisMaskingBrushOption::KisMaskingBrushOption(MasterBrushSizeAdapter masterBrushSizeAdapter)
: KisPaintOpOption(KisPaintOpOption::MASKING_BRUSH, false),
m_d(new Private())
{
m_d->masterBrushSizeAdapter = masterBrushSizeAdapter;
setObjectName("KisMaskingBrushOption");
setConfigurationPage(m_d->ui.data());
connect(m_d->brushChooser, SIGNAL(sigBrushChanged()), SLOT(emitSettingChanged()));
connect(m_d->compositeSelector, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged()));
}
KisMaskingBrushOption::~KisMaskingBrushOption()
{
}
void KisMaskingBrushOption::writeOptionSetting(KisPropertiesConfigurationSP setting) const
{
KisMaskingBrushOptionProperties props;
props.isEnabled = isChecked();
props.brush = m_d->brushChooser->brush();
props.compositeOpId = m_d->compositeSelector->currentData().toString();
props.write(setting.data(), m_d->masterBrushSizeAdapter());
}
void KisMaskingBrushOption::readOptionSetting(const KisPropertiesConfigurationSP setting)
{
KisMaskingBrushOptionProperties props;
props.read(setting.data(), m_d->masterBrushSizeAdapter());
setChecked(props.isEnabled);
const int selectedIndex = qMax(0, m_d->compositeSelector->findData(props.compositeOpId));
m_d->compositeSelector->setCurrentIndex(selectedIndex);
if (props.brush) {
m_d->brushChooser->setCurrentBrush(props.brush);
}
}
void KisMaskingBrushOption::setImage(KisImageWSP image)
{
m_d->brushChooser->setImage(image);
}
void KisMaskingBrushOption::lodLimitations(KisPaintopLodLimitations *l) const
{
KisBrushSP brush = m_d->brushChooser->brush();
if (brush) {
brush->lodLimitations(l);
}
}
diff --git a/plugins/paintops/libpaintop/kis_color_source.h b/plugins/paintops/libpaintop/kis_color_source.h
index eb8386f9c5..2801522860 100644
--- a/plugins/paintops/libpaintop/kis_color_source.h
+++ b/plugins/paintops/libpaintop/kis_color_source.h
@@ -1,151 +1,152 @@
/*
* Copyright (c) 2006-2007, 2009 Cyrille Berger <cberger@cberger.net>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _KIS_DYNAMIC_COLORING_H_
#define _KIS_DYNAMIC_COLORING_H_
#include "kis_paintop_option.h"
#include <QRect>
#include <KoColor.h>
#include <kis_types.h>
#include <kritapaintop_export.h>
class KoAbstractGradient;
class KoColorTransformation;
class KisPaintInformation;
/**
* A color source allow to abstract how a brush is colorized,
* and to apply transformation.
*
* The first function to call is @ref selectColor , then any of the transformation.
*/
class PAINTOP_EXPORT KisColorSource
{
public:
virtual ~KisColorSource();
public:
/**
* This is function is called to initialize the color that will be used for the dab.
* @param mix is a parameter between 0.0 and 1.0
+ * @param pi paint information
*/
virtual void selectColor(double mix, const KisPaintInformation &pi) = 0;
/**
* Apply a color transformation on the selected color
*/
virtual void applyColorTransformation(const KoColorTransformation* transfo) = 0;
virtual const KoColorSpace* colorSpace() const = 0;
/**
* Apply the color on a paint device
*/
virtual void colorize(KisPaintDeviceSP, const QRect& rect, const QPoint& _offset) const = 0;
/**
* @return true if the color is an uniform color
*/
virtual bool isUniformColor() const = 0;
/**
* @return the color if the color is uniformed
*/
virtual const KoColor& uniformColor() const;
};
class PAINTOP_EXPORT KisUniformColorSource : public KisColorSource
{
public:
KisUniformColorSource();
~KisUniformColorSource() override;
virtual void rotate(double);
virtual void resize(double , double);
void applyColorTransformation(const KoColorTransformation* transfo) override;
const KoColorSpace* colorSpace() const override;
void colorize(KisPaintDeviceSP, const QRect& rect, const QPoint& offset) const override;
bool isUniformColor() const override;
const KoColor& uniformColor() const override;
protected:
KoColor m_color;
};
class PAINTOP_EXPORT KisPlainColorSource : public KisUniformColorSource
{
public:
KisPlainColorSource(const KoColor& backGroundColor, const KoColor& foreGroundColor);
~KisPlainColorSource() override;
void selectColor(double mix, const KisPaintInformation &pi) override;
private:
KoColor m_backGroundColor;
KoColor m_cachedBackGroundColor;
KoColor m_foreGroundColor;
};
class PAINTOP_EXPORT KisGradientColorSource : public KisUniformColorSource
{
public:
KisGradientColorSource(const KoAbstractGradient* gradient, const KoColorSpace* workingCS);
~KisGradientColorSource() override;
void selectColor(double mix, const KisPaintInformation &pi) override;
private:
const KoAbstractGradient* m_gradient;
};
class PAINTOP_EXPORT KisUniformRandomColorSource : public KisUniformColorSource
{
public:
KisUniformRandomColorSource();
~KisUniformRandomColorSource() override;
void selectColor(double mix, const KisPaintInformation &pi) override;
};
class PAINTOP_EXPORT KisTotalRandomColorSource : public KisColorSource
{
public:
KisTotalRandomColorSource();
~KisTotalRandomColorSource() override;
public:
void selectColor(double mix, const KisPaintInformation &pi) override;
void applyColorTransformation(const KoColorTransformation* transfo) override;
const KoColorSpace* colorSpace() const override;
void colorize(KisPaintDeviceSP, const QRect& rect, const QPoint& offset) const override;
virtual void rotate(double r);
virtual void resize(double xs, double ys);
bool isUniformColor() const override;
private:
const KoColorSpace* m_colorSpace;
};
class PAINTOP_EXPORT KoPatternColorSource : public KisColorSource
{
public:
KoPatternColorSource(KisPaintDeviceSP _pattern, int _width, int _height, bool _locked);
~KoPatternColorSource() override;
public:
void selectColor(double mix, const KisPaintInformation &pi) override;
void applyColorTransformation(const KoColorTransformation* transfo) override;
const KoColorSpace* colorSpace() const override;
void colorize(KisPaintDeviceSP, const QRect& rect, const QPoint& _offset) const override;
virtual void rotate(double r);
virtual void resize(double xs, double ys);
bool isUniformColor() const override;
private:
const KisPaintDeviceSP m_device;
QRect m_bounds;
bool m_locked;
};
#endif
diff --git a/plugins/paintops/libpaintop/kis_dab_cache_base.h b/plugins/paintops/libpaintop/kis_dab_cache_base.h
index 3dff16df20..3c0d6bc0ba 100644
--- a/plugins/paintops/libpaintop/kis_dab_cache_base.h
+++ b/plugins/paintops/libpaintop/kis_dab_cache_base.h
@@ -1,128 +1,123 @@
/*
* Copyright (c) 2012 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_DAB_CACHE_BASE_H
#define __KIS_DAB_CACHE_BASE_H
#include "kritapaintop_export.h"
#include "kis_brush.h"
#include "KisDabCacheUtils.h"
class KisColorSource;
class KisPressureSharpnessOption;
class KisTextureProperties;
class KisPressureMirrorOption;
class KisPrecisionOption;
struct MirrorProperties;
/**
* @brief The KisDabCacheBase class provides caching for dabs into the brush paintop
*
* This class adds caching of the dabs to the paintop system of Krita.
* Such cache makes the execution of the benchmarks up to 2 times faster.
* Subjectively, the real painting becomes much faster, especially with
* huge brushes. Artists report up to 20% speed gain while painting.
*
* Of course, such caching makes the painting a bit less precise: we need
* to tolerate subpixel differences to allow the cache to work. Sometimes
* small difference in the size of a dab can also be acceptable. That is
* why I introduced levels of precision. They are graded from 1 to 5: from
* the fastest and less precise to the slowest, but with the best quality.
* You can see the slider in the paintop settings dialog. The ToolTip text
* explains which features of the brush are sacrificed on each precision
* level.
*
* The texturing and mirroring problems are solved.
*/
class PAINTOP_EXPORT KisDabCacheBase
{
public:
KisDabCacheBase();
~KisDabCacheBase();
void setMirrorPostprocessing(KisPressureMirrorOption *option);
void setPrecisionOption(KisPrecisionOption *option);
/**
* Disables handling of the subPixelX and subPixelY values, this
* is needed at least for the Color Smudge paint op, which reads
* aligned areas from image, so additional offsets generated by
* the subpixel precision should be avoided
*/
void disableSubpixelPrecision();
/**
* Return true if the dab needs postprocessing by special options
* like 'texture' or 'sharpness'
*/
bool needSeparateOriginal(KisTextureProperties *textureOption,
KisPressureSharpnessOption *sharpnessOption) const;
protected:
/**
* Fetches all the necessary information for dab generation and
* tells if the caller must should reuse the preciously returned dab. *
* Please note that KisDabCacheBase has an internal state, that keeps the
* parameters of the previously generated (on a cache-miss) dab. This function
* automatically updates this state when 'shouldUseCache == false'. Therefore, the
* caller *must* generate the dab if and only if when 'shouldUseCache == false'.
* Otherwise the internal state will become inconsistent.
*
* @param hasDabInCache shows if the caller has something in its cache
* @param resources rendering resources available for this dab
- * @param color current painting color
- * @param cursorPoint cursor point at which the dab should be painted
- * @param shape dab shape requested by the caller. It will be modified before
- * generation to accommodate the mirroring and rotation options.
- * @param info painting info associated with the dab
- * @param softnessFactor softness factor
+ * @param request the request information
* @param di (OUT) calculated dab generation information
* @param shouldUseCache (OUT) shows whether the caller *must* use cache or not
*/
void fetchDabGenerationInfo(bool hasDabInCache,
KisDabCacheUtils::DabRenderingResources *resources,
const KisDabCacheUtils::DabRequestInfo &request,
/* out */
KisDabCacheUtils::DabGenerationInfo *di,
bool *shouldUseCache);
private:
struct SavedDabParameters;
struct DabPosition;
private:
inline SavedDabParameters getDabParameters(KisBrushSP brush, const KoColor& color,
KisDabShape const&,
const KisPaintInformation& info,
double subPixelX, double subPixelY,
qreal softnessFactor,
MirrorProperties mirrorProperties);
inline KisDabCacheBase::DabPosition
calculateDabRect(KisBrushSP brush, const QPointF &cursorPoint,
KisDabShape,
const KisPaintInformation& info,
const MirrorProperties &mirrorProperties, KisPressureSharpnessOption *sharpnessOption);
private:
struct Private;
Private * const m_d;
};
#endif /* __KIS_DAB_CACHE_BASE_H */
diff --git a/plugins/paintops/libpaintop/kis_dynamic_sensor.h b/plugins/paintops/libpaintop/kis_dynamic_sensor.h
index 80a31bce4c..a4b843c36f 100644
--- a/plugins/paintops/libpaintop/kis_dynamic_sensor.h
+++ b/plugins/paintops/libpaintop/kis_dynamic_sensor.h
@@ -1,220 +1,221 @@
/*
* Copyright (c) 2006 Cyrille Berger <cberger@cberger.net>
* Copyright (c) 2011 Lukáš Tvrdý <lukast.dev@gmail.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _KIS_DYNAMIC_SENSOR_H_
#define _KIS_DYNAMIC_SENSOR_H_
#include <kritapaintop_export.h>
#include <QObject>
#include <KoID.h>
#include <klocalizedstring.h>
#include "kis_serializable_configuration.h"
#include "kis_curve_label.h"
#include <kis_cubic_curve.h>
#include <kis_shared_ptr.h>
#include <kis_shared.h>
class QWidget;
class KisPaintInformation;
const KoID FuzzyPerDabId("fuzzy", ki18n("Fuzzy Dab")); ///< generate a random number
const KoID FuzzyPerStrokeId("fuzzystroke", ki18n("Fuzzy Stroke")); ///< generate a random number
const KoID SpeedId("speed", ki18n("Speed")); ///< generate a number depending on the speed of the cursor
const KoID FadeId("fade", ki18n("Fade")); ///< generate a number that increase every time you call it (e.g. per dab)
const KoID DistanceId("distance", ki18n("Distance")); ///< generate a number that increase with distance
const KoID TimeId("time", ki18n("Time")); ///< generate a number that increase with time
const KoID DrawingAngleId("drawingangle", ki18n("Drawing angle")); ///< number depending on the angle
const KoID RotationId("rotation", ki18n("Rotation")); ///< rotation coming from the device
const KoID PressureId("pressure", ki18n("Pressure")); ///< number depending on the pressure
const KoID PressureInId("pressurein", ki18n("PressureIn")); ///< number depending on the pressure
const KoID XTiltId("xtilt", ki18n("X-Tilt")); ///< number depending on X-tilt
const KoID YTiltId("ytilt", ki18n("Y-Tilt")); ///< number depending on Y-tilt
/**
* "TiltDirection" and "TiltElevation" parameters are written to
* preset files as "ascension" and "declination" to keep backward
* compatibility with older presets from the days when they were called
* differently.
*/
const KoID TiltDirectionId("ascension", ki18n("Tilt direction")); /// < number depending on the X and Y tilt, tilt direction is 0 when stylus nib points to you and changes clockwise from -180 to +180.
const KoID TiltElevationId("declination", ki18n("Tilt elevation")); /// < tilt elevation is 90 when stylus is perpendicular to tablet and 0 when it's parallel to tablet
const KoID PerspectiveId("perspective", ki18n("Perspective")); ///< number depending on the distance on the perspective grid
const KoID TangentialPressureId("tangentialpressure", ki18n("Tangential pressure")); ///< the wheel on an airbrush device
const KoID SensorsListId("sensorslist", "SHOULD NOT APPEAR IN THE UI !"); ///< this a non user-visible sensor that can store a list of other sensors, and multiply their output
class KisDynamicSensor;
typedef KisSharedPtr<KisDynamicSensor> KisDynamicSensorSP;
enum DynamicSensorType {
FUZZY_PER_DAB,
FUZZY_PER_STROKE,
SPEED,
FADE,
DISTANCE,
TIME,
ANGLE,
ROTATION,
PRESSURE,
XTILT,
YTILT,
TILT_DIRECTION,
TILT_ELEVATATION,
PERSPECTIVE,
TANGENTIAL_PRESSURE,
SENSORS_LIST,
PRESSURE_IN,
UNKNOWN = 255
};
/**
* Sensors are used to extract from KisPaintInformation a single
* double value which can be used to control the parameters of
* a brush.
*/
class PAINTOP_EXPORT KisDynamicSensor : public KisSerializableConfiguration
{
public:
enum ParameterSign {
NegativeParameter = -1,
UnSignedParameter = 0,
PositiveParameter = 1
};
protected:
KisDynamicSensor(DynamicSensorType type);
public:
~KisDynamicSensor() override;
/**
* @return the value of this sensor for the given KisPaintInformation
*/
qreal parameter(const KisPaintInformation& info);
/**
* This function is call before beginning a stroke to reset the sensor.
* Default implementation does nothing.
*/
virtual void reset();
/**
+ * @param parent the parent QWidget
* @param selector is a \ref QWidget that contains a signal called "parametersChanged()"
*/
virtual QWidget* createConfigurationWidget(QWidget* parent, QWidget* selector);
/**
* Creates a sensor from its identifier.
*/
static KisDynamicSensorSP id2Sensor(const KoID& id, const QString &parentOptionName);
static KisDynamicSensorSP id2Sensor(const QString& s, const QString &parentOptionName) {
return id2Sensor(KoID(s), parentOptionName);
}
static DynamicSensorType id2Type(const KoID& id);
static DynamicSensorType id2Type(const QString& s) {
return id2Type(KoID(s));
}
/**
* type2Sensor creates a new sensor for the give type
*/
static KisDynamicSensorSP type2Sensor(DynamicSensorType sensorType, const QString &parentOptionName);
static QString minimumLabel(DynamicSensorType sensorType);
static QString maximumLabel(DynamicSensorType sensorType, int max = -1);
static int minimumValue(DynamicSensorType sensorType);
static int maximumValue(DynamicSensorType sensorType, int max = -1);
static QString valueSuffix(DynamicSensorType sensorType);
static KisDynamicSensorSP createFromXML(const QString&, const QString &parentOptionName);
static KisDynamicSensorSP createFromXML(const QDomElement&, const QString &parentOptionName);
/**
* @return the list of sensors
*/
static QList<KoID> sensorsIds();
static QList<DynamicSensorType> sensorsTypes();
/**
* @return the identifier of this sensor
*/
static QString id(DynamicSensorType sensorType);
using KisSerializableConfiguration::fromXML;
using KisSerializableConfiguration::toXML;
void toXML(QDomDocument&, QDomElement&) const override;
void fromXML(const QDomElement&) override;
void setCurve(const KisCubicCurve& curve);
const KisCubicCurve& curve() const;
void removeCurve();
bool hasCustomCurve() const;
void setActive(bool active);
bool isActive() const;
virtual bool dependsOnCanvasRotation() const;
virtual bool isAdditive() const;
virtual bool isAbsoluteRotation() const;
inline DynamicSensorType sensorType() const { return m_type; }
/**
* @return the currently set length or -1 if not relevant
*/
int length() { return m_length; }
public:
static inline qreal scalingToAdditive(qreal x) {
return -1.0 + 2.0 * x;
}
static inline qreal additiveToScaling(qreal x) {
return 0.5 * (1.0 + x);
}
protected:
virtual qreal value(const KisPaintInformation& info) = 0;
int m_length;
private:
Q_DISABLE_COPY(KisDynamicSensor)
DynamicSensorType m_type;
bool m_customCurve;
KisCubicCurve m_curve;
bool m_active;
};
#endif
diff --git a/plugins/paintops/libpaintop/kis_paintop_plugin_utils.h b/plugins/paintops/libpaintop/kis_paintop_plugin_utils.h
index 2a8b0e839d..a251d987ed 100644
--- a/plugins/paintops/libpaintop/kis_paintop_plugin_utils.h
+++ b/plugins/paintops/libpaintop/kis_paintop_plugin_utils.h
@@ -1,101 +1,114 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_PAINTOP_PLUGIN_UTILS_H
#define __KIS_PAINTOP_PLUGIN_UTILS_H
#include "kis_paint_information.h"
#include "kis_paintop_utils.h"
#include "kis_paintop_settings.h"
#include "kis_airbrush_option_widget.h"
#include "kis_pressure_spacing_option.h"
#include "kis_pressure_rate_option.h"
namespace KisPaintOpPluginUtils {
/**
* Similar to KisPaintOpUtils::effectiveSpacing, but some of the required parameters are obtained
* from the provided configuration options. This function assumes a common configuration where
* spacing and airbrush settings are configured through a KisPressureSpacingOption and
* KisAirbrushOption. This type of configuration is used by several different paintops.
+ * @param dabWidth - The dab width.
+ * @param dabHeight - The dab height.
+ * @param isotropicSpacing - If @c true the spacing should be isotropic.
+ * @param rotation - The rotation angle in radians.
+ * @param axesFlipped - If @c true the axes should be flipped.
+ * @param spacingVal - The spacing value.
+ * @param autoSpacingActive - If @c true the autospacing will be activated.
+ * @param autoSpacingCoeff - The autospacing coefficient.
+ * @param lodScale - The level of details scale.
* @param airbrushOption - The airbrushing option. Can be null for paintops that don't support
* airbrushing.
* @param spacingOption - The pressure-curve spacing option. Can be null for paintops that don't
* support pressure-based spacing.
+ * @param pi - The paint information.
+ * @see KisPaintInformation
*/
KisSpacingInformation effectiveSpacing(qreal dabWidth,
qreal dabHeight,
bool isotropicSpacing,
qreal rotation,
bool axesFlipped,
qreal spacingVal,
bool autoSpacingActive,
qreal autoSpacingCoeff,
qreal lodScale,
const KisAirbrushOptionProperties *airbrushOption,
const KisPressureSpacingOption *spacingOption,
const KisPaintInformation &pi)
{
// Extract required parameters.
bool distanceSpacingEnabled = true;
if (airbrushOption && airbrushOption->enabled) {
distanceSpacingEnabled = !airbrushOption->ignoreSpacing;
}
qreal extraScale = 1.0;
if (spacingOption && spacingOption->isChecked()) {
extraScale = spacingOption->apply(pi);
}
return KisPaintOpUtils::effectiveSpacing(dabWidth, dabHeight, extraScale,
distanceSpacingEnabled, isotropicSpacing, rotation,
axesFlipped, spacingVal, autoSpacingActive,
autoSpacingCoeff, lodScale);
}
/**
* Similar to KisPaintOpUtils::effectiveTiming, but some of the required parameters are obtained
* from the provided configuration options. This function assumes a common configuration where
* airbrush settings are configured through a KisAirbrushOption and KisPressureRateOption. This type
* of configuration is used by several different paintops.
* @param airbrushOption - The airbrushing option. Can be null for paintops that don't support
* airbrushing.
* @param rateOption - The pressure-curve airbrush rate option. Can be null for paintops that don't
* support a pressure-based airbrush rate.
+ * @param pi - The paint information.
+ * @see KisPaintInformation
*/
KisTimingInformation effectiveTiming(const KisAirbrushOptionProperties *airbrushOption,
const KisPressureRateOption *rateOption,
const KisPaintInformation &pi)
{
// Extract required parameters.
bool timingEnabled = false;
qreal timingInterval = LONG_TIME;
if (airbrushOption) {
timingEnabled = airbrushOption->enabled;
timingInterval = airbrushOption->airbrushInterval;
}
qreal rateExtraScale = 1.0;
if (rateOption && rateOption->isChecked()) {
rateExtraScale = rateOption->apply(pi);
}
return KisPaintOpUtils::effectiveTiming(timingEnabled, timingInterval, rateExtraScale);
}
}
#endif /* __KIS_PAINTOP_PLUGIN_UTILS_H */
diff --git a/plugins/paintops/libpaintop/kis_texture_option.h b/plugins/paintops/libpaintop/kis_texture_option.h
index 3100bfc023..90a6194708 100644
--- a/plugins/paintops/libpaintop/kis_texture_option.h
+++ b/plugins/paintops/libpaintop/kis_texture_option.h
@@ -1,97 +1,98 @@
/* This file is part of the KDE project
* Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2012
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KIS_TEXTURE_OPTION_H
#define KIS_TEXTURE_OPTION_H
#include <kritapaintop_export.h>
#include <kis_paint_device.h>
#include <kis_types.h>
#include "kis_paintop_option.h"
#include "kis_pressure_texture_strength_option.h"
#include "KisTextureMaskInfo.h"
#include <QRect>
class KisTextureChooser;
class KoPattern;
class KoResource;
class KisPropertiesConfiguration;
class KisPaintopLodLimitations;
class PAINTOP_EXPORT KisTextureOption : public KisPaintOpOption
{
Q_OBJECT
public:
explicit KisTextureOption();
~KisTextureOption() override;
public Q_SLOTS:
void writeOptionSetting(KisPropertiesConfigurationSP setting) const override;
void readOptionSetting(const KisPropertiesConfigurationSP setting) override;
void lodLimitations(KisPaintopLodLimitations *l) const override;
private Q_SLOTS:
void resetGUI(KoResource*); /// called when a new pattern is selected
private:
/// UI Widget that stores all the texture options
KisTextureChooser* m_textureOptions;
};
class PAINTOP_EXPORT KisTextureProperties
{
public:
KisTextureProperties(int levelOfDetail);
enum TexturingMode {
MULTIPLY,
SUBTRACT
};
bool m_enabled;
/**
* @brief apply combine the texture map with the dab
* @param dab the colored, final representation of the dab, after mirroring and everything.
* @param offset the position of the dab on the image. used to calculate the position of the mask pattern
+ * @param info the paint information
*/
void apply(KisFixedPaintDeviceSP dab, const QPoint& offset, const KisPaintInformation & info);
void fillProperties(const KisPropertiesConfigurationSP setting);
private:
int m_offsetX;
int m_offsetY;
TexturingMode m_texturingMode;
int m_levelOfDetail;
private:
KisPressureTextureStrengthOption m_strengthOption;
KisTextureMaskInfoSP m_maskInfo;
};
#endif // KIS_TEXTURE_OPTION_H
diff --git a/plugins/python/CMakeLists.txt b/plugins/python/CMakeLists.txt
index f666e0e91b..2624db1435 100644
--- a/plugins/python/CMakeLists.txt
+++ b/plugins/python/CMakeLists.txt
@@ -1,113 +1,117 @@
# Copyright (C) 2012, 2013 Shaheed Haque <srhaque@theiet.org>
# Copyright (C) 2013 Alex Turbov <i.zaufi@gmail.com>
# Copyright (C) 2014-2016 Boudewijn Rempt <boud@valdyas.org>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
include(CMakeParseArguments)
#
# Simple helper function to install plugin and related files
# having only a name of the plugin...
# (just to reduce syntactic noise when a lot of plugins get installed)
#
function(install_pykrita_plugin name)
set(_options)
set(_one_value_args)
set(_multi_value_args PATTERNS FILE)
cmake_parse_arguments(install_pykrita_plugin "${_options}" "${_one_value_args}" "${_multi_value_args}" ${ARGN})
if(NOT name)
message(FATAL_ERROR "Plugin filename is not given")
endif()
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${name}.py)
install(FILES kritapykrita_${name}.desktop DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita)
foreach(_f ${name}.py ${name}.ui ${install_pykrita_plugin_FILE})
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${_f})
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${_f} DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita)
endif()
endforeach()
elseif(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${name})
install(FILES ${name}/kritapykrita_${name}.desktop DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita)
install(
DIRECTORY ${name}
DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita
FILES_MATCHING
PATTERN "*.py"
PATTERN "*.ui"
PATTERN "*.txt"
PATTERN "*.csv"
PATTERN "*.html"
PATTERN "__pycache__*" EXCLUDE
+ PATTERN "tests*" EXCLUDE
)
# TODO Is there any way to form a long PATTERN options string
# and use it in a single install() call?
# NOTE Install specified patterns one-by-one...
foreach(_pattern ${install_pykrita_plugin_PATTERNS})
install(
DIRECTORY ${name}
DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita
FILES_MATCHING
PATTERN "${_pattern}"
PATTERN "__pycache__*" EXCLUDE
+ PATTERN "tests*" EXCLUDE
)
endforeach()
else()
message(FATAL_ERROR "Do not know what to do with ${name}")
endif()
endfunction()
install_pykrita_plugin(hello)
install_pykrita_plugin(assignprofiledialog)
install_pykrita_plugin(scripter)
install_pykrita_plugin(colorspace)
install_pykrita_plugin(documenttools)
install_pykrita_plugin(filtermanager)
install_pykrita_plugin(exportlayers)
#install_pykrita_plugin(highpass)
install_pykrita_plugin(tenbrushes)
install_pykrita_plugin(tenscripts)
install_pykrita_plugin(palette_docker)
install_pykrita_plugin(quick_settings_docker)
install_pykrita_plugin(lastdocumentsdocker)
# install_pykrita_plugin(scriptdocker)
install_pykrita_plugin(comics_project_management_tools)
install_pykrita_plugin(krita_script_starter)
+install_pykrita_plugin(plugin_importer)
# if(PYTHON_VERSION_MAJOR VERSION_EQUAL 3)
# install_pykrita_plugin(cmake_utils)
# install_pykrita_plugin(js_utils PATTERNS "*.json")
# install_pykrita_plugin(expand PATTERNS "*.expand" "templates/*.tpl")
# endif()
install( FILES
hello/hello.action
tenbrushes/tenbrushes.action
tenscripts/tenscripts.action
+ plugin_importer/plugin_importer.action
DESTINATION ${DATA_INSTALL_DIR}/krita/actions)
install(
DIRECTORY libkritapykrita
DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita
FILES_MATCHING
PATTERN "*.py"
PATTERN "__pycache__*" EXCLUDE
)
diff --git a/plugins/python/assignprofiledialog/__init__.py b/plugins/python/assignprofiledialog/__init__.py
index 68c3ce8b95..9d9559d754 100644
--- a/plugins/python/assignprofiledialog/__init__.py
+++ b/plugins/python/assignprofiledialog/__init__.py
@@ -1,2 +1,4 @@
-# let's make a module
-from .assignprofiledialog import *
+from .assignprofiledialog import AssignProfileDialog
+
+
+Scripter.addExtension(AssignProfileDialog(Application))
diff --git a/plugins/python/assignprofiledialog/assignprofiledialog.py b/plugins/python/assignprofiledialog/assignprofiledialog.py
index 443a4dde59..bb47a2b97f 100644
--- a/plugins/python/assignprofiledialog/assignprofiledialog.py
+++ b/plugins/python/assignprofiledialog/assignprofiledialog.py
@@ -1,60 +1,66 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
+
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QDialogButtonBox, QDialog,
QMessageBox, QComboBox, QVBoxLayout)
from krita import Extension
class AssignProfileDialog(Extension):
def __init__(self, parent):
super(AssignProfileDialog, self).__init__(parent)
def assignProfile(self):
doc = Application.activeDocument()
if doc is None:
- QMessageBox.information(Application.activeWindow().qwindow(), i18n("Assign Profile"), i18n("There is no active document."))
+ QMessageBox.information(
+ Application.activeWindow().qwindow(),
+ i18n("Assign Profile"),
+ i18n("There is no active document."))
return
self.dialog = QDialog(Application.activeWindow().qwindow())
self.cmbProfile = QComboBox(self.dialog)
- for profile in sorted(Application.profiles(doc.colorModel(), doc.colorDepth())):
+ for profile in sorted(
+ Application.profiles(doc.colorModel(), doc.colorDepth())):
self.cmbProfile.addItem(profile)
vbox = QVBoxLayout(self.dialog)
vbox.addWidget(self.cmbProfile)
self.buttonBox = QDialogButtonBox(self.dialog)
self.buttonBox.setOrientation(Qt.Horizontal)
- self.buttonBox.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+ self.buttonBox.setStandardButtons(
+ QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.buttonBox.accepted.connect(self.dialog.accept)
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.dialog.reject)
vbox.addWidget(self.buttonBox)
self.dialog.show()
self.dialog.activateWindow()
self.dialog.exec_()
def accept(self):
doc = Application.activeDocument()
doc.setColorProfile(self.cmbProfile.currentText())
def setup(self):
pass
def createActions(self, window):
- action = window.createAction("assing_profile_to_image", i18n("Assign Profile to Image"))
+ action = window.createAction("assing_profile_to_image",
+ i18n("Assign Profile to Image"))
action.triggered.connect(self.assignProfile)
-
-
-Scripter.addExtension(AssignProfileDialog(Application))
diff --git a/plugins/python/assignprofiledialog/kritapykrita_assignprofiledialog.desktop b/plugins/python/assignprofiledialog/kritapykrita_assignprofiledialog.desktop
index e476b542f3..0e54ca976d 100644
--- a/plugins/python/assignprofiledialog/kritapykrita_assignprofiledialog.desktop
+++ b/plugins/python/assignprofiledialog/kritapykrita_assignprofiledialog.desktop
@@ -1,51 +1,53 @@
[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=assignprofiledialog
X-Python-2-Compatible=true
X-Krita-Manual=Manual.html
Name=Assign Profile to Image
Name[ar]=إسناد اللاحات إلى الصور
Name[ca]=Assigna un perfil a una imatge
Name[ca@valencia]=Assigna un perfil a una imatge
Name[cs]=Přiřadit obrázku profil
Name[el]=Αντιστοίχιση προφίλ σε εικόνα
Name[en_GB]=Assign Profile to Image
Name[es]=Asignar perfil a imagen
Name[eu]=Esleitu profila irudiari
+Name[fi]=Liitä kuvaan profiili
Name[fr]=Attribuer un profil à l'image
Name[gl]=Asignar un perfil á imaxe
Name[is]=Úthluta litasniði á myndina
Name[it]=Assegna profilo a immagine
Name[nl]=Profiel aan afbeelding toewijzen
Name[pl]=Przypisz profil do obrazu
Name[pt]=Atribuir um Perfil à Imagem
Name[pt_BR]=Atribuir perfil a imagem
Name[sv]=Tilldela profil till bild
Name[tr]=Görüntüye Profil Ata
Name[uk]=Призначити профіль до зображення
Name[x-test]=xxAssign Profile to Imagexx
Name[zh_CN]=为图像指定色彩配置文件
Name[zh_TW]=指定設定檔到圖像
Comment=Assign a profile to an image without converting it.
Comment[ar]=أسنِد لاحة إلى صورة دون تحويلها.
Comment[ca]=Assigna un perfil a una imatge sense convertir-la.
Comment[ca@valencia]=Assigna un perfil a una imatge sense convertir-la.
Comment[el]=Αντιστοιχίζει ένα προφίλ σε μια εικόνα χωρίς μετατροπή.
Comment[en_GB]=Assign a profile to an image without converting it.
Comment[es]=Asignar un perfil a una imagen sin convertirla.
Comment[eu]=Esleitu profil bat irudi bati hura bihurtu gabe.
+Comment[fi]=Liitä kuvaan profiili muuntamatta kuvaa
Comment[fr]=Attribuer un profil à une image sans la convertir.
Comment[gl]=Asignar un perfil a unha imaxe sen convertela.
Comment[is]=Úthluta litasniði á myndina án þess að umbreyta henni.
Comment[it]=Assegna un profilo a un'immagine senza convertirla.
Comment[nl]=Een profiel aan een afbeelding toewijzen zonder het te converteren.
Comment[pl]=Przypisz profil do obrazu bez jego przekształcania.
Comment[pt]=Atribui um perfil à imagem sem a converter.
Comment[pt_BR]=Atribui um perfil para uma imagem sem convertê-la.
Comment[sv]=Tilldela en profil till en bild utan att konvertera den.
Comment[tr]=Bir görüntüye, görüntüyü değiştirmeden bir profil ata.
Comment[uk]=Призначити профіль до зображення без його перетворення.
Comment[x-test]=xxAssign a profile to an image without converting it.xx
Comment[zh_CN]=仅为图像指定色彩配置文件,不转换其色彩空间
Comment[zh_TW]=將設定檔指定給圖像,而不進行轉換。
diff --git a/plugins/python/colorspace/__init__.py b/plugins/python/colorspace/__init__.py
index 8148cb38f5..cc5cc77064 100644
--- a/plugins/python/colorspace/__init__.py
+++ b/plugins/python/colorspace/__init__.py
@@ -1,13 +1,18 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
-# let's make a module
-from .colorspace import *
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
+
+import krita
+from .colorspace import ColorSpaceExtension
+
+Scripter.addExtension(ColorSpaceExtension(krita.Krita.instance()))
diff --git a/plugins/python/colorspace/colorspace.py b/plugins/python/colorspace/colorspace.py
index fc5a4ab710..7270b31ea9 100644
--- a/plugins/python/colorspace/colorspace.py
+++ b/plugins/python/colorspace/colorspace.py
@@ -1,34 +1,35 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
+
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
import krita
from . import uicolorspace
class ColorSpaceExtension(krita.Extension):
def __init__(self, parent):
super(ColorSpaceExtension, self).__init__(parent)
def setup(self):
pass
def createActions(self, window):
action = window.createAction("color_space", i18n("Color Space"))
- action.setToolTip(i18n("Plugin to change color space of selected documents."))
+ action.setToolTip(
+ i18n("Plugin to change color space of selected documents."))
action.triggered.connect(self.initialize)
def initialize(self):
self.uicolorspace = uicolorspace.UIColorSpace()
self.uicolorspace.initialize()
-
-
-Scripter.addExtension(ColorSpaceExtension(krita.Krita.instance()))
diff --git a/plugins/python/colorspace/colorspacedialog.py b/plugins/python/colorspace/colorspacedialog.py
index 57e27e3d00..e1de2e61c2 100644
--- a/plugins/python/colorspace/colorspacedialog.py
+++ b/plugins/python/colorspace/colorspacedialog.py
@@ -1,21 +1,24 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
+
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
from PyQt5.QtWidgets import QDialog
class ColorSpaceDialog(QDialog):
def __init__(self, parent=None):
super(ColorSpaceDialog, self).__init__(parent)
def closeEvent(self, event):
event.accept()
diff --git a/plugins/python/colorspace/components/colordepthcombobox.py b/plugins/python/colorspace/components/colordepthcombobox.py
index e34d58393c..c97e4bbe36 100644
--- a/plugins/python/colorspace/components/colordepthcombobox.py
+++ b/plugins/python/colorspace/components/colordepthcombobox.py
@@ -1,25 +1,28 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
+
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
from PyQt5.QtWidgets import QComboBox
class ColorDepthComboBox(QComboBox):
def __init__(self, uiColorSpace, parent=None):
super(ColorDepthComboBox, self).__init__(parent)
self.uiColorSpace = uiColorSpace
self.currentTextChanged.connect(self.changedTextColorDepthComboBox)
def changedTextColorDepthComboBox(self, colorDepth):
self.uiColorSpace.loadColorProfiles()
diff --git a/plugins/python/colorspace/components/colormodelcombobox.py b/plugins/python/colorspace/components/colormodelcombobox.py
index 80ac4e7dba..0b55de9874 100644
--- a/plugins/python/colorspace/components/colormodelcombobox.py
+++ b/plugins/python/colorspace/components/colormodelcombobox.py
@@ -1,25 +1,28 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
+
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
from PyQt5.QtWidgets import QComboBox
class ColorModelComboBox(QComboBox):
def __init__(self, uiColorSpace, parent=None):
super(ColorModelComboBox, self).__init__(parent)
self.uiColorSpace = uiColorSpace
self.currentTextChanged.connect(self.changedTextColorModelComboBox)
def changedTextColorModelComboBox(self, colorModel):
self.uiColorSpace.loadColorDepths()
diff --git a/plugins/python/colorspace/components/colorprofilecombobox.py b/plugins/python/colorspace/components/colorprofilecombobox.py
index c067e34738..13a8ec9c36 100644
--- a/plugins/python/colorspace/components/colorprofilecombobox.py
+++ b/plugins/python/colorspace/components/colorprofilecombobox.py
@@ -1,21 +1,24 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
+
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
from PyQt5.QtWidgets import QComboBox
class ColorProfileComboBox(QComboBox):
def __init__(self, uiColorSpace, parent=None):
super(ColorProfileComboBox, self).__init__(parent)
self.uiColorSpace = uiColorSpace
self.setSizeAdjustPolicy(self.AdjustToContents)
diff --git a/plugins/python/colorspace/kritapykrita_colorspace.desktop b/plugins/python/colorspace/kritapykrita_colorspace.desktop
index 86e94cd54e..40f4c3483d 100644
--- a/plugins/python/colorspace/kritapykrita_colorspace.desktop
+++ b/plugins/python/colorspace/kritapykrita_colorspace.desktop
@@ -1,53 +1,55 @@
[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=colorspace
X-Python-2-Compatible=true
X-Krita-Manual=Manual.html
Name=Color Space
Name[ar]=الفضاء اللوني
Name[ca]=Espai de color
Name[ca@valencia]=Espai de color
Name[cs]=Barevný prostor
Name[de]=Farbraum
Name[el]=Χρωματικός χώρος
Name[en_GB]=Colour Space
Name[es]=Espacio de color
Name[eu]=Kolore-espazioa
+Name[fi]=Väriavaruus
Name[fr]=Espace colorimétrique
Name[gl]=Espazo de cores
Name[is]=Litrýmd
Name[it]=Spazio dei colori
Name[nl]=Kleurruimte
Name[pl]=Przestrzeń barw
Name[pt]=Espaço de Cores
Name[pt_BR]=Espaço de cores
Name[sk]=Farebný priestor
Name[sv]=Färgrymd
Name[tr]=Renk Aralığı
Name[uk]=Простір кольорів
Name[x-test]=xxColor Spacexx
Name[zh_CN]=色彩空间
Name[zh_TW]=色彩空間
Comment=Plugin to change color space to selected documents
Comment[ar]=ملحقة لتغيير الفضاء اللوني في المستندات المحددة
-Comment[ca]=Un connector per canviar l'espai de color dels documents seleccionats
-Comment[ca@valencia]=Un connector per canviar l'espai de color dels documents seleccionats
+Comment[ca]=Un connector per a canviar l'espai de color dels documents seleccionats
+Comment[ca@valencia]=Un connector per a canviar l'espai de color dels documents seleccionats
Comment[cs]=Modul pro změnu rozsahu barvy pro vybrané dokumenty
Comment[el]=Πρόσθετο αλλαγής χρωματικού χώρου σε επιλεγμένα έγγραφα
Comment[en_GB]=Plugin to change colour space to selected documents
Comment[es]=Complemento para cambiar el espacio de color de los documentos seleccionados
Comment[eu]=Hautatutako dokumentuei kolore-espazioa aldatzeko plugina
+Comment[fi]=Liitännäinen valittujen tiedostojen väriavaruuden muuttamiseksi
Comment[fr]=Module externe pour l'espace de couleurs des documents sélectionnés
Comment[gl]=Complemento para cambiar o espazo de cores dos documentos seleccionados.
Comment[it]=Estensione per cambiare lo spazio dei colori ai documenti selezionati
Comment[nl]=Plug-in om kleurruimte in geselecteerde documenten te wijzigen
Comment[pl]=Wtyczka do zmiany przestrzeni barw wybranych dokumentów
Comment[pt]='Plugin' para mudar o espaço de cores do documento seleccionado
-Comment[pt_BR]=Plug-in para alterar o espaço de cores em documentos selecionados
+Comment[pt_BR]=Plugin para alterar o espaço de cores em documentos selecionados
Comment[sv]=Insticksprogram för att ändra färgrymd för valda dokument
Comment[tr]=Seçili belgede renk aralığını değiştirmek için eklenti
Comment[uk]=Додаток для зміни простору кольорів у позначених документах
Comment[x-test]=xxPlugin to change color space to selected documentsxx
Comment[zh_CN]=用于更改选定文档色彩空间的插件
Comment[zh_TW]=用於變更色彩空間為選定文件的外掛程式
diff --git a/plugins/python/colorspace/uicolorspace.py b/plugins/python/colorspace/uicolorspace.py
index d757a190f3..80107a87ca 100644
--- a/plugins/python/colorspace/uicolorspace.py
+++ b/plugins/python/colorspace/uicolorspace.py
@@ -1,129 +1,150 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
+
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
from . import colorspacedialog
-from .components import colormodelcombobox, colordepthcombobox, colorprofilecombobox
+from .components import (
+ colordepthcombobox,
+ colormodelcombobox,
+ colorprofilecombobox,
+)
from PyQt5.QtCore import Qt
-from PyQt5.QtWidgets import (QFormLayout, QListWidget, QListWidgetItem,
- QAbstractItemView, QComboBox, QDialogButtonBox,
+from PyQt5.QtWidgets import (QFormLayout, QListWidget,
+ QAbstractItemView, QDialogButtonBox,
QVBoxLayout, QFrame, QMessageBox, QPushButton,
- QHBoxLayout, QAbstractScrollArea)
+ QAbstractScrollArea)
from PyQt5.QtGui import QIcon
import krita
-from . import resources_rc
class UIColorSpace(object):
def __init__(self):
self.mainDialog = colorspacedialog.ColorSpaceDialog()
self.mainLayout = QVBoxLayout(self.mainDialog)
self.formLayout = QFormLayout()
self.documentLayout = QVBoxLayout()
- self.refreshButton = QPushButton(QIcon(':/icons/refresh.svg'), i18n("Refresh"))
+ self.refreshButton = QPushButton(QIcon(':/icons/refresh.svg'),
+ i18n("Refresh"))
self.widgetDocuments = QListWidget()
self.colorModelComboBox = colormodelcombobox.ColorModelComboBox(self)
self.colorDepthComboBox = colordepthcombobox.ColorDepthComboBox(self)
- self.colorProfileComboBox = colorprofilecombobox.ColorProfileComboBox(self)
- self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+ self.colorProfileComboBox = \
+ colorprofilecombobox.ColorProfileComboBox(self)
+ self.buttonBox = QDialogButtonBox(
+ QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.kritaInstance = krita.Krita.instance()
self.documentsList = []
self.colorModelsList = []
self.colorDepthsList = []
self.colorProfilesList = []
self.refreshButton.clicked.connect(self.refreshButtonClicked)
self.buttonBox.accepted.connect(self.confirmButton)
self.buttonBox.rejected.connect(self.mainDialog.close)
self.mainDialog.setWindowModality(Qt.NonModal)
self.widgetDocuments.setSelectionMode(QAbstractItemView.MultiSelection)
- self.widgetDocuments.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents)
+ self.widgetDocuments.setSizeAdjustPolicy(
+ QAbstractScrollArea.AdjustToContents)
def initialize(self):
self.loadDocuments()
self.loadColorModels()
self.loadColorDepths()
self.loadColorProfiles()
self.documentLayout.addWidget(self.widgetDocuments)
self.documentLayout.addWidget(self.refreshButton)
self.formLayout.addRow(i18n("Documents:"), self.documentLayout)
self.formLayout.addRow(i18n("Color model:"), self.colorModelComboBox)
self.formLayout.addRow(i18n("Color depth:"), self.colorDepthComboBox)
- self.formLayout.addRow(i18n("Color profile:"), self.colorProfileComboBox)
+ self.formLayout.addRow(i18n("Color profile:"),
+ self.colorProfileComboBox)
self.line = QFrame()
self.line.setFrameShape(QFrame.HLine)
self.line.setFrameShadow(QFrame.Sunken)
self.mainLayout.addLayout(self.formLayout)
self.mainLayout.addWidget(self.line)
self.mainLayout.addWidget(self.buttonBox)
self.mainDialog.resize(500, 300)
self.mainDialog.setWindowTitle(i18n("Color Space"))
self.mainDialog.setSizeGripEnabled(True)
self.mainDialog.show()
self.mainDialog.activateWindow()
def loadColorModels(self):
self.colorModelsList = sorted(self.kritaInstance.colorModels())
self.colorModelComboBox.addItems(self.colorModelsList)
def loadColorDepths(self):
self.colorDepthComboBox.clear()
colorModel = self.colorModelComboBox.currentText()
- self.colorDepthsList = sorted(self.kritaInstance.colorDepths(colorModel))
+ self.colorDepthsList = sorted(
+ self.kritaInstance.colorDepths(colorModel))
self.colorDepthComboBox.addItems(self.colorDepthsList)
def loadColorProfiles(self):
self.colorProfileComboBox.clear()
colorModel = self.colorModelComboBox.currentText()
colorDepth = self.colorDepthComboBox.currentText()
- self.colorProfilesList = sorted(self.kritaInstance.profiles(colorModel, colorDepth))
+ self.colorProfilesList = sorted(
+ self.kritaInstance.profiles(colorModel, colorDepth))
self.colorProfileComboBox.addItems(self.colorProfilesList)
def loadDocuments(self):
self.widgetDocuments.clear()
- self.documentsList = [document for document in self.kritaInstance.documents() if document.fileName()]
+ self.documentsList = [
+ document for document in self.kritaInstance.documents()
+ if document.fileName()
+ ]
for document in self.documentsList:
self.widgetDocuments.addItem(document.fileName())
def refreshButtonClicked(self):
self.loadDocuments()
def confirmButton(self):
- selectedPaths = [item.text() for item in self.widgetDocuments.selectedItems()]
- selectedDocuments = [document for document in self.documentsList for path in selectedPaths if path == document.fileName()]
+ selectedPaths = [
+ item.text() for item in self.widgetDocuments.selectedItems()]
+ selectedDocuments = [
+ document for document in self.documentsList
+ for path in selectedPaths if path == document.fileName()
+ ]
self.msgBox = QMessageBox(self.mainDialog)
if selectedDocuments:
self.convertColorSpace(selectedDocuments)
- self.msgBox.setText(i18n("The selected documents has been converted."))
+ self.msgBox.setText(
+ i18n("The selected documents has been converted."))
else:
self.msgBox.setText(i18n("Select at least one document."))
self.msgBox.exec_()
def convertColorSpace(self, documents):
for document in documents:
document.setColorSpace(self.colorModelComboBox.currentText(),
self.colorDepthComboBox.currentText(),
self.colorProfileComboBox.currentText())
diff --git a/plugins/python/comics_project_management_tools/kritapykrita_comics_project_management_tools.desktop b/plugins/python/comics_project_management_tools/kritapykrita_comics_project_management_tools.desktop
index 0eb67bf497..0c20dcc466 100644
--- a/plugins/python/comics_project_management_tools/kritapykrita_comics_project_management_tools.desktop
+++ b/plugins/python/comics_project_management_tools/kritapykrita_comics_project_management_tools.desktop
@@ -1,50 +1,52 @@
[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=comics_project_management_tools
X-Krita-Manual=README.html
X-Python-2-Compatible=false
Name=Comics Project Management Tools
Name[ar]=أدوات إدارة المشاريع الهزليّة
Name[ca]=Eines per a la gestió dels projectes de còmics
Name[ca@valencia]=Eines per a la gestió dels projectes de còmics
Name[cs]=Nástroje pro správu projektů komixů
Name[el]=Εργαλεία διαχείρισης έργων ιστοριών σε εικόνες
Name[en_GB]=Comics Project Management Tools
Name[es]=Herramientas de gestión de proyectos de cómics
Name[eu]=Komikien proiektuak kudeatzeko tresnak
+Name[fi]=Sarjakuvaprojektien hallintatyökalut
Name[fr]=Outils de gestion d'un projet de bande dessinée
Name[gl]=Ferramentas de xestión de proxectos de cómics
Name[is]=Verkefnisstjórn teiknimyndasögu
Name[it]=Strumenti per la gestione dei progetti di fumetti
Name[nl]=Hulpmiddelen voor projectbeheer van strips
Name[pl]=Narzędzia do zarządzania projektami komiksów
Name[pt]=Ferramentas de Gestão de Projectos de Banda Desenhada
Name[sv]=Projekthanteringsverktyg för tecknade serier
Name[tr]=Çizgi Roman Projesi Yönetimi Araçları
Name[uk]=Інструменти для керування проектами коміксів
Name[x-test]=xxComics Project Management Toolsxx
Name[zh_CN]=漫画项目管理工具
Name[zh_TW]=漫畫專案管理工具
Comment=Tools for managing comics.
Comment[ar]=أدوات لإدارة الهزليّات.
Comment[ca]=Eines per a gestionar els còmics.
Comment[ca@valencia]=Eines per a gestionar els còmics.
Comment[cs]=Nástroje pro správu komixů.
Comment[el]=Εργαλεία για τη διαχείριση ιστοριών σε εικόνες.
Comment[en_GB]=Tools for managing comics.
Comment[es]=Herramientas para gestionar cómics.
Comment[eu]=Komikiak kudeatzeko tresnak.
+Comment[fi]=Sarjakuvien hallintatyökalut.
Comment[fr]=Outils pour gérer les bandes dessinées.
Comment[gl]=Ferramentas para xestionar cómics.
Comment[is]=Verkfæri til að stýra gerð teiknimyndasögu.
Comment[it]=Strumenti per la gestione dei fumetti.
Comment[nl]=Hulpmiddelen voor beheer van strips.
Comment[pl]=Narzędzie do zarządzania komiksami.
Comment[pt]=Ferramentas para gerir bandas desenhadas.
Comment[sv]=Verktyg för att hantera tecknade serier.
Comment[tr]=Çizgi romanları yönetmek için araçlar.
Comment[uk]=Інструменти для керування коміксами
Comment[x-test]=xxTools for managing comics.xx
Comment[zh_CN]=用于管理漫画的工具。
Comment[zh_TW]=管理漫畫的工具。
diff --git a/plugins/python/documenttools/__init__.py b/plugins/python/documenttools/__init__.py
index 34deb6c918..0908dbe4aa 100644
--- a/plugins/python/documenttools/__init__.py
+++ b/plugins/python/documenttools/__init__.py
@@ -1,2 +1,4 @@
-# let's make a module
-from .documenttools import *
+import krita
+from .documenttools import DocumentToolsExtension
+
+Scripter.addExtension(DocumentToolsExtension(krita.Krita.instance()))
diff --git a/plugins/python/documenttools/documenttools.py b/plugins/python/documenttools/documenttools.py
index 2fbcdd6c77..79c591d2ed 100644
--- a/plugins/python/documenttools/documenttools.py
+++ b/plugins/python/documenttools/documenttools.py
@@ -1,34 +1,35 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
+
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
import krita
from . import uidocumenttools
class DocumentToolsExtension(krita.Extension):
def __init__(self, parent):
super(DocumentToolsExtension, self).__init__(parent)
def setup(self):
pass
def createActions(self, window):
action = window.createAction("document_tools", i18n("Document Tools"))
- action.setToolTip(i18n("Plugin to manipulate properties of selected documents."))
+ action.setToolTip(
+ i18n("Plugin to manipulate properties of selected documents."))
action.triggered.connect(self.initialize)
def initialize(self):
self.uidocumenttools = uidocumenttools.UIDocumentTools()
self.uidocumenttools.initialize()
-
-
-Scripter.addExtension(DocumentToolsExtension(krita.Krita.instance()))
diff --git a/plugins/python/documenttools/documenttoolsdialog.py b/plugins/python/documenttools/documenttoolsdialog.py
index 441d378788..d170617e8e 100644
--- a/plugins/python/documenttools/documenttoolsdialog.py
+++ b/plugins/python/documenttools/documenttoolsdialog.py
@@ -1,21 +1,24 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
+
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
from PyQt5.QtWidgets import QDialog
class DocumentToolsDialog(QDialog):
def __init__(self, parent=None):
super(DocumentToolsDialog, self).__init__(parent)
def closeEvent(self, event):
event.accept()
diff --git a/plugins/python/documenttools/kritapykrita_documenttools.desktop b/plugins/python/documenttools/kritapykrita_documenttools.desktop
index 3baaba4905..0b9116d528 100644
--- a/plugins/python/documenttools/kritapykrita_documenttools.desktop
+++ b/plugins/python/documenttools/kritapykrita_documenttools.desktop
@@ -1,50 +1,52 @@
[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=documenttools
X-Python-2-Compatible=true
X-Krita-Manual=Manual.html
Name=Document Tools
Name[ar]=أدوات المستندات
Name[ca]=Eines de document
Name[ca@valencia]=Eines de document
Name[cs]=Dokumentové nástroje
Name[el]=Εργαλεία για έγγραφα
Name[en_GB]=Document Tools
Name[es]=Herramientas de documentos
Name[eu]=Dokumentuen tresnak
+Name[fi]=Tiedostotyökalut
Name[fr]=Outil Document
Name[gl]=Ferramentas de documentos
Name[it]=Strumenti per i documenti
Name[nl]=Documenthulpmiddelen
Name[pl]=Narzędzia dokumentu
Name[pt]=Ferramentas de Documentos
Name[pt_BR]=Ferramentas de documento
Name[sv]=Dokumentverktyg
Name[tr]=Belge Araçları
Name[uk]=Засоби документа
Name[x-test]=xxDocument Toolsxx
Name[zh_CN]=文档工具
Name[zh_TW]=文件工具
Comment=Plugin to manipulate properties of selected documents
Comment[ar]=ملحقة لتعديل خصائص المستندات المحددة
-Comment[ca]=Un connector per manipular propietats dels documents seleccionats
-Comment[ca@valencia]=Un connector per manipular propietats dels documents seleccionats
+Comment[ca]=Un connector per a manipular les propietats dels documents seleccionats
+Comment[ca@valencia]=Un connector per a manipular les propietats dels documents seleccionats
Comment[cs]=Modul pro správu vlastností vybraných dokumentů
Comment[el]=Πρόσθετο χειρισμού ιδιοτήτων σε επιλεγμένα έγγραφα
Comment[en_GB]=Plugin to manipulate properties of selected documents
Comment[es]=Complemento para manipular las propiedades de los documentos seleccionados
Comment[eu]=Hautatutako dokumentuen propietateak manipulatzeko plugina
+Comment[fi]=Liitännäinen valittujen tiedostojen ominaisuuksien käsittelemiseksi
Comment[fr]=Module externe de gestion des propriétés des documents sélectionnés
Comment[gl]=Complemento para manipular as propiedades dos documentos seleccionados.
Comment[it]=Estensione per manipolare le proprietà dei documenti selezionati
Comment[nl]=Plug-in om eigenschappen van geselecteerde documenten te manipuleren
Comment[pl]=Wtyczka do zmiany właściwości wybranych dokumentów
Comment[pt]='Plugin' para manipular as propriedades dos documentos seleccionados
-Comment[pt_BR]=Plug-in para manipular as propriedades de documentos selecionados
+Comment[pt_BR]=Plugin para manipular as propriedades de documentos selecionados
Comment[sv]=Insticksprogram för att ändra egenskaper för valda dokument
Comment[tr]=Seçili belgelerin özelliklerini değiştirmek için eklenti
Comment[uk]=Додаток для керування властивостями позначених документів
Comment[x-test]=xxPlugin to manipulate properties of selected documentsxx
Comment[zh_CN]=用于编辑选定文档属性的插件
Comment[zh_TW]=用於修改所選文件屬性的外掛程式
diff --git a/plugins/python/documenttools/tools/canvassizetool/canvassizetool.py b/plugins/python/documenttools/tools/canvassizetool/canvassizetool.py
index 5d26f7575c..d58fe5844f 100644
--- a/plugins/python/documenttools/tools/canvassizetool/canvassizetool.py
+++ b/plugins/python/documenttools/tools/canvassizetool/canvassizetool.py
@@ -1,53 +1,55 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
-from PyQt5.QtWidgets import (QWidget, QSpinBox, QHBoxLayout,
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
+
+from PyQt5.QtWidgets import (QWidget, QSpinBox,
QVBoxLayout, QFormLayout)
-import krita
class CanvasSizeTool(QWidget):
def __init__(self, mainDialog, parent=None):
super(CanvasSizeTool, self).__init__(parent)
self.setObjectName("Canvas Size")
self.layout = QFormLayout()
self.offsetLayout = QVBoxLayout()
self.widthSpinBox = QSpinBox()
self.heightSpinBox = QSpinBox()
self.xOffsetSpinBox = QSpinBox()
self.yOffsetSpinBox = QSpinBox()
self.setLayout(self.layout)
self.initialize()
def initialize(self):
self.widthSpinBox.setRange(1, 10000)
self.heightSpinBox.setRange(1, 10000)
self.xOffsetSpinBox.setRange(-10000, 10000)
self.yOffsetSpinBox.setRange(-10000, 10000)
self.offsetLayout.addWidget(self.xOffsetSpinBox)
self.offsetLayout.addWidget(self.yOffsetSpinBox)
self.layout.addRow(i18n("Width:"), self.widthSpinBox)
self.layout.addRow(i18n("Height:"), self.heightSpinBox)
self.layout.addRow(i18n("Offset:"), self.offsetLayout)
def adjust(self, documents):
for document in documents:
document.resizeImage(self.xOffsetSpinBox.value(),
self.yOffsetSpinBox.value(),
self.widthSpinBox.value(),
self.heightSpinBox.value())
diff --git a/plugins/python/documenttools/tools/rotatetool/rotatetool.py b/plugins/python/documenttools/tools/rotatetool/rotatetool.py
index ae3f1e20e0..a4b7574297 100644
--- a/plugins/python/documenttools/tools/rotatetool/rotatetool.py
+++ b/plugins/python/documenttools/tools/rotatetool/rotatetool.py
@@ -1,40 +1,42 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
-from PyQt5.QtWidgets import (QWidget, QSpinBox, QHBoxLayout,
- QVBoxLayout, QFormLayout)
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
+
+from PyQt5.QtWidgets import QWidget, QSpinBox, QFormLayout
import math
-import krita
class RotateTool(QWidget):
def __init__(self, mainDialog, parent=None):
super(RotateTool, self).__init__(parent)
self.setObjectName("Rotate")
self.layout = QFormLayout()
self.degreesSpinBox = QSpinBox()
self.setLayout(self.layout)
self.initialize()
def initialize(self):
self.degreesSpinBox.setRange(-180, 180)
- self.degreesSpinBox.setToolTip(i18n("Negative degrees will rotate the image to the left"))
+ self.degreesSpinBox.setToolTip(
+ i18n("Negative degrees will rotate the image to the left"))
self.layout.addRow(i18n("Degrees:"), self.degreesSpinBox)
def adjust(self, documents):
for document in documents:
document.rotateImage(math.radians(self.degreesSpinBox.value()))
diff --git a/plugins/python/documenttools/tools/scaletool/scaletool.py b/plugins/python/documenttools/tools/scaletool/scaletool.py
index f71e59fe85..10bba1cde5 100644
--- a/plugins/python/documenttools/tools/scaletool/scaletool.py
+++ b/plugins/python/documenttools/tools/scaletool/scaletool.py
@@ -1,61 +1,63 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
-from PyQt5.QtWidgets import (QWidget, QSpinBox, QHBoxLayout,
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
+
+from PyQt5.QtWidgets import (QWidget, QSpinBox,
QVBoxLayout, QFormLayout, QComboBox)
-import krita
class ScaleTool(QWidget):
def __init__(self, mainDialog, parent=None):
super(ScaleTool, self).__init__(parent)
self.setObjectName("Scale")
self.layout = QFormLayout()
self.resolutionLayout = QVBoxLayout()
self.widthSpinBox = QSpinBox()
self.heightSpinBox = QSpinBox()
self.xResSpinBox = QSpinBox()
self.yResSpinBox = QSpinBox()
self.strategyComboBox = QComboBox()
self.strategyComboBox.setSizeAdjustPolicy(QComboBox.AdjustToContents)
self.setLayout(self.layout)
self.initialize()
def initialize(self):
self.widthSpinBox.setRange(1, 10000)
self.heightSpinBox.setRange(1, 10000)
self.xResSpinBox.setRange(1, 10000)
self.yResSpinBox.setRange(1, 10000)
strategies = ['Hermite', 'Bicubic', 'Box',
'Bilinear', 'Bell', 'BSpline',
'Kanczos3', 'Mitchell']
self.strategyComboBox.addItems(strategies)
self.resolutionLayout.addWidget(self.xResSpinBox)
self.resolutionLayout.addWidget(self.yResSpinBox)
self.layout.addRow(i18n("Width:"), self.widthSpinBox)
self.layout.addRow(i18n("Height:"), self.heightSpinBox)
self.layout.addRow(i18n("Resolution:"), self.resolutionLayout)
self.layout.addRow(i18n("Filter:"), self.strategyComboBox)
def adjust(self, documents):
for document in documents:
document.scaleImage(self.widthSpinBox.value(),
self.heightSpinBox.value(),
self.xResSpinBox.value(),
self.yResSpinBox.value(),
self.strategyComboBox.currentText())
diff --git a/plugins/python/documenttools/uidocumenttools.py b/plugins/python/documenttools/uidocumenttools.py
index ae85d202a6..604293d564 100644
--- a/plugins/python/documenttools/uidocumenttools.py
+++ b/plugins/python/documenttools/uidocumenttools.py
@@ -1,107 +1,119 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
+
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
from . import documenttoolsdialog
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QFormLayout, QListWidget, QAbstractItemView,
QDialogButtonBox, QVBoxLayout, QFrame, QTabWidget,
QPushButton, QAbstractScrollArea, QMessageBox)
import krita
import importlib
class UIDocumentTools(object):
def __init__(self):
self.mainDialog = documenttoolsdialog.DocumentToolsDialog()
self.mainLayout = QVBoxLayout(self.mainDialog)
self.formLayout = QFormLayout()
self.documentLayout = QVBoxLayout()
self.refreshButton = QPushButton(i18n("Refresh"))
self.widgetDocuments = QListWidget()
self.tabTools = QTabWidget()
- self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+ self.buttonBox = QDialogButtonBox(
+ QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.kritaInstance = krita.Krita.instance()
self.documentsList = []
self.refreshButton.clicked.connect(self.refreshButtonClicked)
self.buttonBox.accepted.connect(self.confirmButton)
self.buttonBox.rejected.connect(self.mainDialog.close)
self.mainDialog.setWindowModality(Qt.NonModal)
self.widgetDocuments.setSelectionMode(QAbstractItemView.MultiSelection)
- self.widgetDocuments.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents)
+ self.widgetDocuments.setSizeAdjustPolicy(
+ QAbstractScrollArea.AdjustToContents)
def initialize(self):
self.loadDocuments()
self.loadTools()
self.documentLayout.addWidget(self.widgetDocuments)
self.documentLayout.addWidget(self.refreshButton)
self.formLayout.addRow(i18n("Documents:"), self.documentLayout)
self.formLayout.addRow(self.tabTools)
self.line = QFrame()
self.line.setFrameShape(QFrame.HLine)
self.line.setFrameShadow(QFrame.Sunken)
self.mainLayout.addLayout(self.formLayout)
self.mainLayout.addWidget(self.line)
self.mainLayout.addWidget(self.buttonBox)
self.mainDialog.resize(500, 300)
self.mainDialog.setWindowTitle(i18n("Document Tools"))
self.mainDialog.setSizeGripEnabled(True)
self.mainDialog.show()
self.mainDialog.activateWindow()
def loadTools(self):
modulePath = 'documenttools.tools'
toolsModule = importlib.import_module(modulePath)
modules = []
for classPath in toolsModule.ToolClasses:
_module = classPath[:classPath.rfind(".")]
_klass = classPath[classPath.rfind(".") + 1:]
modules.append(dict(module='{0}.{1}'.format(modulePath, _module),
klass=_klass))
for module in modules:
m = importlib.import_module(module['module'])
toolClass = getattr(m, module['klass'])
obj = toolClass(self.mainDialog)
self.tabTools.addTab(obj, obj.objectName())
def loadDocuments(self):
self.widgetDocuments.clear()
- self.documentsList = [document for document in self.kritaInstance.documents() if document.fileName()]
+ self.documentsList = [
+ document for document in self.kritaInstance.documents()
+ if document.fileName()
+ ]
for document in self.documentsList:
self.widgetDocuments.addItem(document.fileName())
def refreshButtonClicked(self):
self.loadDocuments()
def confirmButton(self):
- selectedPaths = [item.text() for item in self.widgetDocuments.selectedItems()]
- selectedDocuments = [document for document in self.documentsList for path in selectedPaths if path == document.fileName()]
+ selectedPaths = [
+ item.text() for item in self.widgetDocuments.selectedItems()]
+ selectedDocuments = [
+ document for document in self.documentsList
+ for path in selectedPaths if path == document.fileName()]
self.msgBox = QMessageBox(self.mainDialog)
if selectedDocuments:
widget = self.tabTools.currentWidget()
widget.adjust(selectedDocuments)
- self.msgBox.setText(i18n("The selected documents has been modified."))
+ self.msgBox.setText(
+ i18n("The selected documents has been modified."))
else:
self.msgBox.setText(i18n("Select at least one document."))
self.msgBox.exec_()
diff --git a/plugins/python/example_scripts/resize_to_all_layers.py b/plugins/python/example_scripts/resize_to_all_layers.py
index e854763086..d6314a7609 100644
--- a/plugins/python/example_scripts/resize_to_all_layers.py
+++ b/plugins/python/example_scripts/resize_to_all_layers.py
@@ -1,34 +1,40 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
+
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
+
+"""
+This script will iterate over all toplevel nodes, create
+keeping track of the layer boundaries and then resize
+the image to the unity of all boundaries.
+"""
+
+from krita import Krita
+from PyQt5.QtCore import QRect
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
-#
-# This script will iterate over all toplevel nodes, create
-# keeping track of the layer boundaries and then resize
-# the image to the unity of all boundaries.
-#
-from krita import *
d = Krita.instance().activeDocument()
w = d.width()
h = d.height()
x = d.xOffset()
y = d.yOffset()
print(x, y, w, h)
r = QRect(x, y, w, h)
print(r)
for n in d.topLevelNodes():
- print (n, n.bounds())
+ print(n, n.bounds())
b = n.bounds()
r = r.united(b)
-
-print (r)
+
+print(r)
d.resizeImage(r.x(), r.y(), r.width(), r.height())
diff --git a/plugins/python/exportlayers/__init__.py b/plugins/python/exportlayers/__init__.py
index b0a4cf13f3..b2074e48a0 100644
--- a/plugins/python/exportlayers/__init__.py
+++ b/plugins/python/exportlayers/__init__.py
@@ -1,2 +1,5 @@
-# let's make a module
-from .exportlayers import *
+import krita
+from .exportlayers import ExportLayersExtension
+
+
+Scripter.addExtension(ExportLayersExtension(krita.Krita.instance()))
diff --git a/plugins/python/exportlayers/exportlayers.py b/plugins/python/exportlayers/exportlayers.py
index 17cb698d71..23d5f63fdd 100644
--- a/plugins/python/exportlayers/exportlayers.py
+++ b/plugins/python/exportlayers/exportlayers.py
@@ -1,34 +1,34 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
+
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
import krita
from . import uiexportlayers
class ExportLayersExtension(krita.Extension):
def __init__(self, parent):
super(ExportLayersExtension, self).__init__(parent)
def setup(self):
pass
def createActions(self, window):
action = window.createAction("export_layers", i18n("Export Layers"))
action.setToolTip(i18n("Plugin to export layers from a document."))
action.triggered.connect(self.initialize)
def initialize(self):
self.uiexportlayers = uiexportlayers.UIExportLayers()
self.uiexportlayers.initialize()
-
-
-Scripter.addExtension(ExportLayersExtension(krita.Krita.instance()))
diff --git a/plugins/python/exportlayers/exportlayersdialog.py b/plugins/python/exportlayers/exportlayersdialog.py
index 4d16217464..ec939eb89a 100644
--- a/plugins/python/exportlayers/exportlayersdialog.py
+++ b/plugins/python/exportlayers/exportlayersdialog.py
@@ -1,21 +1,24 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
+
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
from PyQt5.QtWidgets import QDialog
class ExportLayersDialog(QDialog):
def __init__(self, parent=None):
super(ExportLayersDialog, self).__init__(parent)
def closeEvent(self, event):
event.accept()
diff --git a/plugins/python/exportlayers/kritapykrita_exportlayers.desktop b/plugins/python/exportlayers/kritapykrita_exportlayers.desktop
index 88a079257b..ca621c0684 100644
--- a/plugins/python/exportlayers/kritapykrita_exportlayers.desktop
+++ b/plugins/python/exportlayers/kritapykrita_exportlayers.desktop
@@ -1,53 +1,55 @@
[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=exportlayers
X-Krita-Manual=Manual.html
X-Python-2-Compatible=true
Name=Export Layers
Name[ar]=تصدير الطبقات
Name[ca]=Exportació de capes
Name[ca@valencia]=Exportació de capes
Name[cs]=Exportovat vrstvy
Name[de]=Ebenen exportieren
Name[el]=Εξαγωγή επιπέδων
Name[en_GB]=Export Layers
Name[es]=Exportar capas
Name[eu]=Esportatu geruzak
+Name[fi]=Vie tasoja
Name[fr]=Exporter des calques
Name[gl]=Exportar as capas
Name[is]=Flytja út lög
Name[it]=Esporta livelli
Name[nl]=Lagen exporteren
Name[pl]=Eksportuj warstwy
Name[pt]=Exportar as Camadas
Name[pt_BR]=Exportar camadas
Name[sv]=Exportera lager
Name[tr]=Katmanları Dışa Aktar
Name[uk]=Експортувати шари
Name[x-test]=xxExport Layersxx
Name[zh_CN]=导出图层
Name[zh_TW]=匯出圖層
Comment=Plugin to export layers from a document
Comment[ar]=ملحقة لتصدير الطبقات من مستند
Comment[ca]=Un connector per exportar capes d'un document
Comment[ca@valencia]=Un connector per exportar capes d'un document
Comment[cs]=Modul pro export vrstev z dokumentu
Comment[de]=Modul zum Exportieren von Ebenen aus einem Dokument
Comment[el]=Πρόσθετο εξαγωγής επιπέδων από έγγραφο
Comment[en_GB]=Plugin to export layers from a document
Comment[es]=Complemento para exportar las capas de un documento
Comment[eu]=Dokumentu batetik geruzak esportatzeko plugina
+Comment[fi]=Liitännäisen tiedoston tasojen viemiseksi
Comment[fr]=Module externe d'export de calques d'un document
Comment[gl]=Complemento para exportar as capas dun documento.
Comment[it]=Estensione per esportare i livelli da un documento
Comment[nl]=Plug-in om lagen uit een document te exporteren
Comment[pl]=Wtyczka do eksportowania warstw z dokumentu
Comment[pt]='Plugin' para exportar as camadas de um documento
-Comment[pt_BR]=Plug-in para exportar as camadas de um documento
+Comment[pt_BR]=Plugin para exportar as camadas de um documento
Comment[sv]=Insticksprogram för att exportera lager från ett dokument
Comment[tr]=Belgenin katmanlarını dışa aktarmak için eklenti
Comment[uk]=Додаток для експортування шарів з документа
Comment[x-test]=xxPlugin to export layers from a documentxx
Comment[zh_CN]=用于从文档导出图层的插件
Comment[zh_TW]=用於從文件匯出圖層的外掛程式
diff --git a/plugins/python/exportlayers/uiexportlayers.py b/plugins/python/exportlayers/uiexportlayers.py
index 8d0f66966a..0555bb3dab 100644
--- a/plugins/python/exportlayers/uiexportlayers.py
+++ b/plugins/python/exportlayers/uiexportlayers.py
@@ -1,184 +1,211 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
+
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
from . import exportlayersdialog
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QFormLayout, QListWidget, QHBoxLayout,
QDialogButtonBox, QVBoxLayout, QFrame,
QPushButton, QAbstractScrollArea, QLineEdit,
QMessageBox, QFileDialog, QCheckBox, QSpinBox,
QComboBox)
import os
-import errno
import krita
class UIExportLayers(object):
def __init__(self):
self.mainDialog = exportlayersdialog.ExportLayersDialog()
self.mainLayout = QVBoxLayout(self.mainDialog)
self.formLayout = QFormLayout()
self.documentLayout = QVBoxLayout()
self.directorySelectorLayout = QHBoxLayout()
self.optionsLayout = QVBoxLayout()
self.resolutionLayout = QHBoxLayout()
self.refreshButton = QPushButton(i18n("Refresh"))
self.widgetDocuments = QListWidget()
self.directoryTextField = QLineEdit()
self.directoryDialogButton = QPushButton(i18n("..."))
- self.exportFilterLayersCheckBox = QCheckBox(i18n("Export filter layers"))
+ self.exportFilterLayersCheckBox = QCheckBox(
+ i18n("Export filter layers"))
self.batchmodeCheckBox = QCheckBox(i18n("Export in batchmode"))
- self.ignoreInvisibleLayersCheckBox = QCheckBox(i18n("Ignore invisible layers"))
+ self.ignoreInvisibleLayersCheckBox = QCheckBox(
+ i18n("Ignore invisible layers"))
self.xResSpinBox = QSpinBox()
self.yResSpinBox = QSpinBox()
self.formatsComboBox = QComboBox()
- self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+ self.buttonBox = QDialogButtonBox(
+ QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.kritaInstance = krita.Krita.instance()
self.documentsList = []
self.directoryTextField.setReadOnly(True)
self.batchmodeCheckBox.setChecked(True)
self.directoryDialogButton.clicked.connect(self._selectDir)
self.widgetDocuments.currentRowChanged.connect(self._setResolution)
self.refreshButton.clicked.connect(self.refreshButtonClicked)
self.buttonBox.accepted.connect(self.confirmButton)
self.buttonBox.rejected.connect(self.mainDialog.close)
self.mainDialog.setWindowModality(Qt.NonModal)
- self.widgetDocuments.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents)
+ self.widgetDocuments.setSizeAdjustPolicy(
+ QAbstractScrollArea.AdjustToContents)
def initialize(self):
self.loadDocuments()
self.xResSpinBox.setRange(1, 10000)
self.yResSpinBox.setRange(1, 10000)
self.formatsComboBox.addItem(i18n("JPEG"))
self.formatsComboBox.addItem(i18n("PNG"))
self.documentLayout.addWidget(self.widgetDocuments)
self.documentLayout.addWidget(self.refreshButton)
self.directorySelectorLayout.addWidget(self.directoryTextField)
self.directorySelectorLayout.addWidget(self.directoryDialogButton)
self.optionsLayout.addWidget(self.exportFilterLayersCheckBox)
self.optionsLayout.addWidget(self.batchmodeCheckBox)
self.optionsLayout.addWidget(self.ignoreInvisibleLayersCheckBox)
self.resolutionLayout.addWidget(self.xResSpinBox)
self.resolutionLayout.addWidget(self.yResSpinBox)
self.formLayout.addRow(i18n("Documents:"), self.documentLayout)
- self.formLayout.addRow(i18n("Initial directory:"), self.directorySelectorLayout)
+ self.formLayout.addRow(
+ i18n("Initial directory:"), self.directorySelectorLayout)
self.formLayout.addRow(i18n("Export options:"), self.optionsLayout)
self.formLayout.addRow(i18n("Resolution:"), self.resolutionLayout)
- self.formLayout.addRow(i18n("Images extensions:"), self.formatsComboBox)
+ self.formLayout.addRow(
+ i18n("Images extensions:"), self.formatsComboBox)
self.line = QFrame()
self.line.setFrameShape(QFrame.HLine)
self.line.setFrameShadow(QFrame.Sunken)
self.mainLayout.addLayout(self.formLayout)
self.mainLayout.addWidget(self.line)
self.mainLayout.addWidget(self.buttonBox)
self.mainDialog.resize(500, 300)
self.mainDialog.setWindowTitle(i18n("Export Layers"))
self.mainDialog.setSizeGripEnabled(True)
self.mainDialog.show()
self.mainDialog.activateWindow()
def loadDocuments(self):
self.widgetDocuments.clear()
- self.documentsList = [document for document in self.kritaInstance.documents() if document.fileName()]
+ self.documentsList = [
+ document for document in self.kritaInstance.documents()
+ if document.fileName()
+ ]
for document in self.documentsList:
self.widgetDocuments.addItem(document.fileName())
def refreshButtonClicked(self):
self.loadDocuments()
def confirmButton(self):
- selectedPaths = [item.text() for item in self.widgetDocuments.selectedItems()]
- selectedDocuments = [document for document in self.documentsList for path in selectedPaths if path == document.fileName()]
+ selectedPaths = [
+ item.text() for item in self.widgetDocuments.selectedItems()]
+ selectedDocuments = [
+ document for document in self.documentsList
+ for path in selectedPaths if path == document.fileName()
+ ]
self.msgBox = QMessageBox(self.mainDialog)
if not selectedDocuments:
self.msgBox.setText(i18n("Select one document."))
elif not self.directoryTextField.text():
self.msgBox.setText(i18n("Select the initial directory."))
else:
self.export(selectedDocuments[0])
self.msgBox.setText(i18n("All layers has been exported."))
self.msgBox.exec_()
def mkdir(self, directory):
target_directory = self.directoryTextField.text() + directory
- if os.path.exists(target_directory) and os.path.isdir(target_directory):
+ if (os.path.exists(target_directory)
+ and os.path.isdir(target_directory)):
return
try:
os.makedirs(target_directory)
except OSError as e:
raise e
def export(self, document):
Application.setBatchmode(self.batchmodeCheckBox.isChecked())
- documentName = document.fileName() if document.fileName() else 'Untitled'
+ documentName = document.fileName() if document.fileName() else 'Untitled' # noqa: E501
fileName, extension = os.path.splitext(os.path.basename(documentName))
self.mkdir('/' + fileName)
- self._exportLayers(document.rootNode(), self.formatsComboBox.currentText(), '/' + fileName)
+ self._exportLayers(
+ document.rootNode(),
+ self.formatsComboBox.currentText(),
+ '/' + fileName)
Application.setBatchmode(True)
def _exportLayers(self, parentNode, fileFormat, parentDir):
""" This method get all sub-nodes from the current node and export then in
the defined format."""
for node in parentNode.childNodes():
newDir = ''
if node.type() == 'grouplayer':
newDir = os.path.join(parentDir, node.name())
self.mkdir(newDir)
- elif not self.exportFilterLayersCheckBox.isChecked() and 'filter' in node.type():
+ elif (not self.exportFilterLayersCheckBox.isChecked()
+ and 'filter' in node.type()):
continue
- elif self.ignoreInvisibleLayersCheckBox.isChecked() and not node.visible():
+ elif (self.ignoreInvisibleLayersCheckBox.isChecked()
+ and not node.visible()):
continue
else:
nodeName = node.name()
_fileFormat = self.formatsComboBox.currentText()
if '[jpeg]' in nodeName:
_fileFormat = 'jpeg'
elif '[png]' in nodeName:
_fileFormat = 'png'
- layerFileName = '{0}{1}/{2}.{3}'.format(self.directoryTextField.text(),
- parentDir, node.name(), _fileFormat)
- node.save(layerFileName, self.xResSpinBox.value(), self.yResSpinBox.value())
+ layerFileName = '{0}{1}/{2}.{3}'.format(
+ self.directoryTextField.text(),
+ parentDir, node.name(), _fileFormat)
+ node.save(layerFileName, self.xResSpinBox.value(),
+ self.yResSpinBox.value(), krita.InfoObject())
if node.childNodes():
self._exportLayers(node, fileFormat, newDir)
def _selectDir(self):
- directory = QFileDialog.getExistingDirectory(self.mainDialog, i18n("Select a Folder"), os.path.expanduser("~"), QFileDialog.ShowDirsOnly)
+ directory = QFileDialog.getExistingDirectory(
+ self.mainDialog,
+ i18n("Select a Folder"),
+ os.path.expanduser("~"),
+ QFileDialog.ShowDirsOnly)
self.directoryTextField.setText(directory)
def _setResolution(self, index):
document = self.documentsList[index]
self.xResSpinBox.setValue(document.width())
self.yResSpinBox.setValue(document.height())
diff --git a/plugins/python/filtermanager/__init__.py b/plugins/python/filtermanager/__init__.py
index 3dcf5557d9..ca1fbb26e7 100644
--- a/plugins/python/filtermanager/__init__.py
+++ b/plugins/python/filtermanager/__init__.py
@@ -1,2 +1,5 @@
-# let's make a module
-from .filtermanager import *
+import krita
+from .filtermanager import FilterManagerExtension
+
+
+Scripter.addExtension(FilterManagerExtension(krita.Krita.instance()))
diff --git a/plugins/python/filtermanager/components/filtercombobox.py b/plugins/python/filtermanager/components/filtercombobox.py
index 53641126a5..dcecb3e3c2 100644
--- a/plugins/python/filtermanager/components/filtercombobox.py
+++ b/plugins/python/filtermanager/components/filtercombobox.py
@@ -1,23 +1,25 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
+
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
from PyQt5.QtWidgets import QComboBox
-import krita
class FilterComboBox(QComboBox):
def __init__(self, uiFilterManager, parent=None):
super(FilterComboBox, self).__init__(parent)
self.uiFilterManager = uiFilterManager
self.addItems(self.uiFilterManager.filters)
diff --git a/plugins/python/filtermanager/components/filtermanagertreeitem.py b/plugins/python/filtermanager/components/filtermanagertreeitem.py
index ec73a6a2ba..dd5c37261a 100644
--- a/plugins/python/filtermanager/components/filtermanagertreeitem.py
+++ b/plugins/python/filtermanager/components/filtermanagertreeitem.py
@@ -1,46 +1,50 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
+
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
+
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
class FilterManagerTreeItem(object):
def __init__(self, data, parent=None):
self.itemData = data
self.parentItem = parent
self.childItems = []
def appendChild(self, child):
self.childItems.append(child)
def appenChildren(self, children):
self.childItems.extend(children)
def child(self, row):
return self.childItems[row]
def childCount(self):
return len(self.childItems)
def columnCount(self):
return len(self.itemData)
def data(self, column):
try:
return self.itemData[column]
except IndexError:
return None
def row(self):
if self.parentItem:
return self.parentItem.childItems.index(self)
return 0
def parent(self):
return self.parentItem
diff --git a/plugins/python/filtermanager/components/filtermanagertreemodel.py b/plugins/python/filtermanager/components/filtermanagertreemodel.py
index b2bb646938..c0055ae85d 100644
--- a/plugins/python/filtermanager/components/filtermanagertreemodel.py
+++ b/plugins/python/filtermanager/components/filtermanagertreemodel.py
@@ -1,134 +1,139 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
-from PyQt5.QtCore import QAbstractItemModel, QFile, QIODevice, QModelIndex, Qt
-from PyQt5.QtWidgets import QApplication, QTreeView
-from . import filtermanagertreeitem
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
+
+from PyQt5.QtCore import QAbstractItemModel, QModelIndex, Qt
from PyQt5.QtGui import QPixmap
+from . import filtermanagertreeitem
class FilterManagerTreeModel(QAbstractItemModel):
TYPE_COLUMN = 1
NODE_COLUMN = 3
DOCUMENT_COLUMN = 4
def __init__(self, uiFilterManager, parent=None):
super(FilterManagerTreeModel, self).__init__(parent)
- self.rootItem = filtermanagertreeitem.FilterManagerTreeItem(("Name", "Type", "Thumbnail"))
+ self.rootItem = filtermanagertreeitem.FilterManagerTreeItem(
+ ("Name", "Type", "Thumbnail"))
self.uiFilterManager = uiFilterManager
self._loadTreeModel(self.rootItem)
def index(self, row, column, parent):
if not self.hasIndex(row, column, parent):
return QModelIndex()
if parent.isValid():
parentItem = parent.internalPointer()
else:
parentItem = self.rootItem
# It's a FilterManagerTreeItem
childItem = parentItem.child(row)
if childItem:
return self.createIndex(row, column, childItem)
else:
return QModelIndex()
def parent(self, index):
if not index.isValid():
return QModelIndex()
childItem = index.internalPointer()
parentItem = childItem.parent()
if parentItem == self.rootItem:
return QModelIndex()
return self.createIndex(parentItem.row(), 0, parentItem)
def rowCount(self, parent):
if parent.column() > 0:
return 0
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()
return parentItem.childCount()
def columnCount(self, parent):
if parent.isValid():
return parent.internalPointer().columnCount()
else:
return self.rootItem.columnCount()
def data(self, index, role):
if not index.isValid():
return None
item = index.internalPointer()
if role == Qt.UserRole + 1:
return item.data(self.NODE_COLUMN)
if role == Qt.UserRole + 2:
return item.data(self.DOCUMENT_COLUMN)
if role == Qt.UserRole + 3:
return item.data(self.TYPE_COLUMN)
if role != Qt.DisplayRole and role != Qt.DecorationRole:
return None
return item.data(index.column())
def flags(self, index):
if not index.isValid():
return Qt.NoItemFlags
return Qt.ItemIsEnabled | Qt.ItemIsSelectable
def headerData(self, section, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self.rootItem.data(section)
return None
def _loadTreeModel(self, parent):
for index, document in enumerate(self.uiFilterManager.documents):
rootNode = document.rootNode()
columnData = (document.fileName(),
"Document",
QPixmap.fromImage(document.thumbnail(30, 30)),
rootNode, index)
- item = filtermanagertreeitem.FilterManagerTreeItem(columnData, parent)
+ item = filtermanagertreeitem.FilterManagerTreeItem(
+ columnData, parent)
parent.appendChild(item)
childNodes = rootNode.childNodes()
if len(childNodes):
self._addSubNodes(childNodes[::-1], item, index)
def _addSubNodes(self, nodes, parent, documentIndex):
for node in nodes:
nodeName = node.name()
nodeType = node.type()
columnData = ("Unnamed" if nodeName == '' else nodeName,
"Untyped" if nodeType == '' else nodeType,
QPixmap.fromImage(node.thumbnail(30, 30)),
node, documentIndex)
- item = filtermanagertreeitem.FilterManagerTreeItem(columnData, parent)
+ item = filtermanagertreeitem.FilterManagerTreeItem(
+ columnData, parent)
parent.appendChild(item)
childNodes = node.childNodes()
if len(childNodes):
self._addSubNodes(childNodes[::-1], item, documentIndex)
diff --git a/plugins/python/filtermanager/filtermanager.py b/plugins/python/filtermanager/filtermanager.py
index 806ce710c9..1a073e6428 100644
--- a/plugins/python/filtermanager/filtermanager.py
+++ b/plugins/python/filtermanager/filtermanager.py
@@ -1,34 +1,34 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
+
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
import krita
from . import uifiltermanager
class FilterManagerExtension(krita.Extension):
def __init__(self, parent):
super(FilterManagerExtension, self).__init__(parent)
def setup(self):
pass
def createActions(self, window):
action = window.createAction("filter_manager", i18n("Filter Manager"))
action.setToolTip(i18n("Plugin to filters management."))
action.triggered.connect(self.initialize)
def initialize(self):
self.uifiltermanager = uifiltermanager.UIFilterManager()
self.uifiltermanager.initialize()
-
-
-Scripter.addExtension(FilterManagerExtension(krita.Krita.instance()))
diff --git a/plugins/python/filtermanager/filtermanagerdialog.py b/plugins/python/filtermanager/filtermanagerdialog.py
index 64feab062d..e436e61c5a 100644
--- a/plugins/python/filtermanager/filtermanagerdialog.py
+++ b/plugins/python/filtermanager/filtermanagerdialog.py
@@ -1,21 +1,24 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
+
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
from PyQt5.QtWidgets import QDialog
class FilterManagerDialog(QDialog):
def __init__(self, parent=None):
super(FilterManagerDialog, self).__init__(parent)
def closeEvent(self, event):
event.accept()
diff --git a/plugins/python/filtermanager/kritapykrita_filtermanager.desktop b/plugins/python/filtermanager/kritapykrita_filtermanager.desktop
index 2ca1d2b5df..83c00bbd58 100644
--- a/plugins/python/filtermanager/kritapykrita_filtermanager.desktop
+++ b/plugins/python/filtermanager/kritapykrita_filtermanager.desktop
@@ -1,52 +1,54 @@
[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=filtermanager
X-Python-2-Compatible=true
X-Krita-Manual=Manual.html
Name=Filter Manager
Name[ar]=مدير المرشّحات
Name[ca]=Gestor de filtres
Name[ca@valencia]=Gestor de filtres
Name[cs]=Správce filtrů
Name[de]=Filterverwaltung
Name[el]=Διαχειριστής φίλτρων
Name[en_GB]=Filter Manager
Name[es]=Gestor de filtros
Name[eu]=Iragazki-kudeatzailea
+Name[fi]=Suodatinhallinta
Name[fr]=Gestionnaire de fichiers
Name[gl]=Xestor de filtros
Name[it]=Gestore dei filtri
Name[nl]=Beheerder van filters
Name[pl]=Zarządzanie filtrami
Name[pt]=Gestor de Filtros
Name[pt_BR]=Gerenciador de filtros
Name[sv]=Filterhantering
Name[tr]=Süzgeç Yöneticisi
Name[uk]=Керування фільтрами
Name[x-test]=xxFilter Managerxx
Name[zh_CN]=滤镜管理器
Name[zh_TW]=濾鏡管理器
Comment=Plugin to filters management
Comment[ar]=ملحقة إدارة المرشّحات
-Comment[ca]=Un connector per gestionar filtres
-Comment[ca@valencia]=Un connector per gestionar filtres
+Comment[ca]=Un connector per a gestionar filtres
+Comment[ca@valencia]=Un connector per a gestionar filtres
Comment[cs]=Modul pro správu filtrů
Comment[de]=Modul zum Verwalten von Filtern
Comment[el]=Πρόσθετο για τη διαχείριση φίλτρων
Comment[en_GB]=Plugin to filters management
Comment[es]=Complemento para la gestión de filtros
Comment[eu]=Iragazkiak kudeatzeko plugina
+Comment[fi]=Liitännäinen suodatinten hallintaan
Comment[fr]=Module externe de gestion des filtres
Comment[gl]=Complemento para a xestión de filtros.
Comment[it]=Estensione per la gestione dei filtri
Comment[nl]=Plug-in voor beheer van filters
Comment[pl]=Wtyczka do zarządzania filtrami
Comment[pt]='Plugin' para a gestão de filtros
-Comment[pt_BR]=Plug-in para gerenciamento de filtros
+Comment[pt_BR]=Plugin para gerenciamento de filtros
Comment[sv]=Insticksprogram för filterhantering
Comment[tr]=Süzgeç yönetimi için eklenti
Comment[uk]=Додаток для керування фільтрами
Comment[x-test]=xxPlugin to filters managementxx
-Comment[zh_CN]=滤镜管理插件
+Comment[zh_CN]=用于管理滤镜的插件。
Comment[zh_TW]=用於濾鏡管理的外掛程式
diff --git a/plugins/python/filtermanager/uifiltermanager.py b/plugins/python/filtermanager/uifiltermanager.py
index a270bbdf85..5541bd3dd7 100644
--- a/plugins/python/filtermanager/uifiltermanager.py
+++ b/plugins/python/filtermanager/uifiltermanager.py
@@ -1,107 +1,112 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
+
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
from . import filtermanagerdialog
from .components import (filtercombobox, filtermanagertreemodel)
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QFormLayout, QAbstractItemView, QDialogButtonBox,
- QVBoxLayout, QFrame, QAbstractScrollArea, QWidget,
- QTreeView)
+ QVBoxLayout, QFrame, QTreeView)
import krita
class UIFilterManager(object):
def __init__(self):
self.mainDialog = filtermanagerdialog.FilterManagerDialog()
self.mainLayout = QVBoxLayout(self.mainDialog)
self.formLayout = QFormLayout()
- self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+ self.buttonBox = QDialogButtonBox(
+ QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.kritaInstance = krita.Krita.instance()
self._filters = sorted(self.kritaInstance.filters())
self._documents = self.kritaInstance.documents()
self.treeModel = filtermanagertreemodel.FilterManagerTreeModel(self)
self.documentsTreeView = QTreeView()
self.filterComboBox = filtercombobox.FilterComboBox(self)
self.buttonBox.accepted.connect(self.confirmButton)
self.buttonBox.rejected.connect(self.mainDialog.close)
- self.documentsTreeView.setSelectionMode(QAbstractItemView.SingleSelection)
+ self.documentsTreeView.setSelectionMode(
+ QAbstractItemView.SingleSelection)
self.mainDialog.setWindowModality(Qt.NonModal)
def initialize(self):
self.documentsTreeView.setModel(self.treeModel)
self.documentsTreeView.setWindowTitle(i18n("Document Tree Model"))
self.documentsTreeView.resizeColumnToContents(0)
self.documentsTreeView.resizeColumnToContents(1)
self.documentsTreeView.resizeColumnToContents(2)
self.formLayout.addRow(i18n("Filters:"), self.filterComboBox)
self.line = QFrame()
self.line.setFrameShape(QFrame.HLine)
self.line.setFrameShadow(QFrame.Sunken)
self.mainLayout.addWidget(self.documentsTreeView)
self.mainLayout.addLayout(self.formLayout)
self.mainLayout.addWidget(self.line)
self.mainLayout.addWidget(self.buttonBox)
self.mainDialog.resize(500, 300)
self.mainDialog.setWindowTitle(i18n("Filter Manager"))
self.mainDialog.setSizeGripEnabled(True)
self.mainDialog.show()
self.mainDialog.activateWindow()
def confirmButton(self):
documentsIndexes = []
selectionModel = self.documentsTreeView.selectionModel()
for index in selectionModel.selectedRows():
node = self.treeModel.data(index, Qt.UserRole + 1)
documentIndex = self.treeModel.data(index, Qt.UserRole + 2)
_type = self.treeModel.data(index, Qt.UserRole + 3)
if _type == 'Document':
self.applyFilterOverDocument(self.documents[documentIndex])
else:
self.applyFilterOverNode(node, self.documents[documentIndex])
documentsIndexes.append(documentIndex)
self.refreshDocumentsProjections(set(documentsIndexes))
def refreshDocumentsProjections(self, indexes):
for index in indexes:
document = self.documents[index]
document.refreshProjection()
def applyFilterOverNode(self, node, document):
_filter = self.kritaInstance.filter(self.filterComboBox.currentText())
_filter.apply(node, 0, 0, document.width(), document.height())
def applyFilterOverDocument(self, document):
- """ This method applies the selected filter just to topLevelNodes, then
- if topLevelNodes are GroupLayers, that filter will not be applied. """
+ """This method applies the selected filter just to topLevelNodes,
+ then if topLevelNodes are GroupLayers, that filter will not be
+ applied."""
for node in document.topLevelNodes():
self.applyFilterOverNode(node, document)
@property
def filters(self):
return self._filters
@property
def documents(self):
return self._documents
diff --git a/plugins/python/hello/__init__.py b/plugins/python/hello/__init__.py
index de744cb395..70c5b36650 100644
--- a/plugins/python/hello/__init__.py
+++ b/plugins/python/hello/__init__.py
@@ -1,2 +1,11 @@
-# let's make a module
-from .hello import *
+from krita import Krita, DockWidgetFactory, DockWidgetFactoryBase
+from .hello import HelloExtension, HelloDocker
+
+
+# Initialize and add the extension
+Scripter.addExtension(HelloExtension(Krita.instance()))
+
+
+# Register the docker so Krita can use it!
+Application.addDockWidgetFactory(
+ DockWidgetFactory("hello", DockWidgetFactoryBase.DockRight, HelloDocker))
diff --git a/plugins/python/hello/hello.py b/plugins/python/hello/hello.py
index cf2c6b3c8a..faba69991f 100644
--- a/plugins/python/hello/hello.py
+++ b/plugins/python/hello/hello.py
@@ -1,88 +1,87 @@
-'''
-This script is licensed CC 0 1.0, so that you can learn from it.
+# This script is licensed CC 0 1.0, so that you can learn from it.
------- CC 0 1.0 ---------------
+# ------ CC 0 1.0 ---------------
-The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
+# The person who associated a work with this deed has dedicated the
+# work to the public domain by waiving all of his or her rights to the
+# work worldwide under copyright law, including all related and
+# neighboring rights, to the extent allowed by law.
-You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission.
+# You can copy, modify, distribute and perform the work, even for
+# commercial purposes, all without asking permission.
+
+# https://creativecommons.org/publicdomain/zero/1.0/legalcode
+
+"""
+This is a simple example of a Python script for Krita.
+It demonstrates how to set up a custom extension and a custom docker!
+"""
-https://creativecommons.org/publicdomain/zero/1.0/legalcode
-'''
-#
-# This is a simple example of a Python script for Krita.
-# It demonstrates how to set up a custom extension and a custom docker!
-#
from PyQt5.QtWidgets import QWidget, QLabel, QMessageBox
-from krita import (Krita, Extension, DockWidget,
- DockWidgetFactory, DockWidgetFactoryBase)
+from krita import Extension, DockWidget
def hello():
"""
Show a test message box.
"""
- QMessageBox.information(QWidget(), i18n("Test"), i18n("Hello! This is Krita version %s") % Application.version())
+ QMessageBox.information(
+ QWidget(),
+ i18n("Test"),
+ i18n("Hello! This is Krita version %s") % Application.version())
class HelloExtension(Extension):
"""
- HelloExtension is a small example extension demonstrating basic Python scripting support in Krita!
+ HelloExtension is a small example extension demonstrating basic
+ Python scripting support in Krita!
"""
def __init__(self, parent):
"""
Standard Krita Python extension constructor.
Most of the initialization happens in :func:`setup`
:param parent: Parent widget
:type parent: :class:`QWidget` or None
"""
super(HelloExtension, self).__init__(parent)
def setup(self):
pass
def createActions(self, window):
"""
This is where most of the setup takes place!
"""
action = window.createAction("hello_python", i18n("Hello"))
action.triggered.connect(hello)
-# Initialize and add the extension
-Scripter.addExtension(HelloExtension(Krita.instance()))
-
-
class HelloDocker(DockWidget):
"""
The HelloDocker is an example of a simple Python-based docker.
"""
def __init__(self):
"""
Constructs an instance of HelloDocker and the widget it contains
"""
super(HelloDocker, self).__init__()
# The window title is also used in the Docker menu,
# so it should be set to something sensible!
self.setWindowTitle("HelloDocker")
label = QLabel("Hello", self)
self.setWidget(label)
self._label = label
def canvasChanged(self, canvas):
"""
Override canvasChanged from :class:`DockWidget`.
This gets called when the canvas changes.
You can also access the active canvas via :func:`DockWidget.canvas`
Parameter `canvas` can be null if the last document is closed
"""
self._label.setText("HelloDocker: canvas changed")
-
-
-# Register the docker so Krita can use it!
-Application.addDockWidgetFactory(DockWidgetFactory("hello", DockWidgetFactoryBase.DockRight, HelloDocker))
diff --git a/plugins/python/hello/kritapykrita_hello.desktop b/plugins/python/hello/kritapykrita_hello.desktop
index 9cc1af0736..94b3ad1f2f 100644
--- a/plugins/python/hello/kritapykrita_hello.desktop
+++ b/plugins/python/hello/kritapykrita_hello.desktop
@@ -1,54 +1,56 @@
[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=hello
X-Krita-Manual=Manual.html
X-Python-2-Compatible=true
Name=Hello World
Name[ar]=مرحبا يا عالم
Name[ca]=Hola món
Name[ca@valencia]=Hola món
Name[cs]=Hello World
Name[de]=Hallo Welt
Name[el]=Hello World
Name[en_GB]=Hello World
Name[es]=Hola mundo
Name[eu]=Kaixo mundua
+Name[fi]=Hei maailma
Name[fr]=Bonjour tout le monde
Name[gl]=Ola mundo
Name[is]=Halló Heimur
Name[it]=Ciao mondo
Name[nl]=Hallo wereld
Name[pl]=Witaj świecie
Name[pt]=Olá Mundo
Name[pt_BR]=Olá mundo
Name[sk]=Ahoj svet
Name[sv]=Hello World
Name[tr]=Merhaba Dünya
Name[uk]=Привіт, світе
Name[x-test]=xxHello Worldxx
Name[zh_CN]=Hello World
Name[zh_TW]=你好,世界
Comment=Basic plugin to test PyKrita
Comment[ar]=ملحقة أساسية لاختبار PyKrita
Comment[ca]=Connector bàsic per provar el PyKrita
Comment[ca@valencia]=Connector bàsic per provar el PyKrita
Comment[cs]=Základní modul pro testování PyKrita
Comment[de]=Basismodul zum Testen von PyKrita
Comment[el]=Βασικό πρόσθετο δοκιμής PyKrita
Comment[en_GB]=Basic plugin to test PyKrita
Comment[es]=Complemento básico para probar PyKrita
Comment[eu]=PyKrita probatzeko plugina
+Comment[fi]=Perusliitännäinen PyKritan kokeilemiseksi
Comment[fr]=Module externe élémentaire pour tester PyKrita
Comment[gl]=Complemento básico para probar PyKrita.
Comment[it]=Estensione di base per provare PyKrita
Comment[nl]=Basisplug-in om PyKrita te testen
Comment[pl]=Podstawowa wtyczka do wypróbowania PyKrity
Comment[pt]='Plugin' básico para testar o PyKrita
-Comment[pt_BR]=Plug-in básico para testar o PyKrita
+Comment[pt_BR]=Plugin básico para testar o PyKrita
Comment[sv]=Enkelt insticksprogram för att utprova PyKrita
Comment[tr]=PyKrita'yı test etmek için temel eklenti
Comment[uk]=Базовий додаток для тестування PyKrita
Comment[x-test]=xxBasic plugin to test PyKritaxx
Comment[zh_CN]=用于测试 PyKrita 的简易插件
Comment[zh_TW]=測試 PyKrita 的基本外掛程式
diff --git a/plugins/python/krita_script_starter/kritapykrita_krita_script_starter.desktop b/plugins/python/krita_script_starter/kritapykrita_krita_script_starter.desktop
index f9f11aa2ac..4f7854fb9f 100644
--- a/plugins/python/krita_script_starter/kritapykrita_krita_script_starter.desktop
+++ b/plugins/python/krita_script_starter/kritapykrita_krita_script_starter.desktop
@@ -1,39 +1,39 @@
[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=krita_script_starter
X-Python-2-Compatible=false
X-Krita-Manual=Manual.html
Name=Krita Script Starter
Name[ca]=Iniciador de scripts del Krita
Name[ca@valencia]=Iniciador de scripts del Krita
Name[cs]=Spouštěč skriptů Krita
Name[en_GB]=Krita Script Starter
Name[es]=Iniciador de guiones de Krita
Name[eu]=Krita-ren script abiarazlea
Name[gl]=Iniciador de scripts de Krita
Name[it]=Iniziatore di script per Krita
Name[nl]=Script-starter van Krita
Name[pl]=Starter skryptów Krity
Name[pt]=Inicialização do Programa do Krita
Name[sv]=Krita skriptstart
Name[uk]=Створення скрипту Krita
Name[x-test]=xxKrita Script Starterxx
Name[zh_CN]=Krita 空脚本生成器
Name[zh_TW]=Krita 指令啟動器
Comment=Create the metadata and file structure for a new Krita script
Comment[ca]=Crea les metadades i l'estructura de fitxers d'un script nou del Krita
Comment[ca@valencia]=Crea les metadades i l'estructura de fitxers d'un script nou del Krita
Comment[en_GB]=Create the metadata and file structure for a new Krita script
Comment[es]=Crear los metadatos y la estructura de archivos para un nuevo guion de Krita
Comment[eu]=Sortu Krita-script berri baterako meta-datuak eta fitxategi egitura
Comment[gl]=Crear os metadatos e a estrutura de ficheiros para un novo script de Krita.
Comment[it]=Crea i metadati e la struttura dei file per un nuovo script di Krita
Comment[nl]=Maak de metagegevens en bestandsstructuur voor een nieuw Krita-script
Comment[pl]=Utwórz metadane i strukturę plików dla nowego skryptu Krity
Comment[pt]=Cria os meta-dados e a estrutura de ficheiros para um novo programa do Krita
Comment[sv]=Skapa metadata och filstruktur för ett nytt Krita-skript
Comment[uk]=Створення метаданих і структури файлів для нового скрипту Krita
Comment[x-test]=xxCreate the metadata and file structure for a new Krita scriptxx
-Comment[zh_CN]=给一个新 Krita 脚本创建元数据及文件结构
+Comment[zh_CN]=为新的 Krita 脚本创建元数据及文件结构
Comment[zh_TW]=為新的 Krita 腳本建立中繼資料和檔案建構體
diff --git a/plugins/python/lastdocumentsdocker/kritapykrita_lastdocumentsdocker.desktop b/plugins/python/lastdocumentsdocker/kritapykrita_lastdocumentsdocker.desktop
index d9a41375af..df847362ee 100644
--- a/plugins/python/lastdocumentsdocker/kritapykrita_lastdocumentsdocker.desktop
+++ b/plugins/python/lastdocumentsdocker/kritapykrita_lastdocumentsdocker.desktop
@@ -1,46 +1,48 @@
[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=lastdocumentsdocker
X-Python-2-Compatible=false
X-Krita-Manual=Manual.html
Name=Last Documents Docker
Name[ar]=رصيف بآخر المستندات
Name[ca]=Acoblador dels darrers documents
Name[ca@valencia]=Acoblador dels darrers documents
Name[el]=Προσάρτηση τελευταίων εγγράφοων
Name[en_GB]=Last Documents Docker
Name[es]=Panel de últimos documentos
Name[eu]=Azken dokumentuen panela
+Name[fi]=Viimeisimpien tiedostojen telakka
Name[fr]=Récemment ouverts
Name[gl]=Doca dos últimos documentos
Name[it]=Area di aggancio Ultimi documenti
Name[nl]=Laatste documenten verankering
Name[pl]=Dok ostatnich dokumentów
Name[pt]=Área dos Últimos Documentos
Name[sv]=Dockningsfönster för senaste dokument
Name[tr]=Son Belgeler Doku
Name[uk]=Бічна панель останніх документів
Name[x-test]=xxLast Documents Dockerxx
Name[zh_CN]=最近文档工具面板
Name[zh_TW]=「最後文件」面板
Comment=A Python-based docker for show thumbnails to last ten documents
Comment[ar]=رصيف بِ‍«پيثون» لعرض مصغّرات آخر ١٠ مستندات مفتوحة
-Comment[ca]=Un acoblador basant en el Python per mostrar miniatures dels darrers deu documents
-Comment[ca@valencia]=Un acoblador basant en el Python per mostrar miniatures dels darrers deu documents
+Comment[ca]=Un acoblador basant en el Python per a mostrar miniatures dels darrers deu documents
+Comment[ca@valencia]=Un acoblador basant en el Python per a mostrar miniatures dels darrers deu documents
Comment[el]=Ένα εργαλείο προσάρτησης σε Python για την εμφάνιση επισκοπήσεων των δέκα τελευταίων εγγράφων
Comment[en_GB]=A Python-based docker for show thumbnails to last ten documents
Comment[es]=Un panel basado en Python para mostrar miniaturas de los últimos diez documentos
Comment[eu]=Azken hamar dokumentuen koadro-txikiak erakusteko Python-oinarridun panel bat
+Comment[fi]=Python-pohjainen telakka viimeisimpien kymmenen tiedoston pienoiskuvien näyttämiseen
Comment[fr]=Panneau en Python pour afficher les vignettes des dix derniers documents
Comment[gl]=Unha doca baseada en Python para mostrar as miniaturas dos últimos dez documentos.
Comment[it]=Un'area di aggancio basata su Python per mostrare miniature degli ultimi dieci documenti.
Comment[nl]=Een op Python gebaseerde vastzetter om miniaturen te tonen naar de laatste tien documenten.
Comment[pl]=Dok oparty na pythonie do wyświetlania miniatur ostatnich dziesięciu dokumentów
Comment[pt]=Uma área acoplável, feita em Python, para mostrar as miniaturas dos últimos dez documentos
Comment[sv]=Ett Python-baserat dockningsfönster för att visa miniatyrbilder för de tio senaste dokumenten
Comment[tr]=Son on belgenin küçük resmini göstermek için Python-tabanlı bir dok
Comment[uk]=Бічна панель на основі Python для показу мініатюр останніх десяти документів
Comment[x-test]=xxA Python-based docker for show thumbnails to last ten documentsxx
Comment[zh_CN]=基于 Python 的工具面板,显示最近十个文档的缩略图
Comment[zh_TW]=基於 Python 的面板,用於顯示最後 10 個文件縮圖
diff --git a/plugins/python/palette_docker/kritapykrita_palette_docker.desktop b/plugins/python/palette_docker/kritapykrita_palette_docker.desktop
index 1885104099..89e5716c7c 100644
--- a/plugins/python/palette_docker/kritapykrita_palette_docker.desktop
+++ b/plugins/python/palette_docker/kritapykrita_palette_docker.desktop
@@ -1,49 +1,51 @@
[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=palette_docker
X-Python-2-Compatible=true
X-Krita-Manual=Manual.html
Name=Palette docker
Name[ar]=رصيف اللوحات
Name[ca]=Acoblador de paletes
Name[ca@valencia]=Acoblador de paletes
Name[cs]=Dok palet
Name[de]=Paletten-Docker
Name[el]=Προσάρτηση παλέτας
Name[en_GB]=Palette docker
Name[es]=Panel de paleta
Name[eu]=Paleta-panela
+Name[fi]=Palettitelakka
Name[fr]=Panneau de palette
Name[gl]=Doca de paleta
Name[is]=Tengikví fyrir litaspjald
Name[it]=Area di aggancio della tavolozza
Name[nl]=Vastzetter van palet
Name[pl]=Dok palety
Name[pt]=Área acoplável da paleta
Name[sv]=Dockningsfönster för palett
Name[tr]=Palet doku
Name[uk]=Панель палітри
Name[x-test]=xxPalette dockerxx
Name[zh_CN]=调色板工具面板
Name[zh_TW]=「調色盤」面板
Comment=A Python-based docker to edit color palettes.
Comment[ar]=رصيف بِ‍«پيثون» لتحرير لوحات الألوان.
Comment[ca]=Un acoblador basant en el Python per editar paletes de colors.
Comment[ca@valencia]=Un acoblador basant en el Python per editar paletes de colors.
Comment[el]=Ένα εργαλείο προσάρτησης σε Python για την επεξεργασία παλετών χρώματος.
Comment[en_GB]=A Python-based docker to edit colour palettes.
Comment[es]=Un panel basado en Python para editar paletas de colores.
Comment[eu]=Kolore-paletak editatzeko Python-oinarridun paleta bat.
+Comment[fi]=Python-pohjainen telakka väripalettien muokkaamiseen.
Comment[fr]=Panneau en Python pour éditer les palettes de couleurs.
Comment[gl]=Unha doca baseada en Python para editar paletas de cores.
Comment[it]=Un'area di aggancio per modificare le tavolozze di colori basata su Python.
Comment[nl]=Een op Python gebaseerde vastzetter om kleurpaletten te bewerken.
Comment[pl]=Dok oparty na pythonie do edytowania palet barw.
Comment[pt]=Uma área acoplável, feita em Python, para editar paletas de cores.
Comment[sv]=Ett Python-baserat dockningsfönster för att redigera färgpaletter.
Comment[tr]=Renk paletlerini düzenlemek için Python-tabanlı bir dok.
Comment[uk]=Бічна панель для редагування палітр кольорів на основі Python.
Comment[x-test]=xxA Python-based docker to edit color palettes.xx
Comment[zh_CN]=基于 Python 的调色板编辑器工具面板
Comment[zh_TW]=基於 Python 的面板,用於編輯調色盤。
diff --git a/plugins/python/plugin_importer/__init__.py b/plugins/python/plugin_importer/__init__.py
new file mode 100644
index 0000000000..703ca05f8a
--- /dev/null
+++ b/plugins/python/plugin_importer/__init__.py
@@ -0,0 +1,24 @@
+# Copyright (c) 2019 Rebecca Breu <rebecca@rbreu.de>
+
+# This file is part of Krita.
+
+# Krita 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 3 of the License, or
+# (at your option) any later version.
+
+# Krita 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 Krita. If not, see <https://www.gnu.org/licenses/>.
+
+import krita
+
+from .plugin_importer_extension import PluginImporterExtension
+
+
+krita_instance = krita.Krita.instance()
+krita_instance.addExtension(PluginImporterExtension(krita_instance))
diff --git a/plugins/python/plugin_importer/kritapykrita_plugin_importer.desktop b/plugins/python/plugin_importer/kritapykrita_plugin_importer.desktop
new file mode 100644
index 0000000000..9fa30b9591
--- /dev/null
+++ b/plugins/python/plugin_importer/kritapykrita_plugin_importer.desktop
@@ -0,0 +1,34 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=Krita/PythonPlugin
+X-KDE-Library=plugin_importer
+X-Python-2-Compatible=false
+X-Krita-Manual=manual.html
+Name=Python Plugin Importer
+Name[ca]=Importador de connectors del Python
+Name[ca@valencia]=Importador de connectors del Python
+Name[cs]=Importér modulů pro Python
+Name[es]=Importador de complementos de Python
+Name[gl]=Importador de complementos de Python
+Name[it]=Importatore estensioni Python
+Name[nl]=Importeur van Plugin voor Python
+Name[pl]=Import wtyczek Pythona
+Name[pt]=Importador de 'Plugins' do Python
+Name[sv]=Python-insticksimport
+Name[uk]=Засіб імпортування додатків Python
+Name[x-test]=xxPython Plugin Importerxx
+Name[zh_CN]=Python 插件导入器
+Comment=Imports Python plugins from zip files.
+Comment[ca]=Importa connectors del Python a partir de fitxers «zip».
+Comment[ca@valencia]=Importa connectors del Python a partir de fitxers «zip».
+Comment[cs]=Importuje moduly Pythonu ze souborů zip.
+Comment[es]=Importa complementos de Python desde archivos zip.
+Comment[gl]=Importa complementos de Python de ficheiros zip.
+Comment[it]=Importa le estensioni Python dai file compressi.
+Comment[nl]=Importeert Python-plug-ins uit zip-bestanden.
+Comment[pl]=Importuj wtyczki Pythona z plików zip.
+Comment[pt]=Importa os 'plugins' em Python a partir de ficheiros Zip.
+Comment[sv]=Importerar Python-insticksprogram från zip-filer.
+Comment[uk]=Імпортує додатки Python з файлів zip.
+Comment[x-test]=xxImports Python plugins from zip files.xx
+Comment[zh_CN]=从 zip 文件导入 Python 插件。
diff --git a/plugins/python/plugin_importer/manual.html b/plugins/python/plugin_importer/manual.html
new file mode 100644
index 0000000000..846b1222f3
--- /dev/null
+++ b/plugins/python/plugin_importer/manual.html
@@ -0,0 +1,2 @@
+<h1>Python Plugin Importer</h1>
+<p>This Plugin imports Python plugins from zip files.</p>
diff --git a/plugins/python/plugin_importer/plugin_importer.action b/plugins/python/plugin_importer/plugin_importer.action
new file mode 100644
index 0000000000..aa3d140b36
--- /dev/null
+++ b/plugins/python/plugin_importer/plugin_importer.action
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ActionCollection version="2" name="Scripts">
+ <Actions category="Scripts">
+ <text>Python Plugin Importer</text>
+
+ <Action name="plugin_importer">
+ <icon></icon>
+ <text>Import Python Plugin...</text>
+ <whatsThis></whatsThis>
+ <toolTip></toolTip>
+ <iconText></iconText>
+ <activationFlags>0</activationFlags>
+ <activationConditions>0</activationConditions>
+ <shortcut></shortcut>
+ <isCheckable>false</isCheckable>
+ <statusTip></statusTip>
+ </Action>
+ </Actions>
+</ActionCollection>
diff --git a/plugins/python/plugin_importer/plugin_importer.py b/plugins/python/plugin_importer/plugin_importer.py
new file mode 100644
index 0000000000..c73e8a4bee
--- /dev/null
+++ b/plugins/python/plugin_importer/plugin_importer.py
@@ -0,0 +1,248 @@
+# Copyright (c) 2019 Rebecca Breu <rebecca@rbreu.de>
+
+# This file is part of Krita.
+
+# Krita 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 3 of the License, or
+# (at your option) any later version.
+
+# Krita 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 Krita. If not, see <https://www.gnu.org/licenses/>.
+
+"""This module provides the actual importing logic. See
+`:class:PluginImporter` for more info.
+
+For easy command line testing, call this module like this:
+ > python plugin_importer.py foo.zip /output/path
+"""
+
+from configparser import ConfigParser, Error as ConfigParserError
+import os
+import shutil
+import sys
+from tempfile import TemporaryDirectory
+import zipfile
+from xml.etree import ElementTree
+
+
+class PluginImportError(Exception):
+ """Base class for all exceptions of this module."""
+ pass
+
+
+class NoPluginsFoundException(PluginImportError):
+ """No valid plugins can be found in the zip file."""
+ pass
+
+
+class PluginReadError(PluginImportError):
+ """Zip file can't be read or its content can't be parsed."""
+ pass
+
+
+class PluginImporter:
+ """Import a Krita Python Plugin from a zip file into the given
+ directory.
+
+ The Importer makes barely any assumptions about the file structure
+ in the zip file. It will find one or more plugins with the
+ following strategy:
+
+ 1. Find files with the ending `.desktop` and read the Python
+ module name from them
+ 2. Find directories that correspond to the Python module names
+ and that contain an `__init__.py` file
+ 3. Find files with ending `.action` that have matching
+ `<Action name=...>` tags (these files are optional)
+ 4. Extract the desktop- and action-files and the Python module
+ directories into the corresponding pykrita and actions folders
+
+ Usage:
+
+ >>> importer = PluginImporter(
+ '/path/to/plugin.zip',
+ '/path/to/krita/resources/',
+ confirm_overwrite_callback)
+ >>> imported = importer.import_all()
+
+ """
+
+ def __init__(self, zip_filename, resources_dir,
+ confirm_overwrite_callback):
+
+ """Initialise the importer.
+
+ :param zip_filename: Filename of the zip archive containing the
+ plugin(s)
+ :param resources_dir: The Krita resources directory into which
+ to extract the plugin(s)
+ :param confirm_overwrite_callback: A function that gets called
+ if a plugin already exists in the resources directory. It gets
+ called with a dictionary of information about the plugin and
+ should return whether the user wants to overwrite the plugin
+ (True) or not (False).
+ """
+
+ self.resources_dir = resources_dir
+ self.confirm_overwrite_callback = confirm_overwrite_callback
+ try:
+ self.archive = zipfile.ZipFile(zip_filename)
+ except(zipfile.BadZipFile, zipfile.LargeZipFile, OSError) as e:
+ raise PluginReadError(str(e))
+
+ self.desktop_filenames = []
+ self.action_filenames = []
+ for filename in self.archive.namelist():
+ if filename.endswith('.desktop'):
+ self.desktop_filenames.append(filename)
+ if filename.endswith('.action'):
+ self.action_filenames.append(filename)
+
+ @property
+ def destination_pykrita(self):
+ dest = os.path.join(self.resources_dir, 'pykrita')
+ if not os.path.exists(dest):
+ os.mkdir(dest)
+ return dest
+
+ @property
+ def destination_actions(self):
+ dest = os.path.join(self.resources_dir, 'actions')
+ if not os.path.exists(dest):
+ os.mkdir(dest)
+ return dest
+
+ def get_destination_module(self, plugin):
+ return os.path.join(self.destination_pykrita, plugin['name'])
+
+ def get_destination_desktop(self, plugin):
+ return os.path.join(
+ self.destination_pykrita, '%s.desktop' % plugin['name'])
+
+ def get_destination_actionfile(self, plugin):
+ return os.path.join(
+ self.destination_actions, '%s.action' % plugin['name'])
+
+ def get_source_module(self, name):
+ namelist = self.archive.namelist()
+ for filename in namelist:
+ if (filename.endswith('/%s/' % name)
+ or filename == '%s/' % name):
+ # Sanity check: There should be an __init__.py inside
+ if ('%s__init__.py' % filename) in namelist:
+ return filename
+
+ def get_source_actionfile(self, name):
+ for filename in self.action_filenames:
+ try:
+ root = ElementTree.fromstring(
+ self.archive.read(filename).decode('utf-8'))
+ except ElementTree.ParseError as e:
+ raise PluginReadError(
+ '%s: %s' % (i18n('Action file'), str(e)))
+
+ for action in root.findall('./Actions/Action'):
+ if action.get('name') == name:
+ return filename
+
+ def read_desktop_config(self, desktop_filename):
+ config = ConfigParser()
+ try:
+ config.read_string(
+ self.archive.read(desktop_filename).decode('utf-8'))
+ except ConfigParserError as e:
+ raise PluginReadError(
+ '%s: %s' % (i18n('Desktop file'), str(e)))
+ return config
+
+ def get_plugin_info(self):
+ names = []
+ for filename in self.desktop_filenames:
+ config = self.read_desktop_config(filename)
+ try:
+ name = config['Desktop Entry']['X-KDE-Library']
+ ui_name = config['Desktop Entry']['Name']
+ except KeyError as e:
+ raise PluginReadError(
+ 'Desktop file: Key %s not found' % str(e))
+ module = self.get_source_module(name)
+ if module:
+ names.append({
+ 'name': name,
+ 'ui_name': ui_name,
+ 'desktop': filename,
+ 'module': module,
+ 'action': self.get_source_actionfile(name)
+ })
+ return names
+
+ def extract_desktop(self, plugin):
+ with open(self.get_destination_desktop(plugin), 'wb') as f:
+ f.write(self.archive.read(plugin['desktop']))
+
+ def extract_module(self, plugin):
+ with TemporaryDirectory() as tmp_dir:
+ for name in self.archive.namelist():
+ if name.startswith(plugin['module']):
+ self.archive.extract(name, tmp_dir)
+ module_dirname = os.path.join(
+ tmp_dir, *plugin['module'].split('/'))
+ try:
+ shutil.rmtree(self.get_destination_module(plugin))
+ except FileNotFoundError:
+ pass
+ shutil.copytree(module_dirname,
+ self.get_destination_module(plugin))
+
+ def extract_actionfile(self, plugin):
+ with open(self.get_destination_actionfile(plugin), 'wb') as f:
+ f.write(self.archive.read(plugin['action']))
+
+ def extract_plugin(self, plugin):
+ # Check if the plugin already exists in the source directory:
+ if (os.path.exists(self.get_destination_desktop(plugin))
+ or os.path.exists(self.get_destination_module(plugin))):
+ confirmed = self.confirm_overwrite_callback(plugin)
+ if not confirmed:
+ return False
+
+ self.extract_desktop(plugin)
+ self.extract_module(plugin)
+ if plugin['action']:
+ self.extract_actionfile(plugin)
+ return True
+
+ def import_all(self):
+ """Imports all plugins from the zip archive.
+
+ Returns a list of imported plugins.
+ """
+
+ plugins = self.get_plugin_info()
+ if not plugins:
+ raise NoPluginsFoundException(i18n('No plugins found in archive'))
+
+ imported = []
+ for plugin in plugins:
+ success = self.extract_plugin(plugin)
+ if success:
+ imported.append(plugin)
+
+ return imported
+
+
+if __name__ == '__main__':
+ def callback(plugin):
+ print('Overwriting plugin:', plugin['ui_name'])
+ return True
+
+ imported = PluginImporter(
+ sys.argv[1], sys.argv[2], callback).import_all()
+ for plugin in imported:
+ print('Imported plugin:', plugin['ui_name'])
diff --git a/plugins/python/plugin_importer/plugin_importer_extension.py b/plugins/python/plugin_importer/plugin_importer_extension.py
new file mode 100644
index 0000000000..4fbb3c814a
--- /dev/null
+++ b/plugins/python/plugin_importer/plugin_importer_extension.py
@@ -0,0 +1,106 @@
+# Copyright (c) 2019 Rebecca Breu <rebecca@rbreu.de>
+
+# This file is part of Krita.
+
+# Krita 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 3 of the License, or
+# (at your option) any later version.
+
+# Krita 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 Krita. If not, see <https://www.gnu.org/licenses/>.
+
+import os
+
+import krita
+
+from PyQt5.QtCore import QStandardPaths
+from PyQt5.QtWidgets import QFileDialog, QMessageBox
+
+from .plugin_importer import PluginImporter, PluginImportError
+
+
+class PluginImporterExtension(krita.Extension):
+
+ def __init__(self, parent):
+ super().__init__(parent)
+ self.parent = parent
+
+ def setup(self):
+ pass
+
+ def createActions(self, window):
+ action = window.createAction(
+ 'plugin_importer',
+ i18n('Import Python Plugin...'),
+ 'tools/scripts')
+ action.triggered.connect(self.import_plugin)
+
+ def confirm_overwrite(self, plugin):
+ reply = QMessageBox.question(
+ self.parent.activeWindow().qwindow(),
+ i18n('Overwrite Plugin'),
+ i18n('The plugin "%s" already exists. Overwrite it?') % (
+ plugin['ui_name']),
+ QMessageBox.Yes | QMessageBox.No)
+ return reply == QMessageBox.Yes
+
+ def get_success_text(self, plugins):
+ txt = [
+ '<p>',
+ i18n('The following plugins were imported:'),
+ '</p>',
+ '<ul>'
+ ]
+ for plugin in plugins:
+ txt.append('<li>%s</li>' % plugin['ui_name'])
+
+ txt.append('</ul>')
+ txt.append('<p>')
+ txt.append(i18n(
+ 'Please restart Krita and activate the plugins in '
+ '<em>Settings -> Configure Krita -> '
+ 'Python Plugin Manager</em>.'))
+ txt.append('</p>')
+ return ('\n').join(txt)
+
+ def get_resources_dir(self):
+ return QStandardPaths.writableLocation(
+ QStandardPaths.AppDataLocation)
+
+ def import_plugin(self):
+ zipfile = QFileDialog.getOpenFileName(
+ self.parent.activeWindow().qwindow(),
+ i18n('Import Plugin'),
+ os.path.expanduser('~'),
+ '%s (*.zip)' % i18n('Zip Archives'),
+ )[0]
+
+ if not zipfile:
+ return
+
+ try:
+ imported = PluginImporter(
+ zipfile,
+ self.get_resources_dir(),
+ self.confirm_overwrite
+ ).import_all()
+ except PluginImportError as e:
+ msg = '<p>%s</p><pre>%s</pre>' % (
+ i18n('Error during import:'), str(e))
+ QMessageBox.warning(
+ self.parent.activeWindow().qwindow(),
+ i18n('Error'),
+ msg)
+ return
+
+ if imported:
+ QMessageBox.information(
+ self.parent.activeWindow().qwindow(),
+ i18n('Import successful'),
+ self.get_success_text(imported))
diff --git a/libs/libqml/qmlthemes/default/fonts/SourceSansPro-Black.otf b/plugins/python/plugin_importer/tests/__init__.py
similarity index 100%
copy from libs/libqml/qmlthemes/default/fonts/SourceSansPro-Black.otf
copy to plugins/python/plugin_importer/tests/__init__.py
diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_missing_keys_desktop_file/plugin/foo.action b/plugins/python/plugin_importer/tests/fixtures/fail_missing_keys_desktop_file/plugin/foo.action
new file mode 100644
index 0000000000..517920fef1
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/fail_missing_keys_desktop_file/plugin/foo.action
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ActionCollection version="2" name="Scripts">
+ <Actions category="Scripts">
+ <text>Foo</text>
+
+ <Action name="foo">
+ <icon></icon>
+ <text>Foo...</text>
+ <whatsThis></whatsThis>
+ <toolTip></toolTip>
+ <iconText></iconText>
+ <activationFlags>0</activationFlags>
+ <activationConditions>0</activationConditions>
+ <shortcut></shortcut>
+ <isCheckable>false</isCheckable>
+ <statusTip></statusTip>
+ </Action>
+ </Actions>
+</ActionCollection>
diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_missing_keys_desktop_file/plugin/foo.desktop b/plugins/python/plugin_importer/tests/fixtures/fail_missing_keys_desktop_file/plugin/foo.desktop
new file mode 100644
index 0000000000..30c4cdf613
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/fail_missing_keys_desktop_file/plugin/foo.desktop
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=Krita/PythonPlugin
+X-Python-2-Compatible=false
+Name=Foo
diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_missing_keys_desktop_file/plugin/foo/__init__.py b/plugins/python/plugin_importer/tests/fixtures/fail_missing_keys_desktop_file/plugin/foo/__init__.py
new file mode 100644
index 0000000000..b376c9941f
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/fail_missing_keys_desktop_file/plugin/foo/__init__.py
@@ -0,0 +1 @@
+print('hello')
diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_missing_keys_desktop_file/plugin/foo/foo.py b/plugins/python/plugin_importer/tests/fixtures/fail_missing_keys_desktop_file/plugin/foo/foo.py
new file mode 100644
index 0000000000..b98f02ce6e
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/fail_missing_keys_desktop_file/plugin/foo/foo.py
@@ -0,0 +1 @@
+print('foo')
diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_no_desktop_file/plugin/foo.action b/plugins/python/plugin_importer/tests/fixtures/fail_no_desktop_file/plugin/foo.action
new file mode 100644
index 0000000000..517920fef1
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/fail_no_desktop_file/plugin/foo.action
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ActionCollection version="2" name="Scripts">
+ <Actions category="Scripts">
+ <text>Foo</text>
+
+ <Action name="foo">
+ <icon></icon>
+ <text>Foo...</text>
+ <whatsThis></whatsThis>
+ <toolTip></toolTip>
+ <iconText></iconText>
+ <activationFlags>0</activationFlags>
+ <activationConditions>0</activationConditions>
+ <shortcut></shortcut>
+ <isCheckable>false</isCheckable>
+ <statusTip></statusTip>
+ </Action>
+ </Actions>
+</ActionCollection>
diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_no_desktop_file/plugin/foo/__init__.py b/plugins/python/plugin_importer/tests/fixtures/fail_no_desktop_file/plugin/foo/__init__.py
new file mode 100644
index 0000000000..b376c9941f
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/fail_no_desktop_file/plugin/foo/__init__.py
@@ -0,0 +1 @@
+print('hello')
diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_no_desktop_file/plugin/foo/foo.py b/plugins/python/plugin_importer/tests/fixtures/fail_no_desktop_file/plugin/foo/foo.py
new file mode 100644
index 0000000000..b98f02ce6e
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/fail_no_desktop_file/plugin/foo/foo.py
@@ -0,0 +1 @@
+print('foo')
diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_no_init_file/plugin/foo.action b/plugins/python/plugin_importer/tests/fixtures/fail_no_init_file/plugin/foo.action
new file mode 100644
index 0000000000..517920fef1
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/fail_no_init_file/plugin/foo.action
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ActionCollection version="2" name="Scripts">
+ <Actions category="Scripts">
+ <text>Foo</text>
+
+ <Action name="foo">
+ <icon></icon>
+ <text>Foo...</text>
+ <whatsThis></whatsThis>
+ <toolTip></toolTip>
+ <iconText></iconText>
+ <activationFlags>0</activationFlags>
+ <activationConditions>0</activationConditions>
+ <shortcut></shortcut>
+ <isCheckable>false</isCheckable>
+ <statusTip></statusTip>
+ </Action>
+ </Actions>
+</ActionCollection>
diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_no_init_file/plugin/foo.desktop b/plugins/python/plugin_importer/tests/fixtures/fail_no_init_file/plugin/foo.desktop
new file mode 100644
index 0000000000..622eb6fda1
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/fail_no_init_file/plugin/foo.desktop
@@ -0,0 +1,6 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=Krita/PythonPlugin
+X-KDE-Library=foo
+X-Python-2-Compatible=false
+Name=Foo
diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_no_init_file/plugin/foo/foo.py b/plugins/python/plugin_importer/tests/fixtures/fail_no_init_file/plugin/foo/foo.py
new file mode 100644
index 0000000000..b376c9941f
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/fail_no_init_file/plugin/foo/foo.py
@@ -0,0 +1 @@
+print('hello')
diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_no_matching_plugindir/plugin/foo.action b/plugins/python/plugin_importer/tests/fixtures/fail_no_matching_plugindir/plugin/foo.action
new file mode 100644
index 0000000000..517920fef1
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/fail_no_matching_plugindir/plugin/foo.action
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ActionCollection version="2" name="Scripts">
+ <Actions category="Scripts">
+ <text>Foo</text>
+
+ <Action name="foo">
+ <icon></icon>
+ <text>Foo...</text>
+ <whatsThis></whatsThis>
+ <toolTip></toolTip>
+ <iconText></iconText>
+ <activationFlags>0</activationFlags>
+ <activationConditions>0</activationConditions>
+ <shortcut></shortcut>
+ <isCheckable>false</isCheckable>
+ <statusTip></statusTip>
+ </Action>
+ </Actions>
+</ActionCollection>
diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_no_matching_plugindir/plugin/foo.desktop b/plugins/python/plugin_importer/tests/fixtures/fail_no_matching_plugindir/plugin/foo.desktop
new file mode 100644
index 0000000000..c7bb8d0d48
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/fail_no_matching_plugindir/plugin/foo.desktop
@@ -0,0 +1,6 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=Krita/PythonPlugin
+X-KDE-Library=wrong_name
+X-Python-2-Compatible=false
+Name=Foo
diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_no_matching_plugindir/plugin/foo/__init__.py b/plugins/python/plugin_importer/tests/fixtures/fail_no_matching_plugindir/plugin/foo/__init__.py
new file mode 100644
index 0000000000..b376c9941f
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/fail_no_matching_plugindir/plugin/foo/__init__.py
@@ -0,0 +1 @@
+print('hello')
diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_no_matching_plugindir/plugin/foo/foo.py b/plugins/python/plugin_importer/tests/fixtures/fail_no_matching_plugindir/plugin/foo/foo.py
new file mode 100644
index 0000000000..b98f02ce6e
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/fail_no_matching_plugindir/plugin/foo/foo.py
@@ -0,0 +1 @@
+print('foo')
diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_action_file/plugin/foo.action b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_action_file/plugin/foo.action
new file mode 100644
index 0000000000..95352ffd7b
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_action_file/plugin/foo.action
@@ -0,0 +1 @@
+this is wrong
diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_action_file/plugin/foo.desktop b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_action_file/plugin/foo.desktop
new file mode 100644
index 0000000000..622eb6fda1
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_action_file/plugin/foo.desktop
@@ -0,0 +1,6 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=Krita/PythonPlugin
+X-KDE-Library=foo
+X-Python-2-Compatible=false
+Name=Foo
diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_action_file/plugin/foo/__init__.py b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_action_file/plugin/foo/__init__.py
new file mode 100644
index 0000000000..b376c9941f
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_action_file/plugin/foo/__init__.py
@@ -0,0 +1 @@
+print('hello')
diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_action_file/plugin/foo/foo.py b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_action_file/plugin/foo/foo.py
new file mode 100644
index 0000000000..b98f02ce6e
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_action_file/plugin/foo/foo.py
@@ -0,0 +1 @@
+print('foo')
diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_desktop_file/plugin/foo.action b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_desktop_file/plugin/foo.action
new file mode 100644
index 0000000000..517920fef1
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_desktop_file/plugin/foo.action
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ActionCollection version="2" name="Scripts">
+ <Actions category="Scripts">
+ <text>Foo</text>
+
+ <Action name="foo">
+ <icon></icon>
+ <text>Foo...</text>
+ <whatsThis></whatsThis>
+ <toolTip></toolTip>
+ <iconText></iconText>
+ <activationFlags>0</activationFlags>
+ <activationConditions>0</activationConditions>
+ <shortcut></shortcut>
+ <isCheckable>false</isCheckable>
+ <statusTip></statusTip>
+ </Action>
+ </Actions>
+</ActionCollection>
diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_desktop_file/plugin/foo.desktop b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_desktop_file/plugin/foo.desktop
new file mode 100644
index 0000000000..04552f5f74
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_desktop_file/plugin/foo.desktop
@@ -0,0 +1 @@
+this is wrong
\ No newline at end of file
diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_desktop_file/plugin/foo/__init__.py b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_desktop_file/plugin/foo/__init__.py
new file mode 100644
index 0000000000..b376c9941f
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_desktop_file/plugin/foo/__init__.py
@@ -0,0 +1 @@
+print('hello')
diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_desktop_file/plugin/foo/foo.py b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_desktop_file/plugin/foo/foo.py
new file mode 100644
index 0000000000..b98f02ce6e
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_desktop_file/plugin/foo/foo.py
@@ -0,0 +1 @@
+print('foo')
diff --git a/plugins/python/plugin_importer/tests/fixtures/success_nested/plugin/foo.action b/plugins/python/plugin_importer/tests/fixtures/success_nested/plugin/foo.action
new file mode 100644
index 0000000000..517920fef1
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/success_nested/plugin/foo.action
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ActionCollection version="2" name="Scripts">
+ <Actions category="Scripts">
+ <text>Foo</text>
+
+ <Action name="foo">
+ <icon></icon>
+ <text>Foo...</text>
+ <whatsThis></whatsThis>
+ <toolTip></toolTip>
+ <iconText></iconText>
+ <activationFlags>0</activationFlags>
+ <activationConditions>0</activationConditions>
+ <shortcut></shortcut>
+ <isCheckable>false</isCheckable>
+ <statusTip></statusTip>
+ </Action>
+ </Actions>
+</ActionCollection>
diff --git a/plugins/python/plugin_importer/tests/fixtures/success_nested/plugin/foo.desktop b/plugins/python/plugin_importer/tests/fixtures/success_nested/plugin/foo.desktop
new file mode 100644
index 0000000000..622eb6fda1
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/success_nested/plugin/foo.desktop
@@ -0,0 +1,6 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=Krita/PythonPlugin
+X-KDE-Library=foo
+X-Python-2-Compatible=false
+Name=Foo
diff --git a/plugins/python/plugin_importer/tests/fixtures/success_nested/plugin/somedir/foo/__init__.py b/plugins/python/plugin_importer/tests/fixtures/success_nested/plugin/somedir/foo/__init__.py
new file mode 100644
index 0000000000..b376c9941f
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/success_nested/plugin/somedir/foo/__init__.py
@@ -0,0 +1 @@
+print('hello')
diff --git a/plugins/python/plugin_importer/tests/fixtures/success_nested/plugin/somedir/foo/foo.py b/plugins/python/plugin_importer/tests/fixtures/success_nested/plugin/somedir/foo/foo.py
new file mode 100644
index 0000000000..b98f02ce6e
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/success_nested/plugin/somedir/foo/foo.py
@@ -0,0 +1 @@
+print('foo')
diff --git a/plugins/python/plugin_importer/tests/fixtures/success_no_action/plugin/foo.desktop b/plugins/python/plugin_importer/tests/fixtures/success_no_action/plugin/foo.desktop
new file mode 100644
index 0000000000..622eb6fda1
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/success_no_action/plugin/foo.desktop
@@ -0,0 +1,6 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=Krita/PythonPlugin
+X-KDE-Library=foo
+X-Python-2-Compatible=false
+Name=Foo
diff --git a/plugins/python/plugin_importer/tests/fixtures/success_no_action/plugin/foo/__init__.py b/plugins/python/plugin_importer/tests/fixtures/success_no_action/plugin/foo/__init__.py
new file mode 100644
index 0000000000..b376c9941f
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/success_no_action/plugin/foo/__init__.py
@@ -0,0 +1 @@
+print('hello')
diff --git a/plugins/python/plugin_importer/tests/fixtures/success_no_action/plugin/foo/foo.py b/plugins/python/plugin_importer/tests/fixtures/success_no_action/plugin/foo/foo.py
new file mode 100644
index 0000000000..b98f02ce6e
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/success_no_action/plugin/foo/foo.py
@@ -0,0 +1 @@
+print('foo')
diff --git a/plugins/python/plugin_importer/tests/fixtures/success_simple/plugin/foo.action b/plugins/python/plugin_importer/tests/fixtures/success_simple/plugin/foo.action
new file mode 100644
index 0000000000..517920fef1
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/success_simple/plugin/foo.action
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ActionCollection version="2" name="Scripts">
+ <Actions category="Scripts">
+ <text>Foo</text>
+
+ <Action name="foo">
+ <icon></icon>
+ <text>Foo...</text>
+ <whatsThis></whatsThis>
+ <toolTip></toolTip>
+ <iconText></iconText>
+ <activationFlags>0</activationFlags>
+ <activationConditions>0</activationConditions>
+ <shortcut></shortcut>
+ <isCheckable>false</isCheckable>
+ <statusTip></statusTip>
+ </Action>
+ </Actions>
+</ActionCollection>
diff --git a/plugins/python/plugin_importer/tests/fixtures/success_simple/plugin/foo.desktop b/plugins/python/plugin_importer/tests/fixtures/success_simple/plugin/foo.desktop
new file mode 100644
index 0000000000..622eb6fda1
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/success_simple/plugin/foo.desktop
@@ -0,0 +1,6 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=Krita/PythonPlugin
+X-KDE-Library=foo
+X-Python-2-Compatible=false
+Name=Foo
diff --git a/plugins/python/plugin_importer/tests/fixtures/success_simple/plugin/foo/__init__.py b/plugins/python/plugin_importer/tests/fixtures/success_simple/plugin/foo/__init__.py
new file mode 100644
index 0000000000..b376c9941f
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/success_simple/plugin/foo/__init__.py
@@ -0,0 +1 @@
+print('hello')
diff --git a/plugins/python/plugin_importer/tests/fixtures/success_simple/plugin/foo/foo.py b/plugins/python/plugin_importer/tests/fixtures/success_simple/plugin/foo/foo.py
new file mode 100644
index 0000000000..b98f02ce6e
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/success_simple/plugin/foo/foo.py
@@ -0,0 +1 @@
+print('foo')
diff --git a/plugins/python/plugin_importer/tests/fixtures/success_toplevel/foo.action b/plugins/python/plugin_importer/tests/fixtures/success_toplevel/foo.action
new file mode 100644
index 0000000000..517920fef1
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/success_toplevel/foo.action
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ActionCollection version="2" name="Scripts">
+ <Actions category="Scripts">
+ <text>Foo</text>
+
+ <Action name="foo">
+ <icon></icon>
+ <text>Foo...</text>
+ <whatsThis></whatsThis>
+ <toolTip></toolTip>
+ <iconText></iconText>
+ <activationFlags>0</activationFlags>
+ <activationConditions>0</activationConditions>
+ <shortcut></shortcut>
+ <isCheckable>false</isCheckable>
+ <statusTip></statusTip>
+ </Action>
+ </Actions>
+</ActionCollection>
diff --git a/plugins/python/plugin_importer/tests/fixtures/success_toplevel/foo.desktop b/plugins/python/plugin_importer/tests/fixtures/success_toplevel/foo.desktop
new file mode 100644
index 0000000000..622eb6fda1
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/success_toplevel/foo.desktop
@@ -0,0 +1,6 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=Krita/PythonPlugin
+X-KDE-Library=foo
+X-Python-2-Compatible=false
+Name=Foo
diff --git a/plugins/python/plugin_importer/tests/fixtures/success_toplevel/foo/__init__.py b/plugins/python/plugin_importer/tests/fixtures/success_toplevel/foo/__init__.py
new file mode 100644
index 0000000000..b376c9941f
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/success_toplevel/foo/__init__.py
@@ -0,0 +1 @@
+print('hello')
diff --git a/plugins/python/plugin_importer/tests/fixtures/success_toplevel/foo/foo.py b/plugins/python/plugin_importer/tests/fixtures/success_toplevel/foo/foo.py
new file mode 100644
index 0000000000..b98f02ce6e
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/fixtures/success_toplevel/foo/foo.py
@@ -0,0 +1 @@
+print('foo')
diff --git a/plugins/python/plugin_importer/tests/test_plugin_importer.py b/plugins/python/plugin_importer/tests/test_plugin_importer.py
new file mode 100644
index 0000000000..f402cba1e3
--- /dev/null
+++ b/plugins/python/plugin_importer/tests/test_plugin_importer.py
@@ -0,0 +1,300 @@
+"""Unit tests for the plugin_importer module.
+
+Unit tests can either be toplevel functions whose names start with
+`test_`, or they can be class methods on classes derived from
+`unittest.TestCase`; again the method names need to start with `test_`.
+"""
+
+
+import os
+import pytest
+from tempfile import TemporaryDirectory
+from unittest import mock, TestCase
+from zipfile import ZipFile
+
+from .. plugin_importer import (
+ NoPluginsFoundException,
+ PluginImporter,
+ PluginReadError,
+)
+
+
+class PluginImporterTestCase(TestCase):
+ """Collection of unit tests for the plugin_importer module.
+
+ Since the tests need a bit of setup for file system operations, we gather
+ them together in a TestCase class.
+ """
+
+ def setUp(self):
+ """This method will be run before each test in this class.
+
+ We create temporary directories for creating and extracting
+ zip files: `resources_dir` will be the destination (a stand-in
+ for Krita's resources dir), `plugin_dir` the source where our
+ plugin zip is located.
+ """
+
+ self.resources_dir = TemporaryDirectory()
+ self.plugin_dir = TemporaryDirectory()
+
+ def tearDown(self):
+ """This method will be run after each test in this class.
+
+ We remove the temporary directories created in `setUp`.
+ """
+
+ self.resources_dir.cleanup()
+ self.plugin_dir.cleanup()
+
+ @property
+ def zip_filename(self):
+ """Helper method for easy access to the full path of the zipped
+ plugin.
+ """
+ return os.path.join(self.plugin_dir.name, 'plugin.zip')
+
+ def zip_plugin(self, dirname):
+ """Helper method to zip a plugin from the subdirectory `dirname` in
+ the `fixtures` folder and store it in the temporary directory
+ `self.plugin_dir`.
+
+ The `fixtures` directory contains the non-zipped plugins for easier
+ maintenance.
+ """
+
+ # Get the full name of the folder this file resides in:
+ testroot = os.path.dirname(os.path.realpath(__file__))
+
+ # From that, we can get the full name of the plugin fixture folder:
+ src = os.path.join(testroot, 'fixtures', dirname)
+
+ # Zip it:
+ with ZipFile(self.zip_filename, 'w') as plugin_zip:
+ for root, dirs, files in os.walk(src):
+ dirname = root.replace(src, '')
+ for filename in files + dirs:
+ plugin_zip.write(
+ filename=os.path.join(root, filename),
+ arcname=os.path.join(dirname, filename))
+
+ def assert_in_resources_dir(self, *path):
+ """Helper method to check whether a directory or file exists inside
+ `self.resources_dir`.
+ """
+
+ assert os.path.exists(os.path.join(self.resources_dir.name, *path))
+
+ def assert_not_in_resources_dir(self, *path):
+ """Helper method to check whether a directory or file doesn't exist
+ inside `self.resources_dir`.
+ """
+
+ assert not os.path.exists(os.path.join(self.resources_dir.name, *path))
+
+ ############################################################
+ # The actual tests start below
+
+ def test_zipfile_doesnt_exist(self):
+ """Test: Import a filename that doesn't exist."""
+
+ # Create nothing here...
+
+ with pytest.raises(PluginReadError):
+ # We expect an exception
+ PluginImporter(self.zip_filename,
+ self.resources_dir.name,
+ lambda x: True)
+
+ def test_zipfile_not_a_zip(self):
+ """Test: Import a file that isn't a zip file."""
+
+ # Create a text file:
+ with open(self.zip_filename, 'w') as f:
+ f.write('foo')
+
+ with pytest.raises(PluginReadError):
+ # We expect an exception
+ PluginImporter(self.zip_filename,
+ self.resources_dir.name,
+ lambda x: True)
+
+ def test_simple_plugin_success(self):
+ """Test: Import a basic plugin."""
+
+ self.zip_plugin('success_simple')
+ importer = PluginImporter(self.zip_filename,
+ self.resources_dir.name,
+ lambda x: True)
+ imported = importer.import_all()
+ assert len(imported) == 1
+ self.assert_in_resources_dir('pykrita', 'foo')
+ self.assert_in_resources_dir('pykrita', 'foo', '__init__.py')
+ self.assert_in_resources_dir('pykrita', 'foo', 'foo.py')
+ self.assert_in_resources_dir('pykrita', 'foo.desktop')
+ self.assert_in_resources_dir('actions', 'foo.action')
+
+ def test_toplevel_plugin_success(self):
+ """Test: Import a plugin with everything at toplevel."""
+
+ self.zip_plugin('success_toplevel')
+ importer = PluginImporter(self.zip_filename,
+ self.resources_dir.name,
+ lambda x: True)
+ imported = importer.import_all()
+ assert len(imported) == 1
+ self.assert_in_resources_dir('pykrita', 'foo')
+ self.assert_in_resources_dir('pykrita', 'foo', '__init__.py')
+ self.assert_in_resources_dir('pykrita', 'foo', 'foo.py')
+ self.assert_in_resources_dir('pykrita', 'foo.desktop')
+ self.assert_in_resources_dir('actions', 'foo.action')
+
+ def test_nested_plugin_success(self):
+ """Test: Import a plugin with nested directories."""
+
+ self.zip_plugin('success_nested')
+ importer = PluginImporter(self.zip_filename,
+ self.resources_dir.name,
+ lambda x: True)
+ imported = importer.import_all()
+ assert len(imported) == 1
+ self.assert_in_resources_dir('pykrita', 'foo')
+ self.assert_in_resources_dir('pykrita', 'foo', '__init__.py')
+ self.assert_in_resources_dir('pykrita', 'foo', 'foo.py')
+ self.assert_in_resources_dir('pykrita', 'foo.desktop')
+ self.assert_in_resources_dir('actions', 'foo.action')
+
+ def test_no_action_success(self):
+ """Test: Import a plugin without action file.
+
+ Should import fine without creating an action file."""
+
+ self.zip_plugin('success_no_action')
+ importer = PluginImporter(self.zip_filename,
+ self.resources_dir.name,
+ lambda x: True)
+ imported = importer.import_all()
+ assert len(imported) == 1
+ self.assert_in_resources_dir('pykrita', 'foo')
+ self.assert_in_resources_dir('pykrita', 'foo', '__init__.py')
+ self.assert_in_resources_dir('pykrita', 'foo', 'foo.py')
+ self.assert_in_resources_dir('pykrita', 'foo.desktop')
+ self.assert_not_in_resources_dir('actions', 'foo.action')
+
+ def test_overwrite_existing(self):
+ """Test: Overwrite an existing plugin when overwrite confirmed."""
+
+ self.zip_plugin('success_simple')
+
+ # Create an existing python module in the the resources directory:
+ plugin_dir = os.path.join(self.resources_dir.name, 'pykrita', 'foo')
+ os.makedirs(plugin_dir)
+ init_file = os.path.join(plugin_dir, '__init__.py')
+ with open(init_file, 'w') as f:
+ f.write('# existing module')
+
+ # Create a mock callback on which we can test that it has been called:
+ confirm_callback = mock.MagicMock(return_value=True)
+ importer = PluginImporter(self.zip_filename,
+ self.resources_dir.name,
+ confirm_callback)
+ imported = importer.import_all()
+ assert len(imported) == 1
+ assert confirm_callback.called
+
+ # Existing plugin should be overwritten:
+ with open(init_file, 'r') as f:
+ assert f.read().strip() == "print('hello')"
+
+ def test_dont_overwrite_existing(self):
+ """Test: Don't overwrite an existing plugin when overwrite not
+ confirmed."""
+
+ self.zip_plugin('success_simple')
+
+ # Create an existing python module in the the resources directory:
+ plugin_dir = os.path.join(self.resources_dir.name, 'pykrita', 'foo')
+ os.makedirs(plugin_dir)
+ init_file = os.path.join(plugin_dir, '__init__.py')
+ with open(init_file, 'w') as f:
+ f.write('# existing module')
+
+ # Create a mock callback on which we can test that it has been called:
+ confirm_callback = mock.MagicMock(return_value=False)
+ importer = PluginImporter(self.zip_filename,
+ self.resources_dir.name,
+ confirm_callback)
+ imported = importer.import_all()
+ assert len(imported) == 0
+ assert confirm_callback.called
+
+ # Existing plugin should not be overwritten:
+ with open(init_file, 'r') as f:
+ assert f.read().strip() == '# existing module'
+
+ def test_missing_desktop_file(self):
+ """Test: Import plugin without a desktop file."""
+
+ self.zip_plugin('fail_no_desktop_file')
+ importer = PluginImporter(self.zip_filename,
+ self.resources_dir.name,
+ lambda x: True)
+ with pytest.raises(NoPluginsFoundException):
+ # We expect an exception
+ importer.import_all()
+
+ def test_unparsable_desktop_file(self):
+ """Test: Import plugin whose desktop file is not parsable."""
+
+ self.zip_plugin('fail_unparsable_desktop_file')
+ importer = PluginImporter(self.zip_filename,
+ self.resources_dir.name,
+ lambda x: True)
+ with pytest.raises(PluginReadError):
+ # We expect an exception
+ importer.import_all()
+
+ def test_missing_keys_in_desktop_file(self):
+ """Test: Import plugin whose destkop file is missing needed keys."""
+
+ self.zip_plugin('fail_missing_keys_desktop_file')
+ importer = PluginImporter(self.zip_filename,
+ self.resources_dir.name,
+ lambda x: True)
+ with pytest.raises(PluginReadError):
+ # We expect an exception
+ importer.import_all()
+
+ def test_no_matching_plugindir(self):
+ """Test: Import plugin whose destkop file is missing needed keys."""
+
+ self.zip_plugin('fail_no_matching_plugindir')
+ importer = PluginImporter(self.zip_filename,
+ self.resources_dir.name,
+ lambda x: True)
+ with pytest.raises(NoPluginsFoundException):
+ # We expect an exception
+ importer.import_all()
+
+ def test_no_init_file(self):
+ """Test: Import plugin whose python module is missing the __init__.py
+ file."""
+
+ self.zip_plugin('fail_no_init_file')
+ importer = PluginImporter(self.zip_filename,
+ self.resources_dir.name,
+ lambda x: True)
+ with pytest.raises(NoPluginsFoundException):
+ # We expect an exception
+ importer.import_all()
+
+ def test_unparsable_action_file(self):
+ """Test: Import plugin whose action file isn't parsable."""
+
+ self.zip_plugin('fail_unparsable_action_file')
+ importer = PluginImporter(self.zip_filename,
+ self.resources_dir.name,
+ lambda x: True)
+ with pytest.raises(PluginReadError):
+ # We expect an exception
+ importer.import_all()
diff --git a/plugins/python/quick_settings_docker/kritapykrita_quick_settings_docker.desktop b/plugins/python/quick_settings_docker/kritapykrita_quick_settings_docker.desktop
index 1593499181..c1dff9c568 100644
--- a/plugins/python/quick_settings_docker/kritapykrita_quick_settings_docker.desktop
+++ b/plugins/python/quick_settings_docker/kritapykrita_quick_settings_docker.desktop
@@ -1,47 +1,49 @@
[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=quick_settings_docker
X-Python-2-Compatible=true
X-Krita-Manual=Manual.html
Name=Quick Settings Docker
Name[ar]=رصيف بإعدادات سريعة
Name[ca]=Acoblador d'arranjament ràpid
Name[ca@valencia]=Acoblador d'arranjament ràpid
Name[cs]=Dok pro rychlé nastavení
Name[el]=Προσάρτηση γρήγορων ρυθμίσεων
Name[en_GB]=Quick Settings Docker
Name[es]=Panel de ajustes rápidos
Name[eu]=Ezarpen azkarren panela
+Name[fi]=Pika-asetustelakka
Name[fr]=Réglages rapides
Name[gl]=Doca de configuración rápida
Name[it]=Area di aggancio delle impostazioni rapide
Name[nl]=Verankering voor snelle instellingen
Name[pl]=Dok szybkich ustawień
Name[pt]=Área de Configuração Rápida
Name[sv]=Dockningspanel med snabbinställningar
Name[tr]=Hızlı Ayarlar Doku
Name[uk]=Панель швидких параметрів
Name[x-test]=xxQuick Settings Dockerxx
Name[zh_CN]=快速设置工具面板
Name[zh_TW]=「快速設定」面板
Comment=A Python-based docker for quickly changing brush size and opacity.
Comment[ar]=رصيف بِ‍«پيثون» لتغيير حجم الفرشاة وشفافيّتها بسرعة.
-Comment[ca]=Un acoblador basant en el Python per canviar ràpidament la mida del pinzell i l'opacitat.
-Comment[ca@valencia]=Un acoblador basant en el Python per canviar ràpidament la mida del pinzell i l'opacitat.
+Comment[ca]=Un acoblador basat en el Python per a canviar ràpidament la mida i l'opacitat del pinzell.
+Comment[ca@valencia]=Un acoblador basat en el Python per a canviar ràpidament la mida i l'opacitat del pinzell.
Comment[el]=Ένα εργαλείο προσάρτησης σε Python για γρήγορη αλλαγή του μεγέθους και της αδιαφάνειας του πινέλου.
Comment[en_GB]=A Python-based docker for quickly changing brush size and opacity.
Comment[es]=Un panel basado en Python para cambiar rápidamente el tamaño y la opacidad del pincel.
Comment[eu]=Isipu-neurria eta -opakotasuna aldatzeko Python-oinarridun panel bat.
+Comment[fi]=Python-pohjainen telakka siveltimen koon ja läpikuultavuuden nopean muuttamiseen.
Comment[fr]=Panneau en python pour modifier rapidement la taille et l'opacité des brosses.
Comment[gl]=Unha doca baseada en Python para cambiar rapidamente a opacidade e o tamaño dos pinceis.
Comment[it]=Un'area di aggancio basata su Python per cambiare rapidamente la dimensione del pennello e l'opacità.
Comment[nl]=Een op Python gebaseerde docker voor snel wijzigen van penseelgrootte en dekking.
-Comment[pl]=Dok oparty na pythonie do szybkiej zmiany rozmiaru i nieprzezroczystości pędzla.
+Comment[pl]=Dok oparty na Pythonie do szybkiej zmiany rozmiaru i nieprzezroczystości pędzla.
Comment[pt]=Uma área acoplável em Python para mudar rapidamente o tamanho e opacidade do pincel.
Comment[sv]=En Python-baserad dockningspanel för att snabbt ändra penselstorlek och ogenomskinlighet.
Comment[tr]=Fırça boyutunu ve matlığını hızlıca değiştirmek için Python-tabanlı bir dok.
Comment[uk]=Панель на основі мови програмування Python для швидкої зміни розміру та непрозорості пензля.
Comment[x-test]=xxA Python-based docker for quickly changing brush size and opacity.xx
Comment[zh_CN]=基于 Python 的用于快速更改笔刷尺寸和透明度的工具面板。
Comment[zh_TW]=基於 Python 的面板,用於快速變更筆刷尺寸和不透明度。
diff --git a/plugins/python/scriptdocker/kritapykrita_scriptdocker.desktop b/plugins/python/scriptdocker/kritapykrita_scriptdocker.desktop
index 247838cf42..5c2439093d 100644
--- a/plugins/python/scriptdocker/kritapykrita_scriptdocker.desktop
+++ b/plugins/python/scriptdocker/kritapykrita_scriptdocker.desktop
@@ -1,45 +1,46 @@
[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=scriptdocker
X-Python-2-Compatible=false
Name=Script Docker
Name[ar]=رصيف سكربتات
Name[ca]=Acoblador de scripts
Name[ca@valencia]=Acoblador de scripts
Name[cs]=Dok skriptu
Name[el]=Προσάρτηση σεναρίων
Name[en_GB]=Script Docker
Name[es]=Panel de guiones
Name[eu]=Script-panela
+Name[fi]=Skriptitelakka
Name[fr]=Panneau de script
Name[gl]=Doca de scripts
Name[it]=Area di aggancio degli script
Name[nl]=Verankering van scripts
Name[pl]=Dok skryptów
Name[pt]=Área de Programas
Name[sv]=Dockningsfönster för skript
Name[tr]=Betik Doku
Name[uk]=Бічна панель скриптів
Name[x-test]=xxScript Dockerxx
Name[zh_CN]=脚本工具面板
Name[zh_TW]=「腳本」面板
Comment=A Python-based docker for create actions and point to Python scripts
Comment[ar]=رصيف بِ‍«پيثون» لإنشاء الإجراءات والإشارة إلى سكربتات «پيثون»
-Comment[ca]=Un acoblador basant en el Python per crear accions i apuntar a scripts del Python
-Comment[ca@valencia]=Un acoblador basant en el Python per crear accions i apuntar a scripts del Python
+Comment[ca]=Un acoblador basat en el Python per a crear accions i apuntar a scripts de Python
+Comment[ca@valencia]=Un acoblador basat en el Python per a crear accions i apuntar a scripts de Python
Comment[el]=Ένα εργαλείο προσάρτησης σε Python για τη δημιουργία ενεργειών και τη δεικτοδότηση σεναρίων σε Python
Comment[en_GB]=A Python-based docker for create actions and point to Python scripts
Comment[es]=Un panel basado en Python para crear y apuntar a guiones de Python
Comment[eu]=Python-oinarridun panel bat.
Comment[gl]=Unha doca escrita en Python para crear accións e apuntar a scripts escritos en Python.
Comment[it]=Un'area di aggancio basata su Python per creare azioni e scegliere script Python
Comment[nl]=Een op Python gebaseerde vastzetter voor aanmaakacties en wijzen naar Python-scripts
Comment[pl]=Dok oparty na pythonie do tworzenia działań i punktów dla skryptów pythona
Comment[pt]=Uma área acoplável, feita em Python, para criar acções e apontar para programas em Python
Comment[sv]=Ett Python-baserat dockningsfönster för att skapa åtgärder och peka ut Python-skript
Comment[tr]=Eylemler oluşturmak ve Python betiklerine yönlendirmek için Python-tabanlı bir dok
Comment[uk]=Бічна панель для створення дій і керування скриптами на основі Python.
Comment[x-test]=xxA Python-based docker for create actions and point to Python scriptsxx
Comment[zh_CN]=基于 Python 的用于创建动作并指向 Python 脚本的工具面板
Comment[zh_TW]=基於 Python 的面板,用於建立動作並指向 Python 腳本
diff --git a/plugins/python/scripter/kritapykrita_scripter.desktop b/plugins/python/scripter/kritapykrita_scripter.desktop
index 1b92a8fea1..ef5d9e192d 100644
--- a/plugins/python/scripter/kritapykrita_scripter.desktop
+++ b/plugins/python/scripter/kritapykrita_scripter.desktop
@@ -1,44 +1,45 @@
[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=scripter
X-Python-2-Compatible=true
X-Krita-Manual=Manual.html
Name=Scripter
Name[ca]=Scripter
Name[ca@valencia]=Scripter
Name[de]=Scripter
Name[el]=Σενάρια
Name[en_GB]=Scripter
Name[es]=Guionador
Name[eu]=Script egilea
Name[fr]=Scripter
Name[gl]=Executor de scripts
Name[it]=Scripter
Name[nl]=Scriptmaker
Name[pl]=Skrypter
Name[pt]=Programador
Name[sv]=Skriptgenerator
Name[tr]=Betik yazarı
Name[uk]=Скриптер
Name[x-test]=xxScripterxx
Name[zh_CN]=脚本编写器
Name[zh_TW]=腳本編寫者
Comment=Plugin to execute ad-hoc Python code
Comment[ca]=Connector per executar codi Python ad hoc
Comment[ca@valencia]=Connector per executar codi Python ad hoc
Comment[el]=Πρόσθετο για την εκτέλεση συγκεκριμένου κώδικα Python
Comment[en_GB]=Plugin to execute ad-hoc Python code
Comment[es]=Complemento para ejecutar código Python a medida
Comment[eu]=Berariaz egindako Python kodea exekutatzeko plugina
+Comment[fi]=Liitännäinen satunnaisen Python-koodin suorittamiseksi
Comment[gl]=Complemento para executar código de Python escrito no momento.
Comment[it]=Estensione per eseguire ad-hoc codice Python
Comment[nl]=Plug-in om ad-hoc Python code uit te voeren
Comment[pl]=Wtyczka do wykonywania kodu Pythona ad-hoc
Comment[pt]='Plugin' para executar código em Python arbitrário
Comment[sv]=Insticksprogram för att köra godtycklig Python-kod
Comment[tr]=Geçici Python kodu çalıştırmak için eklenti
Comment[uk]=Додаток для виконання апріорного коду Python
Comment[x-test]=xxPlugin to execute ad-hoc Python codexx
-Comment[zh_CN]=执行现场编写的 Python 代码的插件
+Comment[zh_CN]=用于执行当场编写的 Python 代码的插件
Comment[zh_TW]=外掛程式,用於執行特定 Python 程式碼
diff --git a/plugins/python/scripter/ui_scripter/actions/runaction/runaction.py b/plugins/python/scripter/ui_scripter/actions/runaction/runaction.py
index 98dfb2b550..4fa88388f6 100644
--- a/plugins/python/scripter/ui_scripter/actions/runaction/runaction.py
+++ b/plugins/python/scripter/ui_scripter/actions/runaction/runaction.py
@@ -1,156 +1,159 @@
"""
Copyright (c) 2017 Eliakin Costa <eliakim170@gmail.com>
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.
"""
from PyQt5.QtWidgets import QAction
from PyQt5.QtGui import QIcon, QKeySequence
from PyQt5.QtCore import Qt
import sys
import traceback
import inspect
from . import docwrapper
import krita
if sys.version_info[0] > 2:
import importlib
from importlib.machinery import SourceFileLoader
else:
import imp
PYTHON27 = sys.version_info.major == 2 and sys.version_info.minor == 7
PYTHON33 = sys.version_info.major == 3 and sys.version_info.minor == 3
PYTHON34 = sys.version_info.major == 3 and sys.version_info.minor == 4
EXEC_NAMESPACE = "__main__" # namespace that user scripts will run in
class RunAction(QAction):
def __init__(self, scripter, parent=None):
super(RunAction, self).__init__(parent)
self.scripter = scripter
self.editor = self.scripter.uicontroller.editor
self.output = self.scripter.uicontroller.findTabWidget('OutPut', 'OutPutTextEdit')
self.triggered.connect(self.run)
self.setText(i18n("Run"))
self.setToolTip('Run Ctrl+R')
self.setIcon(QIcon(':/icons/run.svg'))
self.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_R))
@property
def parent(self):
return 'toolBar'
def run(self):
""" This method execute python code from an activeDocument (file) or direct
from editor (ui_scripter/editor/pythoneditor.py). When executing code
from a file, we use importlib to load this module/file and with
"users_script" name. That's implementation seeks for a "main()" function in the script.
When executing code from editor without creating a file, we compile
this script to bytecode and we execute this in an empty scope. That's
faster than use exec directly and cleaner, because we are using an empty scope. """
self.scripter.uicontroller.setActiveWidget('OutPut')
stdout = sys.stdout
stderr = sys.stderr
output = docwrapper.DocWrapper(self.output.document())
if (self.editor._documentModified is True):
output.write("==== Warning: Script not saved! ====\n")
else:
output.write("======================================\n")
sys.stdout = output
sys.stderr = output
script = self.editor.document().toPlainText()
document = self.scripter.documentcontroller.activeDocument
try:
if document and self.editor._documentModified is False:
if PYTHON27:
users_module = self.run_py2_document(document)
else:
users_module = self.run_py3_document(document)
# maybe script is to be execed, maybe main needs to be invoked
# if there is a main() then execute it, otherwise don't worry...
if hasattr(users_module, "main") and inspect.isfunction(users_module.main):
users_module.main()
else:
code = compile(script, '<string>', 'exec')
globals_dict = {"__name__": EXEC_NAMESPACE}
exec(code, globals_dict)
+ except SystemExit:
+ # user typed quit() or exit()
+ self.scripter.uicontroller.closeScripter()
except Exception:
# Provide context (line number and text) for an error that is caught.
# Ordinarily, syntax and Indent errors are caught during initial
# compilation in exec(), and the traceback traces back to this file.
# So these need to be treated separately.
# Other errors trace back to the file/script being run.
type_, value_, traceback_ = sys.exc_info()
if type_ == SyntaxError:
errorMessage = "%s\n%s" % (value_.text.rstrip(), " " * (value_.offset - 1) + "^")
# rstrip to remove trailing \n, output needs to be fixed width font for the ^ to align correctly
errorText = "Syntax Error on line %s" % value_.lineno
elif type_ == IndentationError:
# (no offset is provided for an IndentationError
errorMessage = value_.text.rstrip()
errorText = "Unexpected Indent on line %s" % value_.lineno
else:
errorText = traceback.format_exception_only(type_, value_)[0]
format_string = "In file: {0}\nIn function: {2} at line: {1}. Line with error:\n{3}"
tbList = traceback.extract_tb(traceback_)
tb = tbList[-1]
errorMessage = format_string.format(*tb)
m = "\n**********************\n%s\n%s\n**********************\n" % (errorText, errorMessage)
output.write(m)
sys.stdout = stdout
sys.stderr = stderr
# scroll to bottom of output
bottom = self.output.verticalScrollBar().maximum()
self.output.verticalScrollBar().setValue(bottom)
def run_py2_document(self, document):
""" Loads and executes an external script using Python 2 specific operations
and returns the loaded module for further execution if needed.
"""
try:
user_module = imp.load_source(EXEC_NAMESPACE, document.filePath)
except Exception as e:
raise e
return user_module
def run_py3_document(self, document):
""" Loads and executes an external script using Python 3 specific operations
and returns the loaded module for further execution if needed.
"""
spec = importlib.util.spec_from_file_location(EXEC_NAMESPACE, document.filePath)
try:
users_module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(users_module)
except AttributeError as e: # no module from spec
if PYTHON34 or PYTHON33:
loader = SourceFileLoader(EXEC_NAMESPACE, document.filePath)
users_module = loader.load_module()
else:
raise e
return users_module
diff --git a/plugins/python/selectionsbagdocker/kritapykrita_selectionsbagdocker.desktop b/plugins/python/selectionsbagdocker/kritapykrita_selectionsbagdocker.desktop
index d79d012a89..58b48d5972 100644
--- a/plugins/python/selectionsbagdocker/kritapykrita_selectionsbagdocker.desktop
+++ b/plugins/python/selectionsbagdocker/kritapykrita_selectionsbagdocker.desktop
@@ -1,45 +1,46 @@
[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=selectionsbagdocker
X-Python-2-Compatible=false
Name=Selections Bag Docker
Name[ca]=Acoblador de bossa de seleccions
Name[ca@valencia]=Acoblador de bossa de seleccions
Name[el]=Προσάρτηση σάκου επιλογών
Name[en_GB]=Selections Bag Docker
Name[es]=Panel de selecciones
Name[eu]=Hautapen-zakua panela
Name[fr]=Outils de sélection
Name[gl]=Doca de bolsa das seleccións
Name[it]=Area di raccolta selezioni
Name[nl]=Docker van zak met selecties
Name[pl]=Dok worka zaznaczeń
Name[pt]=Área de Selecções
Name[sv]=Dockningspanel med markeringspåse
Name[tr]=Seçim Çantası Doku
Name[uk]=Бічна панель позначеного
Name[x-test]=xxSelections Bag Dockerxx
Name[zh_CN]=选区列表工具面板
Name[zh_TW]=「選取範圍收藏」面板
Comment=A docker that allow to store a list of selections
Comment[ar]=رصيف يتيح تخزين قائمة تحديدات
Comment[ca]=Un acoblador que permet emmagatzemar una llista de seleccions
Comment[ca@valencia]=Un acoblador que permet emmagatzemar una llista de seleccions
Comment[cs]=Dok umožňující uložit seznam výběrů
Comment[el]=Ένα εργαλείο προσάρτησης που επιτρέπει την αποθήκευση μιας λίστας επιλογών
Comment[en_GB]=A docker that allow to store a list of selections
Comment[es]=Un panel que permite guardar una lista de selecciones
Comment[eu]=Hautapen zerrenda bat biltegiratzen uzten duen panel bat
+Comment[fi]=Telakka, joka sallii tallentaa valintaluettelon
Comment[fr]=Panneau permettant de conserver une liste de sélections
Comment[gl]=Unha doca que permite almacenar unha lista de seleccións.
Comment[it]=Un'area di aggancio che consente di memorizzare un elenco di selezioni
Comment[nl]=Een docker die een lijst met selecties kan opslaan
Comment[pl]=Dok, który umożliwia przechowywanie listy zaznaczeń
Comment[pt]=Uma área acoplável que permite guardar uma lista de selecções
Comment[sv]=En dockningspanel som gör det möjligt att lagra en lista över markeringar
Comment[tr]=Seçimlerin bir listesini saklamayı sağlayan bir dok
Comment[uk]=Бічна панель, на якій можна зберігати список позначеного
Comment[x-test]=xxA docker that allow to store a list of selectionsxx
-Comment[zh_CN]=用于保存一系列选区的工具面板
+Comment[zh_CN]=用于保存一组选区的工具面板
Comment[zh_TW]=允許儲存選取範圍列表的面板
diff --git a/plugins/python/tenbrushes/kritapykrita_tenbrushes.desktop b/plugins/python/tenbrushes/kritapykrita_tenbrushes.desktop
index 79d39a329f..c5378ba5dd 100644
--- a/plugins/python/tenbrushes/kritapykrita_tenbrushes.desktop
+++ b/plugins/python/tenbrushes/kritapykrita_tenbrushes.desktop
@@ -1,46 +1,48 @@
[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=tenbrushes
X-Python-2-Compatible=true
X-Krita-Manual=Manual.html
Name=Ten Brushes
Name[ar]=عشرُ فُرش
Name[ca]=Deu pinzells
Name[ca@valencia]=Deu pinzells
Name[cs]=Deset štětců
Name[el]=Δέκα πινέλα
Name[en_GB]=Ten Brushes
Name[es]=Diez pinceles
Name[eu]=Hamar isipu
+Name[fi]=Kymmenen sivellintä
Name[fr]=Raccourcis des préréglages de brosses
Name[gl]=Dez pinceis
Name[is]=Tíu penslar
Name[it]=Dieci pennelli
Name[nl]=Tien penselen
Name[pl]=Dziesięć pędzli
Name[pt]=Dez Pincéis
Name[sv]=Tio penslar
Name[tr]=On Fırça
Name[uk]=Десять пензлів
Name[x-test]=xxTen Brushesxx
Name[zh_CN]=十大笔刷
Name[zh_TW]=10 個筆刷
Comment=Assign a preset to ctrl-1 to ctrl-0
Comment[ca]=Assigna una predefinició des de Ctrl-1 a Ctrl-0
Comment[ca@valencia]=Assigna una predefinició des de Ctrl-1 a Ctrl-0
Comment[el]=Αντιστοίχιση προκαθορισμένου από ctrl-1 στο ctrl-0
Comment[en_GB]=Assign a preset to ctrl-1 to ctrl-0
Comment[es]=Asignar una preselección a Ctrl-1 hasta Ctrl-0
Comment[eu]=Aurrezarpena ezarri ktrl-1'etik ktrl-0'ra arte
+Comment[fi]=Kytke esiasetukset Ctrl-1:stä Ctrl-0:aan
Comment[gl]=Asigne unha predefinición do Ctrl+1 ao Ctrl+0.
Comment[it]=Assegna una preimpostazione per ctrl-1 a ctrl-0
Comment[nl]=Een voorinstelling toekennen aan Ctrl-1 tot Ctrl-0
Comment[pl]=Przypisz nastawę do ctrl-1 lub ctrl-0
Comment[pt]=Atribuir uma predefinição de Ctrl-1 a Ctrl-0
Comment[sv]=Tilldela en förinställning för Ctrl+1 till Ctrl+0
Comment[tr]= ctrl-1 ve ctrl-0 için ayar atama
Comment[uk]=Прив’язування наборів налаштувань до скорочень від ctrl-1 до ctrl-0
Comment[x-test]=xxAssign a preset to ctrl-1 to ctrl-0xx
-Comment[zh_CN]=将预设分配给 Ctrl+1 到 Ctrl+0 快捷键
+Comment[zh_CN]=将预设分配给由 Ctrl+1 到 Ctrl+0 的快捷键
Comment[zh_TW]=將預設指定給 ctrl-1 至 ctrl-0
diff --git a/plugins/python/tenscripts/kritapykrita_tenscripts.desktop b/plugins/python/tenscripts/kritapykrita_tenscripts.desktop
index 8603796006..24ca96d74d 100644
--- a/plugins/python/tenscripts/kritapykrita_tenscripts.desktop
+++ b/plugins/python/tenscripts/kritapykrita_tenscripts.desktop
@@ -1,43 +1,45 @@
[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=tenscripts
X-Python-2-Compatible=true
X-Krita-Manual=Manual.html
Name=Ten Scripts
Name[ar]=عشرُ سكربتات
Name[ca]=Deu scripts
Name[ca@valencia]=Deu scripts
Name[en_GB]=Ten Scripts
Name[es]=Diez guiones
Name[eu]=Hamar script
+Name[fi]=Kymmenen skriptiä
Name[fr]=Raccourcis des scripts
Name[gl]=Dez scripts
Name[is]=Tíu skriftur
Name[it]=Dieci script
Name[nl]=Tien scripts
Name[pl]=Skrypty Ten
Name[pt]=Dez Programas
Name[sv]=Tio skript
Name[uk]=Десять скриптів
Name[x-test]=xxTen Scriptsxx
Name[zh_CN]=十大脚本
Name[zh_TW]=10 個腳本
Comment=A Python-based plugin for creating ten actions and assign them to Python scripts
Comment[ar]=ملحقة بِ‍«پيثون» لإنشاء ١٠ إجراءات وإسنادها إلى سكربتات «پيثون»
-Comment[ca]=Un connector basant en el Python per crear deu accions i assignar-les a scripts del Python
-Comment[ca@valencia]=Un connector basant en el Python per crear deu accions i assignar-les a scripts del Python
+Comment[ca]=Un connector basat en el Python per a crear deu accions i assignar-les a scripts de Python
+Comment[ca@valencia]=Un connector basat en el Python per a crear deu accions i assignar-les a scripts de Python
Comment[en_GB]=A Python-based plugin for creating ten actions and assign them to Python scripts
Comment[es]=Un complemento basado en Python para crear diez acciones y asignarlas a guiones de Python
Comment[eu]=Hamar ekintza sortu eta haiek Python-scriptei esleitzeko Python-oinarridun plugin bat
+Comment[fi]=Python-pohjainen liitännäinen kymmenen toiminnon luomiseksi kytkemiseksi Python-skripteihin
Comment[fr]=Module externe basé sur Python pour créer dix actions et les assigner à des scripts Python
Comment[gl]=Un complemento escrito en Python para crear dez accións e asignalas a scripts escritos en Python.
Comment[it]=Un'estensione basata su Python per creare dieci azioni e assegnarle a script Python
Comment[nl]=Een op Python gebaseerde plug-in voor aanmaken van tien acties en ze dan toewijzen aan Python-scripts
Comment[pl]=Wtyczka oparta na Pythonie do tworzenia działań i przypisywanie ich skryptom Pythona
Comment[pt]=Um 'plugin' feito em Python para criar dez acções e atribuí-las a programas em Python
Comment[sv]=Ett Python-baserat insticksprogram för att skapa tio åtgärder och tilldela dem till Python-skript
Comment[uk]=Скрипт на основі Python для створення десяти дій і прив'язування до них скриптів Python
Comment[x-test]=xxA Python-based plugin for creating ten actions and assign them to Python scriptsxx
-Comment[zh_CN]=基于 Python 的可创建十个操作并将它们指定到其它 Python 脚本的插件
+Comment[zh_CN]=基于 Python 的用于创建十个操作并将它们指定到特定 Python 脚本的插件
Comment[zh_TW]=基於 Python 的外掛程式,用於建立 10 個動作並且將它們指定給 Python 腳本
diff --git a/plugins/tools/basictools/kis_tool_brush.cc b/plugins/tools/basictools/kis_tool_brush.cc
index e1117114e3..a8b313fcab 100644
--- a/plugins/tools/basictools/kis_tool_brush.cc
+++ b/plugins/tools/basictools/kis_tool_brush.cc
@@ -1,477 +1,485 @@
/*
* kis_tool_brush.cc - part of Krita
*
* Copyright (c) 2003-2004 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2015 Moritz Molch <kde@moritzmolch.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_tool_brush.h"
#include <kis_icon.h>
#include <QCheckBox>
#include <QComboBox>
#include <klocalizedstring.h>
#include <QAction>
#include <QLabel>
#include <kactioncollection.h>
#include <KoCanvasBase.h>
#include <KoCanvasController.h>
#include <kis_action_registry.h>
#include "kis_cursor.h"
#include "kis_config.h"
#include "kis_slider_spin_box.h"
#include "kundo2magicstring.h"
+#include <KisUsageLogger.h>
+
#define MAXIMUM_SMOOTHNESS_DISTANCE 1000.0 // 0..1000.0 == weight in gui
#define MAXIMUM_MAGNETISM 1000
void KisToolBrush::addSmoothingAction(int enumId, const QString &id)
{
/**
* KisToolBrush is the base of several tools, but the actions
* should be unique, so let's be careful with them
*/
QAction *a = action(id);
connect(a, SIGNAL(triggered()), &m_signalMapper, SLOT(map()));
m_signalMapper.setMapping(a, enumId);
}
KisToolBrush::KisToolBrush(KoCanvasBase * canvas)
: KisToolFreehand(canvas,
KisCursor::load("tool_freehand_cursor.png", 5, 5),
kundo2_i18n("Freehand Brush Stroke"))
{
setObjectName("tool_brush");
createOptionWidget();
connect(this, SIGNAL(smoothingTypeChanged()), this, SLOT(resetCursorStyle()));
addSmoothingAction(KisSmoothingOptions::NO_SMOOTHING, "set_no_brush_smoothing");
addSmoothingAction(KisSmoothingOptions::SIMPLE_SMOOTHING, "set_simple_brush_smoothing");
addSmoothingAction(KisSmoothingOptions::WEIGHTED_SMOOTHING, "set_weighted_brush_smoothing");
addSmoothingAction(KisSmoothingOptions::STABILIZER, "set_stabilizer_brush_smoothing");
}
KisToolBrush::~KisToolBrush()
{
}
void KisToolBrush::activate(ToolActivation activation, const QSet<KoShape*> &shapes)
{
KisToolFreehand::activate(activation, shapes);
connect(&m_signalMapper, SIGNAL(mapped(int)), SLOT(slotSetSmoothingType(int)), Qt::UniqueConnection);
QAction *toggleaction = action("toggle_assistant");
connect(toggleaction, SIGNAL(triggered(bool)), m_chkAssistant, SLOT(toggle()), Qt::UniqueConnection);
m_configGroup = KSharedConfig::openConfig()->group(toolId());
}
void KisToolBrush::deactivate()
{
disconnect(&m_signalMapper, 0, this, 0);
QAction *toggleaction = action("toggle_assistant");
disconnect(toggleaction, 0, m_chkAssistant, 0);
KisToolFreehand::deactivate();
}
int KisToolBrush::smoothingType() const
{
return smoothingOptions()->smoothingType();
}
bool KisToolBrush::smoothPressure() const
{
return smoothingOptions()->smoothPressure();
}
int KisToolBrush::smoothnessQuality() const
{
return smoothingOptions()->smoothnessDistance();
}
qreal KisToolBrush::smoothnessFactor() const
{
return smoothingOptions()->tailAggressiveness();
}
void KisToolBrush::slotSetSmoothingType(int index)
{
/**
* The slot can also be called from smoothing-type-switching
* action that would mean the combo box will not be synchronized
*/
if (m_cmbSmoothingType->currentIndex() != index) {
m_cmbSmoothingType->setCurrentIndex(index);
}
+ if (smoothingOptions()->smoothingType() == index) return;
+
switch (index) {
case 0:
+ KisUsageLogger::log("Disabled smoothing.");
smoothingOptions()->setSmoothingType(KisSmoothingOptions::NO_SMOOTHING);
showControl(m_sliderSmoothnessDistance, false);
showControl(m_sliderTailAggressiveness, false);
showControl(m_chkSmoothPressure, false);
showControl(m_chkUseScalableDistance, false);
showControl(m_sliderDelayDistance, false);
showControl(m_chkFinishStabilizedCurve, false);
showControl(m_chkStabilizeSensors, false);
break;
case 1:
+ KisUsageLogger::log("Enabled simple smoothing.");
smoothingOptions()->setSmoothingType(KisSmoothingOptions::SIMPLE_SMOOTHING);
showControl(m_sliderSmoothnessDistance, false);
showControl(m_sliderTailAggressiveness, false);
showControl(m_chkSmoothPressure, false);
showControl(m_chkUseScalableDistance, false);
showControl(m_sliderDelayDistance, false);
showControl(m_chkFinishStabilizedCurve, false);
showControl(m_chkStabilizeSensors, false);
break;
case 2:
+ KisUsageLogger::log("Enabled weighted smoothing.");
smoothingOptions()->setSmoothingType(KisSmoothingOptions::WEIGHTED_SMOOTHING);
showControl(m_sliderSmoothnessDistance, true);
showControl(m_sliderTailAggressiveness, true);
showControl(m_chkSmoothPressure, true);
showControl(m_chkUseScalableDistance, true);
showControl(m_sliderDelayDistance, false);
showControl(m_chkFinishStabilizedCurve, false);
showControl(m_chkStabilizeSensors, false);
break;
case 3:
default:
+ KisUsageLogger::log("Enabled stabilizer.");
smoothingOptions()->setSmoothingType(KisSmoothingOptions::STABILIZER);
showControl(m_sliderSmoothnessDistance, true);
showControl(m_sliderTailAggressiveness, false);
showControl(m_chkSmoothPressure, false);
showControl(m_chkUseScalableDistance, true);
showControl(m_sliderDelayDistance, true);
showControl(m_chkFinishStabilizedCurve, true);
showControl(m_chkStabilizeSensors, true);
}
emit smoothingTypeChanged();
}
void KisToolBrush::slotSetSmoothnessDistance(qreal distance)
{
smoothingOptions()->setSmoothnessDistance(distance);
emit smoothnessQualityChanged();
}
void KisToolBrush::slotSetTailAgressiveness(qreal argh_rhhrr)
{
smoothingOptions()->setTailAggressiveness(argh_rhhrr);
emit smoothnessFactorChanged();
}
// used with weighted smoothing
void KisToolBrush::setSmoothPressure(bool value)
{
smoothingOptions()->setSmoothPressure(value);
}
void KisToolBrush::slotSetMagnetism(int magnetism)
{
m_magnetism = expf(magnetism / (double)MAXIMUM_MAGNETISM) / expf(1.0);
}
bool KisToolBrush::useScalableDistance() const
{
return smoothingOptions()->useScalableDistance();
}
// used with weighted smoothing
void KisToolBrush::setUseScalableDistance(bool value)
{
smoothingOptions()->setUseScalableDistance(value);
emit useScalableDistanceChanged();
}
void KisToolBrush::resetCursorStyle()
{
KisConfig cfg(true);
CursorStyle cursorStyle = cfg.newCursorStyle();
// When the stabilizer is in use, we avoid using the brush outline cursor,
// because it would hide the real position of the cursor to the user,
// yielding unexpected results.
if (smoothingOptions()->smoothingType() == KisSmoothingOptions::STABILIZER &&
smoothingOptions()->useDelayDistance() &&
cursorStyle == CURSOR_STYLE_NO_CURSOR) {
useCursor(KisCursor::roundCursor());
} else {
KisToolFreehand::resetCursorStyle();
}
overrideCursorIfNotEditable();
}
// stabilizer brush settings
bool KisToolBrush::useDelayDistance() const
{
return smoothingOptions()->useDelayDistance();
}
qreal KisToolBrush::delayDistance() const
{
return smoothingOptions()->delayDistance();
}
void KisToolBrush::setUseDelayDistance(bool value)
{
smoothingOptions()->setUseDelayDistance(value);
m_sliderDelayDistance->setEnabled(value);
enableControl(m_chkFinishStabilizedCurve, !value);
emit useDelayDistanceChanged();
}
void KisToolBrush::setDelayDistance(qreal value)
{
smoothingOptions()->setDelayDistance(value);
emit delayDistanceChanged();
}
void KisToolBrush::setFinishStabilizedCurve(bool value)
{
smoothingOptions()->setFinishStabilizedCurve(value);
emit finishStabilizedCurveChanged();
}
bool KisToolBrush::finishStabilizedCurve() const
{
return smoothingOptions()->finishStabilizedCurve();
}
void KisToolBrush::setStabilizeSensors(bool value)
{
smoothingOptions()->setStabilizeSensors(value);
emit stabilizeSensorsChanged();
}
bool KisToolBrush::stabilizeSensors() const
{
return smoothingOptions()->stabilizeSensors();
}
void KisToolBrush::updateSettingsViews()
{
m_cmbSmoothingType->setCurrentIndex(smoothingOptions()->smoothingType());
m_sliderSmoothnessDistance->setValue(smoothingOptions()->smoothnessDistance());
m_chkDelayDistance->setChecked(smoothingOptions()->useDelayDistance());
m_sliderDelayDistance->setValue(smoothingOptions()->delayDistance());
m_sliderTailAggressiveness->setValue(smoothingOptions()->tailAggressiveness());
m_chkSmoothPressure->setChecked(smoothingOptions()->smoothPressure());
m_chkUseScalableDistance->setChecked(smoothingOptions()->useScalableDistance());
m_cmbSmoothingType->setCurrentIndex((int)smoothingOptions()->smoothingType());
m_chkStabilizeSensors->setChecked(smoothingOptions()->stabilizeSensors());
emit smoothnessQualityChanged();
emit smoothnessFactorChanged();
emit smoothPressureChanged();
emit smoothingTypeChanged();
emit useScalableDistanceChanged();
emit useDelayDistanceChanged();
emit delayDistanceChanged();
emit finishStabilizedCurveChanged();
emit stabilizeSensorsChanged();
KisTool::updateSettingsViews();
}
QWidget * KisToolBrush::createOptionWidget()
{
QWidget *optionsWidget = KisToolFreehand::createOptionWidget();
optionsWidget->setObjectName(toolId() + "option widget");
// See https://bugs.kde.org/show_bug.cgi?id=316896
QWidget *specialSpacer = new QWidget(optionsWidget);
specialSpacer->setObjectName("SpecialSpacer");
specialSpacer->setFixedSize(0, 0);
optionsWidget->layout()->addWidget(specialSpacer);
// Line smoothing configuration
m_cmbSmoothingType = new QComboBox(optionsWidget);
m_cmbSmoothingType->addItems(QStringList()
<< i18n("None")
<< i18n("Basic")
<< i18n("Weighted")
<< i18n("Stabilizer"));
connect(m_cmbSmoothingType, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSetSmoothingType(int)));
addOptionWidgetOption(m_cmbSmoothingType, new QLabel(i18n("Brush Smoothing:")));
m_sliderSmoothnessDistance = new KisDoubleSliderSpinBox(optionsWidget);
m_sliderSmoothnessDistance->setRange(3.0, MAXIMUM_SMOOTHNESS_DISTANCE, 1);
m_sliderSmoothnessDistance->setExponentRatio(3.0); // help pick smaller values
m_sliderSmoothnessDistance->setEnabled(true);
connect(m_sliderSmoothnessDistance, SIGNAL(valueChanged(qreal)), SLOT(slotSetSmoothnessDistance(qreal)));
m_sliderSmoothnessDistance->setValue(smoothingOptions()->smoothnessDistance());
addOptionWidgetOption(m_sliderSmoothnessDistance, new QLabel(i18n("Distance:")));
// Finish stabilizer curve
m_chkFinishStabilizedCurve = new QCheckBox(optionsWidget);
m_chkFinishStabilizedCurve->setMinimumHeight(qMax(m_sliderSmoothnessDistance->sizeHint().height()-3,
m_chkFinishStabilizedCurve->sizeHint().height()));
connect(m_chkFinishStabilizedCurve, SIGNAL(toggled(bool)), this, SLOT(setFinishStabilizedCurve(bool)));
m_chkFinishStabilizedCurve->setChecked(smoothingOptions()->finishStabilizedCurve());
// Delay Distance for Stabilizer
QWidget* delayWidget = new QWidget(optionsWidget);
QHBoxLayout* delayLayout = new QHBoxLayout(delayWidget);
delayLayout->setContentsMargins(0,0,0,0);
delayLayout->setSpacing(1);
QLabel* delayLabel = new QLabel(i18n("Delay:"), optionsWidget);
delayLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
delayLayout->addWidget(delayLabel);
m_chkDelayDistance = new QCheckBox(optionsWidget);
m_chkDelayDistance->setLayoutDirection(Qt::RightToLeft);
delayWidget->setToolTip(i18n("Delay the brush stroke to make the line smoother"));
connect(m_chkDelayDistance, SIGNAL(toggled(bool)), this, SLOT(setUseDelayDistance(bool)));
delayLayout->addWidget(m_chkDelayDistance);
m_sliderDelayDistance = new KisDoubleSliderSpinBox(optionsWidget);
m_sliderDelayDistance->setToolTip(i18n("Radius where the brush is blocked"));
m_sliderDelayDistance->setRange(0, 500);
m_sliderDelayDistance->setExponentRatio(3.0); // help pick smaller values
m_sliderDelayDistance->setSuffix(i18n(" px"));
connect(m_sliderDelayDistance, SIGNAL(valueChanged(qreal)), SLOT(setDelayDistance(qreal)));
addOptionWidgetOption(m_sliderDelayDistance, delayWidget);
addOptionWidgetOption(m_chkFinishStabilizedCurve, new QLabel(i18n("Finish line:")));
m_sliderDelayDistance->setValue(smoothingOptions()->delayDistance());
m_chkDelayDistance->setChecked(smoothingOptions()->useDelayDistance());
// if the state is not flipped, then the previous line doesn't generate any signals
setUseDelayDistance(m_chkDelayDistance->isChecked());
// Stabilize sensors
m_chkStabilizeSensors = new QCheckBox(optionsWidget);
m_chkStabilizeSensors->setMinimumHeight(qMax(m_sliderSmoothnessDistance->sizeHint().height()-3,
m_chkStabilizeSensors->sizeHint().height()));
connect(m_chkStabilizeSensors, SIGNAL(toggled(bool)), this, SLOT(setStabilizeSensors(bool)));
m_chkStabilizeSensors->setChecked(smoothingOptions()->stabilizeSensors());
addOptionWidgetOption(m_chkStabilizeSensors, new QLabel(i18n("Stabilize Sensors:")));
m_sliderTailAggressiveness = new KisDoubleSliderSpinBox(optionsWidget);
m_sliderTailAggressiveness->setRange(0.0, 1.0, 2);
m_sliderTailAggressiveness->setEnabled(true);
connect(m_sliderTailAggressiveness, SIGNAL(valueChanged(qreal)), SLOT(slotSetTailAgressiveness(qreal)));
m_sliderTailAggressiveness->setValue(smoothingOptions()->tailAggressiveness());
addOptionWidgetOption(m_sliderTailAggressiveness, new QLabel(i18n("Stroke Ending:")));
m_chkSmoothPressure = new QCheckBox(optionsWidget);
m_chkSmoothPressure->setMinimumHeight(qMax(m_sliderSmoothnessDistance->sizeHint().height()-3,
m_chkSmoothPressure->sizeHint().height()));
m_chkSmoothPressure->setChecked(smoothingOptions()->smoothPressure());
connect(m_chkSmoothPressure, SIGNAL(toggled(bool)), this, SLOT(setSmoothPressure(bool)));
addOptionWidgetOption(m_chkSmoothPressure, new QLabel(QString("%1:").arg(i18n("Smooth Pressure"))));
m_chkUseScalableDistance = new QCheckBox(optionsWidget);
m_chkUseScalableDistance->setChecked(smoothingOptions()->useScalableDistance());
m_chkUseScalableDistance->setMinimumHeight(qMax(m_sliderSmoothnessDistance->sizeHint().height()-3,
m_chkUseScalableDistance->sizeHint().height()));
m_chkUseScalableDistance->setToolTip(i18nc("@info:tooltip",
"Scalable distance takes zoom level "
"into account and makes the distance "
"be visually constant whatever zoom "
"level is chosen"));
connect(m_chkUseScalableDistance, SIGNAL(toggled(bool)), this, SLOT(setUseScalableDistance(bool)));
addOptionWidgetOption(m_chkUseScalableDistance, new QLabel(QString("%1:").arg(i18n("Scalable Distance"))));
// add a line spacer so we know that the next set of options are for different settings
QFrame* line = new QFrame(optionsWidget);
line->setObjectName(QString::fromUtf8("line"));
line->setFrameShape(QFrame::HLine);
addOptionWidgetOption(line);
// Drawing assistant configuration
QWidget* assistantWidget = new QWidget(optionsWidget);
QGridLayout* assistantLayout = new QGridLayout(assistantWidget);
assistantLayout->setContentsMargins(10,0,0,0);
assistantLayout->setSpacing(5);
m_chkAssistant = new QCheckBox(optionsWidget);
m_chkAssistant->setText(i18n("Snap to Assistants"));
assistantWidget->setToolTip(i18n("You need to add Assistants before this tool will work."));
connect(m_chkAssistant, SIGNAL(toggled(bool)), this, SLOT(setAssistant(bool)));
addOptionWidgetOption(assistantWidget, m_chkAssistant);
m_sliderMagnetism = new KisSliderSpinBox(optionsWidget);
m_sliderMagnetism->setToolTip(i18n("Assistant Magnetism"));
m_sliderMagnetism->setRange(0, MAXIMUM_MAGNETISM);
m_sliderMagnetism->setValue(m_magnetism * MAXIMUM_MAGNETISM);
connect(m_sliderMagnetism, SIGNAL(valueChanged(int)), SLOT(slotSetMagnetism(int)));
QLabel* magnetismLabel = new QLabel(i18n("Magnetism:"));
addOptionWidgetOption(m_sliderMagnetism, magnetismLabel);
QLabel* snapSingleLabel = new QLabel(i18n("Snap Single:"));
m_chkOnlyOneAssistant = new QCheckBox(optionsWidget);
m_chkOnlyOneAssistant->setToolTip(i18nc("@info:tooltip","Make it only snap to a single assistant, prevents snapping mess while using the infinite assistants."));
m_chkOnlyOneAssistant->setCheckState(Qt::Checked);//turn on by default.
connect(m_chkOnlyOneAssistant, SIGNAL(toggled(bool)), this, SLOT(setOnlyOneAssistantSnap(bool)));
addOptionWidgetOption(m_chkOnlyOneAssistant, snapSingleLabel);
// set the assistant snapping options to hidden by default and toggle their visibility based based off snapping checkbox
m_sliderMagnetism->setVisible(false);
m_chkOnlyOneAssistant->setVisible(false);
snapSingleLabel->setVisible(false);
magnetismLabel->setVisible(false);
connect(m_chkAssistant, SIGNAL(toggled(bool)), m_sliderMagnetism, SLOT(setVisible(bool)));
connect(m_chkAssistant, SIGNAL(toggled(bool)), m_chkOnlyOneAssistant, SLOT(setVisible(bool)));
connect(m_chkAssistant, SIGNAL(toggled(bool)), snapSingleLabel, SLOT(setVisible(bool)));
connect(m_chkAssistant, SIGNAL(toggled(bool)), magnetismLabel, SLOT(setVisible(bool)));
KisConfig cfg(true);
slotSetSmoothingType(cfg.lineSmoothingType());
return optionsWidget;
}
QList<QAction *> KisToolBrushFactory::createActionsImpl()
{
KisActionRegistry *actionRegistry = KisActionRegistry::instance();
QList<QAction *> actions = KisToolPaintFactoryBase::createActionsImpl();
actions << actionRegistry->makeQAction("set_no_brush_smoothing");
actions << actionRegistry->makeQAction("set_simple_brush_smoothing");
actions << actionRegistry->makeQAction("set_weighted_brush_smoothing");
actions << actionRegistry->makeQAction("set_stabilizer_brush_smoothing");
actions << actionRegistry->makeQAction("toggle_assistant");
return actions;
}
diff --git a/plugins/tools/basictools/kis_tool_ellipse.h b/plugins/tools/basictools/kis_tool_ellipse.h
index 79fd0c9460..f45110c28a 100644
--- a/plugins/tools/basictools/kis_tool_ellipse.h
+++ b/plugins/tools/basictools/kis_tool_ellipse.h
@@ -1,74 +1,74 @@
/*
* kis_tool_ellipse.h - part of Krayon
*
* Copyright (c) 2000 John Califf <jcaliff@compuzone.net>
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004 Clarence Dang <dang@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_TOOL_ELLIPSE_H__
#define __KIS_TOOL_ELLIPSE_H__
#include "kis_tool_shape.h"
#include "kis_types.h"
-#include "KisSelectionToolFactoryBase.h"
+#include "KisToolPaintFactoryBase.h"
#include "flake/kis_node_shape.h"
#include <kis_tool_ellipse_base.h>
#include <kis_icon.h>
class KoCanvasBase;
class KisToolEllipse : public KisToolEllipseBase
{
Q_OBJECT
public:
KisToolEllipse(KoCanvasBase * canvas);
~KisToolEllipse() override;
protected Q_SLOTS:
void resetCursorStyle() override;
protected:
void finishRect(const QRectF& rect, qreal roundCornersX, qreal roundCornersY) override;
};
-class KisToolEllipseFactory : public KisSelectionToolFactoryBase
+class KisToolEllipseFactory : public KisToolPaintFactoryBase
{
public:
KisToolEllipseFactory()
- : KisSelectionToolFactoryBase("KritaShape/KisToolEllipse") {
+ : KisToolPaintFactoryBase("KritaShape/KisToolEllipse") {
setToolTip(i18n("Ellipse Tool"));
setSection(TOOL_TYPE_SHAPE);
setActivationShapeId(KRITA_TOOL_ACTIVATION_ID);
setIconName(koIconNameCStr("krita_tool_ellipse"));
setPriority(3);
}
~KisToolEllipseFactory() override {}
KoToolBase * createTool(KoCanvasBase *canvas) override {
return new KisToolEllipse(canvas);
}
};
#endif //__KIS_TOOL_ELLIPSE_H__
diff --git a/plugins/tools/basictools/kis_tool_move.cc b/plugins/tools/basictools/kis_tool_move.cc
index ee6bc8f467..5c95d1c10c 100644
--- a/plugins/tools/basictools/kis_tool_move.cc
+++ b/plugins/tools/basictools/kis_tool_move.cc
@@ -1,665 +1,655 @@
/*
* Copyright (c) 1999 Matthias Elter <me@kde.org>
* 1999 Michael Koch <koch@kde.org>
* 2002 Patrick Julien <freak@codepimps.org>
* 2004 Boudewijn Rempt <boud@valdyas.org>
* 2016 Michael Abrahams <miabraha@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_tool_move.h"
#include <QPoint>
#include "kis_cursor.h"
#include "kis_selection.h"
#include "kis_canvas2.h"
#include "kis_image.h"
#include "kis_tool_utils.h"
#include "kis_paint_layer.h"
#include "strokes/move_stroke_strategy.h"
#include "kis_tool_movetooloptionswidget.h"
#include "strokes/move_selection_stroke_strategy.h"
#include "kis_resources_snapshot.h"
#include "kis_action_registry.h"
#include "krita_utils.h"
#include <KisViewManager.h>
#include <KisDocument.h>
#include "kis_node_manager.h"
#include "kis_signals_blocker.h"
#include <boost/operators.hpp>
struct KisToolMoveState : KisToolChangesTrackerData, boost::equality_comparable<KisToolMoveState>
{
KisToolMoveState(QPoint _accumulatedOffset) : accumulatedOffset(_accumulatedOffset) {}
KisToolChangesTrackerData* clone() const { return new KisToolMoveState(*this); }
bool operator ==(const KisToolMoveState &rhs) {
return accumulatedOffset == rhs.accumulatedOffset;
}
QPoint accumulatedOffset;
};
-KisToolMove::KisToolMove(KoCanvasBase * canvas)
- : KisTool(canvas, KisCursor::moveCursor()),
- m_updateCursorCompressor(100, KisSignalCompressor::FIRST_ACTIVE)
+KisToolMove::KisToolMove(KoCanvasBase *canvas)
+ : KisTool(canvas, KisCursor::moveCursor())
+ , m_updateCursorCompressor(100, KisSignalCompressor::FIRST_ACTIVE)
{
- m_canvas = dynamic_cast<KisCanvas2*>(canvas);
+ setObjectName("tool_move");
m_showCoordinatesAction = action("movetool-show-coordinates");
- createOptionWidget();
+ m_showCoordinatesAction = action("movetool-show-coordinates");
+ connect(&m_updateCursorCompressor, SIGNAL(timeout()), this, SLOT(resetCursorStyle()));
- setObjectName("tool_move");
+ m_optionsWidget = new MoveToolOptionsWidget(0, currentImage()->xRes(), toolId());
- m_showCoordinatesAction = action("movetool-show-coordinates");
+ // See https://bugs.kde.org/show_bug.cgi?id=316896
+ QWidget *specialSpacer = new QWidget(m_optionsWidget);
+ specialSpacer->setObjectName("SpecialSpacer");
+ specialSpacer->setFixedSize(0, 0);
+ m_optionsWidget->layout()->addWidget(specialSpacer);
- connect(&m_updateCursorCompressor, SIGNAL(timeout()), this, SLOT(resetCursorStyle()));
+ m_optionsWidget->setFixedHeight(m_optionsWidget->sizeHint().height());
+
+ m_showCoordinatesAction->setChecked(m_optionsWidget->showCoordinates());
+
+ m_optionsWidget->slotSetTranslate(m_handlesRect.topLeft() + currentOffset());
+
+ connect(m_optionsWidget, SIGNAL(sigSetTranslateX(int)), SLOT(moveBySpinX(int)), Qt::UniqueConnection);
+ connect(m_optionsWidget, SIGNAL(sigSetTranslateY(int)), SLOT(moveBySpinY(int)), Qt::UniqueConnection);
+ connect(m_optionsWidget, SIGNAL(sigRequestCommitOffsetChanges()), this, SLOT(commitChanges()), Qt::UniqueConnection);
+
+ connect(this, SIGNAL(moveInNewPosition(QPoint)), m_optionsWidget, SLOT(slotSetTranslate(QPoint)), Qt::UniqueConnection);
+
+ connect(qobject_cast<KisCanvas2*>(canvas)->viewManager()->nodeManager(), SIGNAL(sigUiNeedChangeSelectedNodes(KisNodeList)), this, SLOT(slotNodeChanged(KisNodeList)), Qt::UniqueConnection);
}
KisToolMove::~KisToolMove()
{
endStroke();
}
void KisToolMove::resetCursorStyle()
{
KisTool::resetCursorStyle();
if (!isActive()) return;
KisImageSP image = this->image();
KisResourcesSnapshotSP resources =
- new KisResourcesSnapshot(image, currentNode(), this->canvas()->resourceManager());
+ new KisResourcesSnapshot(image, currentNode(), canvas()->resourceManager());
KisSelectionSP selection = resources->activeSelection();
KisNodeList nodes = fetchSelectedNodes(moveToolMode(), &m_lastCursorPos, selection);
if (nodes.isEmpty()) {
canvas()->setCursor(Qt::ForbiddenCursor);
}
}
KisNodeList KisToolMove::fetchSelectedNodes(MoveToolMode mode, const QPoint *pixelPoint, KisSelectionSP selection)
{
KisNodeList nodes;
KisImageSP image = this->image();
if (mode != MoveSelectedLayer && pixelPoint) {
const bool wholeGroup = !selection && mode == MoveGroup;
KisNodeSP node = KisToolUtils::findNode(image->root(), *pixelPoint, wholeGroup);
if (node) {
nodes = {node};
}
}
if (nodes.isEmpty()) {
nodes = this->selectedNodes();
KritaUtils::filterContainer<KisNodeList>(nodes,
[](KisNodeSP node) {
return node->isEditable();
});
}
return nodes;
}
bool KisToolMove::startStrokeImpl(MoveToolMode mode, const QPoint *pos)
{
KisNodeSP node;
KisImageSP image = this->image();
KisResourcesSnapshotSP resources =
- new KisResourcesSnapshot(image, currentNode(), this->canvas()->resourceManager());
+ new KisResourcesSnapshot(image, currentNode(), canvas()->resourceManager());
KisSelectionSP selection = resources->activeSelection();
KisNodeList nodes = fetchSelectedNodes(mode, pos, selection);
if (nodes.size() == 1) {
node = nodes.first();
}
if (nodes.isEmpty()) {
return false;
}
/**
* If the target node has changed, the stroke should be
* restarted. Otherwise just continue processing current node.
*/
if (m_strokeId && !tryEndPreviousStroke(nodes)) {
return true;
}
initHandles(nodes);
KisStrokeStrategy *strategy;
KisPaintLayerSP paintLayer = node ?
dynamic_cast<KisPaintLayer*>(node.data()) : 0;
if (paintLayer && selection &&
!selection->isTotallyUnselected(image->bounds())) {
strategy =
new MoveSelectionStrokeStrategy(paintLayer,
selection,
image.data(),
image.data());
} else {
strategy =
new MoveStrokeStrategy(nodes, image.data(), image.data());
}
m_strokeId = image->startStroke(strategy);
m_currentlyProcessingNodes = nodes;
m_accumulatedOffset = QPoint();
KIS_SAFE_ASSERT_RECOVER(m_changesTracker.isEmpty()) {
m_changesTracker.reset();
}
commitChanges();
return true;
}
QPoint KisToolMove::currentOffset() const
{
return m_accumulatedOffset + m_dragPos - m_dragStart;
}
void KisToolMove::notifyGuiAfterMove(bool showFloatingMessage)
{
if (!m_optionsWidget) return;
const QPoint currentTopLeft = m_handlesRect.topLeft() + currentOffset();
KisSignalsBlocker b(m_optionsWidget);
emit moveInNewPosition(currentTopLeft);
// TODO: fetch this info not from options widget, but from config
const bool showCoordinates = m_optionsWidget->showCoordinates();
if (showCoordinates && showFloatingMessage) {
KisCanvas2 *kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
kisCanvas->viewManager()->
showFloatingMessage(
i18nc("floating message in move tool",
"X: %1 px, Y: %2 px",
currentTopLeft.x(),
currentTopLeft.y()),
QIcon(), 1000, KisFloatingMessage::High);
}
}
bool KisToolMove::tryEndPreviousStroke(KisNodeList nodes)
{
if (!m_strokeId) return false;
bool strokeEnded = false;
if (!KritaUtils::compareListsUnordered(nodes, m_currentlyProcessingNodes)) {
endStroke();
strokeEnded = true;
}
return strokeEnded;
}
void KisToolMove::commitChanges()
{
KIS_SAFE_ASSERT_RECOVER_RETURN(m_strokeId);
QSharedPointer<KisToolMoveState> newState(new KisToolMoveState(m_accumulatedOffset));
KisToolMoveState *lastState = dynamic_cast<KisToolMoveState*>(m_changesTracker.lastState().data());
if (lastState && *lastState == *newState) return;
m_changesTracker.commitConfig(newState);
}
void KisToolMove::moveDiscrete(MoveDirection direction, bool big)
{
if (mode() == KisTool::PAINT_MODE) return; // Don't interact with dragging
if (!currentNode()->isEditable()) return; // Don't move invisible nodes
if (startStrokeImpl(MoveSelectedLayer, 0)) {
setMode(KisTool::PAINT_MODE);
}
// Larger movement if "shift" key is pressed.
qreal scale = big ? m_optionsWidget->moveScale() : 1.0;
qreal moveStep = m_optionsWidget->moveStep() * scale;
const QPoint offset =
direction == Up ? QPoint( 0, -moveStep) :
direction == Down ? QPoint( 0, moveStep) :
direction == Left ? QPoint(-moveStep, 0) :
QPoint( moveStep, 0) ;
m_accumulatedOffset += offset;
image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset));
notifyGuiAfterMove();
commitChanges();
setMode(KisTool::HOVER_MODE);
}
void KisToolMove::activate(ToolActivation toolActivation, const QSet<KoShape*> &shapes)
{
KisTool::activate(toolActivation, shapes);
QAction *a = action("movetool-move-up");
connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Up, false);});
a = action("movetool-move-down");
connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Down, false);});
a = action("movetool-move-left");
connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Left, false);});
a = action("movetool-move-right");
connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Right, false);});
a = action("movetool-move-up-more");
connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Up, true);});
a = action("movetool-move-down-more");
connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Down, true);});
a = action("movetool-move-left-more");
connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Left, true);});
a = action("movetool-move-right-more");
connect(a, &QAction::triggered, [&](){moveDiscrete(MoveDirection::Right, true);});
connect(m_showCoordinatesAction, SIGNAL(triggered(bool)), m_optionsWidget, SLOT(setShowCoordinates(bool)), Qt::UniqueConnection);
connect(m_optionsWidget, SIGNAL(showCoordinatesChanged(bool)), m_showCoordinatesAction, SLOT(setChecked(bool)), Qt::UniqueConnection);
connect(&m_changesTracker,
SIGNAL(sigConfigChanged(KisToolChangesTrackerDataSP)),
SLOT(slotTrackerChangedConfig(KisToolChangesTrackerDataSP)));
slotNodeChanged(this->selectedNodes());
}
void KisToolMove::paint(QPainter& gc, const KoViewConverter &converter)
{
Q_UNUSED(converter);
if (m_strokeId) {
QPainterPath handles;
handles.addRect(m_handlesRect.translated(currentOffset()));
QPainterPath path = pixelToView(handles);
paintToolOutline(&gc, path);
}
}
void KisToolMove::initHandles(const KisNodeList &nodes)
{
/**
* The handles should be initialized only once, **before** the start of
* the stroke. If the nodes change, we should restart the stroke.
*/
KIS_SAFE_ASSERT_RECOVER_NOOP(!m_strokeId);
m_handlesRect = QRect();
for (KisNodeSP node : nodes) {
node->exactBounds();
m_handlesRect |= node->exactBounds();
}
if (image()->globalSelection()) {
m_handlesRect &= image()->globalSelection()->selectedExactRect();
}
}
void KisToolMove::deactivate()
{
QAction *a = action("movetool-move-up");
disconnect(a, 0, this, 0);
a = action("movetool-move-down");
disconnect(a, 0, this, 0);
a = action("movetool-move-left");
disconnect(a, 0, this, 0);
a = action("movetool-move-right");
disconnect(a, 0, this, 0);
a = action("movetool-move-up-more");
disconnect(a, 0, this, 0);
a = action("movetool-move-down-more");
disconnect(a, 0, this, 0);
a = action("movetool-move-left-more");
disconnect(a, 0, this, 0);
a = action("movetool-move-right-more");
disconnect(a, 0, this, 0);
disconnect(m_showCoordinatesAction, 0, this, 0);
disconnect(m_optionsWidget, 0, this, 0);
endStroke();
KisTool::deactivate();
}
void KisToolMove::requestStrokeEnd()
{
endStroke();
}
void KisToolMove::requestStrokeCancellation()
{
cancelStroke();
}
void KisToolMove::requestUndoDuringStroke()
{
if (!m_strokeId) return;
if (m_changesTracker.isEmpty()) {
cancelStroke();
} else {
m_changesTracker.requestUndo();
}
}
void KisToolMove::beginPrimaryAction(KoPointerEvent *event)
{
startAction(event, moveToolMode());
}
void KisToolMove::continuePrimaryAction(KoPointerEvent *event)
{
continueAction(event);
}
void KisToolMove::endPrimaryAction(KoPointerEvent *event)
{
endAction(event);
}
void KisToolMove::beginAlternateAction(KoPointerEvent *event, AlternateAction action)
{
// Ctrl+Right click toggles between moving current layer and moving layer w/ content
if (action == PickFgNode || action == PickBgImage) {
MoveToolMode mode = moveToolMode();
if (mode == MoveSelectedLayer) {
mode = MoveFirstLayer;
} else if (mode == MoveFirstLayer) {
mode = MoveSelectedLayer;
}
startAction(event, mode);
} else {
startAction(event, MoveGroup);
}
}
void KisToolMove::continueAlternateAction(KoPointerEvent *event, AlternateAction action)
{
Q_UNUSED(action)
continueAction(event);
}
void KisToolMove::endAlternateAction(KoPointerEvent *event, AlternateAction action)
{
Q_UNUSED(action)
endAction(event);
}
void KisToolMove::mouseMoveEvent(KoPointerEvent *event)
{
m_lastCursorPos = convertToPixelCoord(event).toPoint();
KisTool::mouseMoveEvent(event);
if (moveToolMode() == MoveFirstLayer) {
m_updateCursorCompressor.start();
}
}
void KisToolMove::startAction(KoPointerEvent *event, MoveToolMode mode)
{
QPoint pos = convertToPixelCoordAndSnap(event).toPoint();
m_dragStart = pos;
m_dragPos = pos;
if (startStrokeImpl(mode, &pos)) {
setMode(KisTool::PAINT_MODE);
} else {
event->ignore();
m_dragPos = QPoint();
m_dragStart = QPoint();
}
- m_canvas->updateCanvas();
+ qobject_cast<KisCanvas2*>(canvas())->updateCanvas();
}
void KisToolMove::continueAction(KoPointerEvent *event)
{
CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE);
if (!m_strokeId) return;
QPoint pos = convertToPixelCoordAndSnap(event).toPoint();
pos = applyModifiers(event->modifiers(), pos);
m_dragPos = pos;
drag(pos);
notifyGuiAfterMove();
- m_canvas->updateCanvas();
+ qobject_cast<KisCanvas2*>(canvas())->updateCanvas();
}
void KisToolMove::endAction(KoPointerEvent *event)
{
CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE);
setMode(KisTool::HOVER_MODE);
if (!m_strokeId) return;
QPoint pos = convertToPixelCoordAndSnap(event).toPoint();
pos = applyModifiers(event->modifiers(), pos);
drag(pos);
m_accumulatedOffset += pos - m_dragStart;
m_dragStart = QPoint();
m_dragPos = QPoint();
commitChanges();
notifyGuiAfterMove();
- m_canvas->updateCanvas();
+ qobject_cast<KisCanvas2*>(canvas())->updateCanvas();
}
void KisToolMove::drag(const QPoint& newPos)
{
KisImageWSP image = currentImage();
QPoint offset = m_accumulatedOffset + newPos - m_dragStart;
image->addJob(m_strokeId,
new MoveStrokeStrategy::Data(offset));
}
void KisToolMove::endStroke()
{
if (!m_strokeId) return;
KisImageSP image = currentImage();
image->endStroke(m_strokeId);
m_strokeId.clear();
m_changesTracker.reset();
m_currentlyProcessingNodes.clear();
m_accumulatedOffset = QPoint();
- m_canvas->updateCanvas();
+ qobject_cast<KisCanvas2*>(canvas())->updateCanvas();
}
void KisToolMove::slotTrackerChangedConfig(KisToolChangesTrackerDataSP state)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(m_strokeId);
KisToolMoveState *newState = dynamic_cast<KisToolMoveState*>(state.data());
KIS_SAFE_ASSERT_RECOVER_RETURN(newState);
if (mode() == KisTool::PAINT_MODE) return; // Don't interact with dragging
m_accumulatedOffset = newState->accumulatedOffset;
image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset));
notifyGuiAfterMove();
}
void KisToolMove::cancelStroke()
{
if (!m_strokeId) return;
KisImageSP image = currentImage();
image->cancelStroke(m_strokeId);
m_strokeId.clear();
m_changesTracker.reset();
m_currentlyProcessingNodes.clear();
m_accumulatedOffset = QPoint();
notifyGuiAfterMove();
- m_canvas->updateCanvas();
+ qobject_cast<KisCanvas2*>(canvas())->updateCanvas();
}
QWidget* KisToolMove::createOptionWidget()
{
- if (!currentImage())
- return 0;
-
- m_optionsWidget = new MoveToolOptionsWidget(0, currentImage()->xRes(), toolId());
- // See https://bugs.kde.org/show_bug.cgi?id=316896
- QWidget *specialSpacer = new QWidget(m_optionsWidget);
- specialSpacer->setObjectName("SpecialSpacer");
- specialSpacer->setFixedSize(0, 0);
- m_optionsWidget->layout()->addWidget(specialSpacer);
-
- m_optionsWidget->setFixedHeight(m_optionsWidget->sizeHint().height());
-
- m_showCoordinatesAction->setChecked(m_optionsWidget->showCoordinates());
-
- m_optionsWidget->slotSetTranslate(m_handlesRect.topLeft() + currentOffset());
-
- connect(m_optionsWidget, SIGNAL(sigSetTranslateX(int)), SLOT(moveBySpinX(int)));
- connect(m_optionsWidget, SIGNAL(sigSetTranslateY(int)), SLOT(moveBySpinY(int)));
- connect(m_optionsWidget, SIGNAL(sigRequestCommitOffsetChanges()), this, SLOT(commitChanges()));
-
- connect(this, SIGNAL(moveInNewPosition(QPoint)), m_optionsWidget, SLOT(slotSetTranslate(QPoint)));
-
- KisCanvas2 *kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
-
- connect(kisCanvas->viewManager()->nodeManager(), SIGNAL(sigUiNeedChangeSelectedNodes(KisNodeList)),
- this, SLOT(slotNodeChanged(KisNodeList)));
-
return m_optionsWidget;
}
KisToolMove::MoveToolMode KisToolMove::moveToolMode() const
{
if (m_optionsWidget)
return m_optionsWidget->mode();
return MoveSelectedLayer;
}
QPoint KisToolMove::applyModifiers(Qt::KeyboardModifiers modifiers, QPoint pos)
{
QPoint move = pos - m_dragStart;
// Snap to axis
if (modifiers & Qt::ShiftModifier) {
move = snapToClosestAxis(move);
}
// "Precision mode" - scale down movement by 1/5
if (modifiers & Qt::AltModifier) {
const qreal SCALE_FACTOR = .2;
move = SCALE_FACTOR * move;
}
return m_dragStart + move;
}
void KisToolMove::moveBySpinX(int newX)
{
if (mode() == KisTool::PAINT_MODE) return; // Don't interact with dragging
if (!currentNode()->isEditable()) return; // Don't move invisible nodes
if (startStrokeImpl(MoveSelectedLayer, 0)) {
setMode(KisTool::PAINT_MODE);
}
m_accumulatedOffset.rx() = newX - m_handlesRect.x();
image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset));
notifyGuiAfterMove(false);
setMode(KisTool::HOVER_MODE);
}
void KisToolMove::moveBySpinY(int newY)
{
if (mode() == KisTool::PAINT_MODE) return; // Don't interact with dragging
if (!currentNode()->isEditable()) return; // Don't move invisible nodes
if (startStrokeImpl(MoveSelectedLayer, 0)) {
setMode(KisTool::PAINT_MODE);
}
m_accumulatedOffset.ry() = newY - m_handlesRect.y();
image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset));
notifyGuiAfterMove(false);
setMode(KisTool::HOVER_MODE);
}
void KisToolMove::slotNodeChanged(KisNodeList nodes)
{
if (m_strokeId && !tryEndPreviousStroke(nodes)) {
return;
}
initHandles(nodes);
notifyGuiAfterMove(false);
}
QList<QAction *> KisToolMoveFactory::createActionsImpl()
{
KisActionRegistry *actionRegistry = KisActionRegistry::instance();
QList<QAction *> actions = KisToolPaintFactoryBase::createActionsImpl();
actions << actionRegistry->makeQAction("movetool-move-up");
actions << actionRegistry->makeQAction("movetool-move-down");
actions << actionRegistry->makeQAction("movetool-move-left");
actions << actionRegistry->makeQAction("movetool-move-right");
actions << actionRegistry->makeQAction("movetool-move-up-more");
actions << actionRegistry->makeQAction("movetool-move-down-more");
actions << actionRegistry->makeQAction("movetool-move-left-more");
actions << actionRegistry->makeQAction("movetool-move-right-more");
actions << actionRegistry->makeQAction("movetool-show-coordinates");
return actions;
}
diff --git a/plugins/tools/basictools/kis_tool_move.h b/plugins/tools/basictools/kis_tool_move.h
index 4bdf2dde41..920b229acc 100644
--- a/plugins/tools/basictools/kis_tool_move.h
+++ b/plugins/tools/basictools/kis_tool_move.h
@@ -1,193 +1,191 @@
/*
* Copyright (c) 1999 Matthias Elter <me@kde.org>
* 1999 Michael Koch <koch@kde.org>
* 2003 Patrick Julien <freak@codepimps.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_TOOL_MOVE_H_
#define KIS_TOOL_MOVE_H_
#include <KisToolPaintFactoryBase.h>
#include <kis_types.h>
#include <kis_tool.h>
#include <flake/kis_node_shape.h>
#include <kis_icon.h>
#include <QKeySequence>
#include <QWidget>
#include <QGroupBox>
#include <QRadioButton>
#include "KisToolChangesTracker.h"
#include "kis_signal_compressor.h"
#include "kis_canvas2.h"
class KoCanvasBase;
class MoveToolOptionsWidget;
class KisDocument;
class KisToolMove : public KisTool
{
Q_OBJECT
Q_ENUMS(MoveToolMode);
public:
KisToolMove(KoCanvasBase * canvas);
~KisToolMove() override;
/**
* @brief wantsAutoScroll
* reimplemented from KoToolBase
* there's an issue where autoscrolling with this tool never makes the
* stroke end, so we return false here so that users don't get stuck with
* the tool. See bug 362659
* @return false
*/
bool wantsAutoScroll() const override {
return false;
}
public Q_SLOTS:
void activate(ToolActivation toolActivation, const QSet<KoShape*> &shapes) override;
void deactivate() override;
public Q_SLOTS:
void requestStrokeEnd() override;
void requestStrokeCancellation() override;
void requestUndoDuringStroke() override;
protected Q_SLOTS:
void resetCursorStyle() override;
public:
enum MoveToolMode {
MoveSelectedLayer,
MoveFirstLayer,
MoveGroup
};
enum MoveDirection {
Up,
Down,
Left,
Right
};
void beginPrimaryAction(KoPointerEvent *event) override;
void continuePrimaryAction(KoPointerEvent *event) override;
void endPrimaryAction(KoPointerEvent *event) override;
void beginAlternateAction(KoPointerEvent *event, AlternateAction action) override;
void continueAlternateAction(KoPointerEvent *event, AlternateAction action) override;
void endAlternateAction(KoPointerEvent *event, AlternateAction action) override;
void mouseMoveEvent(KoPointerEvent *event) override;
void startAction(KoPointerEvent *event, MoveToolMode mode);
void continueAction(KoPointerEvent *event);
void endAction(KoPointerEvent *event);
void paint(QPainter& gc, const KoViewConverter &converter) override;
void initHandles(const KisNodeList &nodes);
QWidget *createOptionWidget() override;
void updateUIUnit(int newUnit);
MoveToolMode moveToolMode() const;
void setShowCoordinates(bool value);
public Q_SLOTS:
void moveDiscrete(MoveDirection direction, bool big);
void moveBySpinX(int newX);
void moveBySpinY(int newY);
void slotNodeChanged(KisNodeList nodes);
void commitChanges();
Q_SIGNALS:
void moveToolModeChanged();
void moveInNewPosition(QPoint);
private:
void drag(const QPoint& newPos);
void cancelStroke();
QPoint applyModifiers(Qt::KeyboardModifiers modifiers, QPoint pos);
bool startStrokeImpl(MoveToolMode mode, const QPoint *pos);
QPoint currentOffset() const;
void notifyGuiAfterMove(bool showFloatingMessage = true);
bool tryEndPreviousStroke(KisNodeList nodes);
KisNodeList fetchSelectedNodes(MoveToolMode mode, const QPoint *pixelPoint, KisSelectionSP selection);
private Q_SLOTS:
void endStroke();
void slotTrackerChangedConfig(KisToolChangesTrackerDataSP state);
private:
MoveToolOptionsWidget* m_optionsWidget {0};
QPoint m_dragStart; ///< Point where current cursor dragging began
QPoint m_accumulatedOffset; ///< Total offset including multiple clicks, up/down/left/right keys, etc. added together
KisStrokeId m_strokeId;
KisNodeList m_currentlyProcessingNodes;
int m_resolution;
QAction *m_showCoordinatesAction {0};
- KisCanvas2 *m_canvas {0};
-
QPoint m_dragPos;
QRect m_handlesRect;
KisToolChangesTracker m_changesTracker;
QPoint m_lastCursorPos;
KisSignalCompressor m_updateCursorCompressor;
};
class KisToolMoveFactory : public KisToolPaintFactoryBase
{
public:
KisToolMoveFactory()
: KisToolPaintFactoryBase("KritaTransform/KisToolMove") {
setToolTip(i18n("Move Tool"));
setSection(TOOL_TYPE_TRANSFORM);
setActivationShapeId(KRITA_TOOL_ACTIVATION_ID);
setPriority(3);
setIconName(koIconNameCStr("krita_tool_move"));
- setShortcut(QKeySequence( Qt::Key_T));
+ setShortcut(QKeySequence(Qt::Key_T));
}
~KisToolMoveFactory() override {}
KoToolBase * createTool(KoCanvasBase *canvas) override {
return new KisToolMove(canvas);
}
QList<QAction *> createActionsImpl() override;
};
#endif // KIS_TOOL_MOVE_H_
diff --git a/plugins/tools/basictools/kis_tool_movetooloptionswidget.cpp b/plugins/tools/basictools/kis_tool_movetooloptionswidget.cpp
index 2aa47948ef..18690632b7 100644
--- a/plugins/tools/basictools/kis_tool_movetooloptionswidget.cpp
+++ b/plugins/tools/basictools/kis_tool_movetooloptionswidget.cpp
@@ -1,189 +1,189 @@
/*
Copyright (C) 2012 Dan Leinir Turthra Jensen <admin@leinir.dk>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "kis_tool_movetooloptionswidget.h"
#include <ksharedconfig.h>
#include <KoUnit.h>
MoveToolOptionsWidget::MoveToolOptionsWidget(QWidget *parent, int resolution, QString toolId)
: QWidget(parent)
, m_resolution(resolution)
, m_showCoordinates(false)
{
setupUi(this);
m_configGroup = KSharedConfig::openConfig()->group(toolId);
// load radio button
m_moveToolMode = static_cast<KisToolMove::MoveToolMode>(m_configGroup.readEntry("moveToolMode", 0));
if(m_moveToolMode == KisToolMove::MoveSelectedLayer)
radioSelectedLayer->setChecked(true);
else if (m_moveToolMode == KisToolMove::MoveFirstLayer)
radioFirstLayer->setChecked(true);
else
radioGroup->setChecked(true);
// Keyboard shortcut move step
m_moveStep = m_configGroup.readEntry<int>("moveToolStep", 1);
m_moveStepUnit = m_configGroup.readEntry<int>("moveToolUnit", KoUnit(KoUnit::Pixel).indexInListForUi());
cmbUnit->addItems(KoUnit::listOfUnitNameForUi());
cmbUnit->setCurrentIndex(m_moveStepUnit);
updateUIUnit(m_moveStepUnit);
// Scale for large moves
m_moveScale = m_configGroup.readEntry<int>("moveToolScale", 10.0);
spinMoveScale->blockSignals(true);
spinMoveScale->setValue(m_moveScale);
spinMoveScale->setSuffix("x");
spinMoveScale->blockSignals(false);
// Switch mode for showing coordinates
m_showCoordinates = m_configGroup.readEntry("moveToolShowCoordinates", false);
connect(chkShowCoordinates, SIGNAL(toggled(bool)), SIGNAL(showCoordinatesChanged(bool)));
chkShowCoordinates->setChecked(m_showCoordinates);
translateXBox->setSuffix(i18n(" px"));
translateYBox->setSuffix(i18n(" px"));
translateXBox->setRange(-10000, 10000);
translateYBox->setRange(-10000, 10000);
}
void MoveToolOptionsWidget::updateUIUnit(int newUnit)
{
const KoUnit selectedUnit = KoUnit::fromListForUi(newUnit);
qreal valueForUI;
if (selectedUnit != KoUnit(KoUnit::Pixel)) {
spinMoveStep->setRange(0.0001, 10000.000);
spinMoveStep->setSingleStep(.1);
spinMoveStep->setDecimals(4);
valueForUI = selectedUnit.toUserValue((qreal)m_moveStep / (qreal)m_resolution);
} else {
spinMoveStep->setRange(1, 10000);
spinMoveStep->setSingleStep(1);
spinMoveStep->setDecimals(0);
valueForUI = m_moveStep;
}
spinMoveStep->blockSignals(true);
spinMoveStep->setValue(valueForUI);
spinMoveStep->blockSignals(false);
- connect(translateXBox, SIGNAL(editingFinished()), SIGNAL(sigRequestCommitOffsetChanges()));
- connect(translateYBox, SIGNAL(editingFinished()), SIGNAL(sigRequestCommitOffsetChanges()));
+ connect(translateXBox, SIGNAL(editingFinished()), SIGNAL(sigRequestCommitOffsetChanges()), Qt::UniqueConnection);
+ connect(translateYBox, SIGNAL(editingFinished()), SIGNAL(sigRequestCommitOffsetChanges()), Qt::UniqueConnection);
}
void MoveToolOptionsWidget::on_spinMoveStep_valueChanged(double UIMoveStep)
{
const KoUnit selectedUnit = KoUnit::fromListForUi(m_moveStepUnit);
const double scaledUiMoveStep = (selectedUnit == KoUnit(KoUnit::Pixel)) ?
UIMoveStep : selectedUnit.fromUserValue(UIMoveStep * m_resolution);
m_moveStep = qRound(scaledUiMoveStep);
m_configGroup.writeEntry("moveToolStep", m_moveStep);
}
void MoveToolOptionsWidget::on_spinMoveScale_valueChanged(double UIMoveScale)
{
m_moveScale = UIMoveScale;
m_configGroup.writeEntry("moveToolScale", m_moveScale);
}
void MoveToolOptionsWidget::on_cmbUnit_currentIndexChanged(int newUnit)
{
m_moveStepUnit = newUnit;
updateUIUnit(newUnit);
m_configGroup.writeEntry("moveToolUnit", newUnit);
}
void MoveToolOptionsWidget::on_radioSelectedLayer_toggled(bool checked)
{
Q_UNUSED(checked);
setMoveToolMode(KisToolMove::MoveSelectedLayer);
}
void MoveToolOptionsWidget::on_radioFirstLayer_toggled(bool checked)
{
Q_UNUSED(checked);
setMoveToolMode(KisToolMove::MoveFirstLayer);
}
void MoveToolOptionsWidget::on_radioGroup_toggled(bool checked)
{
Q_UNUSED(checked);
setMoveToolMode(KisToolMove::MoveGroup);
}
void MoveToolOptionsWidget::setMoveToolMode(KisToolMove::MoveToolMode newMode)
{
m_moveToolMode = newMode;
m_configGroup.writeEntry("moveToolMode", static_cast<int>(newMode));
}
void MoveToolOptionsWidget::on_chkShowCoordinates_toggled(bool checked)
{
m_showCoordinates = checked;
m_configGroup.writeEntry("moveToolShowCoordinates", m_showCoordinates);
}
KisToolMove::MoveToolMode MoveToolOptionsWidget::mode()
{
return m_moveToolMode;
}
int MoveToolOptionsWidget::moveStep()
{
return m_moveStep;
}
double MoveToolOptionsWidget::moveScale()
{
return m_moveScale;
}
bool MoveToolOptionsWidget::showCoordinates() const
{
return m_showCoordinates;
}
void MoveToolOptionsWidget::setShowCoordinates(bool value)
{
chkShowCoordinates->setChecked(value);
}
void MoveToolOptionsWidget::slotSetTranslate(QPoint newPos)
{
translateXBox->setValue(newPos.x());
translateYBox->setValue(newPos.y());
}
void MoveToolOptionsWidget::on_translateXBox_valueChanged(int arg1)
{
m_TranslateX = arg1;
m_configGroup.writeEntry("moveToolChangedValueX", m_TranslateX);
emit sigSetTranslateX(arg1);
}
void MoveToolOptionsWidget::on_translateYBox_valueChanged(int arg1)
{
m_TranslateY = arg1;
m_configGroup.writeEntry("moveToolChangedValueY", m_TranslateY);
emit sigSetTranslateY(arg1);
}
diff --git a/plugins/tools/basictools/kis_tool_multihand.cpp b/plugins/tools/basictools/kis_tool_multihand.cpp
index 79e4928469..38265f1010 100644
--- a/plugins/tools/basictools/kis_tool_multihand.cpp
+++ b/plugins/tools/basictools/kis_tool_multihand.cpp
@@ -1,421 +1,599 @@
/*
* Copyright (c) 2011 Lukáš Tvrdý <lukast.dev@gmail.com>
* Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-
#include "kis_tool_multihand.h"
#include <QTransform>
#include <QPushButton>
#include <QComboBox>
#include <QFormLayout>
#include <QStackedWidget>
#include <kis_slider_spin_box.h>
#include <QLabel>
#include "kis_canvas2.h"
#include "kis_cursor.h"
#include "kis_tool_multihand_helper.h"
static const int MAXIMUM_BRUSHES = 50;
#include <QtGlobal>
#ifdef Q_OS_WIN
// quoting DRAND48(3) man-page:
// These functions are declared obsolete by SVID 3,
// which states that rand(3) should be used instead.
#define drand48() (static_cast<double>(qrand()) / static_cast<double>(RAND_MAX))
#endif
KisToolMultihand::KisToolMultihand(KoCanvasBase *canvas)
: KisToolBrush(canvas),
m_transformMode(SYMMETRY),
m_angle(0),
m_handsCount(6),
m_mirrorVertically(false),
m_mirrorHorizontally(false),
m_showAxes(false),
m_translateRadius(100),
- m_setupAxesFlag(false)
+ m_setupAxesFlag(false),
+ m_addSubbrushesMode(false)
, customUI(0)
{
m_helper =
new KisToolMultihandHelper(paintingInformationBuilder(),
kundo2_i18n("Multibrush Stroke"));
resetHelper(m_helper);
if (image()) {
m_axesPoint = QPointF(0.5 * image()->width(), 0.5 * image()->height());
}
}
KisToolMultihand::~KisToolMultihand()
{
}
-
-
void KisToolMultihand::beginPrimaryAction(KoPointerEvent *event)
{
if(m_setupAxesFlag) {
setMode(KisTool::OTHER);
m_axesPoint = convertToPixelCoord(event->point);
requestUpdateOutline(event->point, 0);
updateCanvas();
}
+ else if (m_addSubbrushesMode){
+ QPointF newPoint = convertToPixelCoord(event->point);
+ m_subbrOriginalLocations << newPoint;
+ requestUpdateOutline(event->point, 0);
+ updateCanvas();
+ }
else {
initTransformations();
KisToolFreehand::beginPrimaryAction(event);
}
}
void KisToolMultihand::continuePrimaryAction(KoPointerEvent *event)
{
if(mode() == KisTool::OTHER) {
m_axesPoint = convertToPixelCoord(event->point);
requestUpdateOutline(event->point, 0);
updateCanvas();
}
else {
+ requestUpdateOutline(event->point, 0);
KisToolFreehand::continuePrimaryAction(event);
}
}
void KisToolMultihand::endPrimaryAction(KoPointerEvent *event)
{
if(mode() == KisTool::OTHER) {
setMode(KisTool::HOVER_MODE);
requestUpdateOutline(event->point, 0);
finishAxesSetup();
}
else {
KisToolFreehand::endPrimaryAction(event);
}
}
-void KisToolMultihand::paint(QPainter& gc, const KoViewConverter &converter)
+void KisToolMultihand::beginAlternateAction(KoPointerEvent* event, AlternateAction action)
{
- if(m_setupAxesFlag) {
- int diagonal = (currentImage()->height() + currentImage()->width());
-
- QPainterPath path;
- path.moveTo(m_axesPoint.x()-diagonal*cos(m_angle), m_axesPoint.y()-diagonal*sin(m_angle));
- path.lineTo(m_axesPoint.x()+diagonal*cos(m_angle), m_axesPoint.y()+diagonal*sin(m_angle));
- path.moveTo(m_axesPoint.x()-diagonal*cos(m_angle+M_PI_2), m_axesPoint.y()-diagonal*sin(m_angle+M_PI_2));
- path.lineTo(m_axesPoint.x()+diagonal*cos(m_angle+M_PI_2), m_axesPoint.y()+diagonal*sin(m_angle+M_PI_2));
- paintToolOutline(&gc, pixelToView(path));
+ if (action != ChangeSize || m_transformMode != COPYTRANSLATE || !m_addSubbrushesMode) {
+ KisToolBrush::beginAlternateAction(event, action);
+ return;
}
- else {
- KisToolFreehand::paint(gc, converter);
- if(m_showAxes){
- int diagonal = (currentImage()->height() + currentImage()->width());
-
- QPainterPath path;
- path.moveTo(m_axesPoint.x()-diagonal*cos(m_angle), m_axesPoint.y()-diagonal*sin(m_angle));
- path.lineTo(m_axesPoint.x()+diagonal*cos(m_angle), m_axesPoint.y()+diagonal*sin(m_angle));
- path.moveTo(m_axesPoint.x()-diagonal*cos(m_angle+M_PI_2), m_axesPoint.y()-diagonal*sin(m_angle+M_PI_2));
- path.lineTo(m_axesPoint.x()+diagonal*cos(m_angle+M_PI_2), m_axesPoint.y()+diagonal*sin(m_angle+M_PI_2));
- paintToolOutline(&gc, pixelToView(path));
+ setMode(KisTool::OTHER_1);
+ m_axesPoint = convertToPixelCoord(event->point);
+ requestUpdateOutline(event->point, 0);
+ updateCanvas();
+}
+
+void KisToolMultihand::continueAlternateAction(KoPointerEvent* event, AlternateAction action)
+{
+ if (action != ChangeSize || m_transformMode != COPYTRANSLATE || !m_addSubbrushesMode) {
+ KisToolBrush::continueAlternateAction(event, action);
+ return;
+ }
+ if (mode() == KisTool::OTHER_1) {
+ m_axesPoint = convertToPixelCoord(event->point);
+ requestUpdateOutline(event->point, 0);
+ updateCanvas();
+ }
+}
+
+void KisToolMultihand::endAlternateAction(KoPointerEvent* event, AlternateAction action)
+{
+ if (action != ChangeSize || m_transformMode != COPYTRANSLATE || !m_addSubbrushesMode) {
+ KisToolBrush::continueAlternateAction(event, action);
+ return;
+ }
+ if (mode() == KisTool::OTHER_1) {
+ setMode(KisTool::HOVER_MODE);
+ }
+}
+
+void KisToolMultihand::mouseMoveEvent(KoPointerEvent* event)
+{
+ if (mode() == HOVER_MODE) {
+ m_lastToolPos=convertToPixelCoord(event->point);
+ }
+ KisToolBrush::mouseMoveEvent(event);
+}
+
+void KisToolMultihand::paint(QPainter& gc, const KoViewConverter &converter)
+{
+ QPainterPath path;
+
+ if (m_showAxes) {
+ int axisLength = currentImage()->height() + currentImage()->width();
+
+ // add division guide lines if using multiple brushes
+ if ((m_handsCount > 1 && m_transformMode == SYMMETRY) ||
+ (m_handsCount > 1 && m_transformMode == SNOWFLAKE) ) {
+
+ qreal axesAngle = 360.0 / float(m_handsCount);
+ float currentAngle = 0.0;
+ float startingInsetLength = 20; // don't start each line at the origin so we can see better when all points converge
+
+ // draw lines radiating from the origin
+ for( int i=0; i < m_handsCount; i++) {
+
+ currentAngle = i*axesAngle;
+
+ // convert angles to radians since cos and sin need that
+ currentAngle = currentAngle * 0.017453 + m_angle; // m_angle is current rotation set on UI
+
+ QPoint startingSpot = QPoint(m_axesPoint.x()+ (sin(currentAngle)*startingInsetLength), m_axesPoint.y()- (cos(currentAngle))*startingInsetLength );
+ path.moveTo(startingSpot.x(), startingSpot.y());
+ QPointF symmetryLinePoint(m_axesPoint.x()+ (sin(currentAngle)*axisLength), m_axesPoint.y()- (cos(currentAngle))*axisLength );
+ path.lineTo(symmetryLinePoint);
+ }
+
}
+ else if(m_transformMode == MIRROR) {
+
+ if (m_mirrorHorizontally) {
+ path.moveTo(m_axesPoint.x()-axisLength*cos(m_angle+M_PI_2), m_axesPoint.y()-axisLength*sin(m_angle+M_PI_2));
+ path.lineTo(m_axesPoint.x()+axisLength*cos(m_angle+M_PI_2), m_axesPoint.y()+axisLength*sin(m_angle+M_PI_2));
+ }
+
+ if(m_mirrorVertically) {
+ path.moveTo(m_axesPoint.x()-axisLength*cos(m_angle), m_axesPoint.y()-axisLength*sin(m_angle));
+ path.lineTo(m_axesPoint.x()+axisLength*cos(m_angle), m_axesPoint.y()+axisLength*sin(m_angle));
+ }
+ }
+ else if (m_transformMode == COPYTRANSLATE) {
+
+ int ellipsePreviewSize = 10;
+ // draw ellipse at origin to emphasize this is a drawing point
+ path.addEllipse(m_axesPoint.x()-(ellipsePreviewSize),
+ m_axesPoint.y()-(ellipsePreviewSize),
+ ellipsePreviewSize*2,
+ ellipsePreviewSize*2);
+
+ for (QPointF dPos : m_subbrOriginalLocations) {
+ path.addEllipse(dPos, ellipsePreviewSize, ellipsePreviewSize); // Show subbrush reference locations while in add mode
+ }
+
+ // draw the horiz/vertical line for axis origin
+ path.moveTo(m_axesPoint.x()-axisLength*cos(m_angle), m_axesPoint.y()-axisLength*sin(m_angle));
+ path.lineTo(m_axesPoint.x()+axisLength*cos(m_angle), m_axesPoint.y()+axisLength*sin(m_angle));
+ path.moveTo(m_axesPoint.x()-axisLength*cos(m_angle+M_PI_2), m_axesPoint.y()-axisLength*sin(m_angle+M_PI_2));
+ path.lineTo(m_axesPoint.x()+axisLength*cos(m_angle+M_PI_2), m_axesPoint.y()+axisLength*sin(m_angle+M_PI_2));
+
+ }
+ else {
+
+ // draw the horiz/vertical line for axis origin
+ path.moveTo(m_axesPoint.x()-axisLength*cos(m_angle), m_axesPoint.y()-axisLength*sin(m_angle));
+ path.lineTo(m_axesPoint.x()+axisLength*cos(m_angle), m_axesPoint.y()+axisLength*sin(m_angle));
+ path.moveTo(m_axesPoint.x()-axisLength*cos(m_angle+M_PI_2), m_axesPoint.y()-axisLength*sin(m_angle+M_PI_2));
+ path.lineTo(m_axesPoint.x()+axisLength*cos(m_angle+M_PI_2), m_axesPoint.y()+axisLength*sin(m_angle+M_PI_2));
+ }
+
+ } else {
+
+ // not showing axis
+ if (m_transformMode == COPYTRANSLATE) {
+
+ for (QPointF dPos : m_subbrOriginalLocations) {
+ // Show subbrush reference locations while in add mode
+ if (m_addSubbrushesMode) {
+ path.addEllipse(dPos, 10, 10);
+ }
+ }
+ }
+ }
+
+ KisToolFreehand::paint(gc, converter);
+
+ // origin point preview line/s
+ gc.save();
+ QPen outlinePen;
+ outlinePen.setColor(QColor(100,100,100,150));
+ outlinePen.setStyle(Qt::PenStyle::SolidLine);
+ gc.setPen(outlinePen);
+ paintToolOutline(&gc, pixelToView(path));
+ gc.restore();
+
+
+ // fill in a dot for the origin if showing axis
+ if (m_showAxes) {
+ // draw a dot at the origin point to help with precisly moving
+ QPainterPath dotPath;
+ int dotRadius = 4;
+ dotPath.moveTo(m_axesPoint.x(), m_axesPoint.y());
+ dotPath.addEllipse(m_axesPoint.x()- dotRadius*0.25, m_axesPoint.y()- dotRadius*0.25, dotRadius, dotRadius); // last 2 parameters are dot's size
+
+ QBrush fillBrush;
+ fillBrush.setColor(QColor(255, 255, 255, 255));
+ fillBrush.setStyle(Qt::SolidPattern);
+ gc.fillPath(pixelToView(dotPath), fillBrush);
+
+
+ // add slight offset circle for contrast to help show it on
+ dotPath = QPainterPath(); // resets path
+ dotPath.addEllipse(m_axesPoint.x() - dotRadius*0.75, m_axesPoint.y()- dotRadius*0.75, dotRadius, dotRadius); // last 2 parameters are dot's size
+ fillBrush.setColor(QColor(120, 120, 120, 255));
+ gc.fillPath(pixelToView(dotPath), fillBrush);
}
+
}
void KisToolMultihand::initTransformations()
{
QVector<QTransform> transformations;
QTransform m;
if(m_transformMode == SYMMETRY) {
qreal angle = 0;
qreal angleStep = (2 * M_PI) / m_handsCount;
for(int i = 0; i < m_handsCount; i++) {
m.translate(m_axesPoint.x(), m_axesPoint.y());
m.rotateRadians(angle);
m.translate(-m_axesPoint.x(), -m_axesPoint.y());
transformations << m;
m.reset();
angle += angleStep;
}
}
else if(m_transformMode == MIRROR) {
transformations << m;
if (m_mirrorHorizontally) {
m.translate(m_axesPoint.x(),m_axesPoint.y());
m.rotateRadians(m_angle);
m.scale(-1,1);
m.rotateRadians(-m_angle);
m.translate(-m_axesPoint.x(), -m_axesPoint.y());
transformations << m;
m.reset();
}
if (m_mirrorVertically) {
m.translate(m_axesPoint.x(),m_axesPoint.y());
m.rotateRadians(m_angle);
m.scale(1,-1);
m.rotateRadians(-m_angle);
m.translate(-m_axesPoint.x(), -m_axesPoint.y());
transformations << m;
m.reset();
}
if (m_mirrorVertically && m_mirrorHorizontally){
m.translate(m_axesPoint.x(),m_axesPoint.y());
m.rotateRadians(m_angle);
m.scale(-1,-1);
m.rotateRadians(-m_angle);
m.translate(-m_axesPoint.x(), -m_axesPoint.y());
transformations << m;
m.reset();
}
}
else if(m_transformMode == SNOWFLAKE) {
qreal angle = 0;
qreal angleStep = (2 * M_PI) / m_handsCount/4;
for(int i = 0; i < m_handsCount*4; i++) {
if ((i%2)==1) {
m.translate(m_axesPoint.x(), m_axesPoint.y());
m.rotateRadians(m_angle-angleStep);
m.rotateRadians(angle);
m.scale(-1,1);
m.rotateRadians(-m_angle+angleStep);
m.translate(-m_axesPoint.x(), -m_axesPoint.y());
transformations << m;
m.reset();
angle += angleStep*2;
} else {
m.translate(m_axesPoint.x(), m_axesPoint.y());
m.rotateRadians(m_angle-angleStep);
m.rotateRadians(angle);
m.rotateRadians(-m_angle+angleStep);
m.translate(-m_axesPoint.x(), -m_axesPoint.y());
transformations << m;
m.reset();
angle += angleStep*2;
}
}
}
- else /* if(m_transformationNode == TRANSLATE) */ {
+ else if(m_transformMode == TRANSLATE) {
/**
* TODO: currently, the seed is the same for all the
* strokes
*/
for (int i = 0; i < m_handsCount; i++){
qreal angle = drand48() * M_PI * 2;
qreal length = drand48();
// convert the Polar coordinates to Cartesian coordinates
qreal nx = (m_translateRadius * cos(angle) * length);
qreal ny = (m_translateRadius * sin(angle) * length);
m.translate(m_axesPoint.x(),m_axesPoint.y());
m.rotateRadians(m_angle);
m.translate(nx,ny);
m.rotateRadians(-m_angle);
m.translate(-m_axesPoint.x(), -m_axesPoint.y());
transformations << m;
m.reset();
}
+ } else if (m_transformMode == COPYTRANSLATE) {
+ transformations << m;
+ for (QPointF dPos : m_subbrOriginalLocations) {
+ QPointF resPos = dPos-m_axesPoint; // Calculate the difference between subbrush reference position and "origin" reference
+ m.translate(resPos.x(), resPos.y());
+ transformations << m;
+ m.reset();
+ }
}
m_helper->setupTransformations(transformations);
}
QWidget* KisToolMultihand::createOptionWidget()
{
QWidget *widget = KisToolBrush::createOptionWidget();
customUI = new KisToolMultiHandConfigWidget();
// brush smoothing option.
- customUI->layout()->addWidget(widget);
+ //customUI->layout()->addWidget(widget);
customUI->smoothingOptionsLayout->addWidget(widget);
// setup common parameters that all of the modes will see
connect(customUI->showAxesCheckbox, SIGNAL(toggled(bool)), this, SLOT(slotSetAxesVisible(bool)));
customUI->showAxesCheckbox->setChecked((bool)m_configGroup.readEntry("showAxes", false));
connect(image(), SIGNAL(sigSizeChanged(QPointF,QPointF)), this, SLOT(resetAxes()));
customUI->moveOriginButton->setCheckable(true);
connect(customUI->moveOriginButton, SIGNAL(clicked(bool)),this, SLOT(activateAxesPointModeSetup()));
connect(customUI->resetOriginButton, SIGNAL(released()), this, SLOT(resetAxes()));
customUI->multihandTypeCombobox->addItem(i18n("Symmetry"),int(SYMMETRY)); // axis mode
customUI->multihandTypeCombobox->addItem(i18n("Mirror"),int(MIRROR));
customUI->multihandTypeCombobox->addItem(i18n("Translate"),int(TRANSLATE));
customUI->multihandTypeCombobox->addItem(i18n("Snowflake"),int(SNOWFLAKE));
+ customUI->multihandTypeCombobox->addItem(i18n("Copy Translate"),int(COPYTRANSLATE));
connect(customUI->multihandTypeCombobox,SIGNAL(currentIndexChanged(int)),this, SLOT(slotSetTransformMode(int)));
customUI->multihandTypeCombobox->setCurrentIndex(m_configGroup.readEntry("transformMode", 0));
slotSetTransformMode(customUI->multihandTypeCombobox->currentIndex());
customUI->axisRotationSpinbox->setSuffix(QChar(Qt::Key_degree)); // origin rotation
customUI->axisRotationSpinbox->setSingleStep(0.5);
customUI->axisRotationSpinbox->setRange(0.0, 90.0, 1);
customUI->axisRotationSpinbox->setValue(m_configGroup.readEntry("axesAngle", 0.0));
connect( customUI->axisRotationSpinbox, SIGNAL(valueChanged(qreal)),this, SLOT(slotSetAxesAngle(qreal)));
-
-
// symmetry mode options
customUI->brushCountSpinBox->setRange(1, MAXIMUM_BRUSHES);
connect(customUI->brushCountSpinBox, SIGNAL(valueChanged(int)),this, SLOT(slotSetHandsCount(int)));
customUI->brushCountSpinBox->setValue(m_configGroup.readEntry("handsCount", 4));
// mirror mode specific options
connect(customUI->horizontalCheckbox, SIGNAL(toggled(bool)), this, SLOT(slotSetMirrorHorizontally(bool)));
customUI->horizontalCheckbox->setChecked((bool)m_configGroup.readEntry("mirrorHorizontally", false));
connect(customUI->verticalCheckbox, SIGNAL(toggled(bool)), this, SLOT(slotSetMirrorVertically(bool)));
customUI->verticalCheckbox->setChecked((bool)m_configGroup.readEntry("mirrorVertically", false));
// translate mode options
customUI->translationRadiusSpinbox->setRange(0, 200);
customUI->translationRadiusSpinbox->setSuffix(i18n(" px"));
customUI->translationRadiusSpinbox->setValue(m_configGroup.readEntry("translateRadius", 0));
connect(customUI->translationRadiusSpinbox,SIGNAL(valueChanged(int)),this,SLOT(slotSetTranslateRadius(int)));
+ // Copy translate mode options and actions
+ connect(customUI->addSubbrushButton, &QPushButton::clicked, this, &KisToolMultihand::slotAddSubbrushesMode);
+ connect(customUI->removeSubbrushButton, &QPushButton::clicked, this, &KisToolMultihand::slotRemoveAllSubbrushes);
// snowflake re-uses the existing options, so there is no special parameters for that...
return static_cast<QWidget*>(customUI); // keeping it in the native class until the end allows us to access the UI components
}
void KisToolMultihand::activateAxesPointModeSetup()
{
if (customUI->moveOriginButton->isChecked()){
m_setupAxesFlag = true;
useCursor(KisCursor::crossCursor());
updateCanvas();
} else {
finishAxesSetup();
}
}
void KisToolMultihand::resetAxes()
{
m_axesPoint = QPointF(0.5 * image()->width(), 0.5 * image()->height());
finishAxesSetup();
}
void KisToolMultihand::finishAxesSetup()
{
m_setupAxesFlag = false;
customUI->moveOriginButton->setChecked(false);
resetCursorStyle();
updateCanvas();
}
void KisToolMultihand::updateCanvas()
{
KisCanvas2 *kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
Q_ASSERT(kisCanvas);
kisCanvas->updateCanvas();
}
void KisToolMultihand::slotSetHandsCount(int count)
{
m_handsCount = count;
m_configGroup.writeEntry("handsCount", count);
+ updateCanvas();
}
void KisToolMultihand::slotSetAxesAngle(qreal angle)
{
//negative so axes rotates counter clockwise
m_angle = -angle*M_PI/180;
updateCanvas();
m_configGroup.writeEntry("axesAngle", angle);
}
void KisToolMultihand::slotSetTransformMode(int index)
{
m_transformMode = enumTransforModes(customUI->multihandTypeCombobox->itemData(index).toInt());
m_configGroup.writeEntry("transformMode", index);
// hide all of the UI elements by default
customUI->horizontalCheckbox->setVisible(false);
customUI->verticalCheckbox->setVisible(false);
customUI->translationRadiusSpinbox->setVisible(false);
customUI->radiusLabel->setVisible(false);
customUI->brushCountSpinBox->setVisible(false);
customUI->brushesLabel->setVisible(false);
+ customUI->subbrushLabel->setVisible(false);
+ customUI->addSubbrushButton->setVisible(false);
+ customUI->removeSubbrushButton->setVisible(false);
// turn on what we need
- if (index == int(MIRROR)) {
+ if (index == MIRROR) {
customUI->horizontalCheckbox->setVisible(true);
customUI->verticalCheckbox->setVisible(true);
}
- if (index == int(TRANSLATE)) {
- customUI->translationRadiusSpinbox->setVisible(true);
- customUI->radiusLabel->setVisible(true);
+ else if (index == TRANSLATE) {
+ customUI->translationRadiusSpinbox->setVisible(true);
+ customUI->radiusLabel->setVisible(true);
+ customUI->brushCountSpinBox->setVisible(true);
+ customUI->brushesLabel->setVisible(true);
}
- if (index == int(SYMMETRY) || index == int(SNOWFLAKE) || index == int(TRANSLATE) ) {
+ else if (index == SYMMETRY || index == SNOWFLAKE || index == TRANSLATE ) {
customUI->brushCountSpinBox->setVisible(true);
customUI->brushesLabel->setVisible(true);
}
+
+ else if (index == COPYTRANSLATE) {
+ customUI->subbrushLabel->setVisible(true);
+ customUI->addSubbrushButton->setVisible(true);
+ customUI->removeSubbrushButton->setVisible(true);
+ }
}
void KisToolMultihand::slotSetAxesVisible(bool vis)
{
m_showAxes = vis;
updateCanvas();
m_configGroup.writeEntry("showAxes", vis);
}
void KisToolMultihand::slotSetMirrorVertically(bool mirror)
{
m_mirrorVertically = mirror;
+ updateCanvas();
m_configGroup.writeEntry("mirrorVertically", mirror);
}
void KisToolMultihand::slotSetMirrorHorizontally(bool mirror)
{
m_mirrorHorizontally = mirror;
+ updateCanvas();
m_configGroup.writeEntry("mirrorHorizontally", mirror);
}
void KisToolMultihand::slotSetTranslateRadius(int radius)
{
m_translateRadius = radius;
m_configGroup.writeEntry("translateRadius", radius);
}
+void KisToolMultihand::slotAddSubbrushesMode(bool checked)
+{
+ m_addSubbrushesMode = checked;
+ updateCanvas();
+}
+
+void KisToolMultihand::slotRemoveAllSubbrushes()
+{
+ m_subbrOriginalLocations.clear();
+ updateCanvas();
+}
+
diff --git a/plugins/tools/basictools/kis_tool_multihand.h b/plugins/tools/basictools/kis_tool_multihand.h
index 2f1f640f6e..a9d1cf7ca1 100644
--- a/plugins/tools/basictools/kis_tool_multihand.h
+++ b/plugins/tools/basictools/kis_tool_multihand.h
@@ -1,120 +1,121 @@
/*
* Copyright (c) 2011 Lukáš Tvrdý <lukast.dev@gmail.com>
* Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_TOOL_MULTIHAND_H
#define __KIS_TOOL_MULTIHAND_H
#include "kis_tool_brush.h"
#include <kis_icon.h>
#include "kis_tool_multihand_config.h"
class QPushButton;
class QCheckBox;
class QComboBox;
class QStackedWidget;
class KisSliderSpinBox;
class KisToolMultihandHelper;
class KisToolMultihand : public KisToolBrush
{
Q_OBJECT
public:
KisToolMultihand(KoCanvasBase *canvas);
~KisToolMultihand() override;
void beginPrimaryAction(KoPointerEvent *event) override;
void continuePrimaryAction(KoPointerEvent *event) override;
void endPrimaryAction(KoPointerEvent *event) override;
+ void beginAlternateAction(KoPointerEvent *event, AlternateAction action) override;
+ void continueAlternateAction(KoPointerEvent *event, AlternateAction action) override;
+ void endAlternateAction(KoPointerEvent *event, AlternateAction action) override;
+
+ void mouseMoveEvent(KoPointerEvent* event) override;
+
protected:
void paint(QPainter& gc, const KoViewConverter &converter) override;
-
+
QWidget* createOptionWidget() override;
private:
void initTransformations();
void finishAxesSetup();
void updateCanvas();
private Q_SLOTS:
void activateAxesPointModeSetup();
void resetAxes();
void slotSetHandsCount(int count);
void slotSetAxesAngle(qreal angle);
void slotSetTransformMode(int qcomboboxIndex);
void slotSetAxesVisible(bool vis);
void slotSetMirrorVertically(bool mirror);
void slotSetMirrorHorizontally(bool mirror);
void slotSetTranslateRadius(int radius);
+ void slotAddSubbrushesMode(bool checked);
+ void slotRemoveAllSubbrushes();
private:
KisToolMultihandHelper *m_helper;
- enum enumTransforModes { SYMMETRY, MIRROR, TRANSLATE, SNOWFLAKE };
+ enum enumTransforModes:int { SYMMETRY=0, MIRROR, TRANSLATE, SNOWFLAKE, COPYTRANSLATE };
enumTransforModes m_transformMode;
QPointF m_axesPoint;
qreal m_angle;
int m_handsCount;
bool m_mirrorVertically;
bool m_mirrorHorizontally;
bool m_showAxes;
int m_translateRadius;
bool m_setupAxesFlag;
- QComboBox * m_transformModesComboBox;
- KisSliderSpinBox *m_handsCountSlider;
- KisDoubleSliderSpinBox *m_axesAngleSlider;
- QCheckBox *m_axesChCkBox;
- QStackedWidget *m_modeCustomOption;
- QCheckBox *m_mirrorVerticallyChCkBox;
- QCheckBox *m_mirrorHorizontallyChCkBox;
- KisSliderSpinBox *m_translateRadiusSlider;
- QPushButton *m_axesPointBtn;
-
+ bool m_addSubbrushesMode;
+ QPointF m_lastToolPos;
+ QVector<QPointF> m_subbrOriginalLocations;
KisToolMultiHandConfigWidget* customUI;
};
class KisToolMultiBrushFactory : public KisToolPaintFactoryBase
{
public:
KisToolMultiBrushFactory()
: KisToolPaintFactoryBase("KritaShape/KisToolMultiBrush") {
setToolTip(i18n("Multibrush Tool"));
// Temporarily
setSection(TOOL_TYPE_SHAPE);
setIconName(koIconNameCStr("krita_tool_multihand"));
setShortcut(QKeySequence(Qt::Key_Q));
setPriority(11);
setActivationShapeId(KRITA_TOOL_ACTIVATION_ID);
}
~KisToolMultiBrushFactory() override {}
KoToolBase * createTool(KoCanvasBase *canvas) override {
return new KisToolMultihand(canvas);
}
};
#endif /* __KIS_TOOL_MULTIHAND_H */
diff --git a/plugins/tools/basictools/kis_tool_multihand_config.h b/plugins/tools/basictools/kis_tool_multihand_config.h
index e67e4cc293..978bc0d657 100644
--- a/plugins/tools/basictools/kis_tool_multihand_config.h
+++ b/plugins/tools/basictools/kis_tool_multihand_config.h
@@ -1,44 +1,43 @@
/*
Copyright (C) 2016 Scott Petrovic <scottpetrovic@gmail.com>
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 KISTOOLMULTIHANDCONFIG_H
#define KISTOOLMULTIHANDCONFIG_H
#include "ui_wdgmultihandtool.h"
class KisToolMultiHandConfigWidget : public QWidget, public Ui::WdgMultiHandTool
{
Q_OBJECT
public:
KisToolMultiHandConfigWidget(QWidget *parent=0);
~KisToolMultiHandConfigWidget() override;
//Q_SIGNALS:
//public Q_SLOTS:
//void cropTypeSelectableChanged();
//private:
//KisToolCrop* m_cropTool;
};
#endif // KISTOOLMULTIHANDCONFIG_H
diff --git a/plugins/tools/basictools/kis_tool_rectangle.h b/plugins/tools/basictools/kis_tool_rectangle.h
index c12392b584..5c01d93f9b 100644
--- a/plugins/tools/basictools/kis_tool_rectangle.h
+++ b/plugins/tools/basictools/kis_tool_rectangle.h
@@ -1,79 +1,79 @@
/*
* kis_tool_rectangle.h - part of KImageShop^WKrayon^WKrita
*
* Copyright (c) 1999 Michael Koch <koch@kde.org>
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2004 Clarence Dang <dang@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_TOOL_RECTANGLE_H__
#define __KIS_TOOL_RECTANGLE_H__
#include "kis_tool_shape.h"
#include "kis_types.h"
-#include "KisSelectionToolFactoryBase.h"
+#include "KisToolPaintFactoryBase.h"
#include "flake/kis_node_shape.h"
#include <kis_tool_rectangle_base.h>
#include <kis_icon.h>
class QRect;
class KoCanvasBase;
class KisToolRectangle : public KisToolRectangleBase
{
Q_OBJECT
public:
KisToolRectangle(KoCanvasBase * canvas);
~KisToolRectangle() override;
protected:
void finishRect(const QRectF& rect, qreal roundCornersX, qreal roundCornersY) override;
protected Q_SLOTS:
void resetCursorStyle() override;
};
-class KisToolRectangleFactory : public KisSelectionToolFactoryBase
+class KisToolRectangleFactory : public KisToolPaintFactoryBase
{
public:
KisToolRectangleFactory()
- : KisSelectionToolFactoryBase("KritaShape/KisToolRectangle") {
+ : KisToolPaintFactoryBase("KritaShape/KisToolRectangle") {
setToolTip(i18n("Rectangle Tool"));
setSection(TOOL_TYPE_SHAPE);
setActivationShapeId(KRITA_TOOL_ACTIVATION_ID);
setIconName(koIconNameCStr("krita_tool_rectangle"));
//setShortcut( Qt::Key_F6 );
setPriority(2);
}
~KisToolRectangleFactory() override {}
KoToolBase * createTool(KoCanvasBase *canvas) override {
return new KisToolRectangle(canvas);
}
};
#endif // __KIS_TOOL_RECTANGLE_H__
diff --git a/plugins/tools/basictools/wdgmultihandtool.ui b/plugins/tools/basictools/wdgmultihandtool.ui
index 8e4e5622a4..3e3e11704e 100644
--- a/plugins/tools/basictools/wdgmultihandtool.ui
+++ b/plugins/tools/basictools/wdgmultihandtool.ui
@@ -1,264 +1,295 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WdgMultiHandTool</class>
<widget class="QWidget" name="WdgMultiHandTool">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>200</width>
- <height>282</height>
+ <height>290</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="smoothingOptionsLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="bottomMargin">
<number>10</number>
</property>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_2">
<property name="horizontalSpacing">
<number>4</number>
</property>
<property name="verticalSpacing">
<number>7</number>
</property>
- <item row="3" column="0">
- <widget class="QLabel" name="axisTypeLabel">
+ <item row="4" column="0">
+ <widget class="QLabel" name="brushesLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
- <string>Type:</string>
+ <string>Brushes:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
- <item row="1" column="1">
- <layout class="QHBoxLayout" name="horizontalLayout">
- <item>
- <widget class="QPushButton" name="moveOriginButton">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Move</string>
- </property>
- <property name="checkable">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="resetOriginButton">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Reset</string>
- </property>
- <property name="checkable">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- </layout>
+ <item row="7" column="1">
+ <widget class="KisSliderSpinBox" name="translationRadiusSpinbox" native="true"/>
</item>
- <item row="3" column="1">
- <widget class="QComboBox" name="multihandTypeCombobox"/>
+ <item row="8" column="0">
+ <widget class="QLabel" name="subbrushLabel">
+ <property name="text">
+ <string>Subbrushes:</string>
+ </property>
+ </widget>
</item>
- <item row="2" column="0">
- <widget class="QLabel" name="axisRotationLabel">
+ <item row="7" column="0">
+ <widget class="QLabel" name="radiusLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
- <string>Rotation:</string>
+ <string>Radius:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
- <item row="2" column="1">
- <widget class="KisDoubleSliderSpinBox" name="axisRotationSpinbox" native="true">
+ <item row="6" column="1">
+ <widget class="QCheckBox" name="horizontalCheckbox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>15</height>
- </size>
+ <property name="text">
+ <string>Horizontal</string>
</property>
</widget>
</item>
- <item row="4" column="1">
- <widget class="KisSliderSpinBox" name="brushCountSpinBox" native="true"/>
- </item>
- <item row="0" column="1">
+ <item row="1" column="1">
<widget class="QCheckBox" name="showAxesCheckbox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Show Origin</string>
</property>
</widget>
</item>
- <item row="4" column="0">
- <widget class="QLabel" name="brushesLabel">
+ <item row="3" column="0">
+ <widget class="QLabel" name="axisRotationLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
- <string>Brushes:</string>
+ <string>Rotation:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
- <item row="6" column="1">
- <widget class="QCheckBox" name="horizontalCheckbox">
+ <item row="3" column="1">
+ <widget class="KisDoubleSliderSpinBox" name="axisRotationSpinbox" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>15</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QCheckBox" name="verticalCheckbox">
<property name="text">
- <string>Horizontal</string>
+ <string>Vertical</string>
</property>
</widget>
</item>
- <item row="1" column="0">
- <widget class="QLabel" name="label">
+ <item row="0" column="0">
+ <widget class="QLabel" name="axisTypeLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="layoutDirection">
- <enum>Qt::LeftToRight</enum>
- </property>
<property name="text">
- <string>Origin:</string>
+ <string>Type:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
- <item row="5" column="1">
- <widget class="QCheckBox" name="verticalCheckbox">
- <property name="text">
- <string>Vertical</string>
- </property>
- </widget>
+ <item row="2" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QPushButton" name="moveOriginButton">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Move</string>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="resetOriginButton">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Reset</string>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
</item>
- <item row="7" column="0">
- <widget class="QLabel" name="radiusLabel">
+ <item row="4" column="1">
+ <widget class="KisSliderSpinBox" name="brushCountSpinBox" native="true"/>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
<property name="text">
- <string>Radius:</string>
+ <string>Origin:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
- <item row="7" column="1">
- <widget class="KisSliderSpinBox" name="translationRadiusSpinbox" native="true"/>
+ <item row="8" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QPushButton" name="addSubbrushButton">
+ <property name="text">
+ <string>Add</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="removeSubbrushButton">
+ <property name="text">
+ <string>Remove All</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="multihandTypeCombobox"/>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KisSliderSpinBox</class>
<extends>QWidget</extends>
<header>kis_slider_spin_box.h</header>
<container>1</container>
</customwidget>
- <customwidget>
+ <customwidget>
<class>KisDoubleSliderSpinBox</class>
<extends>QWidget</extends>
<header>kis_slider_spin_box.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
diff --git a/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp b/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp
index a1e7df8e4e..3b95393eda 100644
--- a/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp
+++ b/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp
@@ -1,1727 +1,1729 @@
/* This file is part of the KDE project
Copyright (C) 2006-2008 Thorsten Zachmann <zachmann@kde.org>
Copyright (C) 2006-2010 Thomas Zander <zander@kde.org>
Copyright (C) 2008-2009 Jan Hambrecht <jaham@gmx.net>
Copyright (C) 2008 C. Boemann <cbo@boemann.dk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "DefaultTool.h"
#include "DefaultToolGeometryWidget.h"
#include "DefaultToolTabbedWidget.h"
#include "SelectionDecorator.h"
#include "ShapeMoveStrategy.h"
#include "ShapeRotateStrategy.h"
#include "ShapeShearStrategy.h"
#include "ShapeResizeStrategy.h"
#include <KoPointerEvent.h>
#include <KoToolSelection.h>
#include <KoToolManager.h>
#include <KoSelection.h>
#include <KoShapeController.h>
#include <KoShapeManager.h>
#include <KoSelectedShapesProxy.h>
#include <KoShapeGroup.h>
#include <KoShapeLayer.h>
#include <KoShapeOdfSaveHelper.h>
#include <KoPathShape.h>
#include <KoDrag.h>
#include <KoCanvasBase.h>
#include <KoCanvasResourceProvider.h>
#include <KoShapeRubberSelectStrategy.h>
#include <commands/KoShapeMoveCommand.h>
#include <commands/KoShapeTransformCommand.h>
#include <commands/KoShapeDeleteCommand.h>
#include <commands/KoShapeCreateCommand.h>
#include <commands/KoShapeGroupCommand.h>
#include <commands/KoShapeUngroupCommand.h>
#include <commands/KoShapeDistributeCommand.h>
#include <commands/KoKeepShapesSelectedCommand.h>
#include <KoSnapGuide.h>
#include <KoStrokeConfigWidget.h>
#include "kis_action_registry.h"
#include "kis_node.h"
#include "kis_node_manager.h"
#include "KisViewManager.h"
#include "kis_canvas2.h"
#include "kis_canvas_resource_provider.h"
#include <KoInteractionStrategyFactory.h>
#include "kis_document_aware_spin_box_unit_manager.h"
#include <KoIcon.h>
#include <QPointer>
#include <QAction>
#include <QKeyEvent>
#include <QSignalMapper>
#include <KoResourcePaths.h>
#include <KoCanvasController.h>
#include <kactioncollection.h>
#include <QMenu>
#include <math.h>
#include "kis_assert.h"
#include "kis_global.h"
#include "kis_debug.h"
#include <QVector2D>
#define HANDLE_DISTANCE 10
#define HANDLE_DISTANCE_SQ (HANDLE_DISTANCE * HANDLE_DISTANCE)
#define INNER_HANDLE_DISTANCE_SQ 16
namespace {
static const QString EditFillGradientFactoryId = "edit_fill_gradient";
static const QString EditStrokeGradientFactoryId = "edit_stroke_gradient";
enum TransformActionType {
TransformRotate90CW,
TransformRotate90CCW,
TransformRotate180,
TransformMirrorX,
TransformMirrorY,
TransformReset
};
enum BooleanOp {
BooleanUnion,
BooleanIntersection,
BooleanSubtraction
};
}
class NopInteractionStrategy : public KoInteractionStrategy
{
public:
explicit NopInteractionStrategy(KoToolBase *parent)
: KoInteractionStrategy(parent)
{
}
KUndo2Command *createCommand() override
{
return 0;
}
void handleMouseMove(const QPointF & /*mouseLocation*/, Qt::KeyboardModifiers /*modifiers*/) override {}
void finishInteraction(Qt::KeyboardModifiers /*modifiers*/) override {}
void paint(QPainter &painter, const KoViewConverter &converter) override {
Q_UNUSED(painter);
Q_UNUSED(converter);
}
};
class SelectionInteractionStrategy : public KoShapeRubberSelectStrategy
{
public:
explicit SelectionInteractionStrategy(KoToolBase *parent, const QPointF &clicked, bool useSnapToGrid)
: KoShapeRubberSelectStrategy(parent, clicked, useSnapToGrid)
{
}
void paint(QPainter &painter, const KoViewConverter &converter) override {
KoShapeRubberSelectStrategy::paint(painter, converter);
}
void finishInteraction(Qt::KeyboardModifiers modifiers = 0) override
{
Q_UNUSED(modifiers);
DefaultTool *defaultTool = dynamic_cast<DefaultTool*>(tool());
KIS_SAFE_ASSERT_RECOVER_RETURN(defaultTool);
KoSelection * selection = defaultTool->koSelection();
const bool useContainedMode = currentMode() == CoveringSelection;
QList<KoShape *> shapes =
defaultTool->shapeManager()->
shapesAt(selectedRectangle(), true, useContainedMode);
Q_FOREACH (KoShape * shape, shapes) {
if (!shape->isSelectable()) continue;
selection->select(shape);
}
defaultTool->repaintDecorations();
defaultTool->canvas()->updateCanvas(selectedRectangle());
}
};
#include <KoGradientBackground.h>
#include "KoShapeGradientHandles.h"
#include "ShapeGradientEditStrategy.h"
class DefaultTool::MoveGradientHandleInteractionFactory : public KoInteractionStrategyFactory
{
public:
MoveGradientHandleInteractionFactory(KoFlake::FillVariant fillVariant,
int priority, const QString &id, DefaultTool *_q)
: KoInteractionStrategyFactory(priority, id),
q(_q),
m_fillVariant(fillVariant)
{
}
KoInteractionStrategy* createStrategy(KoPointerEvent *ev) override
{
m_currentHandle = handleAt(ev->point);
if (m_currentHandle.type != KoShapeGradientHandles::Handle::None) {
KoShape *shape = onlyEditableShape();
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(shape, 0);
return new ShapeGradientEditStrategy(q, m_fillVariant, shape, m_currentHandle.type, ev->point);
}
return 0;
}
bool hoverEvent(KoPointerEvent *ev) override
{
m_currentHandle = handleAt(ev->point);
return false;
}
bool paintOnHover(QPainter &painter, const KoViewConverter &converter) override
{
Q_UNUSED(painter);
Q_UNUSED(converter);
return false;
}
bool tryUseCustomCursor() override {
if (m_currentHandle.type != KoShapeGradientHandles::Handle::None) {
q->useCursor(Qt::OpenHandCursor);
}
return m_currentHandle.type != KoShapeGradientHandles::Handle::None;
}
private:
KoShape* onlyEditableShape() const {
KoSelection *selection = q->koSelection();
QList<KoShape*> shapes = selection->selectedEditableShapes();
KoShape *shape = 0;
if (shapes.size() == 1) {
shape = shapes.first();
}
return shape;
}
KoShapeGradientHandles::Handle handleAt(const QPointF &pos) {
KoShapeGradientHandles::Handle result;
KoShape *shape = onlyEditableShape();
if (shape) {
KoFlake::SelectionHandle globalHandle = q->handleAt(pos);
const qreal distanceThresholdSq =
globalHandle == KoFlake::NoHandle ?
HANDLE_DISTANCE_SQ : 0.25 * HANDLE_DISTANCE_SQ;
const KoViewConverter *converter = q->canvas()->viewConverter();
const QPointF viewPoint = converter->documentToView(pos);
qreal minDistanceSq = std::numeric_limits<qreal>::max();
KoShapeGradientHandles sh(m_fillVariant, shape);
Q_FOREACH (const KoShapeGradientHandles::Handle &handle, sh.handles()) {
const QPointF handlePoint = converter->documentToView(handle.pos);
const qreal distanceSq = kisSquareDistance(viewPoint, handlePoint);
if (distanceSq < distanceThresholdSq && distanceSq < minDistanceSq) {
result = handle;
minDistanceSq = distanceSq;
}
}
}
return result;
}
private:
DefaultTool *q;
KoFlake::FillVariant m_fillVariant;
KoShapeGradientHandles::Handle m_currentHandle;
};
class SelectionHandler : public KoToolSelection
{
public:
SelectionHandler(DefaultTool *parent)
: KoToolSelection(parent)
, m_selection(parent->koSelection())
{
}
bool hasSelection() override
{
if (m_selection) {
return m_selection->count();
}
return false;
}
private:
QPointer<KoSelection> m_selection;
};
-DefaultTool::DefaultTool(KoCanvasBase *canvas)
+DefaultTool::DefaultTool(KoCanvasBase *canvas, bool connectToSelectedShapesProxy)
: KoInteractionTool(canvas)
, m_lastHandle(KoFlake::NoHandle)
, m_hotPosition(KoFlake::TopLeft)
, m_mouseWasInsideHandles(false)
, m_decorator(0)
, m_selectionHandler(new SelectionHandler(this))
, m_tabbedOptionWidget(0)
{
setupActions();
QPixmap rotatePixmap, shearPixmap;
rotatePixmap.load(":/cursor_rotate.png");
Q_ASSERT(!rotatePixmap.isNull());
shearPixmap.load(":/cursor_shear.png");
Q_ASSERT(!shearPixmap.isNull());
m_rotateCursors[0] = QCursor(rotatePixmap.transformed(QTransform().rotate(45)));
m_rotateCursors[1] = QCursor(rotatePixmap.transformed(QTransform().rotate(90)));
m_rotateCursors[2] = QCursor(rotatePixmap.transformed(QTransform().rotate(135)));
m_rotateCursors[3] = QCursor(rotatePixmap.transformed(QTransform().rotate(180)));
m_rotateCursors[4] = QCursor(rotatePixmap.transformed(QTransform().rotate(225)));
m_rotateCursors[5] = QCursor(rotatePixmap.transformed(QTransform().rotate(270)));
m_rotateCursors[6] = QCursor(rotatePixmap.transformed(QTransform().rotate(315)));
m_rotateCursors[7] = QCursor(rotatePixmap);
/*
m_rotateCursors[0] = QCursor(Qt::RotateNCursor);
m_rotateCursors[1] = QCursor(Qt::RotateNECursor);
m_rotateCursors[2] = QCursor(Qt::RotateECursor);
m_rotateCursors[3] = QCursor(Qt::RotateSECursor);
m_rotateCursors[4] = QCursor(Qt::RotateSCursor);
m_rotateCursors[5] = QCursor(Qt::RotateSWCursor);
m_rotateCursors[6] = QCursor(Qt::RotateWCursor);
m_rotateCursors[7] = QCursor(Qt::RotateNWCursor);
*/
m_shearCursors[0] = QCursor(shearPixmap);
m_shearCursors[1] = QCursor(shearPixmap.transformed(QTransform().rotate(45)));
m_shearCursors[2] = QCursor(shearPixmap.transformed(QTransform().rotate(90)));
m_shearCursors[3] = QCursor(shearPixmap.transformed(QTransform().rotate(135)));
m_shearCursors[4] = QCursor(shearPixmap.transformed(QTransform().rotate(180)));
m_shearCursors[5] = QCursor(shearPixmap.transformed(QTransform().rotate(225)));
m_shearCursors[6] = QCursor(shearPixmap.transformed(QTransform().rotate(270)));
m_shearCursors[7] = QCursor(shearPixmap.transformed(QTransform().rotate(315)));
m_sizeCursors[0] = Qt::SizeVerCursor;
m_sizeCursors[1] = Qt::SizeBDiagCursor;
m_sizeCursors[2] = Qt::SizeHorCursor;
m_sizeCursors[3] = Qt::SizeFDiagCursor;
m_sizeCursors[4] = Qt::SizeVerCursor;
m_sizeCursors[5] = Qt::SizeBDiagCursor;
m_sizeCursors[6] = Qt::SizeHorCursor;
m_sizeCursors[7] = Qt::SizeFDiagCursor;
- connect(canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), this, SLOT(updateActions()));
+ if (connectToSelectedShapesProxy) {
+ connect(canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), this, SLOT(updateActions()));
+ }
}
DefaultTool::~DefaultTool()
{
}
void DefaultTool::slotActivateEditFillGradient(bool value)
{
if (value) {
addInteractionFactory(
new MoveGradientHandleInteractionFactory(KoFlake::Fill,
1, EditFillGradientFactoryId, this));
} else {
removeInteractionFactory(EditFillGradientFactoryId);
}
repaintDecorations();
}
void DefaultTool::slotActivateEditStrokeGradient(bool value)
{
if (value) {
addInteractionFactory(
new MoveGradientHandleInteractionFactory(KoFlake::StrokeFill,
0, EditStrokeGradientFactoryId, this));
} else {
removeInteractionFactory(EditStrokeGradientFactoryId);
}
repaintDecorations();
}
bool DefaultTool::wantsAutoScroll() const
{
return true;
}
void DefaultTool::addMappedAction(QSignalMapper *mapper, const QString &actionId, int commandType)
{
QAction *a =action(actionId);
connect(a, SIGNAL(triggered()), mapper, SLOT(map()));
mapper->setMapping(a, commandType);
}
void DefaultTool::setupActions()
{
m_alignSignalsMapper = new QSignalMapper(this);
addMappedAction(m_alignSignalsMapper, "object_align_horizontal_left", KoShapeAlignCommand::HorizontalLeftAlignment);
addMappedAction(m_alignSignalsMapper, "object_align_horizontal_center", KoShapeAlignCommand::HorizontalCenterAlignment);
addMappedAction(m_alignSignalsMapper, "object_align_horizontal_right", KoShapeAlignCommand::HorizontalRightAlignment);
addMappedAction(m_alignSignalsMapper, "object_align_vertical_top", KoShapeAlignCommand::VerticalTopAlignment);
addMappedAction(m_alignSignalsMapper, "object_align_vertical_center", KoShapeAlignCommand::VerticalCenterAlignment);
addMappedAction(m_alignSignalsMapper, "object_align_vertical_bottom", KoShapeAlignCommand::VerticalBottomAlignment);
m_distributeSignalsMapper = new QSignalMapper(this);
addMappedAction(m_distributeSignalsMapper, "object_distribute_horizontal_left", KoShapeDistributeCommand::HorizontalLeftDistribution);
addMappedAction(m_distributeSignalsMapper, "object_distribute_horizontal_center", KoShapeDistributeCommand::HorizontalCenterDistribution);
addMappedAction(m_distributeSignalsMapper, "object_distribute_horizontal_right", KoShapeDistributeCommand::HorizontalRightDistribution);
addMappedAction(m_distributeSignalsMapper, "object_distribute_horizontal_gaps", KoShapeDistributeCommand::HorizontalGapsDistribution);
addMappedAction(m_distributeSignalsMapper, "object_distribute_vertical_top", KoShapeDistributeCommand::VerticalTopDistribution);
addMappedAction(m_distributeSignalsMapper, "object_distribute_vertical_center", KoShapeDistributeCommand::VerticalCenterDistribution);
addMappedAction(m_distributeSignalsMapper, "object_distribute_vertical_bottom", KoShapeDistributeCommand::VerticalBottomDistribution);
addMappedAction(m_distributeSignalsMapper, "object_distribute_vertical_gaps", KoShapeDistributeCommand::VerticalGapsDistribution);
m_transformSignalsMapper = new QSignalMapper(this);
addMappedAction(m_transformSignalsMapper, "object_transform_rotate_90_cw", TransformRotate90CW);
addMappedAction(m_transformSignalsMapper, "object_transform_rotate_90_ccw", TransformRotate90CCW);
addMappedAction(m_transformSignalsMapper, "object_transform_rotate_180", TransformRotate180);
addMappedAction(m_transformSignalsMapper, "object_transform_mirror_horizontally", TransformMirrorX);
addMappedAction(m_transformSignalsMapper, "object_transform_mirror_vertically", TransformMirrorY);
addMappedAction(m_transformSignalsMapper, "object_transform_reset", TransformReset);
m_booleanSignalsMapper = new QSignalMapper(this);
addMappedAction(m_booleanSignalsMapper, "object_unite", BooleanUnion);
addMappedAction(m_booleanSignalsMapper, "object_intersect", BooleanIntersection);
addMappedAction(m_booleanSignalsMapper, "object_subtract", BooleanSubtraction);
m_contextMenu.reset(new QMenu());
}
qreal DefaultTool::rotationOfHandle(KoFlake::SelectionHandle handle, bool useEdgeRotation)
{
QPointF selectionCenter = koSelection()->absolutePosition();
QPointF direction;
switch (handle) {
case KoFlake::TopMiddleHandle:
if (useEdgeRotation) {
direction = koSelection()->absolutePosition(KoFlake::TopRight)
- koSelection()->absolutePosition(KoFlake::TopLeft);
} else {
QPointF handlePosition = koSelection()->absolutePosition(KoFlake::TopLeft);
handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::TopRight) - handlePosition);
direction = handlePosition - selectionCenter;
}
break;
case KoFlake::TopRightHandle:
direction = (QVector2D(koSelection()->absolutePosition(KoFlake::TopRight) - koSelection()->absolutePosition(KoFlake::TopLeft)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::TopRight) - koSelection()->absolutePosition(KoFlake::BottomRight)).normalized()).toPointF();
break;
case KoFlake::RightMiddleHandle:
if (useEdgeRotation) {
direction = koSelection()->absolutePosition(KoFlake::BottomRight)
- koSelection()->absolutePosition(KoFlake::TopRight);
} else {
QPointF handlePosition = koSelection()->absolutePosition(KoFlake::TopRight);
handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::BottomRight) - handlePosition);
direction = handlePosition - selectionCenter;
}
break;
case KoFlake::BottomRightHandle:
direction = (QVector2D(koSelection()->absolutePosition(KoFlake::BottomRight) - koSelection()->absolutePosition(KoFlake::BottomLeft)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::BottomRight) - koSelection()->absolutePosition(KoFlake::TopRight)).normalized()).toPointF();
break;
case KoFlake::BottomMiddleHandle:
if (useEdgeRotation) {
direction = koSelection()->absolutePosition(KoFlake::BottomLeft)
- koSelection()->absolutePosition(KoFlake::BottomRight);
} else {
QPointF handlePosition = koSelection()->absolutePosition(KoFlake::BottomLeft);
handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::BottomRight) - handlePosition);
direction = handlePosition - selectionCenter;
}
break;
case KoFlake::BottomLeftHandle:
direction = koSelection()->absolutePosition(KoFlake::BottomLeft) - selectionCenter;
direction = (QVector2D(koSelection()->absolutePosition(KoFlake::BottomLeft) - koSelection()->absolutePosition(KoFlake::BottomRight)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::BottomLeft) - koSelection()->absolutePosition(KoFlake::TopLeft)).normalized()).toPointF();
break;
case KoFlake::LeftMiddleHandle:
if (useEdgeRotation) {
direction = koSelection()->absolutePosition(KoFlake::TopLeft)
- koSelection()->absolutePosition(KoFlake::BottomLeft);
} else {
QPointF handlePosition = koSelection()->absolutePosition(KoFlake::TopLeft);
handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::BottomLeft) - handlePosition);
direction = handlePosition - selectionCenter;
}
break;
case KoFlake::TopLeftHandle:
direction = koSelection()->absolutePosition(KoFlake::TopLeft) - selectionCenter;
direction = (QVector2D(koSelection()->absolutePosition(KoFlake::TopLeft) - koSelection()->absolutePosition(KoFlake::TopRight)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::TopLeft) - koSelection()->absolutePosition(KoFlake::BottomLeft)).normalized()).toPointF();
break;
case KoFlake::NoHandle:
return 0.0;
break;
}
qreal rotation = atan2(direction.y(), direction.x()) * 180.0 / M_PI;
switch (handle) {
case KoFlake::TopMiddleHandle:
if (useEdgeRotation) {
rotation -= 0.0;
} else {
rotation -= 270.0;
}
break;
case KoFlake::TopRightHandle:
rotation -= 315.0;
break;
case KoFlake::RightMiddleHandle:
if (useEdgeRotation) {
rotation -= 90.0;
} else {
rotation -= 0.0;
}
break;
case KoFlake::BottomRightHandle:
rotation -= 45.0;
break;
case KoFlake::BottomMiddleHandle:
if (useEdgeRotation) {
rotation -= 180.0;
} else {
rotation -= 90.0;
}
break;
case KoFlake::BottomLeftHandle:
rotation -= 135.0;
break;
case KoFlake::LeftMiddleHandle:
if (useEdgeRotation) {
rotation -= 270.0;
} else {
rotation -= 180.0;
}
break;
case KoFlake::TopLeftHandle:
rotation -= 225.0;
break;
case KoFlake::NoHandle:
break;
}
if (rotation < 0.0) {
rotation += 360.0;
}
return rotation;
}
void DefaultTool::updateCursor()
{
if (tryUseCustomCursor()) return;
QCursor cursor = Qt::ArrowCursor;
QString statusText;
KoSelection *selection = koSelection();
if (selection && selection->count() > 0) { // has a selection
bool editable = !selection->selectedEditableShapes().isEmpty();
if (!m_mouseWasInsideHandles) {
m_angle = rotationOfHandle(m_lastHandle, true);
int rotOctant = 8 + int(8.5 + m_angle / 45);
bool rotateHandle = false;
bool shearHandle = false;
switch (m_lastHandle) {
case KoFlake::TopMiddleHandle:
cursor = m_shearCursors[(0 + rotOctant) % 8];
shearHandle = true;
break;
case KoFlake::TopRightHandle:
cursor = m_rotateCursors[(1 + rotOctant) % 8];
rotateHandle = true;
break;
case KoFlake::RightMiddleHandle:
cursor = m_shearCursors[(2 + rotOctant) % 8];
shearHandle = true;
break;
case KoFlake::BottomRightHandle:
cursor = m_rotateCursors[(3 + rotOctant) % 8];
rotateHandle = true;
break;
case KoFlake::BottomMiddleHandle:
cursor = m_shearCursors[(4 + rotOctant) % 8];
shearHandle = true;
break;
case KoFlake::BottomLeftHandle:
cursor = m_rotateCursors[(5 + rotOctant) % 8];
rotateHandle = true;
break;
case KoFlake::LeftMiddleHandle:
cursor = m_shearCursors[(6 + rotOctant) % 8];
shearHandle = true;
break;
case KoFlake::TopLeftHandle:
cursor = m_rotateCursors[(7 + rotOctant) % 8];
rotateHandle = true;
break;
case KoFlake::NoHandle:
cursor = Qt::ArrowCursor;
break;
}
if (rotateHandle) {
statusText = i18n("Left click rotates around center, right click around highlighted position.");
}
if (shearHandle) {
statusText = i18n("Click and drag to shear selection.");
}
} else {
statusText = i18n("Click and drag to resize selection.");
m_angle = rotationOfHandle(m_lastHandle, false);
int rotOctant = 8 + int(8.5 + m_angle / 45);
bool cornerHandle = false;
switch (m_lastHandle) {
case KoFlake::TopMiddleHandle:
cursor = m_sizeCursors[(0 + rotOctant) % 8];
break;
case KoFlake::TopRightHandle:
cursor = m_sizeCursors[(1 + rotOctant) % 8];
cornerHandle = true;
break;
case KoFlake::RightMiddleHandle:
cursor = m_sizeCursors[(2 + rotOctant) % 8];
break;
case KoFlake::BottomRightHandle:
cursor = m_sizeCursors[(3 + rotOctant) % 8];
cornerHandle = true;
break;
case KoFlake::BottomMiddleHandle:
cursor = m_sizeCursors[(4 + rotOctant) % 8];
break;
case KoFlake::BottomLeftHandle:
cursor = m_sizeCursors[(5 + rotOctant) % 8];
cornerHandle = true;
break;
case KoFlake::LeftMiddleHandle:
cursor = m_sizeCursors[(6 + rotOctant) % 8];
break;
case KoFlake::TopLeftHandle:
cursor = m_sizeCursors[(7 + rotOctant) % 8];
cornerHandle = true;
break;
case KoFlake::NoHandle:
cursor = Qt::SizeAllCursor;
statusText = i18n("Click and drag to move selection.");
break;
}
if (cornerHandle) {
statusText = i18n("Click and drag to resize selection. Middle click to set highlighted position.");
}
}
if (!editable) {
cursor = Qt::ArrowCursor;
}
} else {
// there used to be guides... :'''(
}
useCursor(cursor);
if (currentStrategy() == 0) {
emit statusTextChanged(statusText);
}
}
void DefaultTool::paint(QPainter &painter, const KoViewConverter &converter)
{
KoSelection *selection = koSelection();
if (selection) {
this->m_decorator = new SelectionDecorator(canvas()->resourceManager());
{
/**
* Selection masks don't render the outline of the shapes, so we should
* do that explicitly when rendering them via selection
*/
KisCanvas2 *kisCanvas = static_cast<KisCanvas2 *>(canvas());
KisNodeSP node = kisCanvas->viewManager()->nodeManager()->activeNode();
const bool isSelectionMask = node && node->inherits("KisSelectionMask");
m_decorator->setForceShapeOutlines(isSelectionMask);
}
m_decorator->setSelection(selection);
m_decorator->setHandleRadius(handleRadius());
m_decorator->setShowFillGradientHandles(hasInteractioFactory(EditFillGradientFactoryId));
m_decorator->setShowStrokeFillGradientHandles(hasInteractioFactory(EditStrokeGradientFactoryId));
m_decorator->paint(painter, converter);
}
KoInteractionTool::paint(painter, converter);
painter.save();
KoShape::applyConversion(painter, converter);
canvas()->snapGuide()->paint(painter, converter);
painter.restore();
}
bool DefaultTool::isValidForCurrentLayer() const
{
// if the currently active node has a shape manager, then it is
// probably our client :)
KisCanvas2 *kisCanvas = static_cast<KisCanvas2 *>(canvas());
return bool(kisCanvas->localShapeManager());
}
KoShapeManager *DefaultTool::shapeManager() const {
return canvas()->shapeManager();
}
void DefaultTool::mousePressEvent(KoPointerEvent *event)
{
// this tool only works on a vector layer right now, so give a warning if another layer type is trying to use it
if (!isValidForCurrentLayer()) {
KisCanvas2 *kiscanvas = static_cast<KisCanvas2 *>(canvas());
kiscanvas->viewManager()->showFloatingMessage(
i18n("This tool only works on vector layers. You probably want the move tool."),
QIcon(), 2000, KisFloatingMessage::Medium, Qt::AlignCenter);
return;
}
KoInteractionTool::mousePressEvent(event);
updateCursor();
}
void DefaultTool::mouseMoveEvent(KoPointerEvent *event)
{
KoInteractionTool::mouseMoveEvent(event);
if (currentStrategy() == 0 && koSelection() && koSelection()->count() > 0) {
QRectF bound = handlesSize();
if (bound.contains(event->point)) {
bool inside;
KoFlake::SelectionHandle newDirection = handleAt(event->point, &inside);
if (inside != m_mouseWasInsideHandles || m_lastHandle != newDirection) {
m_lastHandle = newDirection;
m_mouseWasInsideHandles = inside;
//repaintDecorations();
}
} else {
/*if (m_lastHandle != KoFlake::NoHandle)
repaintDecorations(); */
m_lastHandle = KoFlake::NoHandle;
m_mouseWasInsideHandles = false;
// there used to be guides... :'''(
}
} else {
// there used to be guides... :'''(
}
updateCursor();
}
QRectF DefaultTool::handlesSize()
{
KoSelection *selection = koSelection();
if (!selection || !selection->count()) return QRectF();
recalcSelectionBox(selection);
QRectF bound = m_selectionOutline.boundingRect();
// expansion Border
if (!canvas() || !canvas()->viewConverter()) {
return bound;
}
QPointF border = canvas()->viewConverter()->viewToDocument(QPointF(HANDLE_DISTANCE, HANDLE_DISTANCE));
bound.adjust(-border.x(), -border.y(), border.x(), border.y());
return bound;
}
void DefaultTool::mouseReleaseEvent(KoPointerEvent *event)
{
KoInteractionTool::mouseReleaseEvent(event);
updateCursor();
// This makes sure the decorations that are shown are refreshed. especally the "T" icon
canvas()->updateCanvas(QRectF(0,0,canvas()->canvasWidget()->width(), canvas()->canvasWidget()->height()));
}
void DefaultTool::mouseDoubleClickEvent(KoPointerEvent *event)
{
KoSelection *selection = koSelection();
KoShape *shape = shapeManager()->shapeAt(event->point, KoFlake::ShapeOnTop);
if (shape && selection && !selection->isSelected(shape)) {
if (!(event->modifiers() & Qt::ShiftModifier)) {
selection->deselectAll();
}
selection->select(shape);
}
explicitUserStrokeEndRequest();
}
bool DefaultTool::moveSelection(int direction, Qt::KeyboardModifiers modifiers)
{
bool result = false;
qreal x = 0.0, y = 0.0;
if (direction == Qt::Key_Left) {
x = -5;
} else if (direction == Qt::Key_Right) {
x = 5;
} else if (direction == Qt::Key_Up) {
y = -5;
} else if (direction == Qt::Key_Down) {
y = 5;
}
if (x != 0.0 || y != 0.0) { // actually move
if ((modifiers & Qt::ShiftModifier) != 0) {
x *= 10;
y *= 10;
} else if ((modifiers & Qt::AltModifier) != 0) { // more precise
x /= 5;
y /= 5;
}
QList<KoShape *> shapes = koSelection()->selectedEditableShapes();
if (!shapes.isEmpty()) {
canvas()->addCommand(new KoShapeMoveCommand(shapes, QPointF(x, y)));
result = true;
}
}
return result;
}
void DefaultTool::keyPressEvent(QKeyEvent *event)
{
KoInteractionTool::keyPressEvent(event);
if (currentStrategy() == 0) {
switch (event->key()) {
case Qt::Key_Left:
case Qt::Key_Right:
case Qt::Key_Up:
case Qt::Key_Down:
if (moveSelection(event->key(), event->modifiers())) {
event->accept();
}
break;
case Qt::Key_1:
case Qt::Key_2:
case Qt::Key_3:
case Qt::Key_4:
case Qt::Key_5:
canvas()->resourceManager()->setResource(HotPosition, event->key() - Qt::Key_1);
event->accept();
break;
default:
return;
}
}
}
void DefaultTool::repaintDecorations()
{
if (koSelection() && koSelection()->count() > 0) {
canvas()->updateCanvas(handlesSize());
}
}
void DefaultTool::copy() const
{
// all the selected shapes, not only editable!
QList<KoShape *> shapes = koSelection()->selectedShapes();
if (!shapes.isEmpty()) {
KoDrag drag;
drag.setSvg(shapes);
drag.addToClipboard();
}
}
void DefaultTool::deleteSelection()
{
QList<KoShape *> shapes;
foreach (KoShape *s, koSelection()->selectedShapes()) {
if (s->isGeometryProtected()) {
continue;
}
shapes << s;
}
if (!shapes.empty()) {
canvas()->addCommand(canvas()->shapeController()->removeShapes(shapes));
}
}
bool DefaultTool::paste()
{
// we no longer have to do anything as tool Proxy will do it for us
return false;
}
KoSelection *DefaultTool::koSelection() const
{
Q_ASSERT(canvas());
Q_ASSERT(canvas()->selectedShapesProxy());
return canvas()->selectedShapesProxy()->selection();
}
KoFlake::SelectionHandle DefaultTool::handleAt(const QPointF &point, bool *innerHandleMeaning)
{
// check for handles in this order; meaning that when handles overlap the one on top is chosen
static const KoFlake::SelectionHandle handleOrder[] = {
KoFlake::BottomRightHandle,
KoFlake::TopLeftHandle,
KoFlake::BottomLeftHandle,
KoFlake::TopRightHandle,
KoFlake::BottomMiddleHandle,
KoFlake::RightMiddleHandle,
KoFlake::LeftMiddleHandle,
KoFlake::TopMiddleHandle,
KoFlake::NoHandle
};
const KoViewConverter *converter = canvas()->viewConverter();
KoSelection *selection = koSelection();
if (!selection || !selection->count() || !converter) {
return KoFlake::NoHandle;
}
recalcSelectionBox(selection);
if (innerHandleMeaning) {
QPainterPath path;
path.addPolygon(m_selectionOutline);
*innerHandleMeaning = path.contains(point) || path.intersects(handlePaintRect(point));
}
const QPointF viewPoint = converter->documentToView(point);
for (int i = 0; i < KoFlake::NoHandle; ++i) {
KoFlake::SelectionHandle handle = handleOrder[i];
const QPointF handlePoint = converter->documentToView(m_selectionBox[handle]);
const qreal distanceSq = kisSquareDistance(viewPoint, handlePoint);
// if just inside the outline
if (distanceSq < HANDLE_DISTANCE_SQ) {
if (innerHandleMeaning) {
if (distanceSq < INNER_HANDLE_DISTANCE_SQ) {
*innerHandleMeaning = true;
}
}
return handle;
}
}
return KoFlake::NoHandle;
}
void DefaultTool::recalcSelectionBox(KoSelection *selection)
{
KIS_ASSERT_RECOVER_RETURN(selection->count());
QTransform matrix = selection->absoluteTransformation(0);
m_selectionOutline = matrix.map(QPolygonF(selection->outlineRect()));
m_angle = 0.0;
QPolygonF outline = m_selectionOutline; //shorter name in the following :)
m_selectionBox[KoFlake::TopMiddleHandle] = (outline.value(0) + outline.value(1)) / 2;
m_selectionBox[KoFlake::TopRightHandle] = outline.value(1);
m_selectionBox[KoFlake::RightMiddleHandle] = (outline.value(1) + outline.value(2)) / 2;
m_selectionBox[KoFlake::BottomRightHandle] = outline.value(2);
m_selectionBox[KoFlake::BottomMiddleHandle] = (outline.value(2) + outline.value(3)) / 2;
m_selectionBox[KoFlake::BottomLeftHandle] = outline.value(3);
m_selectionBox[KoFlake::LeftMiddleHandle] = (outline.value(3) + outline.value(0)) / 2;
m_selectionBox[KoFlake::TopLeftHandle] = outline.value(0);
if (selection->count() == 1) {
#if 0 // TODO detect mirroring
KoShape *s = koSelection()->firstSelectedShape();
if (s->scaleX() < 0) { // vertically mirrored: swap left / right
std::swap(m_selectionBox[KoFlake::TopLeftHandle], m_selectionBox[KoFlake::TopRightHandle]);
std::swap(m_selectionBox[KoFlake::LeftMiddleHandle], m_selectionBox[KoFlake::RightMiddleHandle]);
std::swap(m_selectionBox[KoFlake::BottomLeftHandle], m_selectionBox[KoFlake::BottomRightHandle]);
}
if (s->scaleY() < 0) { // vertically mirrored: swap top / bottom
std::swap(m_selectionBox[KoFlake::TopLeftHandle], m_selectionBox[KoFlake::BottomLeftHandle]);
std::swap(m_selectionBox[KoFlake::TopMiddleHandle], m_selectionBox[KoFlake::BottomMiddleHandle]);
std::swap(m_selectionBox[KoFlake::TopRightHandle], m_selectionBox[KoFlake::BottomRightHandle]);
}
#endif
}
}
void DefaultTool::activate(ToolActivation activation, const QSet<KoShape *> &shapes)
{
KoToolBase::activate(activation, shapes);
QAction *actionBringToFront = action("object_order_front");
connect(actionBringToFront, SIGNAL(triggered()), this, SLOT(selectionBringToFront()), Qt::UniqueConnection);
QAction *actionRaise = action("object_order_raise");
connect(actionRaise, SIGNAL(triggered()), this, SLOT(selectionMoveUp()), Qt::UniqueConnection);
QAction *actionLower = action("object_order_lower");
connect(actionLower, SIGNAL(triggered()), this, SLOT(selectionMoveDown()));
QAction *actionSendToBack = action("object_order_back");
connect(actionSendToBack, SIGNAL(triggered()), this, SLOT(selectionSendToBack()), Qt::UniqueConnection);
QAction *actionGroupBottom = action("object_group");
connect(actionGroupBottom, SIGNAL(triggered()), this, SLOT(selectionGroup()), Qt::UniqueConnection);
QAction *actionUngroupBottom = action("object_ungroup");
connect(actionUngroupBottom, SIGNAL(triggered()), this, SLOT(selectionUngroup()), Qt::UniqueConnection);
QAction *actionSplit = action("object_split");
connect(actionSplit, SIGNAL(triggered()), this, SLOT(selectionSplitShapes()), Qt::UniqueConnection);
connect(m_alignSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionAlign(int)));
connect(m_distributeSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionDistribute(int)));
connect(m_transformSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionTransform(int)));
connect(m_booleanSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionBooleanOp(int)));
m_mouseWasInsideHandles = false;
m_lastHandle = KoFlake::NoHandle;
useCursor(Qt::ArrowCursor);
repaintDecorations();
updateActions();
if (m_tabbedOptionWidget) {
m_tabbedOptionWidget->activate();
}
}
void DefaultTool::deactivate()
{
KoToolBase::deactivate();
QAction *actionBringToFront = action("object_order_front");
disconnect(actionBringToFront, 0, this, 0);
QAction *actionRaise = action("object_order_raise");
disconnect(actionRaise, 0, this, 0);
QAction *actionLower = action("object_order_lower");
disconnect(actionLower, 0, this, 0);
QAction *actionSendToBack = action("object_order_back");
disconnect(actionSendToBack, 0, this, 0);
QAction *actionGroupBottom = action("object_group");
disconnect(actionGroupBottom, 0, this, 0);
QAction *actionUngroupBottom = action("object_ungroup");
disconnect(actionUngroupBottom, 0, this, 0);
QAction *actionSplit = action("object_split");
disconnect(actionSplit, 0, this, 0);
disconnect(m_alignSignalsMapper, 0, this, 0);
disconnect(m_distributeSignalsMapper, 0, this, 0);
disconnect(m_transformSignalsMapper, 0, this, 0);
disconnect(m_booleanSignalsMapper, 0, this, 0);
if (m_tabbedOptionWidget) {
m_tabbedOptionWidget->deactivate();
}
}
void DefaultTool::selectionGroup()
{
KoSelection *selection = koSelection();
if (!selection) return;
QList<KoShape *> selectedShapes = selection->selectedEditableShapes();
std::sort(selectedShapes.begin(), selectedShapes.end(), KoShape::compareShapeZIndex);
if (selectedShapes.isEmpty()) return;
const int groupZIndex = selectedShapes.last()->zIndex();
KoShapeGroup *group = new KoShapeGroup();
group->setZIndex(groupZIndex);
// TODO what if only one shape is left?
KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Group shapes"));
new KoKeepShapesSelectedCommand(selectedShapes, {}, canvas()->selectedShapesProxy(), false, cmd);
canvas()->shapeController()->addShapeDirect(group, 0, cmd);
new KoShapeGroupCommand(group, selectedShapes, true, cmd);
new KoKeepShapesSelectedCommand({}, {group}, canvas()->selectedShapesProxy(), true, cmd);
canvas()->addCommand(cmd);
// update selection so we can ungroup immediately again
selection->deselectAll();
selection->select(group);
}
void DefaultTool::selectionUngroup()
{
KoSelection *selection = koSelection();
if (!selection) return;
QList<KoShape *> selectedShapes = selection->selectedEditableShapes();
std::sort(selectedShapes.begin(), selectedShapes.end(), KoShape::compareShapeZIndex);
KUndo2Command *cmd = 0;
QList<KoShape*> newShapes;
// add a ungroup command for each found shape container to the macro command
Q_FOREACH (KoShape *shape, selectedShapes) {
KoShapeGroup *group = dynamic_cast<KoShapeGroup *>(shape);
if (group) {
if (!cmd) {
cmd = new KUndo2Command(kundo2_i18n("Ungroup shapes"));
new KoKeepShapesSelectedCommand(selectedShapes, {}, canvas()->selectedShapesProxy(), false, cmd);
}
newShapes << group->shapes();
new KoShapeUngroupCommand(group, group->shapes(),
group->parent() ? QList<KoShape *>() : shapeManager()->topLevelShapes(),
cmd);
canvas()->shapeController()->removeShape(group, cmd);
}
}
if (cmd) {
new KoKeepShapesSelectedCommand({}, newShapes, canvas()->selectedShapesProxy(), true, cmd);
canvas()->addCommand(cmd);
}
}
void DefaultTool::selectionTransform(int transformAction)
{
KoSelection *selection = koSelection();
if (!selection) return;
QList<KoShape *> editableShapes = selection->selectedEditableShapes();
if (editableShapes.isEmpty()) {
return;
}
QTransform applyTransform;
bool shouldReset = false;
KUndo2MagicString actionName = kundo2_noi18n("BUG: No transform action");
switch (TransformActionType(transformAction)) {
case TransformRotate90CW:
applyTransform.rotate(90.0);
actionName = kundo2_i18n("Rotate Object 90° CW");
break;
case TransformRotate90CCW:
applyTransform.rotate(-90.0);
actionName = kundo2_i18n("Rotate Object 90° CCW");
break;
case TransformRotate180:
applyTransform.rotate(180.0);
actionName = kundo2_i18n("Rotate Object 180°");
break;
case TransformMirrorX:
applyTransform.scale(-1.0, 1.0);
actionName = kundo2_i18n("Mirror Object Horizontally");
break;
case TransformMirrorY:
applyTransform.scale(1.0, -1.0);
actionName = kundo2_i18n("Mirror Object Vertically");
break;
case TransformReset:
shouldReset = true;
actionName = kundo2_i18n("Reset Object Transformations");
break;
}
if (!shouldReset && applyTransform.isIdentity()) return;
QList<QTransform> oldTransforms;
QList<QTransform> newTransforms;
const QRectF outlineRect = KoShape::absoluteOutlineRect(editableShapes);
const QPointF centerPoint = outlineRect.center();
const QTransform centerTrans = QTransform::fromTranslate(centerPoint.x(), centerPoint.y());
const QTransform centerTransInv = QTransform::fromTranslate(-centerPoint.x(), -centerPoint.y());
// we also add selection to the list of transformed shapes, so that its outline is updated correctly
QList<KoShape*> transformedShapes = editableShapes;
transformedShapes << selection;
Q_FOREACH (KoShape *shape, transformedShapes) {
oldTransforms.append(shape->transformation());
QTransform t;
if (!shouldReset) {
const QTransform world = shape->absoluteTransformation(0);
t = world * centerTransInv * applyTransform * centerTrans * world.inverted() * shape->transformation();
} else {
const QPointF center = shape->outlineRect().center();
const QPointF offset = shape->transformation().map(center) - center;
t = QTransform::fromTranslate(offset.x(), offset.y());
}
newTransforms.append(t);
}
KoShapeTransformCommand *cmd = new KoShapeTransformCommand(transformedShapes, oldTransforms, newTransforms);
cmd->setText(actionName);
canvas()->addCommand(cmd);
}
void DefaultTool::selectionBooleanOp(int booleanOp)
{
KoSelection *selection = koSelection();
if (!selection) return;
QList<KoShape *> editableShapes = selection->selectedEditableShapes();
if (editableShapes.isEmpty()) {
return;
}
QVector<QPainterPath> srcOutlines;
QPainterPath dstOutline;
KUndo2MagicString actionName = kundo2_noi18n("BUG: boolean action name");
// TODO: implement a reference shape selection dialog!
const int referenceShapeIndex = 0;
KoShape *referenceShape = editableShapes[referenceShapeIndex];
Q_FOREACH (KoShape *shape, editableShapes) {
srcOutlines << shape->absoluteTransformation(0).map(shape->outline());
}
if (booleanOp == BooleanUnion) {
Q_FOREACH (const QPainterPath &path, srcOutlines) {
dstOutline |= path;
}
actionName = kundo2_i18n("Unite Shapes");
} else if (booleanOp == BooleanIntersection) {
for (int i = 0; i < srcOutlines.size(); i++) {
if (i == 0) {
dstOutline = srcOutlines[i];
} else {
dstOutline &= srcOutlines[i];
}
}
// there is a bug in Qt, sometimes it leaves the resulting
// outline open, so just close it explicitly.
dstOutline.closeSubpath();
actionName = kundo2_i18n("Intersect Shapes");
} else if (booleanOp == BooleanSubtraction) {
for (int i = 0; i < srcOutlines.size(); i++) {
dstOutline = srcOutlines[referenceShapeIndex];
if (i != referenceShapeIndex) {
dstOutline -= srcOutlines[i];
}
}
actionName = kundo2_i18n("Subtract Shapes");
}
KoShape *newShape = 0;
if (!dstOutline.isEmpty()) {
newShape = KoPathShape::createShapeFromPainterPath(dstOutline);
}
KUndo2Command *cmd = new KUndo2Command(actionName);
new KoKeepShapesSelectedCommand(editableShapes, {}, canvas()->selectedShapesProxy(), false, cmd);
QList<KoShape*> newSelectedShapes;
if (newShape) {
newShape->setBackground(referenceShape->background());
newShape->setStroke(referenceShape->stroke());
newShape->setZIndex(referenceShape->zIndex());
KoShapeContainer *parent = referenceShape->parent();
canvas()->shapeController()->addShapeDirect(newShape, parent, cmd);
newSelectedShapes << newShape;
}
canvas()->shapeController()->removeShapes(editableShapes, cmd);
new KoKeepShapesSelectedCommand({}, newSelectedShapes, canvas()->selectedShapesProxy(), true, cmd);
canvas()->addCommand(cmd);
}
void DefaultTool::selectionSplitShapes()
{
KoSelection *selection = koSelection();
if (!selection) return;
QList<KoShape *> editableShapes = selection->selectedEditableShapes();
if (editableShapes.isEmpty()) {
return;
}
KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Split Shapes"));
new KoKeepShapesSelectedCommand(editableShapes, {}, canvas()->selectedShapesProxy(), false, cmd);
QList<KoShape*> newShapes;
Q_FOREACH (KoShape *shape, editableShapes) {
KoPathShape *pathShape = dynamic_cast<KoPathShape*>(shape);
if (!pathShape) return;
QList<KoPathShape*> splitShapes;
if (pathShape->separate(splitShapes)) {
QList<KoShape*> normalShapes = implicitCastList<KoShape*>(splitShapes);
KoShapeContainer *parent = shape->parent();
canvas()->shapeController()->addShapesDirect(normalShapes, parent, cmd);
canvas()->shapeController()->removeShape(shape, cmd);
newShapes << normalShapes;
}
}
new KoKeepShapesSelectedCommand({}, newShapes, canvas()->selectedShapesProxy(), true, cmd);
canvas()->addCommand(cmd);
}
void DefaultTool::selectionAlign(int _align)
{
KoShapeAlignCommand::Align align =
static_cast<KoShapeAlignCommand::Align>(_align);
KoSelection *selection = koSelection();
if (!selection) return;
QList<KoShape *> editableShapes = selection->selectedEditableShapes();
if (editableShapes.isEmpty()) {
return;
}
// TODO add an option to the widget so that one can align to the page
// with multiple selected shapes too
QRectF bb;
// single selected shape is automatically aligned to document rect
if (editableShapes.count() == 1) {
if (!canvas()->resourceManager()->hasResource(KoCanvasResourceProvider::PageSize)) {
return;
}
bb = QRectF(QPointF(0, 0), canvas()->resourceManager()->sizeResource(KoCanvasResourceProvider::PageSize));
} else {
bb = KoShape::absoluteOutlineRect(editableShapes);
}
KoShapeAlignCommand *cmd = new KoShapeAlignCommand(editableShapes, align, bb);
canvas()->addCommand(cmd);
}
void DefaultTool::selectionDistribute(int _distribute)
{
KoShapeDistributeCommand::Distribute distribute =
static_cast<KoShapeDistributeCommand::Distribute>(_distribute);
KoSelection *selection = koSelection();
if (!selection) return;
QList<KoShape *> editableShapes = selection->selectedEditableShapes();
if (editableShapes.size() < 3) {
return;
}
QRectF bb = KoShape::absoluteOutlineRect(editableShapes);
KoShapeDistributeCommand *cmd = new KoShapeDistributeCommand(editableShapes, distribute, bb);
canvas()->addCommand(cmd);
}
void DefaultTool::selectionBringToFront()
{
selectionReorder(KoShapeReorderCommand::BringToFront);
}
void DefaultTool::selectionMoveUp()
{
selectionReorder(KoShapeReorderCommand::RaiseShape);
}
void DefaultTool::selectionMoveDown()
{
selectionReorder(KoShapeReorderCommand::LowerShape);
}
void DefaultTool::selectionSendToBack()
{
selectionReorder(KoShapeReorderCommand::SendToBack);
}
void DefaultTool::selectionReorder(KoShapeReorderCommand::MoveShapeType order)
{
KoSelection *selection = koSelection();
if (!selection) {
return;
}
QList<KoShape *> selectedShapes = selection->selectedEditableShapes();
if (selectedShapes.isEmpty()) {
return;
}
KUndo2Command *cmd = KoShapeReorderCommand::createCommand(selectedShapes, shapeManager(), order);
if (cmd) {
canvas()->addCommand(cmd);
}
}
QList<QPointer<QWidget> > DefaultTool::createOptionWidgets()
{
QList<QPointer<QWidget> > widgets;
m_tabbedOptionWidget = new DefaultToolTabbedWidget(this);
if (isActivated()) {
m_tabbedOptionWidget->activate();
}
widgets.append(m_tabbedOptionWidget);
connect(m_tabbedOptionWidget,
SIGNAL(sigSwitchModeEditFillGradient(bool)),
SLOT(slotActivateEditFillGradient(bool)));
connect(m_tabbedOptionWidget,
SIGNAL(sigSwitchModeEditStrokeGradient(bool)),
SLOT(slotActivateEditStrokeGradient(bool)));
return widgets;
}
void DefaultTool::canvasResourceChanged(int key, const QVariant &res)
{
if (key == HotPosition) {
m_hotPosition = KoFlake::AnchorPosition(res.toInt());
repaintDecorations();
}
}
KoInteractionStrategy *DefaultTool::createStrategy(KoPointerEvent *event)
{
KoSelection *selection = koSelection();
if (!selection) return nullptr;
bool insideSelection = false;
KoFlake::SelectionHandle handle = handleAt(event->point, &insideSelection);
bool editableShape = !selection->selectedEditableShapes().isEmpty();
const bool selectMultiple = event->modifiers() & Qt::ShiftModifier;
const bool selectNextInStack = event->modifiers() & Qt::ControlModifier;
const bool avoidSelection = event->modifiers() & Qt::AltModifier;
if (selectNextInStack) {
// change the hot selection position when middle clicking on a handle
KoFlake::AnchorPosition newHotPosition = m_hotPosition;
switch (handle) {
case KoFlake::TopMiddleHandle:
newHotPosition = KoFlake::Top;
break;
case KoFlake::TopRightHandle:
newHotPosition = KoFlake::TopRight;
break;
case KoFlake::RightMiddleHandle:
newHotPosition = KoFlake::Right;
break;
case KoFlake::BottomRightHandle:
newHotPosition = KoFlake::BottomRight;
break;
case KoFlake::BottomMiddleHandle:
newHotPosition = KoFlake::Bottom;
break;
case KoFlake::BottomLeftHandle:
newHotPosition = KoFlake::BottomLeft;
break;
case KoFlake::LeftMiddleHandle:
newHotPosition = KoFlake::Left;
break;
case KoFlake::TopLeftHandle:
newHotPosition = KoFlake::TopLeft;
break;
case KoFlake::NoHandle:
default:
// check if we had hit the center point
const KoViewConverter *converter = canvas()->viewConverter();
QPointF pt = converter->documentToView(event->point);
// TODO: use calculated values instead!
QPointF centerPt = converter->documentToView(selection->absolutePosition());
if (kisSquareDistance(pt, centerPt) < HANDLE_DISTANCE_SQ) {
newHotPosition = KoFlake::Center;
}
break;
}
if (m_hotPosition != newHotPosition) {
canvas()->resourceManager()->setResource(HotPosition, newHotPosition);
return new NopInteractionStrategy(this);
}
}
if (!avoidSelection && editableShape) {
// manipulation of selected shapes goes first
if (handle != KoFlake::NoHandle) {
// resizing or shearing only with left mouse button
if (insideSelection) {
bool forceUniformScaling = m_tabbedOptionWidget && m_tabbedOptionWidget->useUniformScaling();
return new ShapeResizeStrategy(this, selection, event->point, handle, forceUniformScaling);
}
if (handle == KoFlake::TopMiddleHandle || handle == KoFlake::RightMiddleHandle ||
handle == KoFlake::BottomMiddleHandle || handle == KoFlake::LeftMiddleHandle) {
return new ShapeShearStrategy(this, selection, event->point, handle);
}
// rotating is allowed for right mouse button too
if (handle == KoFlake::TopLeftHandle || handle == KoFlake::TopRightHandle ||
handle == KoFlake::BottomLeftHandle || handle == KoFlake::BottomRightHandle) {
return new ShapeRotateStrategy(this, selection, event->point, event->buttons());
}
}
if (!selectMultiple && !selectNextInStack) {
if (insideSelection) {
return new ShapeMoveStrategy(this, selection, event->point);
}
}
}
KoShape *shape = shapeManager()->shapeAt(event->point, selectNextInStack ? KoFlake::NextUnselected : KoFlake::ShapeOnTop);
if (avoidSelection || (!shape && handle == KoFlake::NoHandle)) {
if (!selectMultiple) {
repaintDecorations();
selection->deselectAll();
}
return new SelectionInteractionStrategy(this, event->point, false);
}
if (selection->isSelected(shape)) {
if (selectMultiple) {
repaintDecorations();
selection->deselect(shape);
}
} else if (handle == KoFlake::NoHandle) { // clicked on shape which is not selected
repaintDecorations();
if (!selectMultiple) {
selection->deselectAll();
}
selection->select(shape);
repaintDecorations();
// tablet selection isn't precise and may lead to a move, preventing that
if (event->isTabletEvent()) {
return new NopInteractionStrategy(this);
}
return new ShapeMoveStrategy(this, selection, event->point);
}
return 0;
}
void DefaultTool::updateActions()
{
QList<KoShape*> editableShapes;
if (koSelection()) {
editableShapes = koSelection()->selectedEditableShapes();
}
const bool hasEditableShapes = !editableShapes.isEmpty();
action("object_order_front")->setEnabled(hasEditableShapes);
action("object_order_raise")->setEnabled(hasEditableShapes);
action("object_order_lower")->setEnabled(hasEditableShapes);
action("object_order_back")->setEnabled(hasEditableShapes);
action("object_transform_rotate_90_cw")->setEnabled(hasEditableShapes);
action("object_transform_rotate_90_ccw")->setEnabled(hasEditableShapes);
action("object_transform_rotate_180")->setEnabled(hasEditableShapes);
action("object_transform_mirror_horizontally")->setEnabled(hasEditableShapes);
action("object_transform_mirror_vertically")->setEnabled(hasEditableShapes);
action("object_transform_reset")->setEnabled(hasEditableShapes);
const bool multipleSelected = editableShapes.size() > 1;
const bool alignmentEnabled =
multipleSelected ||
(!editableShapes.isEmpty() &&
canvas()->resourceManager()->hasResource(KoCanvasResourceProvider::PageSize));
action("object_align_horizontal_left")->setEnabled(alignmentEnabled);
action("object_align_horizontal_center")->setEnabled(alignmentEnabled);
action("object_align_horizontal_right")->setEnabled(alignmentEnabled);
action("object_align_vertical_top")->setEnabled(alignmentEnabled);
action("object_align_vertical_center")->setEnabled(alignmentEnabled);
action("object_align_vertical_bottom")->setEnabled(alignmentEnabled);
const bool distributionEnabled = editableShapes.size() > 2;
action("object_distribute_horizontal_left")->setEnabled(distributionEnabled);
action("object_distribute_horizontal_center")->setEnabled(distributionEnabled);
action("object_distribute_horizontal_right")->setEnabled(distributionEnabled);
action("object_distribute_horizontal_gaps")->setEnabled(distributionEnabled);
action("object_distribute_vertical_top")->setEnabled(distributionEnabled);
action("object_distribute_vertical_center")->setEnabled(distributionEnabled);
action("object_distribute_vertical_bottom")->setEnabled(distributionEnabled);
action("object_distribute_vertical_gaps")->setEnabled(distributionEnabled);
updateDistinctiveActions(editableShapes);
emit selectionChanged(editableShapes.size());
}
void DefaultTool::updateDistinctiveActions(const QList<KoShape*> &editableShapes) {
const bool multipleSelected = editableShapes.size() > 1;
action("object_group")->setEnabled(multipleSelected);
action("object_unite")->setEnabled(multipleSelected);
action("object_intersect")->setEnabled(multipleSelected);
action("object_subtract")->setEnabled(multipleSelected);
bool hasShapesWithMultipleSegments = false;
Q_FOREACH (KoShape *shape, editableShapes) {
KoPathShape *pathShape = dynamic_cast<KoPathShape *>(shape);
if (pathShape && pathShape->subpathCount() > 1) {
hasShapesWithMultipleSegments = true;
break;
}
}
action("object_split")->setEnabled(hasShapesWithMultipleSegments);
bool hasGroupShape = false;
foreach (KoShape *shape, editableShapes) {
if (dynamic_cast<KoShapeGroup *>(shape)) {
hasGroupShape = true;
break;
}
}
action("object_ungroup")->setEnabled(hasGroupShape);
}
KoToolSelection *DefaultTool::selection()
{
return m_selectionHandler;
}
QMenu* DefaultTool::popupActionsMenu()
{
if (m_contextMenu) {
m_contextMenu->clear();
m_contextMenu->addAction(action("edit_cut"));
m_contextMenu->addAction(action("edit_copy"));
m_contextMenu->addAction(action("edit_paste"));
m_contextMenu->addSeparator();
m_contextMenu->addAction(action("object_order_front"));
m_contextMenu->addAction(action("object_order_raise"));
m_contextMenu->addAction(action("object_order_lower"));
m_contextMenu->addAction(action("object_order_back"));
if (action("object_group")->isEnabled() || action("object_ungroup")->isEnabled()) {
m_contextMenu->addSeparator();
m_contextMenu->addAction(action("object_group"));
m_contextMenu->addAction(action("object_ungroup"));
}
m_contextMenu->addSeparator();
QMenu *transform = m_contextMenu->addMenu(i18n("Transform"));
transform->addAction(action("object_transform_rotate_90_cw"));
transform->addAction(action("object_transform_rotate_90_ccw"));
transform->addAction(action("object_transform_rotate_180"));
transform->addSeparator();
transform->addAction(action("object_transform_mirror_horizontally"));
transform->addAction(action("object_transform_mirror_vertically"));
transform->addSeparator();
transform->addAction(action("object_transform_reset"));
if (action("object_unite")->isEnabled() ||
action("object_intersect")->isEnabled() ||
action("object_subtract")->isEnabled() ||
action("object_split")->isEnabled()) {
QMenu *transform = m_contextMenu->addMenu(i18n("Logical Operations"));
transform->addAction(action("object_unite"));
transform->addAction(action("object_intersect"));
transform->addAction(action("object_subtract"));
transform->addAction(action("object_split"));
}
}
return m_contextMenu.data();
}
void DefaultTool::addTransformActions(QMenu *menu) const {
menu->addAction(action("object_transform_rotate_90_cw"));
menu->addAction(action("object_transform_rotate_90_ccw"));
menu->addAction(action("object_transform_rotate_180"));
menu->addSeparator();
menu->addAction(action("object_transform_mirror_horizontally"));
menu->addAction(action("object_transform_mirror_vertically"));
menu->addSeparator();
menu->addAction(action("object_transform_reset"));
}
void DefaultTool::explicitUserStrokeEndRequest()
{
QList<KoShape *> shapes = koSelection()->selectedEditableShapesAndDelegates();
emit activateTemporary(KoToolManager::instance()->preferredToolForSelection(shapes));
}
diff --git a/plugins/tools/defaulttool/defaulttool/DefaultTool.h b/plugins/tools/defaulttool/defaulttool/DefaultTool.h
index b6d698f995..757b1b4302 100644
--- a/plugins/tools/defaulttool/defaulttool/DefaultTool.h
+++ b/plugins/tools/defaulttool/defaulttool/DefaultTool.h
@@ -1,199 +1,199 @@
/* This file is part of the KDE project
Copyright (C) 2006-2008 Thorsten Zachmann <zachmann@kde.org>
Copyright (C) 2006-2008 Thomas Zander <zander@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef DEFAULTTOOL_H
#define DEFAULTTOOL_H
#include <KoInteractionTool.h>
#include <KoFlake.h>
#include <commands/KoShapeAlignCommand.h>
#include <commands/KoShapeReorderCommand.h>
#include "SelectionDecorator.h"
#include <QPolygonF>
#include <QTime>
class QSignalMapper;
class KoInteractionStrategy;
class KoShapeMoveCommand;
class KoSelection;
class DefaultToolTabbedWidget;
class KisViewManager;
/**
* The default tool (associated with the arrow icon) implements the default
* interactions you have with flake objects.<br>
* The tool provides scaling, moving, selecting, rotation and soon skewing of
* any number of shapes.
* <p>Note that the implementation of those different strategies are delegated
* to the InteractionStrategy class and its subclasses.
*/
class DefaultTool : public KoInteractionTool
{
Q_OBJECT
public:
/**
* Constructor for basic interaction tool where user actions are translated
* and handled by interaction strategies of type KoInteractionStrategy.
* @param canvas the canvas this tool will be working for.
*/
- explicit DefaultTool(KoCanvasBase *canvas);
+ explicit DefaultTool(KoCanvasBase *canvas, bool connectToSelectedShapesProxy = false);
~DefaultTool() override;
enum CanvasResource {
HotPosition = 1410100299
};
public:
bool wantsAutoScroll() const override;
void paint(QPainter &painter, const KoViewConverter &converter) override;
void repaintDecorations() override;
///reimplemented
void copy() const override;
///reimplemented
void deleteSelection() override;
///reimplemented
bool paste() override;
///reimplemented
KoToolSelection *selection() override;
QMenu* popupActionsMenu() override;
/**
* Returns which selection handle is at params point (or NoHandle if none).
* @return which selection handle is at params point (or NoHandle if none).
* @param point the location (in pt) where we should look for a handle
* @param innerHandleMeaning this boolean is altered to true if the point
* is inside the selection rectangle and false if it is just outside.
* The value of innerHandleMeaning is undefined if the handle location is NoHandle
*/
KoFlake::SelectionHandle handleAt(const QPointF &point, bool *innerHandleMeaning = 0);
public Q_SLOTS:
void activate(ToolActivation activation, const QSet<KoShape *> &shapes) override;
void deactivate() override;
private Q_SLOTS:
void selectionAlign(int _align);
void selectionDistribute(int _distribute);
void selectionBringToFront();
void selectionSendToBack();
void selectionMoveUp();
void selectionMoveDown();
void selectionGroup();
void selectionUngroup();
void selectionTransform(int transformAction);
void selectionBooleanOp(int booleanOp);
void selectionSplitShapes();
void slotActivateEditFillGradient(bool value);
void slotActivateEditStrokeGradient(bool value);
protected Q_SLOTS:
/// Update actions on selection change
void updateActions();
public: // Events
void mousePressEvent(KoPointerEvent *event) override;
void mouseMoveEvent(KoPointerEvent *event) override;
void mouseReleaseEvent(KoPointerEvent *event) override;
void mouseDoubleClickEvent(KoPointerEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
void explicitUserStrokeEndRequest() override;
protected:
QList<QPointer<QWidget> > createOptionWidgets() override;
KoInteractionStrategy *createStrategy(KoPointerEvent *event) override;
protected:
friend class SelectionInteractionStrategy;
virtual bool isValidForCurrentLayer() const;
virtual KoShapeManager *shapeManager() const;
virtual KoSelection *koSelection() const;
/**
* Enable/disable actions specific to the tool (vector vs. reference images)
*/
virtual void updateDistinctiveActions(const QList<KoShape*> &editableShapes);
void addTransformActions(QMenu *menu) const;
QScopedPointer<QMenu> m_contextMenu;
private:
class MoveGradientHandleInteractionFactory;
private:
void setupActions();
void recalcSelectionBox(KoSelection *selection);
void updateCursor();
/// Returns rotation angle of given handle of the current selection
qreal rotationOfHandle(KoFlake::SelectionHandle handle, bool useEdgeRotation);
void addMappedAction(QSignalMapper *mapper, const QString &actionId, int type);
void selectionReorder(KoShapeReorderCommand::MoveShapeType order);
bool moveSelection(int direction, Qt::KeyboardModifiers modifiers);
/// Returns selection rectangle adjusted by handle proximity threshold
QRectF handlesSize();
void canvasResourceChanged(int key, const QVariant &res) override;
KoFlake::SelectionHandle m_lastHandle;
KoFlake::AnchorPosition m_hotPosition;
bool m_mouseWasInsideHandles;
QPointF m_selectionBox[8];
QPolygonF m_selectionOutline;
QPointF m_lastPoint;
SelectionDecorator *m_decorator;
// TODO alter these 3 arrays to be static const instead
QCursor m_sizeCursors[8];
QCursor m_rotateCursors[8];
QCursor m_shearCursors[8];
qreal m_angle;
KoToolSelection *m_selectionHandler;
friend class SelectionHandler;
DefaultToolTabbedWidget *m_tabbedOptionWidget;
QSignalMapper *m_alignSignalsMapper {0};
QSignalMapper *m_distributeSignalsMapper {0};
QSignalMapper *m_transformSignalsMapper {0};
QSignalMapper *m_booleanSignalsMapper {0};
};
#endif
diff --git a/plugins/tools/defaulttool/defaulttool/DefaultToolFactory.cpp b/plugins/tools/defaulttool/defaulttool/DefaultToolFactory.cpp
index 3b245543c7..28a02ab2d9 100644
--- a/plugins/tools/defaulttool/defaulttool/DefaultToolFactory.cpp
+++ b/plugins/tools/defaulttool/defaulttool/DefaultToolFactory.cpp
@@ -1,91 +1,91 @@
/* This file is part of the KDE project
*
* Copyright (C) 2006-2007 Thomas Zander <zander@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "DefaultToolFactory.h"
#include "DefaultTool.h"
#include <kis_action_registry.h>
#include <KoIcon.h>
#include <klocalizedstring.h>
DefaultToolFactory::DefaultToolFactory()
: KoToolFactoryBase(KoInteractionTool_ID)
{
setToolTip(i18n("Select Shapes Tool"));
setSection(mainToolType());
setPriority(0);
setIconName(koIconNameCStr("select"));
setActivationShapeId("flake/always");
}
DefaultToolFactory::DefaultToolFactory(const QString &id)
: KoToolFactoryBase(id)
{
}
DefaultToolFactory::~DefaultToolFactory()
{
}
KoToolBase *DefaultToolFactory::createTool(KoCanvasBase *canvas)
{
- return new DefaultTool(canvas);
+ return new DefaultTool(canvas, true);
}
QList<QAction *> DefaultToolFactory::createActionsImpl()
{
KisActionRegistry *actionRegistry = KisActionRegistry::instance();
QList<QAction *> actions;
actions << actionRegistry->makeQAction("object_order_front");
actions << actionRegistry->makeQAction("object_order_raise");
actions << actionRegistry->makeQAction("object_order_lower");
actions << actionRegistry->makeQAction("object_order_back");
actions << actionRegistry->makeQAction("object_align_horizontal_left");
actions << actionRegistry->makeQAction("object_align_horizontal_center");
actions << actionRegistry->makeQAction("object_align_horizontal_right");
actions << actionRegistry->makeQAction("object_align_vertical_top");
actions << actionRegistry->makeQAction("object_align_vertical_center");
actions << actionRegistry->makeQAction("object_align_vertical_bottom");
actions << actionRegistry->makeQAction("object_distribute_horizontal_left");
actions << actionRegistry->makeQAction("object_distribute_horizontal_center");
actions << actionRegistry->makeQAction("object_distribute_horizontal_right");
actions << actionRegistry->makeQAction("object_distribute_horizontal_gaps");
actions << actionRegistry->makeQAction("object_distribute_vertical_top");
actions << actionRegistry->makeQAction("object_distribute_vertical_center");
actions << actionRegistry->makeQAction("object_distribute_vertical_bottom");
actions << actionRegistry->makeQAction("object_distribute_vertical_gaps");
actions << actionRegistry->makeQAction("object_group");
actions << actionRegistry->makeQAction("object_ungroup");
actions << actionRegistry->makeQAction("object_transform_rotate_90_cw");
actions << actionRegistry->makeQAction("object_transform_rotate_90_ccw");
actions << actionRegistry->makeQAction("object_transform_rotate_180");
actions << actionRegistry->makeQAction("object_transform_mirror_horizontally");
actions << actionRegistry->makeQAction("object_transform_mirror_vertically");
actions << actionRegistry->makeQAction("object_transform_reset");
actions << actionRegistry->makeQAction("object_unite");
actions << actionRegistry->makeQAction("object_intersect");
actions << actionRegistry->makeQAction("object_subtract");
actions << actionRegistry->makeQAction("object_split");
return actions;
}
diff --git a/plugins/tools/defaulttool/referenceimagestool/KisReferenceImageCollection.cpp b/plugins/tools/defaulttool/referenceimagestool/KisReferenceImageCollection.cpp
index 2e21125e87..7df30e4e45 100644
--- a/plugins/tools/defaulttool/referenceimagestool/KisReferenceImageCollection.cpp
+++ b/plugins/tools/defaulttool/referenceimagestool/KisReferenceImageCollection.cpp
@@ -1,113 +1,115 @@
/*
* Copyright (c) 2018 Jouni Pentikäinen <joupent@gmail.com>
*
* 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 "KisReferenceImageCollection.h"
#include <QIODevice>
#include <QMessageBox>
#include <libs/store/KoStore.h>
#include <KisReferenceImage.h>
#include <libs/store/KoStoreDevice.h>
const QString METADATA_FILE = "reference_images.xml";
KisReferenceImageCollection::KisReferenceImageCollection(const QVector<KisReferenceImage *> &references)
: references(references)
{}
const QVector<KisReferenceImage*> &KisReferenceImageCollection::referenceImages() const
{
return references;
}
bool KisReferenceImageCollection::save(QIODevice *io)
{
QScopedPointer<KoStore> store(KoStore::createStore(io, KoStore::Write, "application/x-krita-reference-images]", KoStore::Zip));
if (store.isNull()) return false;
QDomDocument doc;
QDomElement root = doc.createElement("referenceimages");
doc.insertBefore(root, QDomNode());
+ std::sort(references.begin(), references.end(), KoShape::compareShapeZIndex);
+
int nextId = 0;
Q_FOREACH(KisReferenceImage *reference, references) {
reference->saveXml(doc, root, nextId++);
if (reference->embed()) {
bool ok = reference->saveImage(store.data());
if (!ok) return false;
}
}
if (!store->open(METADATA_FILE)) {
return false;
}
KoStoreDevice xmlDev(store.data());
xmlDev.write(doc.toByteArray());
xmlDev.close();
store->close();
return true;
}
bool KisReferenceImageCollection::load(QIODevice *io)
{
QScopedPointer<KoStore> store(KoStore::createStore(io, KoStore::Read, "application/x-krita-reference-images", KoStore::Zip));
if (!store || store->bad()) {
return false;
}
if (!store->hasFile(METADATA_FILE) || !store->open(METADATA_FILE)) {
return false;
}
QByteArray xml = store->device()->readAll();
store->close();
QDomDocument doc;
doc.setContent(xml);
QDomElement root = doc.documentElement();
QStringList failures;
QDomElement element = root.firstChildElement("referenceimage");
while (!element.isNull()) {
KisReferenceImage *reference = KisReferenceImage::fromXml(element);
if (reference->loadImage(store.data())) {
references.append(reference);
} else {
failures << (reference->embed() ? reference->internalFile() : reference->filename());
delete reference;
}
element = element.nextSiblingElement("referenceimage");
}
if (!failures.isEmpty()) {
QMessageBox::warning(
0,
i18nc("@title:window", "Krita"),
i18n("The following reference images could not be loaded:\n%1", failures.join('\n')),
QMessageBox::Ok, QMessageBox::Ok
);
}
return true;
}
diff --git a/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.cpp b/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.cpp
index 99afee138a..afe9d5a844 100644
--- a/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.cpp
+++ b/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.cpp
@@ -1,289 +1,289 @@
/*
* Copyright (c) 2017 Boudewijn Rempt <boud@valdyas.org>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "ToolReferenceImages.h"
#include <QDesktopServices>
#include <QFile>
#include <QLayout>
#include <QMenu>
#include <QMessageBox>
#include <QVector>
#include <KoSelection.h>
#include <KoShapeRegistry.h>
#include <KoShapeManager.h>
#include <KoShapeController.h>
#include <KoFileDialog.h>
#include <kis_action_registry.h>
#include <kis_canvas2.h>
#include <kis_canvas_resource_provider.h>
#include <KisViewManager.h>
#include <KisDocument.h>
#include <KisReferenceImagesLayer.h>
#include <kis_image.h>
#include "ToolReferenceImagesWidget.h"
#include "KisReferenceImageCollection.h"
ToolReferenceImages::ToolReferenceImages(KoCanvasBase * canvas)
- : DefaultTool(canvas)
+ : DefaultTool(canvas, false)
{
setObjectName("ToolReferenceImages");
}
ToolReferenceImages::~ToolReferenceImages()
{
}
void ToolReferenceImages::activate(ToolActivation toolActivation, const QSet<KoShape*> &shapes)
{
DefaultTool::activate(toolActivation, shapes);
auto kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
connect(kisCanvas->image(), SIGNAL(sigNodeAddedAsync(KisNodeSP)), this, SLOT(slotNodeAdded(KisNodeSP)));
auto referenceImageLayer = document()->referenceImagesLayer();
if (referenceImageLayer) {
setReferenceImageLayer(referenceImageLayer);
}
}
void ToolReferenceImages::deactivate()
{
DefaultTool::deactivate();
}
void ToolReferenceImages::slotNodeAdded(KisNodeSP node)
{
auto *referenceImagesLayer = dynamic_cast<KisReferenceImagesLayer*>(node.data());
if (referenceImagesLayer) {
setReferenceImageLayer(referenceImagesLayer);
}
}
void ToolReferenceImages::setReferenceImageLayer(KisSharedPtr<KisReferenceImagesLayer> layer)
{
m_layer = layer;
connect(layer.data(), SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged()));
}
void ToolReferenceImages::addReferenceImage()
{
auto kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
KIS_ASSERT_RECOVER_RETURN(kisCanvas)
KoFileDialog dialog(kisCanvas->viewManager()->mainWindow(), KoFileDialog::OpenFile, "OpenReferenceImage");
dialog.setCaption(i18n("Select a Reference Image"));
QStringList locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
if (!locations.isEmpty()) {
dialog.setDefaultDir(locations.first());
}
QString filename = dialog.filename();
if (filename.isEmpty()) return;
if (!QFileInfo(filename).exists()) return;
auto *reference = KisReferenceImage::fromFile(filename, *kisCanvas->coordinatesConverter(), canvas()->canvasWidget());
if (reference) {
KisDocument *doc = document();
doc->addCommand(KisReferenceImagesLayer::addReferenceImages(doc, {reference}));
}
}
void ToolReferenceImages::removeAllReferenceImages()
{
auto layer = m_layer.toStrongRef();
if (!layer) return;
canvas()->addCommand(layer->removeReferenceImages(document(), layer->shapes()));
}
void ToolReferenceImages::loadReferenceImages()
{
auto kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
KIS_ASSERT_RECOVER_RETURN(kisCanvas)
KoFileDialog dialog(kisCanvas->viewManager()->mainWindow(), KoFileDialog::OpenFile, "OpenReferenceImageCollection");
dialog.setMimeTypeFilters(QStringList() << "application/x-krita-reference-images");
dialog.setCaption(i18n("Load Reference Images"));
QStringList locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
if (!locations.isEmpty()) {
dialog.setDefaultDir(locations.first());
}
QString filename = dialog.filename();
if (filename.isEmpty()) return;
if (!QFileInfo(filename).exists()) return;
QFile file(filename);
if (!file.open(QIODevice::ReadOnly)) {
QMessageBox::critical(nullptr, i18nc("@title:window", "Krita"), i18n("Could not open '%1'.", filename));
return;
}
KisReferenceImageCollection collection;
if (collection.load(&file)) {
QList<KoShape*> shapes;
Q_FOREACH(auto *reference, collection.referenceImages()) {
shapes.append(reference);
}
KisDocument *doc = document();
doc->addCommand(KisReferenceImagesLayer::addReferenceImages(doc, shapes));
} else {
QMessageBox::critical(nullptr, i18nc("@title:window", "Krita"), i18n("Could not load reference images from '%1'.", filename));
}
file.close();
}
void ToolReferenceImages::saveReferenceImages()
{
auto layer = m_layer.toStrongRef();
if (!layer || layer->shapeCount() == 0) return;
auto kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
KIS_ASSERT_RECOVER_RETURN(kisCanvas)
KoFileDialog dialog(kisCanvas->viewManager()->mainWindow(), KoFileDialog::SaveFile, "SaveReferenceImageCollection");
dialog.setMimeTypeFilters(QStringList() << "application/x-krita-reference-images");
dialog.setCaption(i18n("Save Reference Images"));
QStringList locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
if (!locations.isEmpty()) {
dialog.setDefaultDir(locations.first());
}
QString filename = dialog.filename();
if (filename.isEmpty()) return;
QFile file(filename);
if (!file.open(QIODevice::WriteOnly)) {
QMessageBox::critical(nullptr, i18nc("@title:window", "Krita"), i18n("Could not open '%1' for saving.", filename));
return;
}
KisReferenceImageCollection collection(layer->referenceImages());
bool ok = collection.save(&file);
file.close();
if (!ok) {
QMessageBox::critical(nullptr, i18nc("@title:window", "Krita"), i18n("Failed to save reference images."));
}
}
void ToolReferenceImages::slotSelectionChanged()
{
auto layer = m_layer.toStrongRef();
if (!layer) return;
m_optionsWidget->selectionChanged(layer->shapeManager()->selection());
updateActions();
}
QList<QPointer<QWidget>> ToolReferenceImages::createOptionWidgets()
{
// Instead of inheriting DefaultTool's multi-tab implementation, inherit straight from KoToolBase
return KoToolBase::createOptionWidgets();
}
QWidget *ToolReferenceImages::createOptionWidget()
{
if (!m_optionsWidget) {
m_optionsWidget = new ToolReferenceImagesWidget(this);
// See https://bugs.kde.org/show_bug.cgi?id=316896
QWidget *specialSpacer = new QWidget(m_optionsWidget);
specialSpacer->setObjectName("SpecialSpacer");
specialSpacer->setFixedSize(0, 0);
m_optionsWidget->layout()->addWidget(specialSpacer);
}
return m_optionsWidget;
}
bool ToolReferenceImages::isValidForCurrentLayer() const
{
return true;
}
KoShapeManager *ToolReferenceImages::shapeManager() const
{
auto layer = m_layer.toStrongRef();
return layer ? layer->shapeManager() : nullptr;
}
KoSelection *ToolReferenceImages::koSelection() const
{
auto manager = shapeManager();
return manager ? manager->selection() : nullptr;
}
void ToolReferenceImages::updateDistinctiveActions(const QList<KoShape*> &)
{
action("object_group")->setEnabled(false);
action("object_unite")->setEnabled(false);
action("object_intersect")->setEnabled(false);
action("object_subtract")->setEnabled(false);
action("object_split")->setEnabled(false);
action("object_ungroup")->setEnabled(false);
}
void ToolReferenceImages::deleteSelection()
{
auto layer = m_layer.toStrongRef();
if (!layer) return;
QList<KoShape *> shapes = koSelection()->selectedShapes();
if (!shapes.empty()) {
canvas()->addCommand(layer->removeReferenceImages(document(), shapes));
}
}
KisDocument *ToolReferenceImages::document() const
{
auto kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
return kisCanvas->imageView()->document();
}
QList<QAction *> ToolReferenceImagesFactory::createActionsImpl()
{
KisActionRegistry *actionRegistry = KisActionRegistry::instance();
QList<QAction *> actions = DefaultToolFactory::createActionsImpl();
actions << actionRegistry->makeQAction("object_order_front");
actions << actionRegistry->makeQAction("object_order_raise");
actions << actionRegistry->makeQAction("object_order_lower");
actions << actionRegistry->makeQAction("object_order_back");
actions << actionRegistry->makeQAction("object_group");
actions << actionRegistry->makeQAction("object_ungroup");
actions << actionRegistry->makeQAction("object_transform_rotate_90_cw");
actions << actionRegistry->makeQAction("object_transform_rotate_90_ccw");
actions << actionRegistry->makeQAction("object_transform_rotate_180");
actions << actionRegistry->makeQAction("object_transform_mirror_horizontally");
actions << actionRegistry->makeQAction("object_transform_mirror_vertically");
actions << actionRegistry->makeQAction("object_transform_reset");
actions << actionRegistry->makeQAction("object_unite");
actions << actionRegistry->makeQAction("object_intersect");
actions << actionRegistry->makeQAction("object_subtract");
actions << actionRegistry->makeQAction("object_split");
return actions;
}
diff --git a/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffect.cpp b/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffect.cpp
index f573f50383..2f6a7f069a 100644
--- a/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffect.cpp
+++ b/plugins/tools/karbonplugins/filtereffects/ConvolveMatrixEffect.cpp
@@ -1,363 +1,365 @@
/* This file is part of the KDE project
* Copyright (c) 2010 Jan Hambrecht <jaham@gmx.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "ConvolveMatrixEffect.h"
#include "KoFilterEffectRenderContext.h"
#include "KoFilterEffectLoadingContext.h"
#include "KoViewConverter.h"
#include "KoXmlWriter.h"
#include "KoXmlReader.h"
#include <klocalizedstring.h>
#include <QRect>
#include <QVector>
#include <QImage>
#include <QColor>
#include <cmath>
ConvolveMatrixEffect::ConvolveMatrixEffect()
: KoFilterEffect(ConvolveMatrixEffectId, i18n("Convolve Matrix"))
{
setDefaults();
}
void ConvolveMatrixEffect::setDefaults()
{
m_order = QPoint(3, 3);
m_divisor = 0.0;
m_bias = 0.0;
m_target = QPoint(-1, -1);
m_edgeMode = Duplicate;
m_preserveAlpha = false;
m_kernel.resize(m_order.x()*m_order.y());
for (int i = 0; i < m_kernel.size(); ++i) {
m_kernel[i] = 0.0;
}
m_kernelUnitLength = QPointF(1, 1);
}
QPoint ConvolveMatrixEffect::order() const
{
return m_order;
}
void ConvolveMatrixEffect::setOrder(const QPoint &order)
{
m_order = QPoint(qMax(1, order.x()), qMax(1, order.y()));
}
QVector<qreal> ConvolveMatrixEffect::kernel() const
{
return m_kernel;
}
void ConvolveMatrixEffect::setKernel(const QVector<qreal> &kernel)
{
if (m_order.x()*m_order.y() != kernel.count()) {
return;
}
m_kernel = kernel;
}
qreal ConvolveMatrixEffect::divisor() const
{
return m_divisor;
}
void ConvolveMatrixEffect::setDivisor(qreal divisor)
{
m_divisor = divisor;
}
qreal ConvolveMatrixEffect::bias() const
{
return m_bias;
}
void ConvolveMatrixEffect::setBias(qreal bias)
{
m_bias = bias;
}
QPoint ConvolveMatrixEffect::target() const
{
return m_target;
}
void ConvolveMatrixEffect::setTarget(const QPoint &target)
{
m_target = target;
}
ConvolveMatrixEffect::EdgeMode ConvolveMatrixEffect::edgeMode() const
{
return m_edgeMode;
}
void ConvolveMatrixEffect::setEdgeMode(EdgeMode edgeMode)
{
m_edgeMode = edgeMode;
}
bool ConvolveMatrixEffect::isPreserveAlphaEnabled() const
{
return m_preserveAlpha;
}
void ConvolveMatrixEffect::enablePreserveAlpha(bool on)
{
m_preserveAlpha = on;
}
QImage ConvolveMatrixEffect::processImage(const QImage &image, const KoFilterEffectRenderContext &context) const
{
QImage result = image;
const int rx = m_order.x();
const int ry = m_order.y();
if (rx == 0 && ry == 0) {
return result;
}
const int tx = m_target.x() >= 0 && m_target.x() <= rx ? m_target.x() : rx >> 1;
const int ty = m_target.y() >= 0 && m_target.y() <= ry ? m_target.y() : ry >> 1;
const int w = result.width();
const int h = result.height();
// setup mask
const int maskSize = rx * ry;
QVector<QPoint> offset(maskSize);
int index = 0;
for (int y = 0; y < ry; ++y) {
for (int x = 0; x < rx; ++x) {
offset[index] = QPoint(x - tx, y - ty);
index++;
}
}
qreal divisor = m_divisor;
// if no divisor given, it is the sum of all kernel values
// if sum of kernel values is zero, divisor is set to 1
if (divisor == 0.0) {
Q_FOREACH (qreal k, m_kernel) {
divisor += k;
}
if (divisor == 0.0) {
divisor = 1.0;
}
}
int dstPixel, srcPixel;
qreal sumA, sumR, sumG, sumB;
const QRgb *src = (const QRgb *)image.constBits();
QRgb *dst = (QRgb *)result.bits();
const QRect roi = context.filterRegion().toRect();
const int minX = roi.left();
const int maxX = roi.right();
const int minY = roi.top();
const int maxY = roi.bottom();
int srcRow, srcCol;
for (int row = minY; row <= maxY; ++row) {
for (int col = minX; col <= maxX; ++col) {
dstPixel = row * w + col;
sumA = sumR = sumG = sumB = 0;
for (int i = 0; i < maskSize; ++i) {
srcRow = row + offset[i].y();
srcCol = col + offset[i].x();
// handle top and bottom edge
if (srcRow < 0 || srcRow >= h) {
switch (m_edgeMode) {
case Duplicate:
srcRow = srcRow >= h ? h - 1 : 0;
break;
case Wrap:
srcRow = (srcRow + h) % h;
break;
case None:
// zero for all color channels
continue;
break;
}
}
// handle left and right edge
if (srcCol < 0 || srcCol >= w) {
switch (m_edgeMode) {
case Duplicate:
srcCol = srcCol >= w ? w - 1 : 0;
break;
case Wrap:
srcCol = (srcCol + w) % w;
break;
case None:
// zero for all color channels
continue;
break;
}
}
srcPixel = srcRow * w + srcCol;
const QRgb &s = src[srcPixel];
const qreal &k = m_kernel[i];
if (!m_preserveAlpha) {
sumA += qAlpha(s) * k;
}
sumR += qRed(s) * k;
sumG += qGreen(s) * k;
sumB += qBlue(s) * k;
}
if (m_preserveAlpha) {
dst[dstPixel] = qRgba(qBound(0, static_cast<int>(sumR / divisor + m_bias), 255),
qBound(0, static_cast<int>(sumG / divisor + m_bias), 255),
qBound(0, static_cast<int>(sumB / divisor + m_bias), 255),
qAlpha(dst[dstPixel]));
} else {
dst[dstPixel] = qRgba(qBound(0, static_cast<int>(sumR / divisor + m_bias), 255),
qBound(0, static_cast<int>(sumG / divisor + m_bias), 255),
qBound(0, static_cast<int>(sumB / divisor + m_bias), 255),
qBound(0, static_cast<int>(sumA / divisor + m_bias), 255));
}
}
}
return result;
}
bool ConvolveMatrixEffect::load(const KoXmlElement &element, const KoFilterEffectLoadingContext &/*context*/)
{
if (element.tagName() != id()) {
return false;
}
setDefaults();
if (element.hasAttribute("order")) {
QString orderStr = element.attribute("order");
QStringList params = orderStr.replace(',', ' ').simplified().split(' ');
switch (params.count()) {
case 1:
m_order.rx() = qMax(1, params[0].toInt());
m_order.ry() = m_order.x();
break;
case 2:
m_order.rx() = qMax(1, params[0].toInt());
m_order.ry() = qMax(1, params[1].toInt());
break;
}
}
if (element.hasAttribute("kernelMatrix")) {
QString matrixStr = element.attribute("kernelMatrix");
// values are separated by whitespace and/or comma
QStringList values = matrixStr.replace(',', ' ').simplified().split(' ');
if (values.count() == m_order.x()*m_order.y()) {
m_kernel.resize(values.count());
for (int i = 0; i < values.count(); ++i) {
m_kernel[i] = values[i].toDouble();
}
} else {
m_kernel.resize(m_order.x()*m_order.y());
for (int i = 0; i < m_kernel.size(); ++i) {
m_kernel[i] = 0.0;
}
}
}
if (element.hasAttribute("divisor")) {
m_divisor = element.attribute("divisor").toDouble();
}
if (element.hasAttribute("bias")) {
m_bias = element.attribute("bias").toDouble();
}
if (element.hasAttribute("targetX")) {
m_target.rx() = qBound<int>(0, element.attribute("targetX").toInt(), m_order.x());
}
if (element.hasAttribute("targetY")) {
m_target.ry() = qBound<int>(0, element.attribute("targetY").toInt(), m_order.y());
}
if (element.hasAttribute("edgeMode")) {
QString mode = element.attribute("edgeMode");
if (mode == "wrap") {
m_edgeMode = Wrap;
} else if (mode == "none") {
m_edgeMode = None;
} else {
m_edgeMode = Duplicate;
}
}
if (element.hasAttribute("kernelUnitLength")) {
QString kernelUnitLengthStr = element.attribute("kernelUnitLength");
QStringList params = kernelUnitLengthStr.replace(',', ' ').simplified().split(' ');
switch (params.count()) {
case 1:
m_kernelUnitLength.rx() = params[0].toDouble();
m_kernelUnitLength.ry() = m_kernelUnitLength.x();
break;
case 2:
m_kernelUnitLength.rx() = params[0].toDouble();
m_kernelUnitLength.ry() = params[1].toDouble();
break;
}
}
if (element.hasAttribute("preserveAlpha")) {
m_preserveAlpha = (element.attribute("preserveAlpha") == "true");
}
return true;
}
void ConvolveMatrixEffect::save(KoXmlWriter &writer)
{
writer.startElement(ConvolveMatrixEffectId);
saveCommonAttributes(writer);
if (m_order.x() == m_order.y()) {
writer.addAttribute("order", QString("%1").arg(m_order.x()));
} else {
writer.addAttribute("order", QString("%1 %2").arg(m_order.x()).arg(m_order.y()));
}
QString kernel;
for (int i = 0; i < m_kernel.size(); ++i) {
kernel += QString("%1 ").arg(m_kernel[i]);
}
writer.addAttribute("kernelMatrix", kernel);
writer.addAttribute("divisor", QString("%1").arg(m_divisor));
if (m_bias != 0.0) {
writer.addAttribute("bias", QString("%1").arg(m_bias));
}
writer.addAttribute("targetX", QString("%1").arg(m_target.x()));
writer.addAttribute("targetY", QString("%1").arg(m_target.y()));
switch (m_edgeMode) {
case Wrap:
writer.addAttribute("edgeMode", "wrap");
break;
case None:
writer.addAttribute("edgeMode", "none");
break;
case Duplicate:
// fall through as it is the default
- break;
+ Q_FALLTHROUGH();
+ default:
+ ;
}
writer.addAttribute("kernelUnitLength", QString("%1 %2").arg(m_kernelUnitLength.x()).arg(m_kernelUnitLength.y()));
if (m_preserveAlpha) {
writer.addAttribute("preserveAlpha", "true");
}
writer.endElement();
}
diff --git a/plugins/tools/selectiontools/kis_tool_select_contiguous.h b/plugins/tools/selectiontools/kis_tool_select_contiguous.h
index c3f5b875a3..b4f1cb8730 100644
--- a/plugins/tools/selectiontools/kis_tool_select_contiguous.h
+++ b/plugins/tools/selectiontools/kis_tool_select_contiguous.h
@@ -1,95 +1,95 @@
/*
* kis_tool_select_contiguous.h - part of KImageShop^WKrayon^Krita
*
* Copyright (c) 1999 Michael Koch <koch@kde.org>
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2015 Michael Abrahams <miabraha@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_TOOL_SELECT_CONTIGUOUS_H__
#define __KIS_TOOL_SELECT_CONTIGUOUS_H__
-#include "KoToolFactoryBase.h"
+#include "KisSelectionToolFactoryBase.h"
#include "kis_tool_select_base.h"
#include <kis_icon.h>
#include <kconfig.h>
#include <kconfiggroup.h>
/**
* The 'magic wand' selection tool -- in fact just
* a floodfill that only creates a selection.
*/
class KisToolSelectContiguous : public KisToolSelect
{
Q_OBJECT
public:
KisToolSelectContiguous(KoCanvasBase *canvas);
~KisToolSelectContiguous() override;
QWidget* createOptionWidget() override;
void paint(QPainter &painter, const KoViewConverter &converter) override;
void beginPrimaryAction(KoPointerEvent *event) override;
void resetCursorStyle();
protected:
bool wantsAutoScroll() const override { return false; }
public Q_SLOTS:
void activate(ToolActivation toolActivation, const QSet<KoShape*> &shapes) override;
virtual void slotSetFuzziness(int);
virtual void slotSetSizemod(int);
virtual void slotSetFeather(int);
virtual void slotLimitToCurrentLayer(int);
//virtual bool antiAliasSelection();
protected:
using KisToolSelectBase::m_widgetHelper;
private:
int m_fuzziness;
int m_sizemod;
int m_feather;
bool m_limitToCurrentLayer;
KConfigGroup m_configGroup;
};
-class KisToolSelectContiguousFactory : public KoToolFactoryBase
+class KisToolSelectContiguousFactory : public KisSelectionToolFactoryBase
{
public:
KisToolSelectContiguousFactory()
- : KoToolFactoryBase("KisToolSelectContiguous")
+ : KisSelectionToolFactoryBase("KisToolSelectContiguous")
{
setToolTip(i18n("Contiguous Selection Tool"));
setSection(TOOL_TYPE_SELECTION);
setIconName(koIconNameCStr("tool_contiguous_selection"));
setPriority(4);
setActivationShapeId(KRITA_TOOL_ACTIVATION_ID);
}
~KisToolSelectContiguousFactory() override {}
KoToolBase * createTool(KoCanvasBase *canvas) override {
return new KisToolSelectContiguous(canvas);
}
};
#endif //__KIS_TOOL_SELECT_CONTIGUOUS_H__
diff --git a/plugins/tools/selectiontools/kis_tool_select_elliptical.h b/plugins/tools/selectiontools/kis_tool_select_elliptical.h
index 4b29742910..ef6aee434c 100644
--- a/plugins/tools/selectiontools/kis_tool_select_elliptical.h
+++ b/plugins/tools/selectiontools/kis_tool_select_elliptical.h
@@ -1,91 +1,91 @@
/*
* kis_tool_select_elliptical.h - part of Krayon^WKrita
*
* Copyright (c) 2000 John Califf <jcaliff@compuzone.net>
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org> *
* Copyright (c) 2015 Michael Abrahams <miabraha@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_TOOL_SELECT_ELLIPTICAL_H__
#define __KIS_TOOL_SELECT_ELLIPTICAL_H__
-#include "KoToolFactoryBase.h"
+#include "KisSelectionToolFactoryBase.h"
#include "kis_tool_ellipse_base.h"
#include <kis_tool_select_base.h>
#include "kis_selection_tool_config_widget_helper.h"
#include <KoIcon.h>
#include <QKeySequence>
#include <kis_icon.h>
#include <QMenu>
class __KisToolSelectEllipticalLocal : public KisToolEllipseBase
{
Q_OBJECT
public:
__KisToolSelectEllipticalLocal(KoCanvasBase *canvas);
bool hasUserInteractionRunning() const;
protected:
virtual SelectionMode selectionMode() const = 0;
virtual SelectionAction selectionAction() const = 0;
virtual bool antiAliasSelection() const = 0;
private:
void finishRect(const QRectF &rect, qreal roundCornersX, qreal roundCornersY) override;
};
typedef KisToolSelectBase<__KisToolSelectEllipticalLocal> KisToolSelectEllipticalTemplate;
class KisToolSelectElliptical : public KisToolSelectEllipticalTemplate
{
Q_OBJECT
public:
KisToolSelectElliptical(KoCanvasBase* canvas);
void resetCursorStyle();
};
-class KisToolSelectEllipticalFactory : public KoToolFactoryBase
+class KisToolSelectEllipticalFactory : public KisSelectionToolFactoryBase
{
public:
KisToolSelectEllipticalFactory()
- : KoToolFactoryBase("KisToolSelectElliptical")
+ : KisSelectionToolFactoryBase("KisToolSelectElliptical")
{
setToolTip(i18n("Elliptical Selection Tool"));
setSection(TOOL_TYPE_SELECTION);
setActivationShapeId(KRITA_TOOL_ACTIVATION_ID);
setIconName(koIconNameCStr("tool_elliptical_selection"));
setShortcut(QKeySequence(Qt::Key_J));
setPriority(1);
}
~KisToolSelectEllipticalFactory() override {}
KoToolBase * createTool(KoCanvasBase *canvas) override {
return new KisToolSelectElliptical(canvas);
}
};
#endif //__KIS_TOOL_SELECT_ELLIPTICAL_H__
diff --git a/plugins/tools/selectiontools/kis_tool_select_outline.h b/plugins/tools/selectiontools/kis_tool_select_outline.h
index 27c894a0d5..35b9b65f5d 100644
--- a/plugins/tools/selectiontools/kis_tool_select_outline.h
+++ b/plugins/tools/selectiontools/kis_tool_select_outline.h
@@ -1,93 +1,93 @@
/*
* kis_tool_select_freehand.h - part of Krayon^WKrita
*
* Copyright (c) 2000 John Califf <jcaliff@compuzone.net>
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2015 Michael Abrahams <miabraha@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_TOOL_SELECT_OUTLINE_H_
#define KIS_TOOL_SELECT_OUTLINE_H_
#include <QPoint>
-#include <KoToolFactoryBase.h>
+#include "KisSelectionToolFactoryBase.h"
#include <kis_tool_select_base.h>
#include <kis_icon.h>
class QPainterPath;
class KisToolSelectOutline : public KisToolSelect
{
Q_OBJECT
public:
KisToolSelectOutline(KoCanvasBase *canvas);
~KisToolSelectOutline() override;
void beginPrimaryAction(KoPointerEvent *event) override;
void continuePrimaryAction(KoPointerEvent *event) override;
void endPrimaryAction(KoPointerEvent *event) override;
void paint(QPainter& gc, const KoViewConverter &converter) override;
void keyPressEvent(QKeyEvent *event) override;
void keyReleaseEvent(QKeyEvent *event) override;
void mouseMoveEvent(KoPointerEvent *event) override;
void resetCursorStyle();
public Q_SLOTS:
void deactivate() override;
protected:
using KisToolSelectBase::m_widgetHelper;
private:
void finishSelectionAction();
void updateFeedback();
void updateContinuedMode();
void updateCanvas();
QPainterPath m_paintPath;
vQPointF m_points;
bool m_continuedMode;
QPointF m_lastCursorPos;
};
-class KisToolSelectOutlineFactory : public KoToolFactoryBase
+class KisToolSelectOutlineFactory : public KisSelectionToolFactoryBase
{
public:
KisToolSelectOutlineFactory()
- : KoToolFactoryBase("KisToolSelectOutline")
+ : KisSelectionToolFactoryBase("KisToolSelectOutline")
{
setToolTip(i18n("Outline Selection Tool"));
setSection(TOOL_TYPE_SELECTION);
setIconName(koIconNameCStr("tool_outline_selection"));
setPriority(3);
setActivationShapeId(KRITA_TOOL_ACTIVATION_ID);
}
~KisToolSelectOutlineFactory() override {}
KoToolBase * createTool(KoCanvasBase *canvas) override {
return new KisToolSelectOutline(canvas);
}
};
#endif //__selecttoolfreehand_h__
diff --git a/plugins/tools/selectiontools/kis_tool_select_path.h b/plugins/tools/selectiontools/kis_tool_select_path.h
index 4382299545..0c9a4424c7 100644
--- a/plugins/tools/selectiontools/kis_tool_select_path.h
+++ b/plugins/tools/selectiontools/kis_tool_select_path.h
@@ -1,110 +1,110 @@
/*
* Copyright (c) 2007 Sven Langkamp <sven.langkamp@gmail.com>
* Copyright (c) 2015 Michael Abrahams <miabraha@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_TOOL_SELECT_PATH_H_
#define KIS_TOOL_SELECT_PATH_H_
#include <KoCreatePathTool.h>
-#include <KoToolFactoryBase.h>
+#include <KisSelectionToolFactoryBase.h>
#include "kis_tool_select_base.h"
#include "kis_delegated_tool.h"
#include <kis_icon.h>
class KoCanvasBase;
class KisToolSelectPath;
class __KisToolSelectPathLocalTool : public KoCreatePathTool {
public:
__KisToolSelectPathLocalTool(KoCanvasBase * canvas, KisToolSelectPath* parentTool);
void paintPath(KoPathShape &path, QPainter &painter, const KoViewConverter &converter) override;
void addPathShape(KoPathShape* pathShape) override;
using KoCreatePathTool::createOptionWidgets;
using KoCreatePathTool::endPathWithoutLastPoint;
using KoCreatePathTool::endPath;
using KoCreatePathTool::cancelPath;
using KoCreatePathTool::removeLastPoint;
private:
KisToolSelectPath* const m_selectionTool;
};
typedef KisDelegatedTool<KisTool, __KisToolSelectPathLocalTool,
DeselectShapesActivationPolicy> DelegatedSelectPathTool;
struct KisDelegatedSelectPathWrapper : public DelegatedSelectPathTool {
KisDelegatedSelectPathWrapper(KoCanvasBase *canvas,
const QCursor &cursor,
KisTool* delegateTool)
: DelegatedSelectPathTool(canvas, cursor, (__KisToolSelectPathLocalTool*) delegateTool)
{
}
// If an event is explicitly forwarded only as an action (e.g. shift-click is captured by "change size")
// we will receive a primary action but no mousePressEvent. Thus these events must be explicitly forwarded.
void beginPrimaryAction(KoPointerEvent *event) override;
void continuePrimaryAction(KoPointerEvent *event) override;
void endPrimaryAction(KoPointerEvent *event) override;
bool hasUserInteractionRunning() const;
};
class KisToolSelectPath : public KisToolSelectBase<KisDelegatedSelectPathWrapper>
{
Q_OBJECT
public:
KisToolSelectPath(KoCanvasBase * canvas);
void mousePressEvent(KoPointerEvent* event) override;
bool eventFilter(QObject *obj, QEvent *event) override;
void resetCursorStyle();
protected:
void requestStrokeCancellation() override;
void requestStrokeEnd() override;
friend class __KisToolSelectPathLocalTool;
QList<QPointer<QWidget> > createOptionWidgets() override;
};
-class KisToolSelectPathFactory : public KoToolFactoryBase
+class KisToolSelectPathFactory : public KisSelectionToolFactoryBase
{
public:
KisToolSelectPathFactory()
- : KoToolFactoryBase("KisToolSelectPath") {
+ : KisSelectionToolFactoryBase("KisToolSelectPath") {
setToolTip(i18n("Bezier Curve Selection Tool"));
setSection(TOOL_TYPE_SELECTION);
setActivationShapeId(KRITA_TOOL_ACTIVATION_ID);
setIconName(koIconNameCStr("tool_path_selection"));
setPriority(6);
}
~KisToolSelectPathFactory() override {}
KoToolBase * createTool(KoCanvasBase *canvas) override {
return new KisToolSelectPath(canvas);
}
};
#endif // KIS_TOOL_SELECT_PATH_H_
diff --git a/plugins/tools/selectiontools/kis_tool_select_polygonal.h b/plugins/tools/selectiontools/kis_tool_select_polygonal.h
index 1777407ac0..c114511ff3 100644
--- a/plugins/tools/selectiontools/kis_tool_select_polygonal.h
+++ b/plugins/tools/selectiontools/kis_tool_select_polygonal.h
@@ -1,78 +1,91 @@
/*
* kis_tool_select_polygonal.h - part of Krayon^WKrita
*
* Copyright (c) 2000 John Califf <jcaliff@compuzone.net>
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2015 Michael Abrahams <miabraha@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_TOOL_SELECT_POLYGONAL_H_
#define KIS_TOOL_SELECT_POLYGONAL_H_
-#include "KoToolFactoryBase.h"
+#include "KisSelectionToolFactoryBase.h"
#include "kis_tool_polyline_base.h"
#include <kis_tool_select_base.h>
#include "kis_selection_tool_config_widget_helper.h"
#include <kis_icon.h>
class __KisToolSelectPolygonalLocal : public KisToolPolylineBase
{
Q_OBJECT
public:
__KisToolSelectPolygonalLocal(KoCanvasBase *canvas);
protected:
virtual SelectionMode selectionMode() const = 0;
virtual SelectionAction selectionAction() const = 0;
virtual bool antiAliasSelection() const = 0;
private:
void finishPolyline(const QVector<QPointF> &points) override;
private:
};
class KisToolSelectPolygonal : public KisToolSelectBase<__KisToolSelectPolygonalLocal>
{
Q_OBJECT
public:
KisToolSelectPolygonal(KoCanvasBase* canvas);
void resetCursorStyle();
};
-class KisToolSelectPolygonalFactory : public KoToolFactoryBase
+class KisToolSelectPolygonalFactory : public KisSelectionToolFactoryBase
{
public:
KisToolSelectPolygonalFactory()
- : KoToolFactoryBase("KisToolSelectPolygonal")
+ : KisSelectionToolFactoryBase("KisToolSelectPolygonal")
{
setToolTip(i18n("Polygonal Selection Tool"));
setSection(TOOL_TYPE_SELECTION);
setIconName(koIconNameCStr("tool_polygonal_selection"));
setPriority(2);
setActivationShapeId(KRITA_TOOL_ACTIVATION_ID);
}
~KisToolSelectPolygonalFactory() override {}
KoToolBase * createTool(KoCanvasBase *canvas) override {
return new KisToolSelectPolygonal(canvas);
}
+
+ QList<QAction *> createActionsImpl()
+ {
+ KisActionRegistry *actionRegistry = KisActionRegistry::instance();
+ QList<QAction *> actions = KisSelectionToolFactoryBase::createActionsImpl();
+
+ actions << actionRegistry->makeQAction("undo_polygon_selection");
+ actions << actionRegistry->makeQAction("selection_tool_mode_add");
+
+ return actions;
+ }
+
+
};
#endif //__selecttoolpolygonal_h__
diff --git a/plugins/tools/selectiontools/kis_tool_select_rectangular.h b/plugins/tools/selectiontools/kis_tool_select_rectangular.h
index 7effa6e42e..3ce49b29e8 100644
--- a/plugins/tools/selectiontools/kis_tool_select_rectangular.h
+++ b/plugins/tools/selectiontools/kis_tool_select_rectangular.h
@@ -1,86 +1,86 @@
/*
* kis_tool_select_rectangular.h - part of Krita
*
* Copyright (c) 1999 Michael Koch <koch@kde.org>
* 2002 Patrick Julien <freak@codepimps.org>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_TOOL_SELECT_RECTANGULAR_H_
#define KIS_TOOL_SELECT_RECTANGULAR_H_
-#include "KoToolFactoryBase.h"
+#include "KisSelectionToolFactoryBase.h"
#include "kis_tool_rectangle_base.h"
#include <kis_tool_select_base.h>
#include "kis_selection_tool_config_widget_helper.h"
#include <kis_icon.h>
#include <QKeySequence>
class __KisToolSelectRectangularLocal : public KisToolRectangleBase
{
Q_OBJECT
public:
__KisToolSelectRectangularLocal(KoCanvasBase * canvas);
bool hasUserInteractionRunning() const;
protected:
virtual SelectionMode selectionMode() const = 0;
virtual SelectionAction selectionAction() const = 0;
private:
void finishRect(const QRectF& rect, qreal roundCornersX, qreal roundCornersY) override;
};
class KisToolSelectRectangular : public KisToolSelectBase<__KisToolSelectRectangularLocal>
{
Q_OBJECT
public:
KisToolSelectRectangular(KoCanvasBase* canvas);
void resetCursorStyle();
};
-class KisToolSelectRectangularFactory : public KoToolFactoryBase
+class KisToolSelectRectangularFactory : public KisSelectionToolFactoryBase
{
public:
KisToolSelectRectangularFactory()
- : KoToolFactoryBase("KisToolSelectRectangular")
+ : KisSelectionToolFactoryBase("KisToolSelectRectangular")
{
setToolTip(i18n("Rectangular Selection Tool"));
setSection(TOOL_TYPE_SELECTION);
setActivationShapeId(KRITA_TOOL_ACTIVATION_ID);
setIconName(koIconNameCStr("tool_rect_selection"));
setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R));
setPriority(0);
}
~KisToolSelectRectangularFactory() override {}
KoToolBase * createTool(KoCanvasBase *canvas) override {
return new KisToolSelectRectangular(canvas);
}
};
#endif // KIS_TOOL_SELECT_RECTANGULAR_H_
diff --git a/plugins/tools/selectiontools/kis_tool_select_similar.h b/plugins/tools/selectiontools/kis_tool_select_similar.h
index bbe6a8d313..6ce41645fb 100644
--- a/plugins/tools/selectiontools/kis_tool_select_similar.h
+++ b/plugins/tools/selectiontools/kis_tool_select_similar.h
@@ -1,75 +1,75 @@
/*
* Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org)
* Copyright (c) 2015 Michael Abrahams <miabraha@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_TOOL_SELECT_SIMILAR_H_
#define KIS_TOOL_SELECT_SIMILAR_H_
-#include <KoToolFactoryBase.h>
+#include <KisSelectionToolFactoryBase.h>
#include <kis_icon.h>
#include <kconfig.h>
#include "kis_tool_select_base.h"
#include <kconfiggroup.h>
/*
* Tool to select colors by pointing at a color on the image.
*/
class KisToolSelectSimilar: public KisToolSelect
{
Q_OBJECT
public:
KisToolSelectSimilar(KoCanvasBase * canvas);
void beginPrimaryAction(KoPointerEvent *event) override;
void paint(QPainter&, const KoViewConverter &) override {}
QWidget* createOptionWidget() override;
void resetCursorStyle();
public Q_SLOTS:
void activate(ToolActivation toolActivation, const QSet<KoShape*> &shapes) override;
void slotSetFuzziness(int);
protected:
using KisToolSelectBase::m_widgetHelper;
private:
int m_fuzziness;
KConfigGroup m_configGroup;
};
-class KisToolSelectSimilarFactory : public KoToolFactoryBase
+class KisToolSelectSimilarFactory : public KisSelectionToolFactoryBase
{
public:
KisToolSelectSimilarFactory()
- : KoToolFactoryBase("KisToolSelectSimilar")
+ : KisSelectionToolFactoryBase("KisToolSelectSimilar")
{
setToolTip(i18n("Similar Color Selection Tool"));
setSection(TOOL_TYPE_SELECTION);
setActivationShapeId(KRITA_TOOL_ACTIVATION_ID);
setIconName(koIconNameCStr("tool_similar_selection"));
setPriority(5);
}
~KisToolSelectSimilarFactory() override {}
KoToolBase * createTool(KoCanvasBase *canvas) override {
return new KisToolSelectSimilar(canvas);
}
};
#endif // KIS_TOOL_SELECT_SIMILAR_H_
diff --git a/plugins/tools/svgtexttool/SvgRichTextCtrl.cpp b/plugins/tools/svgtexttool/SvgRichTextCtrl.cpp
index c5c3e4c455..8a601817a4 100644
--- a/plugins/tools/svgtexttool/SvgRichTextCtrl.cpp
+++ b/plugins/tools/svgtexttool/SvgRichTextCtrl.cpp
@@ -1,17 +1,37 @@
+/* This file is part of the KDE project
+ *
+ * Copyright 2018 Mehmet Salih Çalışkan <msalihcaliskan@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
#include "SvgRichTextCtrl.h"
#include <QMimeData>
SvgRichTextCtrl::SvgRichTextCtrl(QWidget* parent /*= nullptr*/)
: QTextEdit(parent)
{
}
void SvgRichTextCtrl::insertFromMimeData(const QMimeData *source)
{
if (!source->hasHtml() && source->hasText()) {
QTextCursor cursor = textCursor();
cursor.insertText(source->text());
} else {
QTextEdit::insertFromMimeData(source);
}
}
diff --git a/plugins/tools/svgtexttool/SvgRichTextCtrl.h b/plugins/tools/svgtexttool/SvgRichTextCtrl.h
index 889c4d89ea..9409120d9b 100644
--- a/plugins/tools/svgtexttool/SvgRichTextCtrl.h
+++ b/plugins/tools/svgtexttool/SvgRichTextCtrl.h
@@ -1,15 +1,36 @@
+/* This file is part of the KDE project
+ *
+ * Copyright 2018 Mehmet Salih Çalışkan <msalihcaliskan@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+
#ifndef SVGRICHTEXTCTRL_H
#define SVGRICHTEXTCTRL_H
#include <QTextEdit>
class SvgRichTextCtrl : public QTextEdit
{
public:
SvgRichTextCtrl(QWidget* parent = nullptr);
protected:
void insertFromMimeData(const QMimeData* source) override;
};
#endif // SVGRICHTEXTCTRL_H
diff --git a/plugins/tools/svgtexttool/SvgTextEditor.cpp b/plugins/tools/svgtexttool/SvgTextEditor.cpp
index 9c570a4a4a..6066bd11ee 100644
--- a/plugins/tools/svgtexttool/SvgTextEditor.cpp
+++ b/plugins/tools/svgtexttool/SvgTextEditor.cpp
@@ -1,1115 +1,1113 @@
/* This file is part of the KDE project
*
* Copyright 2017 Boudewijn Rempt <boud@valdyas.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "SvgTextEditor.h"
#include <QAction>
#include <QApplication>
#include <QBuffer>
#include <QComboBox>
#include <QDialogButtonBox>
#include <QDoubleSpinBox>
#include <QFontComboBox>
#include <QFontDatabase>
#include <QFormLayout>
#include <QLineEdit>
#include <QListView>
#include <QMenu>
#include <QMessageBox>
#include <QPainter>
#include <QPalette>
#include <QPushButton>
#include <QStandardItem>
#include <QStandardItemModel>
#include <QSvgGenerator>
#include <QTabWidget>
#include <QTextEdit>
#include <QUrl>
#include <QVBoxLayout>
#include <QWidgetAction>
#include <kcharselect.h>
#include <klocalizedstring.h>
#include <ksharedconfig.h>
#include <kconfiggroup.h>
#include <kactioncollection.h>
#include <kxmlguifactory.h>
#include <ktoolbar.h>
#include <ktoggleaction.h>
#include <kguiitem.h>
#include <KoDialog.h>
#include <KoResourcePaths.h>
#include <KoSvgTextShape.h>
#include <KoSvgTextShapeMarkupConverter.h>
#include <KoColorSpaceRegistry.h>
#include <KoColorPopupAction.h>
#include <svg/SvgUtil.h>
#include <KisScreenColorPicker.h>
#include <kis_icon.h>
#include <kis_config.h>
#include <kis_file_name_requester.h>
#include <kis_action_registry.h>
#include "kis_font_family_combo_box.h"
#include "FontSizeAction.h"
#include "kis_signals_blocker.h"
SvgTextEditor::SvgTextEditor(QWidget *parent, Qt::WindowFlags flags)
: KXmlGuiWindow(parent, flags)
, m_page(new QWidget(this))
#ifndef Q_OS_WIN
, m_charSelectDialog(new KoDialog(this))
#endif
{
m_textEditorWidget.setupUi(m_page);
setCentralWidget(m_page);
m_textEditorWidget.chkVertical->setVisible(false);
#ifndef Q_OS_WIN
KCharSelect *charSelector = new KCharSelect(m_charSelectDialog, 0, KCharSelect::AllGuiElements);
m_charSelectDialog->setMainWidget(charSelector);
connect(charSelector, SIGNAL(currentCharChanged(QChar)), SLOT(insertCharacter(QChar)));
m_charSelectDialog->hide();
m_charSelectDialog->setButtons(KoDialog::Close);
#endif
connect(m_textEditorWidget.buttons, SIGNAL(accepted()), this, SLOT(save()));
connect(m_textEditorWidget.buttons, SIGNAL(rejected()), this, SLOT(slotCloseEditor()));
connect(m_textEditorWidget.buttons, SIGNAL(clicked(QAbstractButton*)), this, SLOT(dialogButtonClicked(QAbstractButton*)));
KConfigGroup cg(KSharedConfig::openConfig(), "SvgTextTool");
actionCollection()->setConfigGroup("SvgTextTool");
actionCollection()->setComponentName("svgtexttool");
actionCollection()->setComponentDisplayName(i18n("Text Tool"));
QByteArray state;
if (cg.hasKey("WindowState")) {
state = cg.readEntry("State", state);
state = QByteArray::fromBase64(state);
// One day will need to load the version number, but for now, assume 0
restoreState(state);
}
setAcceptDrops(true);
//setStandardToolBarMenuEnabled(true);
#ifdef Q_OS_OSX
setUnifiedTitleAndToolBarOnMac(true);
#endif
setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North);
m_syntaxHighlighter = new BasicXMLSyntaxHighlighter(m_textEditorWidget.svgTextEdit);
m_textEditorWidget.svgTextEdit->setFont(QFontDatabase().systemFont(QFontDatabase::FixedFont));
createActions();
// If we have customized the toolbars, load that first
setLocalXMLFile(KoResourcePaths::locateLocal("data", "svgtexttool.xmlgui"));
setXMLFile(":/kxmlgui5/svgtexttool.xmlgui");
guiFactory()->addClient(this);
// Create and plug toolbar list for Settings menu
QList<QAction *> toolbarList;
Q_FOREACH (QWidget* it, guiFactory()->containers("ToolBar")) {
KToolBar * toolBar = ::qobject_cast<KToolBar *>(it);
if (toolBar) {
toolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
KToggleAction* act = new KToggleAction(i18n("Show %1 Toolbar", toolBar->windowTitle()), this);
actionCollection()->addAction(toolBar->objectName().toUtf8(), act);
act->setCheckedState(KGuiItem(i18n("Hide %1 Toolbar", toolBar->windowTitle())));
connect(act, SIGNAL(toggled(bool)), this, SLOT(slotToolbarToggled(bool)));
act->setChecked(!toolBar->isHidden());
toolbarList.append(act);
}
}
plugActionList("toolbarlist", toolbarList);
connect(m_textEditorWidget.textTab, SIGNAL(currentChanged(int)), this, SLOT(switchTextEditorTab()));
switchTextEditorTab();
m_textEditorWidget.richTextEdit->document()->setDefaultStyleSheet("p {margin:0px;}");
applySettings();
}
SvgTextEditor::~SvgTextEditor()
{
KConfigGroup g(KSharedConfig::openConfig(), "SvgTextTool");
QByteArray ba = saveState();
g.writeEntry("windowState", ba.toBase64());
}
void SvgTextEditor::setShape(KoSvgTextShape *shape)
{
m_shape = shape;
if (m_shape) {
KoSvgTextShapeMarkupConverter converter(m_shape);
QString svg;
QString styles;
QTextDocument *doc = m_textEditorWidget.richTextEdit->document();
if (converter.convertToSvg(&svg, &styles)) {
m_textEditorWidget.svgTextEdit->setPlainText(svg);
m_textEditorWidget.svgStylesEdit->setPlainText(styles);
m_textEditorWidget.svgTextEdit->document()->setModified(false);
if (shape->isRichTextPreferred() &&
converter.convertSvgToDocument(svg, doc)) {
m_textEditorWidget.richTextEdit->setDocument(doc);
KisSignalsBlocker b(m_textEditorWidget.textTab);
m_textEditorWidget.textTab->setCurrentIndex(Richtext);
switchTextEditorTab(false);
} else {
KisSignalsBlocker b(m_textEditorWidget.textTab);
m_textEditorWidget.textTab->setCurrentIndex(SvgSource);
switchTextEditorTab(false);
}
}
else {
QMessageBox::warning(this, i18n("Conversion failed"), "Could not get svg text from the shape:\n" + converter.errors().join('\n') + "\n" + converter.warnings().join('\n'));
}
}
}
void SvgTextEditor::save()
{
if (m_shape) {
if (m_textEditorWidget.textTab->currentIndex() == Richtext) {
QString svg;
QString styles = m_textEditorWidget.svgStylesEdit->document()->toPlainText();
KoSvgTextShapeMarkupConverter converter(m_shape);
if (!converter.convertDocumentToSvg(m_textEditorWidget.richTextEdit->document(), &svg)) {
qWarning()<<"new converter doesn't work!";
}
m_textEditorWidget.richTextEdit->document()->setModified(false);
emit textUpdated(m_shape, svg, styles, true);
}
else {
emit textUpdated(m_shape, m_textEditorWidget.svgTextEdit->document()->toPlainText(), m_textEditorWidget.svgStylesEdit->document()->toPlainText(), false);
m_textEditorWidget.svgTextEdit->document()->setModified(false);
}
}
}
void SvgTextEditor::switchTextEditorTab(bool convertData)
{
KoSvgTextShape shape;
KoSvgTextShapeMarkupConverter converter(&shape);
if (m_currentEditor) {
disconnect(m_currentEditor->document(), SIGNAL(modificationChanged(bool)), this, SLOT(setModified(bool)));
}
if (m_textEditorWidget.textTab->currentIndex() == Richtext) {
//first, make buttons checkable
enableRichTextActions(true);
enableSvgTextActions(false);
//then connect the cursor change to the checkformat();
connect(m_textEditorWidget.richTextEdit, SIGNAL(cursorPositionChanged()), this, SLOT(checkFormat()));
if (m_shape && convertData) {
QTextDocument *doc = m_textEditorWidget.richTextEdit->document();
if (!converter.convertSvgToDocument(m_textEditorWidget.svgTextEdit->document()->toPlainText(), doc)) {
qWarning()<<"new converter svgToDoc doesn't work!";
}
m_textEditorWidget.richTextEdit->setDocument(doc);
}
m_currentEditor = m_textEditorWidget.richTextEdit;
}
else {
//first, make buttons uncheckable
enableRichTextActions(false);
enableSvgTextActions(true);
disconnect(m_textEditorWidget.richTextEdit, SIGNAL(cursorPositionChanged()), this, SLOT(checkFormat()));
// Convert the rich text to svg and styles strings
if (m_shape && convertData) {
QString svg;
QString styles;
if (!converter.convertDocumentToSvg(m_textEditorWidget.richTextEdit->document(), &svg)) {
qWarning()<<"new converter docToSVG doesn't work!";
}
m_textEditorWidget.svgTextEdit->setPlainText(svg);
}
m_currentEditor = m_textEditorWidget.svgTextEdit;
}
connect(m_currentEditor->document(), SIGNAL(modificationChanged(bool)), SLOT(setModified(bool)));
}
void SvgTextEditor::checkFormat()
{
QTextCharFormat format = m_textEditorWidget.richTextEdit->textCursor().charFormat();
QTextBlockFormat blockFormat = m_textEditorWidget.richTextEdit->textCursor().blockFormat();
// checkboxes do not emit signals on manual switching, so we
// can avoid blocking them
if (format.fontWeight() > QFont::Normal) {
actionCollection()->action("svg_weight_bold")->setChecked(true);
} else {
actionCollection()->action("svg_weight_bold")->setChecked(false);
}
actionCollection()->action("svg_format_italic")->setChecked(format.fontItalic());
actionCollection()->action("svg_format_underline")->setChecked(format.fontUnderline());
actionCollection()->action("svg_format_strike_through")->setChecked(format.fontStrikeOut());
{
FontSizeAction *fontSizeAction = qobject_cast<FontSizeAction*>(actionCollection()->action("svg_font_size"));
KisSignalsBlocker b(fontSizeAction);
fontSizeAction->setFontSize(format.font().pointSize());
}
{
KoColor fg(format.foreground().color(), KoColorSpaceRegistry::instance()->rgb8());
KoColorPopupAction *fgColorPopup = qobject_cast<KoColorPopupAction*>(actionCollection()->action("svg_format_textcolor"));
KisSignalsBlocker b(fgColorPopup);
fgColorPopup->setCurrentColor(fg);
}
{
KoColor bg(format.foreground().color(), KoColorSpaceRegistry::instance()->rgb8());
KoColorPopupAction *bgColorPopup = qobject_cast<KoColorPopupAction*>(actionCollection()->action("svg_background_color"));
KisSignalsBlocker b(bgColorPopup);
bgColorPopup->setCurrentColor(bg);
}
{
KisFontComboBoxes* fontComboBox = qobject_cast<KisFontComboBoxes*>(qobject_cast<QWidgetAction*>(actionCollection()->action("svg_font"))->defaultWidget());
KisSignalsBlocker b(fontComboBox);
fontComboBox->setCurrentFont(format.font());
}
{
QDoubleSpinBox *spnLineHeight = qobject_cast<QDoubleSpinBox*>(qobject_cast<QWidgetAction*>(actionCollection()->action("svg_line_height"))->defaultWidget());
KisSignalsBlocker b(spnLineHeight);
if (blockFormat.lineHeightType() == QTextBlockFormat::SingleHeight) {
spnLineHeight->setValue(100.0);
} else if(blockFormat.lineHeightType() == QTextBlockFormat::ProportionalHeight) {
spnLineHeight->setValue(double(blockFormat.lineHeight()));
}
}
}
void SvgTextEditor::undo()
{
m_currentEditor->undo();
}
void SvgTextEditor::redo()
{
m_currentEditor->redo();
}
void SvgTextEditor::cut()
{
m_currentEditor->cut();
}
void SvgTextEditor::copy()
{
m_currentEditor->copy();
}
void SvgTextEditor::paste()
{
m_currentEditor->paste();
}
void SvgTextEditor::selectAll()
{
m_currentEditor->selectAll();
}
void SvgTextEditor::deselect()
{
QTextCursor cursor(m_currentEditor->textCursor());
cursor.clearSelection();
m_currentEditor->setTextCursor(cursor);
}
void SvgTextEditor::find()
{
QDialog *findDialog = new QDialog(this);
findDialog->setWindowTitle(i18n("Find Text"));
QFormLayout *layout = new QFormLayout();
findDialog->setLayout(layout);
QLineEdit *lnSearchKey = new QLineEdit();
layout->addRow(i18n("Find:"), lnSearchKey);
QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
findDialog->layout()->addWidget(buttons);
connect(buttons, SIGNAL(accepted()), findDialog, SLOT(accept()));
connect(buttons, SIGNAL(rejected()), findDialog, SLOT(reject()));
if (findDialog->exec()==QDialog::Accepted) {
m_searchKey = lnSearchKey->text();
m_currentEditor->find(m_searchKey);
}
}
void SvgTextEditor::findNext()
{
if (!m_currentEditor->find(m_searchKey)) {
QTextCursor cursor(m_currentEditor->textCursor());
cursor.movePosition(QTextCursor::Start);
m_currentEditor->setTextCursor(cursor);
m_currentEditor->find(m_searchKey);
}
}
void SvgTextEditor::findPrev()
{
if (!m_currentEditor->find(m_searchKey,QTextDocument::FindBackward)) {
QTextCursor cursor(m_currentEditor->textCursor());
cursor.movePosition(QTextCursor::End);
m_currentEditor->setTextCursor(cursor);
m_currentEditor->find(m_searchKey,QTextDocument::FindBackward);
}
}
void SvgTextEditor::replace()
{
QDialog *findDialog = new QDialog(this);
findDialog->setWindowTitle(i18n("Find and Replace all"));
QFormLayout *layout = new QFormLayout();
findDialog->setLayout(layout);
QLineEdit *lnSearchKey = new QLineEdit();
QLineEdit *lnReplaceKey = new QLineEdit();
layout->addRow(i18n("Find:"), lnSearchKey);
QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
layout->addRow(i18n("Replace:"), lnReplaceKey);
findDialog->layout()->addWidget(buttons);
connect(buttons, SIGNAL(accepted()), findDialog, SLOT(accept()));
connect(buttons, SIGNAL(rejected()), findDialog, SLOT(reject()));
if (findDialog->exec()==QDialog::Accepted) {
QString search = lnSearchKey->text();
QString replace = lnReplaceKey->text();
QTextCursor cursor(m_currentEditor->textCursor());
cursor.movePosition(QTextCursor::Start);
m_currentEditor->setTextCursor(cursor);
while(m_currentEditor->find(search)) {
m_currentEditor->textCursor().removeSelectedText();
m_currentEditor->textCursor().insertText(replace);
}
}
}
void SvgTextEditor::zoomOut()
{
m_currentEditor->zoomOut();
}
void SvgTextEditor::zoomIn()
{
m_currentEditor->zoomIn();
}
#ifndef Q_OS_WIN
void SvgTextEditor::showInsertSpecialCharacterDialog()
{
m_charSelectDialog->setVisible(!m_charSelectDialog->isVisible());
}
void SvgTextEditor::insertCharacter(const QChar &c)
{
m_currentEditor->textCursor().insertText(QString(c));
}
#endif
void SvgTextEditor::setTextBold(QFont::Weight weight)
{
if (m_textEditorWidget.textTab->currentIndex() == Richtext) {
QTextCharFormat format;
if (m_textEditorWidget.richTextEdit->textCursor().charFormat().fontWeight() > QFont::Normal && weight==QFont::Bold) {
format.setFontWeight(QFont::Normal);
} else {
format.setFontWeight(weight);
}
m_textEditorWidget.richTextEdit->mergeCurrentCharFormat(format);
} else {
QTextCursor cursor = m_textEditorWidget.svgTextEdit->textCursor();
if (cursor.hasSelection()) {
QString selectionModified = "<tspan style=\"font-weight:700;\">" + cursor.selectedText() + "</tspan>";
cursor.removeSelectedText();
cursor.insertText(selectionModified);
}
}
}
void SvgTextEditor::setTextWeightLight()
{
if (m_textEditorWidget.richTextEdit->textCursor().charFormat().fontWeight() < QFont::Normal) {
setTextBold(QFont::Normal);
} else {
setTextBold(QFont::Light);
}
}
void SvgTextEditor::setTextWeightNormal()
{
setTextBold(QFont::Normal);
}
void SvgTextEditor::setTextWeightDemi()
{
if (m_textEditorWidget.richTextEdit->textCursor().charFormat().fontWeight() != QFont::Normal) {
setTextBold(QFont::Normal);
} else {
setTextBold(QFont::DemiBold);
}
}
void SvgTextEditor::setTextWeightBlack()
{
if (m_textEditorWidget.richTextEdit->textCursor().charFormat().fontWeight()>QFont::Normal) {
setTextBold(QFont::Normal);
} else {
setTextBold(QFont::Black);
}
}
void SvgTextEditor::setTextItalic(QFont::Style style)
{
QTextCursor cursor = m_textEditorWidget.svgTextEdit->textCursor();
QString fontStyle = "inherit";
if (style == QFont::StyleItalic) {
fontStyle = "italic";
} else if(style == QFont::StyleOblique) {
fontStyle = "oblique";
}
if (m_textEditorWidget.textTab->currentIndex() == Richtext) {
QTextCharFormat format;
format.setFontItalic(!m_textEditorWidget.richTextEdit->textCursor().charFormat().fontItalic());
m_textEditorWidget.richTextEdit->mergeCurrentCharFormat(format);
}
else {
if (cursor.hasSelection()) {
QString selectionModified = "<tspan style=\"font-style:"+fontStyle+";\">" + cursor.selectedText() + "</tspan>";
cursor.removeSelectedText();
cursor.insertText(selectionModified);
}
}
}
void SvgTextEditor::setTextDecoration(KoSvgText::TextDecoration decor)
{
QTextCursor cursor = m_textEditorWidget.svgTextEdit->textCursor();
QTextCharFormat currentFormat = m_textEditorWidget.richTextEdit->textCursor().charFormat();
QTextCharFormat format;
QString textDecoration = "inherit";
if (decor == KoSvgText::DecorationUnderline) {
textDecoration = "underline";
if (currentFormat.fontUnderline()) {
format.setFontUnderline(false);
}
else {
format.setFontUnderline(true);
}
format.setFontOverline(false);
format.setFontStrikeOut(false);
}
else if (decor == KoSvgText::DecorationLineThrough) {
textDecoration = "line-through";
format.setFontUnderline(false);
format.setFontOverline(false);
if (currentFormat.fontStrikeOut()) {
format.setFontStrikeOut(false);
}
else {
format.setFontStrikeOut(true);
}
}
else if (decor == KoSvgText::DecorationOverline) {
textDecoration = "overline";
format.setFontUnderline(false);
if (currentFormat.fontOverline()) {
format.setFontOverline(false);
}
else {
format.setFontOverline(true);
}
format.setFontStrikeOut(false);
}
if (m_textEditorWidget.textTab->currentIndex() == Richtext) {
m_textEditorWidget.richTextEdit->mergeCurrentCharFormat(format);
}
else {
if (cursor.hasSelection()) {
QString selectionModified = "<tspan style=\"text-decoration:" + textDecoration + ";\">" + cursor.selectedText() + "</tspan>";
cursor.removeSelectedText();
cursor.insertText(selectionModified);
}
}
}
void SvgTextEditor::setTextUnderline()
{
setTextDecoration(KoSvgText::DecorationUnderline);
}
void SvgTextEditor::setTextOverline()
{
setTextDecoration(KoSvgText::DecorationOverline);
}
void SvgTextEditor::setTextStrikethrough()
{
setTextDecoration(KoSvgText::DecorationLineThrough);
}
void SvgTextEditor::setTextSubscript()
{
QTextCharFormat format = m_textEditorWidget.richTextEdit->textCursor().charFormat();
if (format.verticalAlignment()==QTextCharFormat::AlignSubScript) {
format.setVerticalAlignment(QTextCharFormat::AlignNormal);
} else {
format.setVerticalAlignment(QTextCharFormat::AlignSubScript);
}
m_textEditorWidget.richTextEdit->mergeCurrentCharFormat(format);
}
void SvgTextEditor::setTextSuperScript()
{
QTextCharFormat format = m_textEditorWidget.richTextEdit->textCursor().charFormat();
if (format.verticalAlignment()==QTextCharFormat::AlignSuperScript) {
format.setVerticalAlignment(QTextCharFormat::AlignNormal);
} else {
format.setVerticalAlignment(QTextCharFormat::AlignSuperScript);
}
m_textEditorWidget.richTextEdit->mergeCurrentCharFormat(format);
}
void SvgTextEditor::increaseTextSize()
{
QTextCharFormat format;
int pointSize = m_textEditorWidget.richTextEdit->textCursor().charFormat().font().pointSize();
if (pointSize<0) {
pointSize = m_textEditorWidget.richTextEdit->textCursor().charFormat().font().pixelSize();
}
format.setFontPointSize(pointSize+1.0);
m_textEditorWidget.richTextEdit->mergeCurrentCharFormat(format);
}
void SvgTextEditor::decreaseTextSize()
{
QTextCharFormat format;
int pointSize = m_textEditorWidget.richTextEdit->textCursor().charFormat().font().pointSize();
if (pointSize<1) {
pointSize = m_textEditorWidget.richTextEdit->textCursor().charFormat().font().pixelSize();
}
format.setFontPointSize(qMax(pointSize-1.0, 1.0));
m_textEditorWidget.richTextEdit->mergeCurrentCharFormat(format);
}
void SvgTextEditor::setLineHeight(double lineHeightPercentage)
{
QTextBlockFormat format = m_textEditorWidget.richTextEdit->textCursor().blockFormat();
format.setLineHeight(lineHeightPercentage, QTextBlockFormat::ProportionalHeight);
m_textEditorWidget.richTextEdit->textCursor().mergeBlockFormat(format);
}
void SvgTextEditor::alignLeft()
{
QTextBlockFormat format = m_textEditorWidget.richTextEdit->textCursor().blockFormat();
format.setAlignment(Qt::AlignLeft);
m_textEditorWidget.richTextEdit->textCursor().mergeBlockFormat(format);
}
void SvgTextEditor::alignRight()
{
QTextBlockFormat format = m_textEditorWidget.richTextEdit->textCursor().blockFormat();
format.setAlignment(Qt::AlignRight);
m_textEditorWidget.richTextEdit->textCursor().mergeBlockFormat(format);
}
void SvgTextEditor::alignCenter()
{
QTextBlockFormat format = m_textEditorWidget.richTextEdit->textCursor().blockFormat();
format.setAlignment(Qt::AlignCenter);
m_textEditorWidget.richTextEdit->textCursor().mergeBlockFormat(format);
}
void SvgTextEditor::alignJustified()
{
QTextBlockFormat format = m_textEditorWidget.richTextEdit->textCursor().blockFormat();
format.setAlignment(Qt::AlignJustify);
m_textEditorWidget.richTextEdit->textCursor().mergeBlockFormat(format);
}
void SvgTextEditor::setSettings()
{
KoDialog settingsDialog(this);
Ui_WdgSvgTextSettings textSettings;
QWidget *settingsPage = new QWidget(&settingsDialog, 0);
settingsDialog.setMainWidget(settingsPage);
textSettings.setupUi(settingsPage);
// get the settings and initialize the dialog
KConfigGroup cfg(KSharedConfig::openConfig(), "SvgTextTool");
QStringList selectedWritingSystems = cfg.readEntry("selectedWritingSystems", "").split(",");
QList<QFontDatabase::WritingSystem> scripts = QFontDatabase().writingSystems();
QStandardItemModel *writingSystemsModel = new QStandardItemModel(&settingsDialog);
for (int s = 0; s < scripts.size(); s ++) {
QString writingSystem = QFontDatabase().writingSystemName(scripts.at(s));
QStandardItem *script = new QStandardItem(writingSystem);
script->setCheckable(true);
script->setCheckState(selectedWritingSystems.contains(QString::number(scripts.at(s))) ? Qt::Checked : Qt::Unchecked);
script->setData((int)scripts.at(s));
writingSystemsModel->appendRow(script);
}
textSettings.lwScripts->setModel(writingSystemsModel);
EditorMode mode = (EditorMode)cfg.readEntry("EditorMode", (int)Both);
switch(mode) {
case(RichText):
textSettings.radioRichText->setChecked(true);
break;
case(SvgSource):
textSettings.radioSvgSource->setChecked(true);
break;
case(Both):
textSettings.radioBoth->setChecked(true);
}
QColor background = cfg.readEntry("colorEditorBackground", qApp->palette().background().color());
textSettings.colorEditorBackground->setColor(background);
textSettings.colorEditorForeground->setColor(cfg.readEntry("colorEditorForeground", qApp->palette().text().color()));
textSettings.colorKeyword->setColor(cfg.readEntry("colorKeyword", QColor(background.value() < 100 ? Qt::cyan : Qt::blue)));
textSettings.chkBoldKeyword->setChecked(cfg.readEntry("BoldKeyword", true));
textSettings.chkItalicKeyword->setChecked(cfg.readEntry("ItalicKeyword", false));
textSettings.colorElement->setColor(cfg.readEntry("colorElement", QColor(background.value() < 100 ? Qt::magenta : Qt::darkMagenta)));
textSettings.chkBoldElement->setChecked(cfg.readEntry("BoldElement", true));
textSettings.chkItalicElement->setChecked(cfg.readEntry("ItalicElement", false));
textSettings.colorAttribute->setColor(cfg.readEntry("colorAttribute", QColor(background.value() < 100 ? Qt::green : Qt::darkGreen)));
textSettings.chkBoldAttribute->setChecked(cfg.readEntry("BoldAttribute", true));
textSettings.chkItalicAttribute->setChecked(cfg.readEntry("ItalicAttribute", true));
textSettings.colorValue->setColor(cfg.readEntry("colorValue", QColor(background.value() < 100 ? Qt::red: Qt::darkRed)));
textSettings.chkBoldValue->setChecked(cfg.readEntry("BoldValue", true));
textSettings.chkItalicValue->setChecked(cfg.readEntry("ItalicValue", false));
textSettings.colorComment->setColor(cfg.readEntry("colorComment", QColor(background.value() < 100 ? Qt::lightGray : Qt::gray)));
textSettings.chkBoldComment->setChecked(cfg.readEntry("BoldComment", false));
textSettings.chkItalicComment->setChecked(cfg.readEntry("ItalicComment", false));
settingsDialog.setButtons(KoDialog::Ok | KoDialog::Cancel);
if (settingsDialog.exec() == QDialog::Accepted) {
// save and set the settings
QStringList writingSystems;
for (int i = 0; i < writingSystemsModel->rowCount(); i++) {
QStandardItem *item = writingSystemsModel->item(i);
if (item->checkState() == Qt::Checked) {
writingSystems.append(QString::number(item->data().toInt()));
}
}
- if (!writingSystems.isEmpty()) {
- cfg.writeEntry("selectedWritingSystems", writingSystems.join(','));
- }
+ cfg.writeEntry("selectedWritingSystems", writingSystems.join(','));
if (textSettings.radioRichText->isChecked()) {
cfg.writeEntry("EditorMode", (int)Richtext);
}
else if (textSettings.radioSvgSource->isChecked()) {
cfg.writeEntry("EditorMode", (int)SvgSource);
}
else if (textSettings.radioBoth->isChecked()) {
cfg.writeEntry("EditorMode", (int)Both);
}
cfg.writeEntry("colorEditorBackground", textSettings.colorEditorBackground->color());
cfg.writeEntry("colorEditorForeground", textSettings.colorEditorForeground->color());
cfg.writeEntry("colorKeyword", textSettings.colorKeyword->color());
cfg.writeEntry("BoldKeyword", textSettings.chkBoldKeyword->isChecked());
cfg.writeEntry("ItalicKeyWord", textSettings.chkItalicKeyword->isChecked());
cfg.writeEntry("colorElement", textSettings.colorElement->color());
cfg.writeEntry("BoldElement", textSettings.chkBoldElement->isChecked());
cfg.writeEntry("ItalicElement", textSettings.chkItalicElement->isChecked());
cfg.writeEntry("colorAttribute", textSettings.colorAttribute->color());
cfg.writeEntry("BoldAttribute", textSettings.chkBoldAttribute->isChecked());
cfg.writeEntry("ItalicAttribute", textSettings.chkItalicAttribute->isChecked());
cfg.writeEntry("colorValue", textSettings.colorValue->color());
cfg.writeEntry("BoldValue", textSettings.chkBoldValue->isChecked());
cfg.writeEntry("ItalicValue", textSettings.chkItalicValue->isChecked());
cfg.writeEntry("colorComment", textSettings.colorComment->color());
cfg.writeEntry("BoldComment", textSettings.chkBoldComment->isChecked());
cfg.writeEntry("ItalicComment", textSettings.chkItalicComment->isChecked());
applySettings();
}
}
void SvgTextEditor::slotToolbarToggled(bool)
{
}
void SvgTextEditor::setFontColor(const KoColor &c)
{
QColor color = c.toQColor();
if (m_textEditorWidget.textTab->currentIndex() == Richtext) {
QTextCharFormat format;
format.setForeground(QBrush(color));
m_textEditorWidget.richTextEdit->mergeCurrentCharFormat(format);
}
else {
QTextCursor cursor = m_textEditorWidget.svgTextEdit->textCursor();
if (cursor.hasSelection()) {
QString selectionModified = "<tspan fill=\""+color.name()+"\">" + cursor.selectedText() + "</tspan>";
cursor.removeSelectedText();
cursor.insertText(selectionModified);
}
}
}
void SvgTextEditor::setBackgroundColor(const KoColor &c)
{
QColor color = c.toQColor();
QTextCursor cursor = m_textEditorWidget.svgTextEdit->textCursor();
if (cursor.hasSelection()) {
QString selectionModified = "<tspan stroke=\""+color.name()+"\">" + cursor.selectedText() + "</tspan>";
cursor.removeSelectedText();
cursor.insertText(selectionModified);
}
}
void SvgTextEditor::setModified(bool modified)
{
if (modified) {
m_textEditorWidget.buttons->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Discard);
}
else {
m_textEditorWidget.buttons->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Close);
}
}
void SvgTextEditor::dialogButtonClicked(QAbstractButton *button)
{
if (m_textEditorWidget.buttons->standardButton(button) == QDialogButtonBox::Discard) {
if (QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("You have modified the text. Discard changes?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
close();
}
}
}
void SvgTextEditor::setFont(const QString &fontName)
{
QFont font;
font.fromString(fontName);
QTextCharFormat curFormat = m_textEditorWidget.richTextEdit->textCursor().charFormat();
font.setPointSize(curFormat.font().pointSize());
QTextCharFormat format;
//This disables the style being set from the font-comboboxes too, so we need to rethink how we use that.
format.setFontFamily(font.family());
if (m_textEditorWidget.textTab->currentIndex() == Richtext) {
m_textEditorWidget.richTextEdit->mergeCurrentCharFormat(format);
} else {
QTextCursor cursor = m_textEditorWidget.svgTextEdit->textCursor();
if (cursor.hasSelection()) {
QString selectionModified = "<tspan style=\"font-family:"+font.family()+" "+font.styleName()+";\">" + cursor.selectedText() + "</tspan>";
cursor.removeSelectedText();
cursor.insertText(selectionModified);
}
}
}
void SvgTextEditor::setFontSize(qreal fontSize)
{
if (m_textEditorWidget.textTab->currentIndex() == Richtext) {
QTextCharFormat format;
format.setFontPointSize(fontSize);
m_textEditorWidget.richTextEdit->mergeCurrentCharFormat(format);
} else {
QTextCursor cursor = m_textEditorWidget.svgTextEdit->textCursor();
if (cursor.hasSelection()) {
QString selectionModified = "<tspan style=\"font-size:" + QString::number(fontSize) + ";\">" + cursor.selectedText() + "</tspan>";
cursor.removeSelectedText();
cursor.insertText(selectionModified);
}
}
}
void SvgTextEditor::setBaseline(KoSvgText::BaselineShiftMode)
{
QTextCursor cursor = m_textEditorWidget.svgTextEdit->textCursor();
if (cursor.hasSelection()) {
QString selectionModified = "<tspan style=\"font-size:50%;baseline-shift:super;\">" + cursor.selectedText() + "</tspan>";
cursor.removeSelectedText();
cursor.insertText(selectionModified);
}
}
void SvgTextEditor::wheelEvent(QWheelEvent *event)
{
if (event->modifiers() & Qt::ControlModifier) {
int numDegrees = event->delta() / 8;
int numSteps = numDegrees / 7;
m_textEditorWidget.svgTextEdit->zoomOut(numSteps);
event->accept();
}
}
void SvgTextEditor::applySettings()
{
KConfigGroup cfg(KSharedConfig::openConfig(), "SvgTextTool");
EditorMode mode = (EditorMode)cfg.readEntry("EditorMode", (int)Both);
QWidget *richTab = m_textEditorWidget.richTab;
QWidget *svgTab = m_textEditorWidget.svgTab;
m_page->setUpdatesEnabled(false);
m_textEditorWidget.textTab->clear();
switch(mode) {
case(RichText):
m_textEditorWidget.textTab->addTab(richTab, i18n("Rich text"));
break;
case(SvgSource):
m_textEditorWidget.textTab->addTab(svgTab, i18n("SVG Source"));
break;
case(Both):
m_textEditorWidget.textTab->addTab(richTab, i18n("Rich text"));
m_textEditorWidget.textTab->addTab(svgTab, i18n("SVG Source"));
}
m_syntaxHighlighter->setFormats();
QPalette palette = m_textEditorWidget.svgTextEdit->palette();
QColor background = cfg.readEntry("colorEditorBackground", qApp->palette().background().color());
palette.setBrush(QPalette::Active, QPalette::Background, QBrush(background));
QColor foreground = cfg.readEntry("colorEditorForeground", qApp->palette().text().color());
palette.setBrush(QPalette::Active, QPalette::Text, QBrush(foreground));
QStringList selectedWritingSystems = cfg.readEntry("selectedWritingSystems", "").split(",");
QVector<QFontDatabase::WritingSystem> writingSystems;
for (int i=0; i<selectedWritingSystems.size(); i++) {
writingSystems.append((QFontDatabase::WritingSystem)QString(selectedWritingSystems.at(i)).toInt());
}
qobject_cast<KisFontComboBoxes*>(qobject_cast<QWidgetAction*>(actionCollection()->action("svg_font"))->defaultWidget())->refillComboBox(writingSystems);
m_page->setUpdatesEnabled(true);
}
QAction *SvgTextEditor::createAction(const QString &name, const char *member)
{
QAction *action = new QAction(this);
KisActionRegistry *actionRegistry = KisActionRegistry::instance();
actionRegistry->propertizeAction(name, action);
actionCollection()->addAction(name, action);
QObject::connect(action, SIGNAL(triggered(bool)), this, member);
return action;
}
void SvgTextEditor::createActions()
{
KisActionRegistry *actionRegistry = KisActionRegistry::instance();
// File: new, open, save, save as, close
KStandardAction::save(this, SLOT(save()), actionCollection());
KStandardAction::close(this, SLOT(slotCloseEditor()), actionCollection());
// Edit
KStandardAction::undo(this, SLOT(undo()), actionCollection());
KStandardAction::redo(this, SLOT(redo()), actionCollection());
KStandardAction::cut(this, SLOT(cut()), actionCollection());
KStandardAction::copy(this, SLOT(copy()), actionCollection());
KStandardAction::paste(this, SLOT(paste()), actionCollection());
KStandardAction::selectAll(this, SLOT(selectAll()), actionCollection());
KStandardAction::deselect(this, SLOT(deselect()), actionCollection());
KStandardAction::find(this, SLOT(find()), actionCollection());
KStandardAction::findNext(this, SLOT(findNext()), actionCollection());
KStandardAction::findPrev(this, SLOT(findPrev()), actionCollection());
KStandardAction::replace(this, SLOT(replace()), actionCollection());
// View
// WISH: we cannot zoom-in/out in rech-text mode
m_svgTextActions << KStandardAction::zoomOut(this, SLOT(zoomOut()), actionCollection());
m_svgTextActions << KStandardAction::zoomIn(this, SLOT(zoomIn()), actionCollection());
#ifndef Q_OS_WIN
// Insert:
QAction * insertAction = createAction("svg_insert_special_character",
SLOT(showInsertSpecialCharacterDialog()));
insertAction->setCheckable(true);
insertAction->setChecked(false);
#endif
// Format:
m_richTextActions << createAction("svg_weight_bold",
SLOT(setTextBold()));
m_richTextActions << createAction("svg_format_italic",
SLOT(setTextItalic()));
m_richTextActions << createAction("svg_format_underline",
SLOT(setTextUnderline()));
m_richTextActions << createAction("svg_format_strike_through",
SLOT(setTextStrikethrough()));
m_richTextActions << createAction("svg_format_superscript",
SLOT(setTextSuperScript()));
m_richTextActions << createAction("svg_format_subscript",
SLOT(setTextSubscript()));
m_richTextActions << createAction("svg_weight_light",
SLOT(setTextWeightLight()));
m_richTextActions << createAction("svg_weight_normal",
SLOT(setTextWeightNormal()));
m_richTextActions << createAction("svg_weight_demi",
SLOT(setTextWeightDemi()));
m_richTextActions << createAction("svg_weight_black",
SLOT(setTextWeightBlack()));
m_richTextActions << createAction("svg_increase_font_size",
SLOT(increaseTextSize()));
m_richTextActions << createAction("svg_decrease_font_size",
SLOT(decreaseTextSize()));
m_richTextActions << createAction("svg_align_left",
SLOT(alignLeft()));
m_richTextActions << createAction("svg_align_right",
SLOT(alignRight()));
m_richTextActions << createAction("svg_align_center",
SLOT(alignCenter()));
// m_richTextActions << createAction("svg_align_justified",
// SLOT(alignJustified()));
// Settings
m_richTextActions << createAction("svg_settings",
SLOT(setSettings()));
QWidgetAction *fontComboAction = new QWidgetAction(this);
fontComboAction->setToolTip(i18n("Font"));
KisFontComboBoxes *fontCombo = new KisFontComboBoxes();
connect(fontCombo, SIGNAL(fontChanged(QString)), SLOT(setFont(QString)));
fontComboAction->setDefaultWidget(fontCombo);
actionCollection()->addAction("svg_font", fontComboAction);
m_richTextActions << fontComboAction;
actionRegistry->propertizeAction("svg_font", fontComboAction);
QWidgetAction *fontSizeAction = new FontSizeAction(this);
fontSizeAction->setToolTip(i18n("Size"));
connect(fontSizeAction, SIGNAL(fontSizeChanged(qreal)), this, SLOT(setFontSize(qreal)));
actionCollection()->addAction("svg_font_size", fontSizeAction);
m_richTextActions << fontSizeAction;
actionRegistry->propertizeAction("svg_font_size", fontSizeAction);
KoColorPopupAction *fgColor = new KoColorPopupAction(this);
fgColor->setCurrentColor(QColor(Qt::black));
fgColor->setToolTip(i18n("Text Color"));
connect(fgColor, SIGNAL(colorChanged(KoColor)), SLOT(setFontColor(KoColor)));
actionCollection()->addAction("svg_format_textcolor", fgColor);
m_richTextActions << fgColor;
actionRegistry->propertizeAction("svg_format_textcolor", fgColor);
KoColorPopupAction *bgColor = new KoColorPopupAction(this);
bgColor->setCurrentColor(QColor(Qt::white));
bgColor->setToolTip(i18n("Background Color"));
connect(bgColor, SIGNAL(colorChanged(KoColor)), SLOT(setBackgroundColor(KoColor)));
actionCollection()->addAction("svg_background_color", bgColor);
actionRegistry->propertizeAction("svg_background_color", bgColor);
m_richTextActions << bgColor;
QWidgetAction *colorPickerAction = new QWidgetAction(this);
colorPickerAction->setToolTip(i18n("Pick a Color"));
KisScreenColorPicker *colorPicker = new KisScreenColorPicker(false);
connect(colorPicker, SIGNAL(sigNewColorPicked(KoColor)), fgColor, SLOT(setCurrentColor(KoColor)));
connect(colorPicker, SIGNAL(sigNewColorPicked(KoColor)), SLOT(setFontColor(KoColor)));
colorPickerAction->setDefaultWidget(colorPicker);
actionCollection()->addAction("svg_pick_color", colorPickerAction);
m_richTextActions << colorPickerAction;
actionRegistry->propertizeAction("svg_pick_color", colorPickerAction);
QWidgetAction *lineHeight = new QWidgetAction(this);
lineHeight->setToolTip(i18n("Line height"));
QDoubleSpinBox *spnLineHeight = new QDoubleSpinBox();
spnLineHeight->setRange(0.0, 1000.0);
spnLineHeight->setSingleStep(10.0);
spnLineHeight->setSuffix("%");
connect(spnLineHeight, SIGNAL(valueChanged(double)), SLOT(setLineHeight(double)));
lineHeight->setDefaultWidget(spnLineHeight);
actionCollection()->addAction("svg_line_height", lineHeight);
m_richTextActions << lineHeight;
actionRegistry->propertizeAction("svg_line_height", lineHeight);
}
void SvgTextEditor::enableRichTextActions(bool enable)
{
Q_FOREACH(QAction *action, m_richTextActions) {
action->setEnabled(enable);
}
}
void SvgTextEditor::enableSvgTextActions(bool enable)
{
Q_FOREACH(QAction *action, m_svgTextActions) {
action->setEnabled(enable);
}
}
void SvgTextEditor::slotCloseEditor()
{
close();
emit textEditorClosed();
}
diff --git a/plugins/tools/svgtexttool/SvgTextTool.action b/plugins/tools/svgtexttool/SvgTextTool.action
index f4f417f9ce..498e83cf6a 100644
--- a/plugins/tools/svgtexttool/SvgTextTool.action
+++ b/plugins/tools/svgtexttool/SvgTextTool.action
@@ -1,255 +1,255 @@
<?xml version="1.0" encoding="UTF-8"?>
<ActionCollection version="2" name="SVG Tools">
<Actions category="Text Tool">
<text>SVG Text Tool</text>
- <Action name="KritaTransform/KisToolMove">
+ <Action name="SvgTextTool">
<icon></icon>
<text>Text Tool</text>
<whatsThis></whatsThis>
<toolTip>Text Tool</toolTip>
<iconText>Text Tool</iconText>
- <shortcut>CTRL+T</shortcut>
+ <shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip/>
</Action>
<Action name="svg_format_textcolor">
<iconText>Text Color</iconText>
<shortcut/>
<toolTip>Text Color...</toolTip>
<icon>format-text-color</icon>
<whatsThis/>
<statusTip/>
<isCheckable>false</isCheckable>
<text>Text Color</text>
</Action>
<Action name="svg_background_color">
<iconText>Background</iconText>
<shortcut/>
<toolTip>Background Color...</toolTip>
<icon>format-fill-color</icon>
<whatsThis/>
<statusTip/>
<isCheckable>false</isCheckable>
<text>Background</text>
</Action>
<Action name="svg_font_size">
<iconText>Font Size</iconText>
<shortcut/>
<toolTip>Font Size</toolTip>
<icon/>
<whatsThis/>
<statusTip/>
<isCheckable>false</isCheckable>
<text>Font Size</text>
</Action>
<Action name="svg_font">
<iconText>Font</iconText>
<shortcut>Ctrl+Alt+F</shortcut>
<toolTip>Change character size, font, boldface, italics etc.</toolTip>
<icon/>
<whatsThis>Change the attributes of the currently selected characters.</whatsThis>
<statusTip/>
<isCheckable>false</isCheckable>
<text>Font...</text>
</Action>
<Action name="svg_insert_special_character">
<iconText>Special Character</iconText>
<shortcut>Alt+Shift+C</shortcut>
<toolTip>Insert one or more symbols or characters not found on the keyboard</toolTip>
<icon>character-set</icon>
<whatsThis>Insert one or more symbols or characters not found on the keyboard.</whatsThis>
<statusTip/>
<isCheckable>false</isCheckable>
<text>Special Character...</text>
</Action>
<Action name="svg_align_right">
<iconText>Align Right</iconText>
<shortcut>Ctrl+Alt+R</shortcut>
<toolTip>Align Right</toolTip>
<icon>format-justify-right</icon>
<whatsThis/>
<statusTip/>
<isCheckable>false</isCheckable>
<text>Align Right</text>
</Action>
<Action name="svg_align_left">
<iconText>Align Left</iconText>
<shortcut/>
<toolTip>Align Left</toolTip>
<icon>format-justify-left</icon>
<whatsThis/>
<statusTip/>
<isCheckable>false</isCheckable>
<text>Align Left</text>
</Action>
<Action name="svg_align_justified">
<iconText>Align Block</iconText>
<shortcut>Ctrl+Alt+R</shortcut>
<toolTip>Align Block</toolTip>
<icon>format-justify-fill</icon>
<whatsThis/>
<statusTip/>
<isCheckable>false</isCheckable>
<text>Align Block</text>
</Action>
<Action name="svg_align_center">
<iconText>Align Center</iconText>
<shortcut>Ctrl+Alt+C</shortcut>
<toolTip>Align Center</toolTip>
<icon>format-justify-center</icon>
<whatsThis/>
<statusTip/>
<isCheckable>false</isCheckable>
<text>Align Center</text>
</Action>
<Action name="svg_decrease_font_size">
<iconText>Decrease Font Size</iconText>
<shortcut>Ctrl+&lt;</shortcut>
<toolTip>Decrease Font Size</toolTip>
<icon/>
<whatsThis/>
<statusTip/>
<isCheckable>false</isCheckable>
<text>Decrease Font Size</text>
</Action>
<Action name="svg_increase_font_size">
<iconText>Increase Font Size</iconText>
<shortcut>Ctrl+&gt;</shortcut>
<toolTip>Increase Font Size</toolTip>
<icon/>
<whatsThis/>
<statusTip/>
<isCheckable>false</isCheckable>
<text>Increase Font Size</text>
</Action>
<Action name="svg_format_subscript">
<iconText>Subscript</iconText>
<shortcut>Ctrl+Shift+B</shortcut>
<toolTip>Subscript</toolTip>
<icon>format-text-subscript</icon>
<whatsThis/>
<statusTip/>
<isCheckable>false</isCheckable>
<text>Subscript</text>
</Action>
<Action name="svg_format_superscript">
<iconText>Superscript</iconText>
<shortcut>Ctrl+Shift+P</shortcut>
<toolTip>Superscript</toolTip>
<icon>format-text-superscript</icon>
<whatsThis/>
<statusTip/>
<isCheckable>false</isCheckable>
<text>Superscript</text>
</Action>
<Action name="svg_format_underline">
<iconText>Underline</iconText>
<shortcut>Ctrl+U</shortcut>
<toolTip>Underline</toolTip>
<icon>format-text-underline</icon>
<whatsThis/>
<statusTip/>
<isCheckable>false</isCheckable>
<text>Underline</text>
</Action>
<Action name="svg_format_strike_through">
<iconText>Strikethrough</iconText>
<shortcut/>
<toolTip>Strikethrough</toolTip>
<icon>format-text-strikethrough</icon>
<whatsThis/>
<statusTip/>
<isCheckable>false</isCheckable>
<text>Strikethrough</text>
</Action>
<Action name="svg_weight_bold">
<iconText>Bold</iconText>
<shortcut>Ctrl+B</shortcut>
<toolTip>Bold</toolTip>
<icon>format-text-bold</icon>
<whatsThis/>
<statusTip/>
<isCheckable>true</isCheckable>
<text>Bold</text>
</Action>
<Action name="svg_format_italic">
<iconText>Italic</iconText>
<shortcut>Ctrl+I</shortcut>
<toolTip>Italic</toolTip>
<icon>format-text-italic</icon>
<whatsThis/>
<statusTip/>
<isCheckable>true</isCheckable>
<text>Italic</text>
</Action>
<Action name="svg_weight_normal">
<iconText>Normal</iconText>
<shortcut>Ctrl+N</shortcut>
<toolTip>Normal</toolTip>
<icon>format-text-normal</icon>
<whatsThis/>
<statusTip/>
<isCheckable>false</isCheckable>
<text>Normal</text>
</Action>
<Action name="svg_weight_demi">
<iconText>Demi</iconText>
<shortcut/>
<toolTip>Demi</toolTip>
<icon>format-text-demi</icon>
<whatsThis/>
<statusTip/>
<isCheckable>false</isCheckable>
<text>Demi</text>
</Action>
<Action name="svg_weight_black">
<iconText>Black</iconText>
<shortcut/>
<toolTip>Black</toolTip>
<icon>format-text-black</icon>
<whatsThis/>
<statusTip/>
<isCheckable>false</isCheckable>
<text>Black</text>
</Action>
<Action name="svg_weight_light">
<iconText>Light</iconText>
<shortcut>Ctrl+L</shortcut>
<toolTip>Light</toolTip>
<icon>format-text-light</icon>
<whatsThis/>
<statusTip/>
<isCheckable>false</isCheckable>
<text>Light</text>
</Action>
<Action name="svg_line_height">
<iconText>Line Height</iconText>
<shortcut>Ctrl+H</shortcut>
<toolTip>Line Height in em</toolTip>
<icon></icon>
<whatsThis/>
<statusTip/>
<isCheckable>false</isCheckable>
<text>Line Height</text>
</Action>
<Action name="svg_settings">
<iconText>Settings</iconText>
<shortcut></shortcut>
<toolTip>Text Editor Settings</toolTip>
<icon></icon>
<whatsThis/>
<statusTip/>
<isCheckable>false</isCheckable>
<text>Settings...</text>
</Action></Actions>
<Action name="svg_pick_color">
<iconText>Colorpicker</iconText>
<shortcut></shortcut>
<toolTip>Pick a color from the screen</toolTip>
<icon></icon>
<whatsThis/>
<statusTip/>
<isCheckable>false</isCheckable>
<text>Color Picker</text>
</Action></Actions>
</ActionCollection>
diff --git a/plugins/tools/tool_crop/kis_constrained_rect.cpp b/plugins/tools/tool_crop/kis_constrained_rect.cpp
index e83b825944..9358fbca6a 100644
--- a/plugins/tools/tool_crop/kis_constrained_rect.cpp
+++ b/plugins/tools/tool_crop/kis_constrained_rect.cpp
@@ -1,401 +1,401 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_constrained_rect.h"
#include <cmath>
#include "kis_debug.h"
#include "kis_algebra_2d.h"
KisConstrainedRect::KisConstrainedRect()
: m_centered(false),
m_canGrow(true),
m_ratio(1.0),
m_widthLocked(false),
m_heightLocked(false),
m_ratioLocked(false)
{
}
KisConstrainedRect::~KisConstrainedRect()
{
}
void KisConstrainedRect::setRectInitial(const QRect &rect)
{
m_rect = rect;
if (!ratioLocked()) {
storeRatioSafe(m_rect.size());
}
emit sigValuesChanged();
}
void KisConstrainedRect::setCropRect(const QRect &cropRect)
{
m_cropRect = cropRect;
}
bool KisConstrainedRect::centered() const {
return m_centered;
}
void KisConstrainedRect::setCentered(bool value) {
m_centered = value;
}
bool KisConstrainedRect::canGrow() const {
return m_canGrow;
}
void KisConstrainedRect::setCanGrow(bool value) {
m_canGrow = value;
}
QRect KisConstrainedRect::rect() const {
return m_rect.normalized();
}
qreal KisConstrainedRect::ratio() const {
return qAbs(m_ratio);
}
void KisConstrainedRect::moveHandle(HandleType handle, const QPoint &offset, const QRect &oldRect)
{
const QSize oldSize = oldRect.size();
QSize newSize = oldSize;
QPoint newOffset = oldRect.topLeft();
int xSizeCoeff = 1;
int ySizeCoeff = 1;
qreal xOffsetFromSizeChange = 1.0;
qreal yOffsetFromSizeChange = 1.0;
int baseSizeCoeff = 1;
bool useMoveOnly = false;
switch (handle) {
case UpperLeft:
xSizeCoeff = -1;
ySizeCoeff = -1;
xOffsetFromSizeChange = -1.0;
yOffsetFromSizeChange = -1.0;
break;
case UpperRight:
xSizeCoeff = 1;
ySizeCoeff = -1;
xOffsetFromSizeChange = 0.0;
yOffsetFromSizeChange = -1.0;
break;
case Creation:
baseSizeCoeff = 0;
- /* Falls through */
+ Q_FALLTHROUGH();
case LowerRight:
xSizeCoeff = 1;
ySizeCoeff = 1;
xOffsetFromSizeChange = 0.0;
yOffsetFromSizeChange = 0.0;
break;
case LowerLeft:
xSizeCoeff = -1;
ySizeCoeff = 1;
xOffsetFromSizeChange = -1.0;
yOffsetFromSizeChange = 0.0;
break;
case Upper:
xSizeCoeff = 0;
ySizeCoeff = -1;
xOffsetFromSizeChange = -0.5;
yOffsetFromSizeChange = -1.0;
break;
case Right:
xSizeCoeff = 1;
ySizeCoeff = 0;
xOffsetFromSizeChange = 0.0;
yOffsetFromSizeChange = -0.5;
break;
case Lower:
xSizeCoeff = 0;
ySizeCoeff = 1;
xOffsetFromSizeChange = -0.5;
yOffsetFromSizeChange = 0.0;
break;
case Left:
xSizeCoeff = -1;
ySizeCoeff = 0;
xOffsetFromSizeChange = -1.0;
yOffsetFromSizeChange = -0.5;
break;
case Inside:
useMoveOnly = true;
break;
case None: // should never happen
break;
}
if (!useMoveOnly) {
const int centeringSizeCoeff = m_centered ? 2 : 1;
if (m_centered) {
xOffsetFromSizeChange = -0.5;
yOffsetFromSizeChange = -0.5;
}
QSize sizeDiff(offset.x() * xSizeCoeff * centeringSizeCoeff,
offset.y() * ySizeCoeff * centeringSizeCoeff);
QSize tempSize = baseSizeCoeff * oldSize + sizeDiff;
bool widthPreferrable = qAbs(tempSize.width()) > qAbs(tempSize.height() * m_ratio);
if (ratioLocked() && ((widthPreferrable && xSizeCoeff != 0) || ySizeCoeff == 0)) {
newSize.setWidth(tempSize.width());
newSize.setHeight(heightFromWidthUnsignedRatio(newSize.width(), m_ratio, tempSize.height()));
} else if (ratioLocked() && ((!widthPreferrable && ySizeCoeff != 0) || xSizeCoeff == 0)) {
newSize.setHeight(tempSize.height());
newSize.setWidth(widthFromHeightUnsignedRatio(newSize.height(), m_ratio, tempSize.width()));
} else if (widthLocked() && heightLocked()) {
newSize.setWidth(KisAlgebra2D::copysign(newSize.width(), tempSize.width()));
newSize.setHeight(KisAlgebra2D::copysign(newSize.height(), tempSize.height()));
} else if (widthLocked()) {
newSize.setWidth(KisAlgebra2D::copysign(newSize.width(), tempSize.width()));
newSize.setHeight(tempSize.height());
storeRatioSafe(newSize);
} else if (heightLocked()) {
newSize.setHeight(KisAlgebra2D::copysign(newSize.height(), tempSize.height()));
newSize.setWidth(tempSize.width());
storeRatioSafe(newSize);
} else {
newSize = baseSizeCoeff * oldSize + sizeDiff;
storeRatioSafe(newSize);
}
QSize realSizeDiff = newSize - baseSizeCoeff * oldSize;
QPoint offsetDiff(realSizeDiff.width() * xOffsetFromSizeChange,
realSizeDiff.height() * yOffsetFromSizeChange);
newOffset = oldRect.topLeft() + offsetDiff;
} else {
newOffset = oldRect.topLeft() + offset;
}
m_rect = QRect(newOffset, newSize);
if (!m_canGrow) {
m_rect &= m_cropRect;
}
emit sigValuesChanged();
}
QPointF KisConstrainedRect::handleSnapPoint(HandleType handle, const QPointF &cursorPos)
{
QPointF snapPoint = cursorPos;
switch (handle) {
case UpperLeft:
snapPoint = m_rect.topLeft();
break;
case UpperRight:
snapPoint = m_rect.topRight() + QPointF(1, 0);
break;
case Creation:
break;
case LowerRight:
snapPoint = m_rect.bottomRight() + QPointF(1, 1);
break;
case LowerLeft:
snapPoint = m_rect.bottomLeft() + QPointF(0, 1);
break;
case Upper:
snapPoint.ry() = m_rect.y();
break;
case Right:
snapPoint.rx() = m_rect.right() + 1;
break;
case Lower:
snapPoint.ry() = m_rect.bottom() + 1;
break;
case Left:
snapPoint.rx() = m_rect.x();
break;
case Inside:
break;
case None: // should never happen
break;
}
return snapPoint;
}
void KisConstrainedRect::normalize()
{
setRectInitial(m_rect.normalized());
}
void KisConstrainedRect::setOffset(const QPoint &offset)
{
QRect newRect = m_rect;
newRect.moveTo(offset);
if (!m_canGrow) {
newRect &= m_cropRect;
}
if (!newRect.isEmpty()) {
m_rect = newRect;
}
emit sigValuesChanged();
}
void KisConstrainedRect::setRatio(qreal value) {
KIS_ASSERT_RECOVER_RETURN(value >= 0);
const qreal eps = 1e-7;
const qreal invEps = 1.0 / eps;
if (value < eps || value > invEps) {
emit sigValuesChanged();
return;
}
const QSize oldSize = m_rect.size();
QSize newSize = oldSize;
if (widthLocked() && heightLocked()) {
setHeightLocked(false);
}
m_ratio = value;
if (!widthLocked() && !heightLocked()) {
int area = oldSize.width() * oldSize.height();
newSize.setWidth(qRound(std::sqrt(area * m_ratio)));
newSize.setHeight(qRound(newSize.width() / m_ratio));
} else if (widthLocked()) {
newSize.setHeight(newSize.width() / m_ratio);
} else if (heightLocked()) {
newSize.setWidth(newSize.height() * m_ratio);
}
assignNewSize(newSize);
}
void KisConstrainedRect::setWidth(int value)
{
KIS_ASSERT_RECOVER_RETURN(value >= 0);
const QSize oldSize = m_rect.size();
QSize newSize = oldSize;
if (ratioLocked()) {
newSize.setWidth(value);
newSize.setHeight(newSize.width() / m_ratio);
} else {
newSize.setWidth(value);
storeRatioSafe(newSize);
}
assignNewSize(newSize);
}
void KisConstrainedRect::setHeight(int value)
{
KIS_ASSERT_RECOVER_RETURN(value >= 0);
const QSize oldSize = m_rect.size();
QSize newSize = oldSize;
if (ratioLocked()) {
newSize.setHeight(value);
newSize.setWidth(newSize.height() * m_ratio);
} else {
newSize.setHeight(value);
storeRatioSafe(newSize);
}
assignNewSize(newSize);
}
void KisConstrainedRect::assignNewSize(const QSize &newSize)
{
if (!m_centered) {
m_rect.setSize(newSize);
} else {
QSize sizeDiff = newSize - m_rect.size();
m_rect.translate(-qRound(sizeDiff.width() / 2.0), -qRound(sizeDiff.height() / 2.0));
m_rect.setSize(newSize);
}
if (!m_canGrow) {
m_rect &= m_cropRect;
}
emit sigValuesChanged();
}
void KisConstrainedRect::storeRatioSafe(const QSize &newSize)
{
m_ratio = qAbs(qreal(newSize.width()) / newSize.height());
}
int KisConstrainedRect::widthFromHeightUnsignedRatio(int height, qreal ratio, int oldWidth) const
{
int newWidth = qRound(height * ratio);
return KisAlgebra2D::copysign(newWidth, oldWidth);
}
int KisConstrainedRect::heightFromWidthUnsignedRatio(int width, qreal ratio, int oldHeight) const
{
int newHeight = qRound(width / ratio);
return KisAlgebra2D::copysign(newHeight, oldHeight);
}
bool KisConstrainedRect::widthLocked() const {
return m_widthLocked;
}
bool KisConstrainedRect::heightLocked() const {
return m_heightLocked;
}
bool KisConstrainedRect::ratioLocked() const {
return m_ratioLocked;
}
void KisConstrainedRect::setWidthLocked(bool value) {
m_widthLocked = value;
m_ratioLocked &= !(m_widthLocked || m_heightLocked);
emit sigLockValuesChanged();
}
void KisConstrainedRect::setHeightLocked(bool value) {
m_heightLocked = value;
m_ratioLocked &= !(m_widthLocked || m_heightLocked);
emit sigLockValuesChanged();
}
void KisConstrainedRect::setRatioLocked(bool value) {
m_ratioLocked = value;
m_widthLocked &= !m_ratioLocked;
m_heightLocked &= !m_ratioLocked;
emit sigLockValuesChanged();
}
diff --git a/plugins/tools/tool_transform2/kis_cage_transform_strategy.cpp b/plugins/tools/tool_transform2/kis_cage_transform_strategy.cpp
index e61cb1e550..075fc93d74 100644
--- a/plugins/tools/tool_transform2/kis_cage_transform_strategy.cpp
+++ b/plugins/tools/tool_transform2/kis_cage_transform_strategy.cpp
@@ -1,101 +1,99 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_cage_transform_strategy.h"
#include "tool_transform_args.h"
#include <QPointF>
#include <QPainter>
#include "kis_painting_tweaks.h"
#include "kis_cursor.h"
#include <kis_cage_transform_worker.h>
struct KisCageTransformStrategy::Private
{
Private(KisCageTransformStrategy *_q)
: q(_q)
{
}
KisCageTransformStrategy * const q;
};
KisCageTransformStrategy::KisCageTransformStrategy(const KisCoordinatesConverter *converter,
ToolTransformArgs &currentArgs,
TransformTransactionProperties &transaction)
: KisWarpTransformStrategy(converter, currentArgs, transaction),
m_d(new Private(this))
{
overrideDrawingItems(true, false, true);
setCloseOnStartPointClick(true);
setClipOriginalPointsPosition(false);
setTransformType(TransformType::CAGE_TRANSFORM);
}
KisCageTransformStrategy::~KisCageTransformStrategy()
{
}
void KisCageTransformStrategy::drawConnectionLines(QPainter &gc,
const QVector<QPointF> &origPoints,
const QVector<QPointF> &transfPoints,
bool isEditingPoints)
{
const int numPoints = origPoints.size();
if (numPoints <= 1) return;
QPen antsPen;
QPen outlinePen;
KisPaintingTweaks::initAntsPen(&antsPen, &outlinePen);
const int iterateLimit = isEditingPoints ? numPoints : numPoints + 1;
for (int i = 1; i < iterateLimit; ++i) {
int idx = i % numPoints;
int prevIdx = (i - 1) % numPoints;
gc.setPen(outlinePen);
gc.drawLine(transfPoints[prevIdx], transfPoints[idx]);
gc.setPen(antsPen);
gc.drawLine(transfPoints[prevIdx], transfPoints[idx]);
}
}
QImage KisCageTransformStrategy::calculateTransformedImage(ToolTransformArgs &currentArgs,
const QImage &srcImage,
const QVector<QPointF> &origPoints,
const QVector<QPointF> &transfPoints,
const QPointF &srcOffset,
QPointF *dstOffset)
{
- Q_UNUSED(currentArgs);
-
KisCageTransformWorker worker(srcImage,
srcOffset,
origPoints,
0,
- 16);
+ currentArgs.previewPixelPrecision());
worker.prepareTransform();
worker.setTransformedCage(transfPoints);
return worker.runOnQImage(dstOffset);
}
diff --git a/plugins/tools/tool_transform2/kis_tool_transform_config_widget.cpp b/plugins/tools/tool_transform2/kis_tool_transform_config_widget.cpp
index 0e4ac11dca..e824a5855c 100644
--- a/plugins/tools/tool_transform2/kis_tool_transform_config_widget.cpp
+++ b/plugins/tools/tool_transform2/kis_tool_transform_config_widget.cpp
@@ -1,1297 +1,1330 @@
/*
* Copyright (c) 2013 Dmitry Kazakov <dimula73@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_tool_transform_config_widget.h"
#include <kis_icon.h>
#include "rotation_icons.h"
#include "kis_canvas2.h"
#include <QSignalMapper>
#include "kis_liquify_properties.h"
#include "KisMainWindow.h"
#include "KisViewManager.h"
#include "kis_transform_utils.h"
template<typename T> inline T sign(T x) {
return x > 0 ? 1 : x == (T)0 ? 0 : -1;
}
const int KisToolTransformConfigWidget::DEFAULT_POINTS_PER_LINE = 3;
KisToolTransformConfigWidget::KisToolTransformConfigWidget(TransformTransactionProperties *transaction, KisCanvas2 *canvas, bool workRecursively, QWidget *parent)
: QWidget(parent),
m_transaction(transaction),
m_notificationsBlocked(0),
m_uiSlotsBlocked(0),
m_configChanged(false)
{
setupUi(this);
showDecorationsBox->setIcon(KisIconUtils::loadIcon("krita_tool_transform"));
chkWorkRecursively->setIcon(KisIconUtils::loadIcon("krita_tool_transform_recursive"));
flipXButton->setIcon(KisIconUtils::loadIcon("transform_icons_mirror_x"));
flipYButton->setIcon(KisIconUtils::loadIcon("transform_icons_mirror_y"));
rotateCWButton->setIcon(KisIconUtils::loadIcon("transform_icons_rotate_cw"));
rotateCCWButton->setIcon(KisIconUtils::loadIcon("transform_icons_rotate_ccw"));
chkWorkRecursively->setChecked(workRecursively);
connect(chkWorkRecursively, SIGNAL(toggled(bool)), this, SIGNAL(sigRestartTransform()));
+ // Granularity can only be specified in the power of 2's
+ QStringList granularityValues{"4","8","16","32"};
+ changeGranularity->addItems(granularityValues);
+ changeGranularity->setCurrentIndex(1);
+ granularityPreview->addItems(granularityValues);
+ granularityPreview->setCurrentIndex(2);
+
+ connect(changeGranularity,SIGNAL(currentIndexChanged(QString)),
+ this,SLOT(slotGranularityChanged(QString)));
+ connect(granularityPreview, SIGNAL(currentIndexChanged(QString)),
+ this,SLOT(slotPreviewGranularityChanged(QString)));
+
// Init Filter combo
cmbFilter->setIDList(KisFilterStrategyRegistry::instance()->listKeys());
cmbFilter->setCurrent("Bicubic");
cmbFilter->setToolTip(i18nc("@info:tooltip",
"<p>Select filtering mode:\n"
"<ul>"
"<li><b>Bilinear</b> for areas with uniform color to avoid artifacts</li>"
"<li><b>Bicubic</b> for smoother results</li>"
"<li><b>Lanczos3</b> for sharp results. May produce aerials.</li>"
"</ul></p>"));
connect(cmbFilter, SIGNAL(activated(KoID)),
this, SLOT(slotFilterChanged(KoID)));
// Init Warp Type combo
cmbWarpType->insertItem(KisWarpTransformWorker::AFFINE_TRANSFORM,i18n("Default (Affine)"));
cmbWarpType->insertItem(KisWarpTransformWorker::RIGID_TRANSFORM,i18n("Strong (Rigid)"));
cmbWarpType->insertItem(KisWarpTransformWorker::SIMILITUDE_TRANSFORM,i18n("Strongest (Similitude)"));
cmbWarpType->setCurrentIndex(KisWarpTransformWorker::AFFINE_TRANSFORM);
connect(cmbWarpType, SIGNAL(currentIndexChanged(int)), this, SLOT(slotWarpTypeChanged(int)));
// Init Rotation Center buttons
m_handleDir[0] = QPointF(1, 0);
m_handleDir[1] = QPointF(1, -1);
m_handleDir[2] = QPointF(0, -1);
m_handleDir[3] = QPointF(-1, -1);
m_handleDir[4] = QPointF(-1, 0);
m_handleDir[5] = QPointF(-1, 1);
m_handleDir[6] = QPointF(0, 1);
m_handleDir[7] = QPointF(1, 1);
m_handleDir[8] = QPointF(0, 0); // also add the center
m_rotationCenterButtons = new QButtonGroup(0);
// we set the ids to match m_handleDir
m_rotationCenterButtons->addButton(middleRightButton, 0);
m_rotationCenterButtons->addButton(topRightButton, 1);
m_rotationCenterButtons->addButton(middleTopButton, 2);
m_rotationCenterButtons->addButton(topLeftButton, 3);
m_rotationCenterButtons->addButton(middleLeftButton, 4);
m_rotationCenterButtons->addButton(bottomLeftButton, 5);
m_rotationCenterButtons->addButton(middleBottomButton, 6);
m_rotationCenterButtons->addButton(bottomRightButton, 7);
m_rotationCenterButtons->addButton(centerButton, 8);
QToolButton *nothingSelected = new QToolButton(0);
nothingSelected->setCheckable(true);
nothingSelected->setAutoExclusive(true);
nothingSelected->hide(); // a convenient button for when no button is checked in the group
m_rotationCenterButtons->addButton(nothingSelected, 9);
// initialize values for free transform sliders
shearXBox->setSuffix(i18n(" px"));
shearYBox->setSuffix(i18n(" px"));
shearXBox->setRange(-5.0, 5.0, 2);
shearYBox->setRange(-5.0, 5.0, 2);
shearXBox->setSingleStep(0.01);
shearYBox->setSingleStep(0.01);
shearXBox->setValue(0.0);
shearYBox->setValue(0.0);
translateXBox->setSuffix(i18n(" px"));
translateYBox->setSuffix(i18n(" px"));
translateXBox->setRange(-10000, 10000);
translateYBox->setRange(-10000, 10000);
scaleXBox->setSuffix("%");
scaleYBox->setSuffix("%");
scaleXBox->setRange(-10000, 10000);
scaleYBox->setRange(-10000, 10000);
scaleXBox->setValue(100.0);
scaleYBox->setValue(100.0);
m_scaleRatio = 1.0;
aXBox->setSuffix(QChar(Qt::Key_degree));
aYBox->setSuffix(QChar(Qt::Key_degree));
aZBox->setSuffix(QChar(Qt::Key_degree));
aXBox->setRange(0.0, 360.0, 2);
aYBox->setRange(0.0, 360.0, 2);
aZBox->setRange(0.0, 360.0, 2);
aXBox->setValue(0.0);
aYBox->setValue(0.0);
aZBox->setValue(0.0);
aXBox->setSingleStep(1.0);
aYBox->setSingleStep(1.0);
aZBox->setSingleStep(1.0);
connect(m_rotationCenterButtons, SIGNAL(buttonPressed(int)), this, SLOT(slotRotationCenterChanged(int)));
connect(btnTransformAroundPivotPoint, SIGNAL(clicked(bool)), this, SLOT(slotTransformAroundRotationCenter(bool)));
// Init Free Transform Values
connect(scaleXBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetScaleX(int)));
connect(scaleYBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetScaleY(int)));
connect(shearXBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetShearX(qreal)));
connect(shearYBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetShearY(qreal)));
connect(translateXBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetTranslateX(int)));
connect(translateYBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetTranslateY(int)));
connect(aXBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetAX(qreal)));
connect(aYBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetAY(qreal)));
connect(aZBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetAZ(qreal)));
connect(aspectButton, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(slotSetKeepAspectRatio(bool)));
connect(flipXButton, SIGNAL(clicked(bool)), this, SLOT(slotFlipX()));
connect(flipYButton, SIGNAL(clicked(bool)), this, SLOT(slotFlipY()));
connect(rotateCWButton, SIGNAL(clicked(bool)), this, SLOT(slotRotateCW()));
connect(rotateCCWButton, SIGNAL(clicked(bool)), this, SLOT(slotRotateCCW()));
// toggle visibility of different free buttons
connect(freeMoveRadioButton, SIGNAL(clicked(bool)), SLOT(slotTransformAreaVisible(bool)));
connect(freeRotationRadioButton, SIGNAL(clicked(bool)), SLOT(slotTransformAreaVisible(bool)));
connect(freeScaleRadioButton, SIGNAL(clicked(bool)), SLOT(slotTransformAreaVisible(bool)));
connect(freeShearRadioButton, SIGNAL(clicked(bool)), SLOT(slotTransformAreaVisible(bool)));
// only first group for free transform
rotationGroup->hide();
moveGroup->show();
scaleGroup->hide();
shearGroup->hide();
// Init Warp Transform Values
alphaBox->setSingleStep(0.1);
alphaBox->setRange(0, 10, 1);
connect(alphaBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetWarpAlpha(qreal)));
connect(densityBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetWarpDensity(int)));
connect(defaultRadioButton, SIGNAL(clicked(bool)), this, SLOT(slotWarpDefaultPointsButtonClicked(bool)));
connect(customRadioButton, SIGNAL(clicked(bool)), this, SLOT(slotWarpCustomPointsButtonClicked(bool)));
connect(lockUnlockPointsButton, SIGNAL(clicked()), this, SLOT(slotWarpLockPointsButtonClicked()));
connect(resetPointsButton, SIGNAL(clicked()), this, SLOT(slotWarpResetPointsButtonClicked()));
// Init Cage Transform Values
cageTransformButtonGroup->setId(cageAddEditRadio, 0); // we need to set manually since Qt Designer generates negative by default
cageTransformButtonGroup->setId(cageDeformRadio, 1);
connect(cageTransformButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(slotCageOptionsChanged(int)));
// Init Liquify Transform Values
liquifySizeSlider->setRange(KisLiquifyProperties::minSize(),
KisLiquifyProperties::maxSize(), 2);
liquifySizeSlider->setExponentRatio(4);
liquifySizeSlider->setValue(60.0);
connect(liquifySizeSlider, SIGNAL(valueChanged(qreal)), this, SLOT(liquifySizeChanged(qreal)));
liquifySizeSlider->setToolTip(i18nc("@info:tooltip", "Size of the deformation brush"));
liquifyAmountSlider->setRange(0.0, 1.0, 2);
liquifyAmountSlider->setValue(0.05);
connect(liquifyAmountSlider, SIGNAL(valueChanged(qreal)), this, SLOT(liquifyAmountChanged(qreal)));
liquifyAmountSlider->setToolTip(i18nc("@info:tooltip", "Amount of the deformation you get"));
liquifyFlowSlider->setRange(0.0, 1.0, 2);
liquifyFlowSlider->setValue(1.0);
connect(liquifyFlowSlider, SIGNAL(valueChanged(qreal)), this, SLOT(liquifyFlowChanged(qreal)));
liquifyFlowSlider->setToolTip(i18nc("@info:tooltip", "When in non-buildup mode, shows how fast the deformation limit is reached."));
buidupModeComboBox->setCurrentIndex(0); // set to build-up mode by default
connect(buidupModeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(liquifyBuildUpChanged(int)));
buidupModeComboBox->setToolTip(i18nc("@info:tooltip", "Switch between Build Up and Wash mode of painting. Build Up mode adds deformations one on top of the other without any limits. Wash mode gradually deforms the piece to the selected deformation level."));
liquifySpacingSlider->setRange(0.0, 3.0, 2);
liquifySizeSlider->setExponentRatio(3);
liquifySpacingSlider->setSingleStep(0.01);
liquifySpacingSlider->setValue(0.2);
connect(liquifySpacingSlider, SIGNAL(valueChanged(qreal)), this, SLOT(liquifySpacingChanged(qreal)));
liquifySpacingSlider->setToolTip(i18nc("@info:tooltip", "Space between two sequential applications of the deformation"));
liquifySizePressureBox->setChecked(true);
connect(liquifySizePressureBox, SIGNAL(toggled(bool)), this, SLOT(liquifySizePressureChanged(bool)));
liquifySizePressureBox->setToolTip(i18nc("@info:tooltip", "Scale <b>Size</b> value according to current stylus pressure"));
liquifyAmountPressureBox->setChecked(true);
connect(liquifyAmountPressureBox, SIGNAL(toggled(bool)), this, SLOT(liquifyAmountPressureChanged(bool)));
liquifyAmountPressureBox->setToolTip(i18nc("@info:tooltip", "Scale <b>Amount</b> value according to current stylus pressure"));
liquifyReverseDirectionChk->setChecked(false);
connect(liquifyReverseDirectionChk, SIGNAL(toggled(bool)), this, SLOT(liquifyReverseDirectionChanged(bool)));
liquifyReverseDirectionChk->setToolTip(i18nc("@info:tooltip", "Reverse direction of the current deformation tool"));
QSignalMapper *liquifyModeMapper = new QSignalMapper(this);
connect(liquifyMove, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
connect(liquifyScale, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
connect(liquifyRotate, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
connect(liquifyOffset, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
connect(liquifyUndo, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
liquifyModeMapper->setMapping(liquifyMove, (int)KisLiquifyProperties::MOVE);
liquifyModeMapper->setMapping(liquifyScale, (int)KisLiquifyProperties::SCALE);
liquifyModeMapper->setMapping(liquifyRotate, (int)KisLiquifyProperties::ROTATE);
liquifyModeMapper->setMapping(liquifyOffset, (int)KisLiquifyProperties::OFFSET);
liquifyModeMapper->setMapping(liquifyUndo, (int)KisLiquifyProperties::UNDO);
connect(liquifyModeMapper, SIGNAL(mapped(int)), SLOT(slotLiquifyModeChanged(int)));
liquifyMove->setToolTip(i18nc("@info:tooltip", "Move: drag the image along the brush stroke"));
liquifyScale->setToolTip(i18nc("@info:tooltip", "Scale: grow/shrink image under cursor"));
liquifyRotate->setToolTip(i18nc("@info:tooltip", "Rotate: twirl image under cursor"));
liquifyOffset->setToolTip(i18nc("@info:tooltip", "Offset: shift the image to the right of the stroke direction"));
liquifyUndo->setToolTip(i18nc("@info:tooltip", "Undo: erase actions of other tools"));
// Connect all edit boxes to the Editing Finished signal
connect(densityBox, SIGNAL(editingFinished()), this, SLOT(notifyEditingFinished()));
// Connect other widget (not having editingFinished signal) to
// the same slot. From Qt 4.6 onwards the sequence of the signal
// delivery is definite.
connect(cmbFilter, SIGNAL(activated(KoID)), this, SLOT(notifyEditingFinished()));
connect(cmbWarpType, SIGNAL(currentIndexChanged(int)), this, SLOT(notifyEditingFinished()));
connect(m_rotationCenterButtons, SIGNAL(buttonPressed(int)), this, SLOT(notifyEditingFinished()));
connect(aspectButton, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(notifyEditingFinished()));
connect(lockUnlockPointsButton, SIGNAL(clicked()), this, SLOT(notifyEditingFinished()));
connect(resetPointsButton, SIGNAL(clicked()), this, SLOT(notifyEditingFinished()));
connect(defaultRadioButton, SIGNAL(clicked(bool)), this, SLOT(notifyEditingFinished()));
connect(customRadioButton, SIGNAL(clicked(bool)), this, SLOT(notifyEditingFinished()));
// Liquify
//
// liquify brush options do not affect the image directly and are not
// saved to undo, so we don't emit notifyEditingFinished() for them
// Connect Apply/Reset buttons
connect(buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(slotButtonBoxClicked(QAbstractButton*)));
// Mode switch buttons
connect(freeTransformButton, SIGNAL(clicked(bool)), this, SLOT(slotSetFreeTransformModeButtonClicked(bool)));
connect(warpButton, SIGNAL(clicked(bool)), this, SLOT(slotSetWarpModeButtonClicked(bool)));
connect(cageButton, SIGNAL(clicked(bool)), this, SLOT(slotSetCageModeButtonClicked(bool)));
connect(perspectiveTransformButton, SIGNAL(clicked(bool)), this, SLOT(slotSetPerspectiveModeButtonClicked(bool)));
connect(liquifyButton, SIGNAL(clicked(bool)), this, SLOT(slotSetLiquifyModeButtonClicked(bool)));
// Connect Decorations switcher
connect(showDecorationsBox, SIGNAL(toggled(bool)), canvas, SLOT(updateCanvas()));
tooBigLabelWidget->hide();
connect(canvas->viewManager()->mainWindow(), SIGNAL(themeChanged()), SLOT(slotUpdateIcons()), Qt::UniqueConnection);
slotUpdateIcons();
}
void KisToolTransformConfigWidget::slotUpdateIcons()
{
freeTransformButton->setIcon(KisIconUtils::loadIcon("transform_icons_main"));
warpButton->setIcon(KisIconUtils::loadIcon("transform_icons_warp"));
cageButton->setIcon(KisIconUtils::loadIcon("transform_icons_cage"));
perspectiveTransformButton->setIcon(KisIconUtils::loadIcon("transform_icons_perspective"));
liquifyButton->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_main"));
liquifyMove->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_move"));
liquifyScale->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_resize"));
liquifyRotate->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_rotate"));
liquifyOffset->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_offset"));
liquifyUndo->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_erase"));
middleRightButton->setIcon(KisIconUtils::loadIcon("arrow-right"));
topRightButton->setIcon(KisIconUtils::loadIcon("arrow-topright"));
middleTopButton->setIcon(KisIconUtils::loadIcon("arrow-up"));
topLeftButton->setIcon(KisIconUtils::loadIcon("arrow-topleft"));
middleLeftButton->setIcon(KisIconUtils::loadIcon("arrow-left"));
bottomLeftButton->setIcon(KisIconUtils::loadIcon("arrow-downleft"));
middleBottomButton->setIcon(KisIconUtils::loadIcon("arrow-down"));
bottomRightButton->setIcon(KisIconUtils::loadIcon("arrow-downright"));
btnTransformAroundPivotPoint->setIcon(KisIconUtils::loadIcon("pivot-point"));
// pressure icons
liquifySizePressureBox->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure"));
liquifyAmountPressureBox->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure"));
}
double KisToolTransformConfigWidget::radianToDegree(double rad)
{
double piX2 = 2 * M_PI;
if (rad < 0 || rad >= piX2) {
rad = fmod(rad, piX2);
if (rad < 0) {
rad += piX2;
}
}
return (rad * 360. / piX2);
}
double KisToolTransformConfigWidget::degreeToRadian(double degree)
{
if (degree < 0. || degree >= 360.) {
degree = fmod(degree, 360.);
if (degree < 0)
degree += 360.;
}
return (degree * M_PI / 180.);
}
void KisToolTransformConfigWidget::updateLiquifyControls()
{
blockUiSlots();
ToolTransformArgs *config = m_transaction->currentConfig();
KisLiquifyProperties *props =
config->liquifyProperties();
const bool useWashMode = props->useWashMode();
liquifySizeSlider->setValue(props->size());
liquifyAmountSlider->setValue(props->amount());
liquifyFlowSlider->setValue(props->flow());
buidupModeComboBox->setCurrentIndex(useWashMode);
liquifySpacingSlider->setValue(props->spacing());
liquifySizePressureBox->setChecked(props->sizeHasPressure());
liquifyAmountPressureBox->setChecked(props->amountHasPressure());
liquifyReverseDirectionChk->setChecked(props->reverseDirection());
KisLiquifyProperties::LiquifyMode mode =
static_cast<KisLiquifyProperties::LiquifyMode>(props->mode());
bool canInverseDirection =
mode != KisLiquifyProperties::UNDO;
bool canUseWashMode = mode != KisLiquifyProperties::UNDO;
liquifyReverseDirectionChk->setEnabled(canInverseDirection);
liquifyFlowSlider->setEnabled(canUseWashMode && useWashMode);
buidupModeComboBox->setEnabled(canUseWashMode);
const qreal maxAmount = canUseWashMode ? 5.0 : 1.0;
liquifyAmountSlider->setRange(0.0, maxAmount, 2);
unblockUiSlots();
}
void KisToolTransformConfigWidget::slotLiquifyModeChanged(int value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
KisLiquifyProperties *props =
config->liquifyProperties();
KisLiquifyProperties::LiquifyMode mode =
static_cast<KisLiquifyProperties::LiquifyMode>(value);
if (mode == props->mode()) return;
props->setMode(mode);
props->loadMode();
updateLiquifyControls();
notifyConfigChanged();
}
void KisToolTransformConfigWidget::liquifySizeChanged(qreal value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
KisLiquifyProperties *props =
config->liquifyProperties();
props->setSize(value);
notifyConfigChanged();
}
void KisToolTransformConfigWidget::liquifyAmountChanged(qreal value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
KisLiquifyProperties *props =
config->liquifyProperties();
props->setAmount(value);
notifyConfigChanged();
}
void KisToolTransformConfigWidget::liquifyFlowChanged(qreal value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
KisLiquifyProperties *props =
config->liquifyProperties();
props->setFlow(value);
notifyConfigChanged();
}
void KisToolTransformConfigWidget::liquifyBuildUpChanged(int value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
KisLiquifyProperties *props =
config->liquifyProperties();
props->setUseWashMode(value); // 0 == build up mode / 1 == wash mode
notifyConfigChanged();
// we need to enable/disable flow slider
updateLiquifyControls();
}
void KisToolTransformConfigWidget::liquifySpacingChanged(qreal value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
KisLiquifyProperties *props =
config->liquifyProperties();
props->setSpacing(value);
notifyConfigChanged();
}
void KisToolTransformConfigWidget::liquifySizePressureChanged(bool value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
KisLiquifyProperties *props =
config->liquifyProperties();
props->setSizeHasPressure(value);
notifyConfigChanged();
}
void KisToolTransformConfigWidget::liquifyAmountPressureChanged(bool value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
KisLiquifyProperties *props =
config->liquifyProperties();
props->setAmountHasPressure(value);
notifyConfigChanged();
}
void KisToolTransformConfigWidget::liquifyReverseDirectionChanged(bool value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
KisLiquifyProperties *props =
config->liquifyProperties();
props->setReverseDirection(value);
notifyConfigChanged();
}
void KisToolTransformConfigWidget::updateConfig(const ToolTransformArgs &config)
{
blockUiSlots();
if (config.mode() == ToolTransformArgs::FREE_TRANSFORM ||
config.mode() == ToolTransformArgs::PERSPECTIVE_4POINT) {
quickTransformGroup->setEnabled(config.mode() == ToolTransformArgs::FREE_TRANSFORM);
stackedWidget->setCurrentIndex(0);
bool freeTransformIsActive = config.mode() == ToolTransformArgs::FREE_TRANSFORM;
if (freeTransformIsActive)
{
freeTransformButton->setChecked(true);
}
else
{
perspectiveTransformButton->setChecked(true);
}
aXBox->setEnabled(freeTransformIsActive);
aYBox->setEnabled(freeTransformIsActive);
aZBox->setEnabled(freeTransformIsActive);
freeRotationRadioButton->setEnabled(freeTransformIsActive);
scaleXBox->setValue(config.scaleX() * 100.);
scaleYBox->setValue(config.scaleY() * 100.);
shearXBox->setValue(config.shearX());
shearYBox->setValue(config.shearY());
const QPointF anchorPoint = config.originalCenter() + config.rotationCenterOffset();
const KisTransformUtils::MatricesPack m(config);
const QPointF anchorPointView = m.finalTransform().map(anchorPoint);
translateXBox->setValue(anchorPointView.x());
translateYBox->setValue(anchorPointView.y());
aXBox->setValue(radianToDegree(config.aX()));
aYBox->setValue(radianToDegree(config.aY()));
aZBox->setValue(radianToDegree(config.aZ()));
aspectButton->setKeepAspectRatio(config.keepAspectRatio());
cmbFilter->setCurrent(config.filterId());
QPointF pt = m_transaction->currentConfig()->rotationCenterOffset();
pt.rx() /= m_transaction->originalHalfWidth();
pt.ry() /= m_transaction->originalHalfHeight();
for (int i = 0; i < 9; i++) {
if (qFuzzyCompare(m_handleDir[i].x(), pt.x()) &&
qFuzzyCompare(m_handleDir[i].y(), pt.y())) {
m_rotationCenterButtons->button(i)->setChecked(true);
break;
}
}
btnTransformAroundPivotPoint->setChecked(config.transformAroundRotationCenter());
} else if (config.mode() == ToolTransformArgs::WARP) {
stackedWidget->setCurrentIndex(1);
warpButton->setChecked(true);
if (config.defaultPoints()) {
densityBox->setValue(std::sqrt(config.numPoints()));
}
cmbWarpType->setCurrentIndex((int)config.warpType());
defaultRadioButton->setChecked(config.defaultPoints());
customRadioButton->setChecked(!config.defaultPoints());
densityBox->setEnabled(config.defaultPoints());
customWarpWidget->setEnabled(!config.defaultPoints());
updateLockPointsButtonCaption();
} else if (config.mode() == ToolTransformArgs::CAGE) {
// default UI options
resetUIOptions();
// we need at least 3 point before we can start actively deforming
if (config.origPoints().size() >= 3)
{
cageTransformDirections->setText(i18n("Switch between editing and deforming cage"));
cageAddEditRadio->setVisible(true);
cageDeformRadio->setVisible(true);
// update to correct radio button
if (config.isEditingTransformPoints())
cageAddEditRadio->setChecked(true);
else
cageDeformRadio->setChecked(true);
+ changeGranularity->setCurrentIndex(log2(config.pixelPrecision()) - 2);
+ granularityPreview->setCurrentIndex(log2(config.previewPixelPrecision()) - 2);
+
}
} else if (config.mode() == ToolTransformArgs::LIQUIFY) {
stackedWidget->setCurrentIndex(3);
liquifyButton->setChecked(true);
const KisLiquifyProperties *props =
config.liquifyProperties();
switch (props->mode()) {
case KisLiquifyProperties::MOVE:
liquifyMove->setChecked(true);
break;
case KisLiquifyProperties::SCALE:
liquifyScale->setChecked(true);
break;
case KisLiquifyProperties::ROTATE:
liquifyRotate->setChecked(true);
break;
case KisLiquifyProperties::OFFSET:
liquifyOffset->setChecked(true);
break;
case KisLiquifyProperties::UNDO:
liquifyUndo->setChecked(true);
break;
case KisLiquifyProperties::N_MODES:
qFatal("Unsupported mode");
}
updateLiquifyControls();
}
unblockUiSlots();
}
void KisToolTransformConfigWidget::updateLockPointsButtonCaption()
{
ToolTransformArgs *config = m_transaction->currentConfig();
if (config->isEditingTransformPoints()) {
lockUnlockPointsButton->setText(i18n("Lock Points"));
} else {
lockUnlockPointsButton->setText(i18n("Unlock Points"));
}
}
void KisToolTransformConfigWidget::setApplyResetDisabled(bool disabled)
{
QAbstractButton *applyButton = buttonBox->button(QDialogButtonBox::Apply);
QAbstractButton *resetButton = buttonBox->button(QDialogButtonBox::Reset);
Q_ASSERT(applyButton);
Q_ASSERT(resetButton);
applyButton->setDisabled(disabled);
resetButton->setDisabled(disabled);
}
void KisToolTransformConfigWidget::resetRotationCenterButtons()
{
int checkedId = m_rotationCenterButtons->checkedId();
if (checkedId >= 0 && checkedId <= 8) {
// uncheck the current checked button
m_rotationCenterButtons->button(9)->setChecked(true);
}
}
bool KisToolTransformConfigWidget::workRecursively() const
{
return chkWorkRecursively->isChecked();
}
void KisToolTransformConfigWidget::setTooBigLabelVisible(bool value)
{
tooBigLabelWidget->setVisible(value);
}
bool KisToolTransformConfigWidget::showDecorations() const
{
return showDecorationsBox->isChecked();
}
void KisToolTransformConfigWidget::blockNotifications()
{
m_notificationsBlocked++;
}
void KisToolTransformConfigWidget::unblockNotifications()
{
m_notificationsBlocked--;
}
void KisToolTransformConfigWidget::notifyConfigChanged()
{
if (!m_notificationsBlocked) {
emit sigConfigChanged();
}
m_configChanged = true;
}
void KisToolTransformConfigWidget::blockUiSlots()
{
m_uiSlotsBlocked++;
}
void KisToolTransformConfigWidget::unblockUiSlots()
{
m_uiSlotsBlocked--;
}
void KisToolTransformConfigWidget::notifyEditingFinished()
{
if (m_uiSlotsBlocked || m_notificationsBlocked || !m_configChanged) return;
emit sigEditingFinished();
m_configChanged = false;
}
void KisToolTransformConfigWidget::resetUIOptions()
{
// reset tool states since we are done (probably can encapsulate this later)
ToolTransformArgs *config = m_transaction->currentConfig();
if (config->mode() == ToolTransformArgs::CAGE)
{
cageAddEditRadio->setVisible(false);
cageAddEditRadio->setChecked(true);
cageDeformRadio->setVisible(false);
cageTransformDirections->setText(i18n("Create 3 points on the canvas to begin"));
// ensure we are on the right options view
stackedWidget->setCurrentIndex(2);
}
}
void KisToolTransformConfigWidget::slotButtonBoxClicked(QAbstractButton *button)
{
QAbstractButton *applyButton = buttonBox->button(QDialogButtonBox::Apply);
QAbstractButton *resetButton = buttonBox->button(QDialogButtonBox::Reset);
resetUIOptions();
if (button == applyButton) {
emit sigApplyTransform();
}
else if (button == resetButton) {
emit sigResetTransform();
}
}
void KisToolTransformConfigWidget::slotSetFreeTransformModeButtonClicked(bool value)
{
if (!value) return;
lblTransformType->setText(freeTransformButton->toolTip());
ToolTransformArgs *config = m_transaction->currentConfig();
config->setMode(ToolTransformArgs::FREE_TRANSFORM);
emit sigResetTransform();
}
void KisToolTransformConfigWidget::slotSetWarpModeButtonClicked(bool value)
{
if (!value) return;
lblTransformType->setText(warpButton->toolTip());
ToolTransformArgs *config = m_transaction->currentConfig();
config->setMode(ToolTransformArgs::WARP);
config->setWarpCalculation(KisWarpTransformWorker::WarpCalculation::GRID);
emit sigResetTransform();
}
void KisToolTransformConfigWidget::slotSetCageModeButtonClicked(bool value)
{
if (!value) return;
lblTransformType->setText(cageButton->toolTip());
ToolTransformArgs *config = m_transaction->currentConfig();
config->setMode(ToolTransformArgs::CAGE);
emit sigResetTransform();
}
void KisToolTransformConfigWidget::slotSetLiquifyModeButtonClicked(bool value)
{
if (!value) return;
lblTransformType->setText(liquifyButton->toolTip());
ToolTransformArgs *config = m_transaction->currentConfig();
config->setMode(ToolTransformArgs::LIQUIFY);
emit sigResetTransform();
}
void KisToolTransformConfigWidget::slotSetPerspectiveModeButtonClicked(bool value)
{
if (!value) return;
lblTransformType->setText(perspectiveTransformButton->toolTip());
ToolTransformArgs *config = m_transaction->currentConfig();
config->setMode(ToolTransformArgs::PERSPECTIVE_4POINT);
emit sigResetTransform();
}
void KisToolTransformConfigWidget::slotFilterChanged(const KoID &filterId)
{
ToolTransformArgs *config = m_transaction->currentConfig();
config->setFilterId(filterId.id());
notifyConfigChanged();
}
void KisToolTransformConfigWidget::slotRotationCenterChanged(int index)
{
if (m_uiSlotsBlocked) return;
if (index >= 0 && index <= 8) {
ToolTransformArgs *config = m_transaction->currentConfig();
double i = m_handleDir[index].x();
double j = m_handleDir[index].y();
config->setRotationCenterOffset(QPointF(i * m_transaction->originalHalfWidth(),
j * m_transaction->originalHalfHeight()));
notifyConfigChanged();
updateConfig(*config);
}
}
void KisToolTransformConfigWidget::slotTransformAroundRotationCenter(bool value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
config->setTransformAroundRotationCenter(value);
notifyConfigChanged();
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotSetScaleX(int value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setScaleX(value / 100.);
}
if (config->keepAspectRatio()) {
blockNotifications();
int calculatedValue = int( value/ m_scaleRatio );
scaleYBox->blockSignals(true);
scaleYBox->setValue(calculatedValue);
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setScaleY(calculatedValue / 100.);
}
scaleYBox->blockSignals(false);
unblockNotifications();
}
notifyConfigChanged();
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotSetScaleY(int value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setScaleY(value / 100.);
}
if (config->keepAspectRatio()) {
blockNotifications();
int calculatedValue = int(m_scaleRatio * value);
scaleXBox->blockSignals(true);
scaleXBox->setValue(calculatedValue);
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setScaleX(calculatedValue / 100.);
}
scaleXBox->blockSignals(false);
unblockNotifications();
}
notifyConfigChanged();
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotSetShearX(qreal value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setShearX((double)value);
}
notifyConfigChanged();
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotSetShearY(qreal value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setShearY((double)value);
}
notifyConfigChanged();
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotSetTranslateX(int value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
const QPointF anchorPoint = config->originalCenter() + config->rotationCenterOffset();
const KisTransformUtils::MatricesPack m(*config);
const QPointF anchorPointView = m.finalTransform().map(anchorPoint);
const QPointF newAnchorPointView(value, anchorPointView.y());
config->setTransformedCenter(config->transformedCenter() + newAnchorPointView - anchorPointView);
notifyConfigChanged();
}
void KisToolTransformConfigWidget::slotSetTranslateY(int value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
const QPointF anchorPoint = config->originalCenter() + config->rotationCenterOffset();
const KisTransformUtils::MatricesPack m(*config);
const QPointF anchorPointView = m.finalTransform().map(anchorPoint);
const QPointF newAnchorPointView(anchorPointView.x(), value);
config->setTransformedCenter(config->transformedCenter() + newAnchorPointView - anchorPointView);
notifyConfigChanged();
}
void KisToolTransformConfigWidget::slotSetAX(qreal value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setAX(degreeToRadian((double)value));
}
notifyConfigChanged();
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotSetAY(qreal value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setAY(degreeToRadian((double)value));
}
notifyConfigChanged();
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotSetAZ(qreal value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setAZ(degreeToRadian((double)value));
}
notifyConfigChanged();
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotFlipX()
{
ToolTransformArgs *config = m_transaction->currentConfig();
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setScaleX(config->scaleX() * -1);
}
notifyConfigChanged();
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotFlipY()
{
ToolTransformArgs *config = m_transaction->currentConfig();
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setScaleY(config->scaleY() * -1);
}
notifyConfigChanged();
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotRotateCW()
{
ToolTransformArgs *config = m_transaction->currentConfig();
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setAZ(normalizeAngle(config->aZ() + M_PI_2));
}
notifyConfigChanged();
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotRotateCCW()
{
ToolTransformArgs *config = m_transaction->currentConfig();
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setAZ(normalizeAngle(config->aZ() - M_PI_2));
}
notifyConfigChanged();
notifyEditingFinished();
}
// change free transform setting we want to alter (all radio buttons call this)
void KisToolTransformConfigWidget::slotTransformAreaVisible(bool value)
{
Q_UNUSED(value);
//QCheckBox sender = (QCheckBox)(*)(QObject::sender());
QString senderName = QObject::sender()->objectName();
// only show setting with what we have selected
rotationGroup->hide();
shearGroup->hide();
scaleGroup->hide();
moveGroup->hide();
if ("freeMoveRadioButton" == senderName)
{
moveGroup->show();
}
else if ("freeShearRadioButton" == senderName)
{
shearGroup->show();
}
else if ("freeScaleRadioButton" == senderName)
{
scaleGroup->show();
}
else
{
rotationGroup->show();
}
}
void KisToolTransformConfigWidget::slotSetKeepAspectRatio(bool value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
config->setKeepAspectRatio(value);
if (value) {
blockNotifications();
int tmpXScaleBox = scaleXBox->value();
int tmpYScaleBox = scaleYBox->value();
m_scaleRatio = (tmpXScaleBox / (double) tmpYScaleBox);
unblockNotifications();
}
notifyConfigChanged();
}
void KisToolTransformConfigWidget::slotSetWarpAlpha(qreal value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
config->setAlpha((double)value);
notifyConfigChanged();
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotSetWarpDensity(int value)
{
if (m_uiSlotsBlocked) return;
setDefaultWarpPoints(value);
}
void KisToolTransformConfigWidget::setDefaultWarpPoints(int pointsPerLine)
{
if (pointsPerLine < 0) {
pointsPerLine = DEFAULT_POINTS_PER_LINE;
}
int nbPoints = pointsPerLine * pointsPerLine;
QVector<QPointF> origPoints(nbPoints);
QVector<QPointF> transfPoints(nbPoints);
qreal gridSpaceX, gridSpaceY;
if (nbPoints == 1) {
//there is actually no grid
origPoints[0] = m_transaction->originalCenterGeometric();
transfPoints[0] = m_transaction->originalCenterGeometric();
}
else if (nbPoints > 1) {
gridSpaceX = m_transaction->originalRect().width() / (pointsPerLine - 1);
gridSpaceY = m_transaction->originalRect().height() / (pointsPerLine - 1);
double y = m_transaction->originalRect().top();
for (int i = 0; i < pointsPerLine; ++i) {
double x = m_transaction->originalRect().left();
for (int j = 0 ; j < pointsPerLine; ++j) {
origPoints[i * pointsPerLine + j] = QPointF(x, y);
transfPoints[i * pointsPerLine + j] = QPointF(x, y);
x += gridSpaceX;
}
y += gridSpaceY;
}
}
ToolTransformArgs *config = m_transaction->currentConfig();
config->setDefaultPoints(nbPoints > 0);
config->setPoints(origPoints, transfPoints);
notifyConfigChanged();
}
void KisToolTransformConfigWidget::activateCustomWarpPoints(bool enabled)
{
ToolTransformArgs *config = m_transaction->currentConfig();
densityBox->setEnabled(!enabled);
customWarpWidget->setEnabled(enabled);
if (!enabled) {
config->setEditingTransformPoints(false);
setDefaultWarpPoints(densityBox->value());
config->setWarpCalculation(KisWarpTransformWorker::WarpCalculation::GRID);
} else {
config->setEditingTransformPoints(true);
config->setWarpCalculation(KisWarpTransformWorker::WarpCalculation::DRAW);
setDefaultWarpPoints(0);
}
updateLockPointsButtonCaption();
}
void KisToolTransformConfigWidget::slotWarpDefaultPointsButtonClicked(bool value)
{
if (m_uiSlotsBlocked) return;
activateCustomWarpPoints(!value);
}
void KisToolTransformConfigWidget::slotWarpCustomPointsButtonClicked(bool value)
{
if (m_uiSlotsBlocked) return;
activateCustomWarpPoints(value);
}
void KisToolTransformConfigWidget::slotWarpResetPointsButtonClicked()
{
if (m_uiSlotsBlocked) return;
activateCustomWarpPoints(true);
}
void KisToolTransformConfigWidget::slotWarpLockPointsButtonClicked()
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
config->setEditingTransformPoints(!config->isEditingTransformPoints());
if (config->isEditingTransformPoints()) {
// reinit the transf points to their original value
ToolTransformArgs *config = m_transaction->currentConfig();
int nbPoints = config->origPoints().size();
for (int i = 0; i < nbPoints; ++i) {
config->transfPoint(i) = config->origPoint(i);
}
}
updateLockPointsButtonCaption();
notifyConfigChanged();
}
void KisToolTransformConfigWidget::slotWarpTypeChanged(int index)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
switch (index) {
case KisWarpTransformWorker::AFFINE_TRANSFORM:
case KisWarpTransformWorker::SIMILITUDE_TRANSFORM:
case KisWarpTransformWorker::RIGID_TRANSFORM:
config->setWarpType((KisWarpTransformWorker::WarpType)index);
break;
default:
config->setWarpType(KisWarpTransformWorker::RIGID_TRANSFORM);
break;
}
notifyConfigChanged();
}
void KisToolTransformConfigWidget::slotCageOptionsChanged(int value)
{
if ( value == 0)
{
slotEditCagePoints(true);
}
else
{
slotEditCagePoints(false);
}
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotEditCagePoints(bool value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
config->refTransformedPoints() = config->origPoints();
config->setEditingTransformPoints(value);
notifyConfigChanged();
}
+
+void KisToolTransformConfigWidget::slotGranularityChanged(QString value)
+{
+ if (m_uiSlotsBlocked) return;
+ KIS_SAFE_ASSERT_RECOVER_RETURN(value.toInt() > 1);
+ ToolTransformArgs *config = m_transaction->currentConfig();
+ config->setPixelPrecision(value.toInt());
+ notifyConfigChanged();
+}
+
+void KisToolTransformConfigWidget::slotPreviewGranularityChanged(QString value)
+{
+ if (m_uiSlotsBlocked) return;
+ KIS_SAFE_ASSERT_RECOVER_RETURN(value.toInt() > 1);
+ ToolTransformArgs *config = m_transaction->currentConfig();
+ config->setPreviewPixelPrecision(value.toInt());
+ notifyConfigChanged();
+}
diff --git a/plugins/tools/tool_transform2/kis_tool_transform_config_widget.h b/plugins/tools/tool_transform2/kis_tool_transform_config_widget.h
index 364cc3f3e1..23304e360b 100644
--- a/plugins/tools/tool_transform2/kis_tool_transform_config_widget.h
+++ b/plugins/tools/tool_transform2/kis_tool_transform_config_widget.h
@@ -1,149 +1,152 @@
/*
* Copyright (c) 2013 Dmitry Kazakov <dimula73@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_TOOL_TRANSFORM_CONFIG_WIDGET_H
#define __KIS_TOOL_TRANSFORM_CONFIG_WIDGET_H
#include "transform_transaction_properties.h"
#include "tool_transform_args.h"
#include "ui_wdg_tool_transform.h"
class KisCanvas2;
class KisToolTransformConfigWidget : public QWidget, private Ui::WdgToolTransform
{
Q_OBJECT
public:
KisToolTransformConfigWidget(TransformTransactionProperties *transaction, KisCanvas2 *canvas, bool workRecursively, QWidget *parent);
void setApplyResetDisabled(bool disabled);
void resetRotationCenterButtons();
void setDefaultWarpPoints(int pointsPerLine = -1);
void setTooBigLabelVisible(bool value);
bool showDecorations() const;
bool workRecursively() const;
public Q_SLOTS:
void updateConfig(const ToolTransformArgs &config);
void slotUpdateIcons();
Q_SIGNALS:
void sigConfigChanged();
void sigApplyTransform();
void sigResetTransform();
void sigRestartTransform();
void sigEditingFinished();
public Q_SLOTS:
void slotFilterChanged(const KoID &filter);
void slotWarpTypeChanged(int index);
void slotRotationCenterChanged(int index);
void slotTransformAroundRotationCenter(bool value);
void slotSetScaleX(int value);
void slotSetScaleY(int value);
void slotSetShearX(qreal value);
void slotSetShearY(qreal value);
void slotSetTranslateX(int value);
void slotSetTranslateY(int value);
void slotSetAX(qreal value);
void slotSetAY(qreal value);
void slotSetAZ(qreal value);
void slotFlipX();
void slotFlipY();
void slotRotateCW();
void slotRotateCCW();
void slotSetWarpAlpha(qreal value);
void slotSetWarpDensity(int value);
void slotSetKeepAspectRatio(bool value);
void slotTransformAreaVisible(bool value);
void slotWarpDefaultPointsButtonClicked(bool value);
void slotWarpCustomPointsButtonClicked(bool value);
void slotWarpLockPointsButtonClicked();
void slotWarpResetPointsButtonClicked();
void slotSetFreeTransformModeButtonClicked(bool);
void slotSetWarpModeButtonClicked(bool);
void slotSetCageModeButtonClicked(bool);
void slotCageOptionsChanged(int);
void slotSetPerspectiveModeButtonClicked(bool);
void slotSetLiquifyModeButtonClicked(bool);
void slotButtonBoxClicked(QAbstractButton *button);
void slotEditCagePoints(bool value);
void liquifySizeChanged(qreal value);
void liquifyAmountChanged(qreal value);
void liquifyFlowChanged(qreal value);
void liquifyBuildUpChanged(int value);
void liquifySpacingChanged(qreal value);
void liquifySizePressureChanged(bool value);
void liquifyAmountPressureChanged(bool value);
void liquifyReverseDirectionChanged(bool value);
void slotLiquifyModeChanged(int value);
void notifyEditingFinished();
+ void slotGranularityChanged(QString value);
+ void slotPreviewGranularityChanged(QString value);
+
private:
// rad being in |R, the returned value is in [0; 360]
double radianToDegree(double rad);
// degree being in |R, the returned value is in [0; 2*M_PI]
double degreeToRadian(double degree);
void blockNotifications();
void unblockNotifications();
void notifyConfigChanged();
void blockUiSlots();
void unblockUiSlots();
void activateCustomWarpPoints(bool enabled);
void updateLockPointsButtonCaption();
void updateLiquifyControls();
void resetUIOptions();
private:
static const int DEFAULT_POINTS_PER_LINE;
private:
TransformTransactionProperties *m_transaction;
QPointF m_handleDir[9];
QButtonGroup *m_rotationCenterButtons;
int m_notificationsBlocked;
int m_uiSlotsBlocked;
double m_scaleRatio;
bool m_configChanged;
};
#endif /* __KIS_TOOL_TRANSFORM_CONFIG_WIDGET_H */
diff --git a/plugins/tools/tool_transform2/kis_transform_utils.cpp b/plugins/tools/tool_transform2/kis_transform_utils.cpp
index a3b2ab9c84..f860e9f3c1 100644
--- a/plugins/tools/tool_transform2/kis_transform_utils.cpp
+++ b/plugins/tools/tool_transform2/kis_transform_utils.cpp
@@ -1,397 +1,397 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_transform_utils.h"
#include <cmath>
#include <QTransform>
#include <KoUnit.h>
#include "tool_transform_args.h"
#include "kis_paint_device.h"
#include "kis_algebra_2d.h"
const int KisTransformUtils::rotationHandleVisualRadius = 12;
const int KisTransformUtils::rotationHandleRadius = 8;
const int KisTransformUtils::handleVisualRadius = 12;
const int KisTransformUtils::handleRadius = 8;
QTransform KisTransformUtils::imageToFlakeTransform(const KisCoordinatesConverter *converter)
{
return converter->imageToDocumentTransform() * converter->documentToFlakeTransform();
}
qreal KisTransformUtils::effectiveHandleGrabRadius(const KisCoordinatesConverter *converter)
{
QPointF handleRadiusPt = flakeToImage(converter, QPointF(handleRadius, handleRadius));
return (handleRadiusPt.x() > handleRadiusPt.y()) ? handleRadiusPt.x() : handleRadiusPt.y();
}
qreal KisTransformUtils::effectiveRotationHandleGrabRadius(const KisCoordinatesConverter *converter)
{
QPointF handleRadiusPt = flakeToImage(converter, QPointF(rotationHandleRadius, rotationHandleRadius));
return (handleRadiusPt.x() > handleRadiusPt.y()) ? handleRadiusPt.x() : handleRadiusPt.y();
}
qreal KisTransformUtils::scaleFromAffineMatrix(const QTransform &t) {
return KoUnit::approxTransformScale(t);
}
qreal KisTransformUtils::scaleFromPerspectiveMatrixX(const QTransform &t, const QPointF &basePt) {
const QPointF pt = basePt + QPointF(1.0, 0);
return kisDistance(t.map(pt), t.map(basePt));
}
qreal KisTransformUtils::scaleFromPerspectiveMatrixY(const QTransform &t, const QPointF &basePt) {
const QPointF pt = basePt + QPointF(0, 1.0);
return kisDistance(t.map(pt), t.map(basePt));
}
qreal KisTransformUtils::effectiveSize(const QRectF &rc) {
return 0.5 * (rc.width() + rc.height());
}
bool KisTransformUtils::thumbnailTooSmall(const QTransform &resultThumbTransform, const QRect &originalImageRect)
{
return KisAlgebra2D::minDimension(resultThumbTransform.mapRect(originalImageRect)) < 32;
}
QRectF handleRectImpl(qreal radius, const QTransform &t, const QRectF &limitingRect, const QPointF &basePoint, qreal *dOutX, qreal *dOutY) {
const qreal handlesExtraScaleX =
KisTransformUtils::scaleFromPerspectiveMatrixX(t, basePoint);
const qreal handlesExtraScaleY =
KisTransformUtils::scaleFromPerspectiveMatrixY(t, basePoint);
const qreal maxD = 0.2 * KisTransformUtils::effectiveSize(limitingRect);
const qreal dX = qMin(maxD, radius / handlesExtraScaleX);
const qreal dY = qMin(maxD, radius / handlesExtraScaleY);
QRectF handleRect(-0.5 * dX, -0.5 * dY, dX, dY);
if (dOutX) {
*dOutX = dX;
}
if (dOutY) {
*dOutY = dY;
}
return handleRect;
}
QRectF KisTransformUtils::handleRect(qreal radius, const QTransform &t, const QRectF &limitingRect, qreal *dOutX, qreal *dOutY) {
return handleRectImpl(radius, t, limitingRect, limitingRect.center(), dOutX, dOutY);
}
QRectF KisTransformUtils::handleRect(qreal radius, const QTransform &t, const QRectF &limitingRect, const QPointF &basePoint) {
return handleRectImpl(radius, t, limitingRect, basePoint, 0, 0);
}
QPointF KisTransformUtils::clipInRect(QPointF p, QRectF r)
{
QPointF center = r.center();
QPointF t = p - center;
r.translate(- center);
if (t.y() != 0) {
if (t.x() != 0) {
double slope = t.y() / t.x();
if (t.x() < r.left()) {
t.setY(r.left() * slope);
t.setX(r.left());
}
else if (t.x() > r.right()) {
t.setY(r.right() * slope);
t.setX(r.right());
}
if (t.y() < r.top()) {
t.setX(r.top() / slope);
t.setY(r.top());
}
else if (t.y() > r.bottom()) {
t.setX(r.bottom() / slope);
t.setY(r.bottom());
}
}
else {
if (t.y() < r.top())
t.setY(r.top());
else if (t.y() > r.bottom())
t.setY(r.bottom());
}
}
else {
if (t.x() < r.left())
t.setX(r.left());
else if (t.x() > r.right())
t.setX(r.right());
}
t += center;
return t;
}
KisTransformUtils::MatricesPack::MatricesPack(const ToolTransformArgs &args)
{
TS = QTransform::fromTranslate(-args.originalCenter().x(), -args.originalCenter().y());
SC = QTransform::fromScale(args.scaleX(), args.scaleY());
S.shear(0, args.shearY()); S.shear(args.shearX(), 0);
if (args.mode() == ToolTransformArgs::FREE_TRANSFORM) {
P.rotate(180. * args.aX() / M_PI, QVector3D(1, 0, 0));
P.rotate(180. * args.aY() / M_PI, QVector3D(0, 1, 0));
P.rotate(180. * args.aZ() / M_PI, QVector3D(0, 0, 1));
projectedP = P.toTransform(args.cameraPos().z());
} else if (args.mode() == ToolTransformArgs::PERSPECTIVE_4POINT) {
projectedP = args.flattenedPerspectiveTransform();
P = QMatrix4x4(projectedP);
}
QPointF translation = args.transformedCenter();
T = QTransform::fromTranslate(translation.x(), translation.y());
}
QTransform KisTransformUtils::MatricesPack::finalTransform() const
{
return TS * SC * S * projectedP * T;
}
bool KisTransformUtils::checkImageTooBig(const QRectF &bounds, const MatricesPack &m)
{
bool imageTooBig = false;
QMatrix4x4 unprojectedMatrix = QMatrix4x4(m.T) * m.P * QMatrix4x4(m.TS * m.SC * m.S);
QVector<QPointF> points;
points << bounds.topLeft();
points << bounds.topRight();
points << bounds.bottomRight();
points << bounds.bottomLeft();
Q_FOREACH (const QPointF &pt, points) {
QVector4D v(pt.x(), pt.y(), 0, 1);
v = unprojectedMatrix * v;
qreal z = v.z() / v.w();
imageTooBig = z > 1024.0;
if (imageTooBig) {
break;
}
}
return imageTooBig;
}
#include <kis_transform_worker.h>
#include <kis_perspectivetransform_worker.h>
#include <kis_warptransform_worker.h>
#include <kis_cage_transform_worker.h>
#include <kis_liquify_transform_worker.h>
KisTransformWorker KisTransformUtils::createTransformWorker(const ToolTransformArgs &config,
KisPaintDeviceSP device,
KoUpdaterPtr updater,
QVector3D *transformedCenter /* OUT */)
{
{
KisTransformWorker t(0,
config.scaleX(), config.scaleY(),
config.shearX(), config.shearY(),
config.originalCenter().x(),
config.originalCenter().y(),
config.aZ(),
0, // set X and Y translation
0, // to null for calculation
0,
config.filter());
*transformedCenter = QVector3D(t.transform().map(config.originalCenter()));
}
QPointF translation = config.transformedCenter() - (*transformedCenter).toPointF();
KisTransformWorker transformWorker(device,
config.scaleX(), config.scaleY(),
config.shearX(), config.shearY(),
config.originalCenter().x(),
config.originalCenter().y(),
config.aZ(),
(int)(translation.x()),
(int)(translation.y()),
updater,
config.filter());
return transformWorker;
}
void KisTransformUtils::transformDevice(const ToolTransformArgs &config,
KisPaintDeviceSP device,
KisProcessingVisitor::ProgressHelper *helper)
{
if (config.mode() == ToolTransformArgs::WARP) {
KoUpdaterPtr updater = helper->updater();
KisWarpTransformWorker worker(config.warpType(),
device,
config.origPoints(),
config.transfPoints(),
config.alpha(),
updater);
worker.run();
} else if (config.mode() == ToolTransformArgs::CAGE) {
KoUpdaterPtr updater = helper->updater();
KisCageTransformWorker worker(device,
config.origPoints(),
updater,
- 8);
+ config.pixelPrecision());
worker.prepareTransform();
worker.setTransformedCage(config.transfPoints());
worker.run();
} else if (config.mode() == ToolTransformArgs::LIQUIFY) {
KoUpdaterPtr updater = helper->updater();
//FIXME:
Q_UNUSED(updater);
config.liquifyWorker()->run(device);
} else {
QVector3D transformedCenter;
KoUpdaterPtr updater1 = helper->updater();
KoUpdaterPtr updater2 = helper->updater();
KisTransformWorker transformWorker =
createTransformWorker(config, device, updater1, &transformedCenter);
transformWorker.run();
if (config.mode() == ToolTransformArgs::FREE_TRANSFORM) {
KisPerspectiveTransformWorker perspectiveWorker(device,
config.transformedCenter(),
config.aX(),
config.aY(),
config.cameraPos().z(),
updater2);
perspectiveWorker.run();
} else if (config.mode() == ToolTransformArgs::PERSPECTIVE_4POINT) {
QTransform T =
QTransform::fromTranslate(config.transformedCenter().x(),
config.transformedCenter().y());
KisPerspectiveTransformWorker perspectiveWorker(device,
T.inverted() * config.flattenedPerspectiveTransform() * T,
updater2);
perspectiveWorker.run();
}
}
}
QRect KisTransformUtils::needRect(const ToolTransformArgs &config,
const QRect &rc,
const QRect &srcBounds)
{
QRect result = rc;
if (config.mode() == ToolTransformArgs::WARP) {
KisWarpTransformWorker worker(config.warpType(),
0,
config.origPoints(),
config.transfPoints(),
config.alpha(),
0);
result = worker.approxNeedRect(rc, srcBounds);
} else if (config.mode() == ToolTransformArgs::CAGE) {
KisCageTransformWorker worker(0,
config.origPoints(),
0,
- 8);
+ config.pixelPrecision());
worker.setTransformedCage(config.transfPoints());
result = worker.approxNeedRect(rc, srcBounds);
} else if (config.mode() == ToolTransformArgs::LIQUIFY) {
result = config.liquifyWorker() ?
config.liquifyWorker()->approxNeedRect(rc, srcBounds) : rc;
} else {
KIS_ASSERT_RECOVER_NOOP(0 && "this works for non-affine transformations only!");
}
return result;
}
QRect KisTransformUtils::changeRect(const ToolTransformArgs &config,
const QRect &rc)
{
QRect result = rc;
if (config.mode() == ToolTransformArgs::WARP) {
KisWarpTransformWorker worker(config.warpType(),
0,
config.origPoints(),
config.transfPoints(),
config.alpha(),
0);
result = worker.approxChangeRect(rc);
} else if (config.mode() == ToolTransformArgs::CAGE) {
KisCageTransformWorker worker(0,
config.origPoints(),
0,
- 8);
+ config.pixelPrecision());
worker.setTransformedCage(config.transfPoints());
result = worker.approxChangeRect(rc);
} else if (config.mode() == ToolTransformArgs::LIQUIFY) {
result = config.liquifyWorker() ?
config.liquifyWorker()->approxChangeRect(rc) : rc;
} else {
KIS_ASSERT_RECOVER_NOOP(0 && "this works for non-affine transformations only!");
}
return result;
}
KisTransformUtils::AnchorHolder::AnchorHolder(bool enabled, ToolTransformArgs *config)
: m_enabled(enabled),
m_config(config)
{
if (!m_enabled) return;
m_staticPoint = m_config->originalCenter() + m_config->rotationCenterOffset();
const KisTransformUtils::MatricesPack m(*m_config);
m_oldStaticPointInView = m.finalTransform().map(m_staticPoint);
}
KisTransformUtils::AnchorHolder::~AnchorHolder() {
if (!m_enabled) return;
const KisTransformUtils::MatricesPack m(*m_config);
const QPointF newStaticPointInView = m.finalTransform().map(m_staticPoint);
const QPointF diff = m_oldStaticPointInView - newStaticPointInView;
m_config->setTransformedCenter(m_config->transformedCenter() + diff);
}
diff --git a/plugins/tools/tool_transform2/tests/test_save_load_transform_args.cpp b/plugins/tools/tool_transform2/tests/test_save_load_transform_args.cpp
index 30eb9d9640..a433155e30 100644
--- a/plugins/tools/tool_transform2/tests/test_save_load_transform_args.cpp
+++ b/plugins/tools/tool_transform2/tests/test_save_load_transform_args.cpp
@@ -1,126 +1,158 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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 "test_save_load_transform_args.h"
#include <QTest>
#include <QDomDocument>
#include "tool_transform_args.h"
#include "kis_liquify_transform_worker.h"
void TestSaveLoadTransformArgs::testFreeTransform()
{
ToolTransformArgs args;
args.setMode(ToolTransformArgs::FREE_TRANSFORM);
args.setTransformedCenter(QPointF(1.0, 2.0));
args.setOriginalCenter(QPointF(2.0, 3.0));
args.setRotationCenterOffset(QPointF(3.0, 4.0));
args.setAX(0.5);
args.setAY(0.6);
args.setAZ(0.7);
args.setCameraPos(QVector3D(7.0, 8.0, 9.0));
args.setScaleX(10.0);
args.setScaleY(11.0);
args.setShearX(10.0);
args.setShearY(11.0);
args.setKeepAspectRatio(true);
args.setFlattenedPerspectiveTransform(QTransform::fromScale(12.0, 13.0));
QDomDocument doc("test_type");
QDomElement root = doc.createElement("root");
doc.appendChild(root);
args.toXML(&root);
ToolTransformArgs newArgs = ToolTransformArgs::fromXML(root);
QCOMPARE(newArgs, args);
}
void TestSaveLoadTransformArgs::testWarp()
{
ToolTransformArgs args;
args.setMode(ToolTransformArgs::WARP);
args.setDefaultPoints(false);
args.refOriginalPoints() << QPointF(1.0, 2.0);
args.refOriginalPoints() << QPointF(2.0, 3.0);
args.refOriginalPoints() << QPointF(3.0, 4.0);
args.refOriginalPoints() << QPointF(4.0, 5.0);
args.refTransformedPoints() << QPointF(6.0, 7.0);
args.refTransformedPoints() << QPointF(7.0, 8.0);
args.refTransformedPoints() << QPointF(8.0, 9.0);
args.refTransformedPoints() << QPointF(9.0, 8.0);
args.setWarpType(KisWarpTransformWorker::RIGID_TRANSFORM);
args.setAlpha(0.5);
QDomDocument doc("test_type");
QDomElement root = doc.createElement("root");
doc.appendChild(root);
args.toXML(&root);
ToolTransformArgs newArgs = ToolTransformArgs::fromXML(root);
QCOMPARE(newArgs, args);
}
+void TestSaveLoadTransformArgs::testCage()
+{
+ ToolTransformArgs args;
+
+ args.setMode(ToolTransformArgs::CAGE);
+ args.setDefaultPoints(false);
+ qDebug() << "Running";
+
+ args.refOriginalPoints() << QPointF(1.0, 2.0);
+ args.refOriginalPoints() << QPointF(2.0, 3.0);
+ args.refOriginalPoints() << QPointF(3.0, 4.0);
+ args.refOriginalPoints() << QPointF(4.0, 5.0);
+
+ args.refTransformedPoints() << QPointF(6.0, 7.0);
+ args.refTransformedPoints() << QPointF(7.0, 8.0);
+ args.refTransformedPoints() << QPointF(8.0, 9.0);
+ args.refTransformedPoints() << QPointF(9.0, 8.0);
+ args.setWarpType(KisWarpTransformWorker::RIGID_TRANSFORM);
+ args.setAlpha(0.5);
+ args.setPixelPrecision(8);
+ args.setPreviewPixelPrecision(16);
+
+ QDomDocument doc("test_type");
+ QDomElement root = doc.createElement("root");
+ doc.appendChild(root);
+
+ args.toXML(&root);
+
+ ToolTransformArgs newArgs = ToolTransformArgs::fromXML(root);
+ QCOMPARE(newArgs, args);
+}
+
void TestSaveLoadTransformArgs::testLiquify()
{
ToolTransformArgs args;
args.setMode(ToolTransformArgs::LIQUIFY);
args.initLiquifyTransformMode(QRect(100, 200, 300, 400));
args.liquifyProperties()->setMode(KisLiquifyProperties::ROTATE);
args.liquifyProperties()->setSize(101);
args.liquifyProperties()->setAmount(0.5);
args.liquifyProperties()->setSpacing(0.87);
args.liquifyProperties()->setSizeHasPressure(true);
args.liquifyProperties()->setAmountHasPressure(true);
args.liquifyProperties()->setReverseDirection(true);
args.liquifyProperties()->setUseWashMode(true);
args.liquifyProperties()->setFlow(0.63);
args.liquifyWorker()->rotatePoints(QPointF(150, 250),
M_PI / 3,
50.0,
false,
1.0);
QDomDocument doc("test_type");
QDomElement root = doc.createElement("root");
doc.appendChild(root);
args.toXML(&root);
ToolTransformArgs newArgs = ToolTransformArgs::fromXML(root);
QCOMPARE(newArgs, args);
}
QTEST_MAIN(TestSaveLoadTransformArgs)
diff --git a/plugins/tools/tool_transform2/tests/test_save_load_transform_args.h b/plugins/tools/tool_transform2/tests/test_save_load_transform_args.h
index 9ad04979c5..f2b8ae5e11 100644
--- a/plugins/tools/tool_transform2/tests/test_save_load_transform_args.h
+++ b/plugins/tools/tool_transform2/tests/test_save_load_transform_args.h
@@ -1,33 +1,34 @@
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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 __TEST_SAVE_LOAD_TRANSFORM_ARGS_H
#define __TEST_SAVE_LOAD_TRANSFORM_ARGS_H
#include <QtTest>
class TestSaveLoadTransformArgs : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testFreeTransform();
void testWarp();
void testLiquify();
+ void testCage();
};
#endif /* __TEST_SAVE_LOAD_TRANSFORM_ARGS_H */
diff --git a/plugins/tools/tool_transform2/tool_transform_args.cc b/plugins/tools/tool_transform2/tool_transform_args.cc
index 74272ce705..20cc237267 100644
--- a/plugins/tools/tool_transform2/tool_transform_args.cc
+++ b/plugins/tools/tool_transform2/tool_transform_args.cc
@@ -1,483 +1,503 @@
/*
* tool_transform_args.h - part of Krita
*
* Copyright (c) 2010 Marc Pegon <pe.marc@free.fr>
*
* 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 "tool_transform_args.h"
#include <QDomElement>
#include <ksharedconfig.h>
#include <kconfig.h>
#include <kconfiggroup.h>
#include "kis_liquify_transform_worker.h"
#include "kis_dom_utils.h"
ToolTransformArgs::ToolTransformArgs()
: m_mode(FREE_TRANSFORM)
, m_defaultPoints(true)
, m_origPoints {QVector<QPointF>()}
, m_transfPoints {QVector<QPointF>()}
, m_warpType(KisWarpTransformWorker::RIGID_TRANSFORM)
, m_alpha(1.0)
, m_transformedCenter(QPointF(0, 0))
, m_originalCenter(QPointF(0, 0))
, m_rotationCenterOffset(QPointF(0, 0))
, m_aX(0)
, m_aY(0)
, m_aZ(0)
, m_scaleX(1.0)
, m_scaleY(1.0)
, m_shearX(0.0)
, m_shearY(0.0)
, m_liquifyProperties(new KisLiquifyProperties())
+ , m_pixelPrecision(8)
+ , m_previewPixelPrecision(16)
{
KConfigGroup configGroup = KSharedConfig::openConfig()->group("KisToolTransform");
QString savedFilterId = configGroup.readEntry("filterId", "Bicubic");
setFilterId(savedFilterId);
m_transformAroundRotationCenter = configGroup.readEntry("transformAroundRotationCenter", "0").toInt();
}
void ToolTransformArgs::setFilterId(const QString &id) {
m_filter = KisFilterStrategyRegistry::instance()->value(id);
if (m_filter) {
KConfigGroup configGroup = KSharedConfig::openConfig()->group("KisToolTransform");
configGroup.writeEntry("filterId", id);
}
}
void ToolTransformArgs::setTransformAroundRotationCenter(bool value)
{
m_transformAroundRotationCenter = value;
KConfigGroup configGroup = KSharedConfig::openConfig()->group("KisToolTransform");
configGroup.writeEntry("transformAroundRotationCenter", int(value));
}
void ToolTransformArgs::init(const ToolTransformArgs& args)
{
m_mode = args.mode();
m_transformedCenter = args.transformedCenter();
m_originalCenter = args.originalCenter();
m_rotationCenterOffset = args.rotationCenterOffset();
m_transformAroundRotationCenter = args.transformAroundRotationCenter();
m_cameraPos = args.m_cameraPos;
m_aX = args.aX();
m_aY = args.aY();
m_aZ = args.aZ();
m_scaleX = args.scaleX();
m_scaleY = args.scaleY();
m_shearX = args.shearX();
m_shearY = args.shearY();
m_origPoints = args.origPoints(); //it's a copy
m_transfPoints = args.transfPoints();
m_warpType = args.warpType();
m_alpha = args.alpha();
m_defaultPoints = args.defaultPoints();
m_keepAspectRatio = args.keepAspectRatio();
m_filter = args.m_filter;
m_flattenedPerspectiveTransform = args.m_flattenedPerspectiveTransform;
m_editTransformPoints = args.m_editTransformPoints;
+ m_pixelPrecision = args.pixelPrecision();
+ m_previewPixelPrecision = args.previewPixelPrecision();
if (args.m_liquifyWorker) {
m_liquifyWorker.reset(new KisLiquifyTransformWorker(*args.m_liquifyWorker.data()));
}
m_continuedTransformation.reset(args.m_continuedTransformation ? new ToolTransformArgs(*args.m_continuedTransformation) : 0);
}
void ToolTransformArgs::clear()
{
m_origPoints.clear();
m_transfPoints.clear();
}
ToolTransformArgs::ToolTransformArgs(const ToolTransformArgs& args)
: m_liquifyProperties(args.m_liquifyProperties)
{
init(args);
}
KisToolChangesTrackerData *ToolTransformArgs::clone() const
{
return new ToolTransformArgs(*this);
}
ToolTransformArgs& ToolTransformArgs::operator=(const ToolTransformArgs& args)
{
clear();
m_liquifyProperties = args.m_liquifyProperties;
init(args);
return *this;
}
bool ToolTransformArgs::operator==(const ToolTransformArgs& other) const
{
return
m_mode == other.m_mode &&
m_defaultPoints == other.m_defaultPoints &&
m_origPoints == other.m_origPoints &&
m_transfPoints == other.m_transfPoints &&
m_warpType == other.m_warpType &&
m_alpha == other.m_alpha &&
m_transformedCenter == other.m_transformedCenter &&
m_originalCenter == other.m_originalCenter &&
m_rotationCenterOffset == other.m_rotationCenterOffset &&
m_transformAroundRotationCenter == other.m_transformAroundRotationCenter &&
m_aX == other.m_aX &&
m_aY == other.m_aY &&
m_aZ == other.m_aZ &&
m_cameraPos == other.m_cameraPos &&
m_scaleX == other.m_scaleX &&
m_scaleY == other.m_scaleY &&
m_shearX == other.m_shearX &&
m_shearY == other.m_shearY &&
m_keepAspectRatio == other.m_keepAspectRatio &&
m_flattenedPerspectiveTransform == other.m_flattenedPerspectiveTransform &&
m_editTransformPoints == other.m_editTransformPoints &&
(m_liquifyProperties == other.m_liquifyProperties ||
*m_liquifyProperties == *other.m_liquifyProperties) &&
// pointer types
((m_filter && other.m_filter &&
m_filter->id() == other.m_filter->id())
|| m_filter == other.m_filter) &&
((m_liquifyWorker && other.m_liquifyWorker &&
*m_liquifyWorker == *other.m_liquifyWorker)
- || m_liquifyWorker == other.m_liquifyWorker);
+ || m_liquifyWorker == other.m_liquifyWorker) &&
+ m_pixelPrecision == other.m_pixelPrecision &&
+ m_previewPixelPrecision == other.m_previewPixelPrecision;
}
bool ToolTransformArgs::isSameMode(const ToolTransformArgs& other) const
{
if (m_mode != other.m_mode) return false;
bool result = true;
if (m_mode == FREE_TRANSFORM) {
result &= m_transformedCenter == other.m_transformedCenter;
result &= m_originalCenter == other.m_originalCenter;
result &= m_scaleX == other.m_scaleX;
result &= m_scaleY == other.m_scaleY;
result &= m_shearX == other.m_shearX;
result &= m_shearY == other.m_shearY;
result &= m_aX == other.m_aX;
result &= m_aY == other.m_aY;
result &= m_aZ == other.m_aZ;
} else if (m_mode == PERSPECTIVE_4POINT) {
result &= m_transformedCenter == other.m_transformedCenter;
result &= m_originalCenter == other.m_originalCenter;
result &= m_scaleX == other.m_scaleX;
result &= m_scaleY == other.m_scaleY;
result &= m_shearX == other.m_shearX;
result &= m_shearY == other.m_shearY;
result &= m_flattenedPerspectiveTransform == other.m_flattenedPerspectiveTransform;
} else if(m_mode == WARP || m_mode == CAGE) {
result &= m_origPoints == other.m_origPoints;
result &= m_transfPoints == other.m_transfPoints;
} else if (m_mode == LIQUIFY) {
result &= m_liquifyProperties &&
(m_liquifyProperties == other.m_liquifyProperties ||
*m_liquifyProperties == *other.m_liquifyProperties);
result &=
(m_liquifyWorker && other.m_liquifyWorker &&
*m_liquifyWorker == *other.m_liquifyWorker)
|| m_liquifyWorker == other.m_liquifyWorker;
} else {
KIS_SAFE_ASSERT_RECOVER_NOOP(0 && "unknown transform mode");
}
return result;
}
ToolTransformArgs::ToolTransformArgs(TransformMode mode,
QPointF transformedCenter,
QPointF originalCenter,
QPointF rotationCenterOffset,
bool transformAroundRotationCenter,
double aX, double aY, double aZ,
double scaleX, double scaleY,
double shearX, double shearY,
KisWarpTransformWorker::WarpType warpType,
double alpha,
bool defaultPoints,
- const QString &filterId)
+ const QString &filterId,
+ int pixelPrecision, int previewPixelPrecision)
: m_mode(mode)
, m_defaultPoints(defaultPoints)
, m_origPoints {QVector<QPointF>()}
, m_transfPoints {QVector<QPointF>()}
, m_warpType(warpType)
, m_alpha(alpha)
, m_transformedCenter(transformedCenter)
, m_originalCenter(originalCenter)
, m_rotationCenterOffset(rotationCenterOffset)
, m_transformAroundRotationCenter(transformAroundRotationCenter)
, m_aX(aX)
, m_aY(aY)
, m_aZ(aZ)
, m_scaleX(scaleX)
, m_scaleY(scaleY)
, m_shearX(shearX)
, m_shearY(shearY)
, m_liquifyProperties(new KisLiquifyProperties())
+ , m_pixelPrecision(pixelPrecision)
+ , m_previewPixelPrecision(previewPixelPrecision)
{
setFilterId(filterId);
}
ToolTransformArgs::~ToolTransformArgs()
{
clear();
}
void ToolTransformArgs::translate(const QPointF &offset)
{
if (m_mode == FREE_TRANSFORM || m_mode == PERSPECTIVE_4POINT) {
m_originalCenter += offset;
m_rotationCenterOffset += offset;
m_transformedCenter += offset;
} else if(m_mode == WARP || m_mode == CAGE) {
for (auto &pt : m_origPoints) {
pt += offset;
}
for (auto &pt : m_transfPoints) {
pt += offset;
}
} else if (m_mode == LIQUIFY) {
KIS_ASSERT_RECOVER_RETURN(m_liquifyWorker);
m_liquifyWorker->translate(offset);
} else {
KIS_ASSERT_RECOVER_NOOP(0 && "unknown transform mode");
}
}
bool ToolTransformArgs::isIdentity() const
{
if (m_mode == FREE_TRANSFORM) {
return (m_transformedCenter == m_originalCenter && m_scaleX == 1
&& m_scaleY == 1 && m_shearX == 0 && m_shearY == 0
&& m_aX == 0 && m_aY == 0 && m_aZ == 0);
} else if (m_mode == PERSPECTIVE_4POINT) {
return (m_transformedCenter == m_originalCenter && m_scaleX == 1
&& m_scaleY == 1 && m_shearX == 0 && m_shearY == 0
&& m_flattenedPerspectiveTransform.isIdentity());
} else if(m_mode == WARP || m_mode == CAGE) {
for (int i = 0; i < m_origPoints.size(); ++i)
if (m_origPoints[i] != m_transfPoints[i])
return false;
return true;
} else if (m_mode == LIQUIFY) {
// Not implemented!
return false;
} else {
KIS_ASSERT_RECOVER_NOOP(0 && "unknown transform mode");
return true;
}
}
void ToolTransformArgs::initLiquifyTransformMode(const QRect &srcRect)
{
m_liquifyWorker.reset(new KisLiquifyTransformWorker(srcRect, 0, 8));
m_liquifyProperties->loadAndResetMode();
}
void ToolTransformArgs::saveLiquifyTransformMode() const
{
m_liquifyProperties->saveMode();
}
void ToolTransformArgs::toXML(QDomElement *e) const
{
e->setAttribute("mode", (int) m_mode);
QDomDocument doc = e->ownerDocument();
if (m_mode == FREE_TRANSFORM || m_mode == PERSPECTIVE_4POINT) {
QDomElement freeEl = doc.createElement("free_transform");
e->appendChild(freeEl);
KisDomUtils::saveValue(&freeEl, "transformedCenter", m_transformedCenter);
KisDomUtils::saveValue(&freeEl, "originalCenter", m_originalCenter);
KisDomUtils::saveValue(&freeEl, "rotationCenterOffset", m_rotationCenterOffset);
KisDomUtils::saveValue(&freeEl, "transformAroundRotationCenter", m_transformAroundRotationCenter);
KisDomUtils::saveValue(&freeEl, "aX", m_aX);
KisDomUtils::saveValue(&freeEl, "aY", m_aY);
KisDomUtils::saveValue(&freeEl, "aZ", m_aZ);
KisDomUtils::saveValue(&freeEl, "cameraPos", m_cameraPos);
KisDomUtils::saveValue(&freeEl, "scaleX", m_scaleX);
KisDomUtils::saveValue(&freeEl, "scaleY", m_scaleY);
KisDomUtils::saveValue(&freeEl, "shearX", m_shearX);
KisDomUtils::saveValue(&freeEl, "shearY", m_shearY);
KisDomUtils::saveValue(&freeEl, "keepAspectRatio", m_keepAspectRatio);
KisDomUtils::saveValue(&freeEl, "flattenedPerspectiveTransform", m_flattenedPerspectiveTransform);
KisDomUtils::saveValue(&freeEl, "filterId", m_filter->id());
} else if (m_mode == WARP || m_mode == CAGE) {
QDomElement warpEl = doc.createElement("warp_transform");
e->appendChild(warpEl);
KisDomUtils::saveValue(&warpEl, "defaultPoints", m_defaultPoints);
KisDomUtils::saveValue(&warpEl, "originalPoints", m_origPoints);
KisDomUtils::saveValue(&warpEl, "transformedPoints", m_transfPoints);
KisDomUtils::saveValue(&warpEl, "warpType", (int)m_warpType); // limited!
KisDomUtils::saveValue(&warpEl, "alpha", m_alpha);
+ if(m_mode == CAGE){
+ KisDomUtils::saveValue(&warpEl,"pixelPrecision",m_pixelPrecision);
+ KisDomUtils::saveValue(&warpEl,"previewPixelPrecision",m_previewPixelPrecision);
+ }
+
} else if (m_mode == LIQUIFY) {
QDomElement liqEl = doc.createElement("liquify_transform");
e->appendChild(liqEl);
m_liquifyProperties->toXML(&liqEl);
m_liquifyWorker->toXML(&liqEl);
} else {
KIS_ASSERT_RECOVER_RETURN(0 && "Unknown transform mode");
}
// m_editTransformPoints should not be saved since it is reset explicitly
}
ToolTransformArgs ToolTransformArgs::fromXML(const QDomElement &e)
{
ToolTransformArgs args;
int newMode = e.attribute("mode", "0").toInt();
if (newMode < 0 || newMode >= N_MODES) return ToolTransformArgs();
args.m_mode = (TransformMode) newMode;
// reset explicitly
args.m_editTransformPoints = false;
bool result = false;
if (args.m_mode == FREE_TRANSFORM || args.m_mode == PERSPECTIVE_4POINT) {
QDomElement freeEl;
QString filterId;
result =
KisDomUtils::findOnlyElement(e, "free_transform", &freeEl) &&
KisDomUtils::loadValue(freeEl, "transformedCenter", &args.m_transformedCenter) &&
KisDomUtils::loadValue(freeEl, "originalCenter", &args.m_originalCenter) &&
KisDomUtils::loadValue(freeEl, "rotationCenterOffset", &args.m_rotationCenterOffset) &&
KisDomUtils::loadValue(freeEl, "transformAroundRotationCenter", &args.m_transformAroundRotationCenter) &&
KisDomUtils::loadValue(freeEl, "aX", &args.m_aX) &&
KisDomUtils::loadValue(freeEl, "aY", &args.m_aY) &&
KisDomUtils::loadValue(freeEl, "aZ", &args.m_aZ) &&
KisDomUtils::loadValue(freeEl, "cameraPos", &args.m_cameraPos) &&
KisDomUtils::loadValue(freeEl, "scaleX", &args.m_scaleX) &&
KisDomUtils::loadValue(freeEl, "scaleY", &args.m_scaleY) &&
KisDomUtils::loadValue(freeEl, "shearX", &args.m_shearX) &&
KisDomUtils::loadValue(freeEl, "shearY", &args.m_shearY) &&
KisDomUtils::loadValue(freeEl, "keepAspectRatio", &args.m_keepAspectRatio) &&
KisDomUtils::loadValue(freeEl, "flattenedPerspectiveTransform", &args.m_flattenedPerspectiveTransform) &&
KisDomUtils::loadValue(freeEl, "filterId", &filterId);
if (result) {
args.m_filter = KisFilterStrategyRegistry::instance()->value(filterId);
result = (bool) args.m_filter;
}
} else if (args.m_mode == WARP || args.m_mode == CAGE) {
QDomElement warpEl;
int warpType = 0;
result =
KisDomUtils::findOnlyElement(e, "warp_transform", &warpEl) &&
KisDomUtils::loadValue(warpEl, "defaultPoints", &args.m_defaultPoints) &&
KisDomUtils::loadValue(warpEl, "originalPoints", &args.m_origPoints) &&
KisDomUtils::loadValue(warpEl, "transformedPoints", &args.m_transfPoints) &&
KisDomUtils::loadValue(warpEl, "warpType", &warpType) &&
KisDomUtils::loadValue(warpEl, "alpha", &args.m_alpha);
+ if(args.m_mode == CAGE){
+ result = result &&
+ KisDomUtils::loadValue(warpEl, "pixelPrecision", &args.m_pixelPrecision) &&
+ KisDomUtils::loadValue(warpEl, "previewPixelPrecision", &args.m_previewPixelPrecision);
+ }
+
if (result && warpType >= 0 && warpType < KisWarpTransformWorker::N_MODES) {
args.m_warpType = (KisWarpTransformWorker::WarpType_) warpType;
} else {
result = false;
}
} else if (args.m_mode == LIQUIFY) {
QDomElement liquifyEl;
result =
KisDomUtils::findOnlyElement(e, "liquify_transform", &liquifyEl);
*args.m_liquifyProperties = KisLiquifyProperties::fromXML(e);
args.m_liquifyWorker.reset(KisLiquifyTransformWorker::fromXML(e));
} else {
KIS_ASSERT_RECOVER_NOOP(0 && "Unknown transform mode");
}
if (!result) {
args = ToolTransformArgs();
}
return args;
}
void ToolTransformArgs::saveContinuedState()
{
m_continuedTransformation.reset();
m_continuedTransformation.reset(new ToolTransformArgs(*this));
}
void ToolTransformArgs::restoreContinuedState()
{
QScopedPointer<ToolTransformArgs> tempTransformation(
new ToolTransformArgs(*m_continuedTransformation));
*this = *tempTransformation;
m_continuedTransformation.swap(tempTransformation);
}
const ToolTransformArgs* ToolTransformArgs::continuedTransform() const
{
return m_continuedTransformation.data();
}
diff --git a/plugins/tools/tool_transform2/tool_transform_args.h b/plugins/tools/tool_transform2/tool_transform_args.h
index 40fb4aed30..9cb1589950 100644
--- a/plugins/tools/tool_transform2/tool_transform_args.h
+++ b/plugins/tools/tool_transform2/tool_transform_args.h
@@ -1,335 +1,356 @@
/*
* tool_transform_args.h - part of Krita
*
* Copyright (c) 2010 Marc Pegon <pe.marc@free.fr>
*
* 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 TOOL_TRANSFORM_ARGS_H_
#define TOOL_TRANSFORM_ARGS_H_
#include <QPointF>
#include <QVector3D>
#include <kis_warptransform_worker.h>
#include <kis_filter_strategy.h>
#include "kis_liquify_properties.h"
#include "kritatooltransform_export.h"
#include "kis_global.h"
#include "KisToolChangesTrackerData.h"
#include <QScopedPointer>
class KisLiquifyTransformWorker;
class QDomElement;
/**
* Class used to store the parameters of a transformation.
* Some parameters are specific to free transform mode, and
* others to warp mode : maybe add a union to save a little more
* memory.
*/
class KRITATOOLTRANSFORM_EXPORT ToolTransformArgs : public KisToolChangesTrackerData
{
public:
enum TransformMode {FREE_TRANSFORM = 0,
WARP,
CAGE,
LIQUIFY,
PERSPECTIVE_4POINT,
N_MODES};
/**
* Initializes the parameters for an identity transformation,
* with mode set to free transform.
*/
ToolTransformArgs();
/**
* The object return will be a copy of args.
*/
ToolTransformArgs(const ToolTransformArgs& args);
KisToolChangesTrackerData *clone() const;
/**
* If mode is warp, original and transformed vector points will be of size 0.
* Use setPoints method to set those vectors.
*/
ToolTransformArgs(TransformMode mode,
QPointF transformedCenter,
QPointF originalCenter,
QPointF rotationCenterOffset, bool transformAroundRotationCenter,
double aX, double aY, double aZ,
double scaleX, double scaleY,
double shearX, double shearY,
KisWarpTransformWorker::WarpType warpType,
double alpha,
bool defaultPoints,
- const QString &filterId);
+ const QString &filterId,
+ int pixelPrecision, int previewPixelPrecision);
~ToolTransformArgs();
ToolTransformArgs& operator=(const ToolTransformArgs& args);
bool operator==(const ToolTransformArgs& other) const;
bool isSameMode(const ToolTransformArgs& other) const;
inline TransformMode mode() const {
return m_mode;
}
inline void setMode(TransformMode mode) {
m_mode = mode;
}
+ inline int pixelPrecision() const {
+ return m_pixelPrecision;
+ }
+
+ inline void setPixelPrecision(int precision) {
+ m_pixelPrecision = precision;
+ }
+
+ inline int previewPixelPrecision() const {
+ return m_previewPixelPrecision;
+ }
+
+ inline void setPreviewPixelPrecision(int precision) {
+ m_previewPixelPrecision = precision;
+ }
+
//warp-related
inline int numPoints() const {
KIS_ASSERT_RECOVER_NOOP(m_origPoints.size() == m_transfPoints.size());
return m_origPoints.size();
}
inline QPointF &origPoint(int i) {
return m_origPoints[i];
}
inline QPointF &transfPoint(int i) {
return m_transfPoints[i];
}
inline const QVector<QPointF> &origPoints() const {
return m_origPoints;
}
inline const QVector<QPointF> &transfPoints() const {
return m_transfPoints;
}
inline QVector<QPointF> &refOriginalPoints() {
return m_origPoints;
}
inline QVector<QPointF> &refTransformedPoints() {
return m_transfPoints;
}
inline KisWarpTransformWorker::WarpType warpType() const {
return m_warpType;
}
inline double alpha() const {
return m_alpha;
}
inline bool defaultPoints() const {
return m_defaultPoints;
}
inline void setPoints(QVector<QPointF> origPoints, QVector<QPointF> transfPoints) {
m_origPoints = QVector<QPointF>(origPoints);
m_transfPoints = QVector<QPointF>(transfPoints);
}
inline void setWarpType(KisWarpTransformWorker::WarpType warpType) {
m_warpType = warpType;
}
inline void setWarpCalculation(KisWarpTransformWorker::WarpCalculation warpCalc) {
m_warpCalculation = warpCalc;
}
inline KisWarpTransformWorker::WarpCalculation warpCalculation() {
return m_warpCalculation;
}
inline void setAlpha(double alpha) {
m_alpha = alpha;
}
inline void setDefaultPoints(bool defaultPoints) {
m_defaultPoints = defaultPoints;
}
//"free transform"-related
inline QPointF transformedCenter() const {
return m_transformedCenter;
}
inline QPointF originalCenter() const {
return m_originalCenter;
}
inline QPointF rotationCenterOffset() const {
return m_rotationCenterOffset;
}
inline bool transformAroundRotationCenter() const {
return m_transformAroundRotationCenter;
}
inline double aX() const {
return m_aX;
}
inline double aY() const {
return m_aY;
}
inline double aZ() const {
return m_aZ;
}
inline QVector3D cameraPos() const {
return m_cameraPos;
}
inline double scaleX() const {
return m_scaleX;
}
inline double scaleY() const {
return m_scaleY;
}
inline bool keepAspectRatio() const {
return m_keepAspectRatio;
}
inline double shearX() const {
return m_shearX;
}
inline double shearY() const {
return m_shearY;
}
inline void setTransformedCenter(QPointF transformedCenter) {
m_transformedCenter = transformedCenter;
}
inline void setOriginalCenter(QPointF originalCenter) {
m_originalCenter = originalCenter;
}
inline void setRotationCenterOffset(QPointF rotationCenterOffset) {
m_rotationCenterOffset = rotationCenterOffset;
}
void setTransformAroundRotationCenter(bool value);
inline void setAX(double aX) {
KIS_ASSERT_RECOVER_NOOP(aX == normalizeAngle(aX));
m_aX = aX;
}
inline void setAY(double aY) {
KIS_ASSERT_RECOVER_NOOP(aY == normalizeAngle(aY));
m_aY = aY;
}
inline void setAZ(double aZ) {
KIS_ASSERT_RECOVER_NOOP(aZ == normalizeAngle(aZ));
m_aZ = aZ;
}
inline void setCameraPos(const QVector3D &pos) {
m_cameraPos = pos;
}
inline void setScaleX(double scaleX) {
m_scaleX = scaleX;
}
inline void setScaleY(double scaleY) {
m_scaleY = scaleY;
}
inline void setKeepAspectRatio(bool value) {
m_keepAspectRatio = value;
}
inline void setShearX(double shearX) {
m_shearX = shearX;
}
inline void setShearY(double shearY) {
m_shearY = shearY;
}
inline QString filterId() const {
return m_filter->id();
}
void setFilterId(const QString &id);
inline KisFilterStrategy* filter() const {
return m_filter;
}
bool isIdentity() const;
inline QTransform flattenedPerspectiveTransform() const {
return m_flattenedPerspectiveTransform;
}
inline void setFlattenedPerspectiveTransform(const QTransform &value) {
m_flattenedPerspectiveTransform = value;
}
bool isEditingTransformPoints() const {
return m_editTransformPoints;
}
void setEditingTransformPoints(bool value) {
m_editTransformPoints = value;
}
const KisLiquifyProperties* liquifyProperties() const {
return m_liquifyProperties.data();
}
KisLiquifyProperties* liquifyProperties() {
return m_liquifyProperties.data();
}
void initLiquifyTransformMode(const QRect &srcRect);
void saveLiquifyTransformMode() const;
KisLiquifyTransformWorker* liquifyWorker() const {
return m_liquifyWorker.data();
}
void toXML(QDomElement *e) const;
static ToolTransformArgs fromXML(const QDomElement &e);
void translate(const QPointF &offset);
void saveContinuedState();
void restoreContinuedState();
const ToolTransformArgs* continuedTransform() const;
private:
void clear();
void init(const ToolTransformArgs& args);
TransformMode m_mode;
// warp-related arguments
// these are basically the arguments taken by the warp transform worker
bool m_defaultPoints; // true : the original points are set to make a grid
// which density is given by numPoints()
QVector<QPointF> m_origPoints;
QVector<QPointF> m_transfPoints;
KisWarpTransformWorker::WarpType m_warpType;
KisWarpTransformWorker::WarpCalculation m_warpCalculation; // DRAW or GRID
double m_alpha;
//'free transform'-related
// basically the arguments taken by the transform worker
QPointF m_transformedCenter;
QPointF m_originalCenter;
QPointF m_rotationCenterOffset; // the position of the rotation center relative to
// the original top left corner of the selection
// before any transformation
bool m_transformAroundRotationCenter; // In freehand mode makes the scaling and other transformations
// be anchored to the rotation center point.
double m_aX;
double m_aY;
double m_aZ;
QVector3D m_cameraPos {QVector3D(0,0,1024)};
double m_scaleX;
double m_scaleY;
double m_shearX;
double m_shearY;
bool m_keepAspectRatio {false};
// perspective trasform related
QTransform m_flattenedPerspectiveTransform;
KisFilterStrategy *m_filter;
bool m_editTransformPoints {false};
QSharedPointer<KisLiquifyProperties> m_liquifyProperties;
QScopedPointer<KisLiquifyTransformWorker> m_liquifyWorker;
/**
* When we continue a transformation, m_continuedTransformation
* stores the initial step of our transform. All cancel and revert
* operations should revert to it.
*/
QScopedPointer<ToolTransformArgs> m_continuedTransformation;
+
+ //PixelPrecision should always be in powers of 2
+ int m_pixelPrecision;
+ int m_previewPixelPrecision;
};
#endif // TOOL_TRANSFORM_ARGS_H_
diff --git a/plugins/tools/tool_transform2/wdg_tool_transform.ui b/plugins/tools/tool_transform2/wdg_tool_transform.ui
index 467d799f89..c9c378769e 100644
--- a/plugins/tools/tool_transform2/wdg_tool_transform.ui
+++ b/plugins/tools/tool_transform2/wdg_tool_transform.ui
@@ -1,2207 +1,2265 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WdgToolTransform</class>
<widget class="QWidget" name="WdgToolTransform">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
- <width>489</width>
- <height>674</height>
+ <width>499</width>
+ <height>751</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
- <property name="spacing">
- <number>0</number>
- </property>
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="bottomMargin">
<number>4</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<property name="leftMargin">
<number>1</number>
</property>
<property name="topMargin">
<number>1</number>
</property>
<property name="rightMargin">
<number>1</number>
</property>
<property name="bottomMargin">
<number>1</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QToolButton" name="freeTransformButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Free</string>
</property>
<property name="text">
<string>Free Transform</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="perspectiveTransformButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Perspective</string>
</property>
<property name="text">
<string>Perspective</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="warpButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Warp</string>
</property>
<property name="text">
<string>Warp</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="cageButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Cage</string>
</property>
<property name="text">
<string>Cage</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="liquifyButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Liquify</string>
</property>
<property name="text">
<string>Liquify</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="lblTransformType">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Free Transform</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<property name="currentIndex">
- <number>0</number>
+ <number>2</number>
</property>
<widget class="QWidget" name="freeTransformWidget">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="spacing">
<number>0</number>
</property>
<property name="bottomMargin">
<number>12</number>
</property>
<item>
<layout class="QGridLayout" name="freeMainOptions">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<property name="leftMargin">
<number>10</number>
</property>
<property name="topMargin">
<number>10</number>
</property>
<property name="rightMargin">
<number>20</number>
</property>
<property name="horizontalSpacing">
<number>10</number>
</property>
<property name="verticalSpacing">
<number>15</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_10">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string>&amp;Filter:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="buddy">
<cstring>cmbFilter</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QGridLayout" name="freeAnchorOriginGroup" rowstretch="0,0,0,0" columnstretch="0,0,0">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="1" column="0">
<widget class="QToolButton" name="topLeftButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="topRightButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QToolButton" name="bottomLeftButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QToolButton" name="centerButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QToolButton" name="middleRightButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QToolButton" name="middleBottomButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QToolButton" name="bottomRightButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QToolButton" name="middleTopButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QToolButton" name="middleLeftButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
<widget class="KisCmbIDList" name="cmbFilter" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QToolButton" name="btnTransformAroundPivotPoint">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Transform around pivot point (Alt)</string>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QRadioButton" name="freeMoveRadioButton">
<property name="text">
<string>Posi&amp;tion</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">freeTransformRadioGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="freeRotationRadioButton">
<property name="text">
<string>&amp;Rotate</string>
</property>
<attribute name="buttonGroup">
<string notr="true">freeTransformRadioGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="freeScaleRadioButton">
<property name="text">
<string>Scale</string>
</property>
<attribute name="buttonGroup">
<string notr="true">freeTransformRadioGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="freeShearRadioButton">
<property name="text">
<string>Shear</string>
</property>
<attribute name="buttonGroup">
<string notr="true">freeTransformRadioGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QLabel" name="tooBigLabelWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="toolTip">
<string extracomment="warning: this string shouldn' be wider, as otherwise the widget would resize and flicker"/>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="text">
<string>off canvas</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::NoTextInteraction</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer0">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="rotationGroup">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>240</width>
<height>100</height>
</size>
</property>
<property name="title">
<string>Rotation</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3" stretch="0">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>7</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="horizontalSpacing">
<number>5</number>
</property>
<property name="verticalSpacing">
<number>5</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_rotateX">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Rotate around X-Axis</string>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>&amp;x:</string>
</property>
<property name="buddy">
<cstring>aXBox</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="KisDoubleSliderSpinBox" name="aXBox" native="true">
<property name="toolTip">
<string>Rotate around X-Axis</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_rotateY">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Rotate around Y-Axis</string>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>&amp;y:</string>
</property>
<property name="buddy">
<cstring>aYBox</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="KisDoubleSliderSpinBox" name="aYBox" native="true">
<property name="toolTip">
<string>Rotate around Y-Axis</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="KisDoubleSliderSpinBox" name="aZBox" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Rotate around Z-Axis</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_rotateZ">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Rotate around Z-Axis</string>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>&amp;z:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>aZBox</cstring>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="scaleGroup">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>240</width>
<height>70</height>
</size>
</property>
<property name="title">
<string>Scale</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<layout class="QFormLayout" name="formLayout_6">
<property name="verticalSpacing">
<number>5</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_width">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Horizontal Scale</string>
</property>
<property name="text">
<string>w&amp;idth:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>scaleXBox</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="KisIntParseSpinBox" name="scaleXBox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Horizontal Scale</string>
</property>
<property name="suffix">
<string> %</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="KisIntParseSpinBox" name="scaleYBox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Vertical Scale</string>
</property>
<property name="suffix">
<string> %</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_height">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Vertical Scale</string>
</property>
<property name="text">
<string>&amp;height:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>scaleYBox</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="KoAspectButton" name="aspectButton" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>20</width>
<height>25</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="moveGroup">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>240</width>
<height>70</height>
</size>
</property>
<property name="title">
<string>Position</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_11">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QFormLayout" name="formLayout_3">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_offsetX">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Horizontal Translation</string>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>x:</string>
</property>
<property name="buddy">
<cstring>translateXBox</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="KisIntParseSpinBox" name="translateXBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Horizontal Translation</string>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="KisIntParseSpinBox" name="translateYBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Vertical Translation</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_offsetY">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Vertical Translation</string>
</property>
<property name="text">
<string>y:</string>
</property>
<property name="buddy">
<cstring>translateYBox</cstring>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="shearGroup">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>240</width>
<height>80</height>
</size>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="title">
<string>Shear</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="flat">
<bool>false</bool>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_12">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QFormLayout" name="formLayout_4">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="verticalSpacing">
<number>8</number>
</property>
<property name="leftMargin">
<number>20</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_shearX">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Horizontal Shear</string>
</property>
<property name="text">
<string>x:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>shearXBox</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="KisDoubleSliderSpinBox" name="shearYBox" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Vertical Shear</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_shearY">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Vertical Shear</string>
</property>
<property name="text">
<string>y:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>shearYBox</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="KisDoubleSliderSpinBox" name="shearXBox" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Horizontal Shear</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer0">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QGroupBox" name="quickTransformGroup">
<layout class="QHBoxLayout" name="quickTransformLayout">
<property name="spacing">
<number>6</number>
</property>
<item>
<widget class="QPushButton" name="flipXButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>32</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Flip selection horizontally</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="flipYButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>32</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Flip selection vertically</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalFlipRotateSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="rotateCCWButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>32</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Rotate selection counter-clockwise 90 degrees</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="rotateCWButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>32</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Rotate the selection clockwise 90 degrees</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer1">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer00">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="warpTransformWidget">
<layout class="QVBoxLayout" name="verticalLayout_10">
<item>
<layout class="QGridLayout" name="gridLayout_4">
<property name="horizontalSpacing">
<number>10</number>
</property>
<property name="verticalSpacing">
<number>0</number>
</property>
<item row="1" column="1">
<widget class="KisDoubleSliderSpinBox" name="alphaBox" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="flexibilityLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>2</width>
<height>0</height>
</size>
</property>
<property name="text">
- <string>&amp;Flexibility:</string>
+ <string>Fle&amp;xibility:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>alphaBox</cstring>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QComboBox" name="cmbWarpType">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="warpTypeLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Anc&amp;hor Strength:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>cmbWarpType</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="title">
<string>Anchor Points</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QRadioButton" name="defaultRadioButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
- <string>Subd&amp;ivide</string>
+ <string>Subdi&amp;vide</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="KisIntParseSpinBox" name="densityBox">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="minimum">
<number>2</number>
</property>
<property name="maximum">
<number>30</number>
</property>
<property name="value">
<number>3</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_8">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QRadioButton" name="customRadioButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Draw</string>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QWidget" name="customWarpWidget" native="true">
<property name="enabled">
<bool>false</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="resetPointsButton">
<property name="text">
<string>Clear Points</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="lockUnlockPointsButton">
<property name="text">
<string>Lock Points</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="cageTransformPage">
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QLabel" name="cageTransformDirections">
<property name="text">
<string>Create 3 points on the canvas to begin</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="cageAddEditRadio">
<property name="text">
<string>Add/Ed&amp;it Anchor Points</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">cageTransformButtonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="cageDeformRadio">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
- <string>De&amp;form Layer</string>
+ <string>Defor&amp;m Layer</string>
</property>
<attribute name="buttonGroup">
<string notr="true">cageTransformButtonGroup</string>
</attribute>
</widget>
</item>
<item>
- <spacer name="verticalSpacer_4">
+ <spacer name="verticalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Minimum</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>17</width>
+ <height>17</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="adjustGranularity">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Adjust Granularity :</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_13">
+ <item>
+ <widget class="QLabel" name="previewGranularity">
+ <property name="text">
+ <string>Preview</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="granularityPreview">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="realGranularity">
+ <property name="text">
+ <string>Real</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="changeGranularity">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
- <height>40</height>
+ <height>10</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="liquifyTransformPage">
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_11" stretch="0,0">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="liquifyOptionsLayout">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<layout class="QVBoxLayout" name="verticalLayout_9">
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>15</number>
</property>
<item>
<widget class="QToolButton" name="liquifyMove">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Move</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="liquifyScale">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Scale</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="liquifyRotate">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Rotate</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="liquifyOffset">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Offset</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="liquifyUndo">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Undo</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_3">
<property name="horizontalSpacing">
<number>3</number>
</property>
<property name="verticalSpacing">
<number>5</number>
</property>
<item row="5" column="0">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>51</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Reverse:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="KisDoubleSliderSpinBox" name="liquifySpacingSlider" native="true"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="lblSpacing">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>49</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Spacing:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="lblFlow">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Flow:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="KisDoubleSliderSpinBox" name="liquifyFlowSlider" native="true"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lblSize">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>27</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Size:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="KisDoubleSliderSpinBox" name="liquifySizeSlider" native="true"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lblAmount">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>51</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Amount:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="KisDoubleSliderSpinBox" name="liquifyAmountSlider" native="true"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>38</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Mode:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="buidupModeComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>Build Up</string>
</property>
</item>
<item>
<property name="text">
<string>Wash</string>
</property>
</item>
</widget>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="liquifyReverseDirectionChk">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="liquifySizePressureBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Pressure</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QToolButton" name="liquifyAmountPressureBox">
<property name="text">
<string>Pressure</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<item>
<widget class="QPushButton" name="showDecorationsBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>32</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Show Decorations</string>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="chkWorkRecursively">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>32</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Work Recursively</string>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>13</width>
<height>13</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Apply|QDialogButtonBox::Reset</set>
</property>
<property name="centerButtons">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QWidget" name="SpecialSpacer" native="true"/>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KisCmbIDList</class>
<extends></extends>
<header>widgets/kis_cmb_idlist.h</header>
</customwidget>
<customwidget>
<class>KoAspectButton</class>
<extends>QWidget</extends>
<header>KoAspectButton.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>KisIntParseSpinBox</class>
<extends>QSpinBox</extends>
<header>kis_int_parse_spin_box.h</header>
</customwidget>
<customwidget>
<class>KisDoubleSliderSpinBox</class>
<extends>QWidget</extends>
<header>kis_slider_spin_box.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
<buttongroups>
<buttongroup name="freeTransformRadioGroup"/>
<buttongroup name="cageTransformButtonGroup"/>
<buttongroup name="buttonGroup"/>
</buttongroups>
</ui>
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000000..8f9693a3da
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,5 @@
+# Setting for various Python tools (code checks, unit tests etc)
+
+[flake8]
+builtins = i18n,Scripter,Application,Krita
+exclude = resources_rc.py